diff --git a/.builds/alpine-x64.yml b/.build.yml similarity index 84% rename from .builds/alpine-x64.yml rename to .build.yml index 1703a3d..22b6766 100644 --- a/.builds/alpine-x64.yml +++ b/.build.yml @@ -1,4 +1,4 @@ -image: alpine/latest +image: alpine/edge packages: - musl-dev - eudev-libs @@ -19,28 +19,22 @@ packages: - json-c-dev - libmpdclient-dev - alsa-lib-dev - - pulseaudio-dev - - pipewire-dev - ttf-dejavu - gcovr - - python3 - - py3-pip - - flex - - bison sources: - https://git.sr.ht/~dnkl/yambar -# triggers: -# - action: email -# condition: failure -# to: +triggers: + - action: email + condition: failure + to: daniel@ekloef.se tasks: - - fcft: | - cd yambar/subprojects - git clone https://codeberg.org/dnkl/fcft.git - cd ../.. + - 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 diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 2436e90..0000000 --- a/.clang-format +++ /dev/null @@ -1,24 +0,0 @@ ---- -BasedOnStyle: GNU -IndentWidth: 4 ---- -Language: Cpp -Standard: Auto -PointerAlignment: Right -ColumnLimit: 120 -BreakBeforeBraces: Custom -BraceWrapping: - AfterEnum: false - AfterClass: false - SplitEmptyFunction: true - AfterFunction: true - AfterStruct: false - -SpaceBeforeParens: ControlStatements -Cpp11BracedListStyle: true - -WhitespaceSensitiveMacros: - - REGISTER_CORE_PARTICLE - - REGISTER_CORE_DECORATION - - REGISTER_CORE_PLUGIN - - REGISTER_CORE_MODULE diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index ef74858..0000000 --- a/.editorconfig +++ /dev/null @@ -1,17 +0,0 @@ -root = true - -[*] -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true -charset = utf-8 -indent_style = space -indent_size = 4 -max_line_length = 70 - -[{meson.build,PKGBUILD}] -indent_size = 2 - -[*.scd] -indent_style = tab -trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore index 6630775..1e67045 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /bld/ /pkg/ /src/ -/subprojects/* -!/subprojects/*.wrap +/subprojects/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..cb34bae --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,94 @@ +image: alpine:edge + +stages: + - info + - build + +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 + - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev + - apk add wayland-dev wayland-protocols wlroots-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add ttf-dejavu + - apk add git + - 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 + script: + - meson --version + - ninja --version + - cc --version + +debug: + stage: build + script: + - apk add gcovr + - mkdir -p bld/debug + - cd bld/debug + - meson --buildtype=debug -Db_coverage=true ../.. + - ninja -k0 + - meson test --print-errorlogs + - ninja coverage-html + - mv meson-logs/coveragereport ../../coverage + - ninja coverage-text + - tail -2 meson-logs/coverage.txt + artifacts: + paths: + - coverage + coverage: '/^TOTAL.*\s+(\d+\%)$/' + +# valgrind: +# stage: build +# script: +# - apk add valgrind +# - mkdir -p bld/debug +# - cd bld/debug +# - meson --buildtype=debug ../.. +# - ninja -k0 +# - meson test --verbose --wrapper "valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=3" + +release: + stage: build + script: + - mkdir -p bld/release + - cd bld/release + - meson --buildtype=minsize ../../ + - ninja -k0 + - meson test --print-errorlogs + +x11_only: + stage: build + script: + - mkdir -p bld/debug + - cd bld/debug + - meson --buildtype=debug -Dbackend-x11=enabled -Dbackend-wayland=disabled ../../ + - ninja -k0 + - meson test --print-errorlogs + +wayland_only: + stage: build + script: + - mkdir -p bld/debug + - cd bld/debug + - meson --buildtype=debug -Dbackend-x11=disabled -Dbackend-wayland=enabled ../../ + - ninja -k0 + - meson test --print-errorlogs + +plugins_as_shared_modules: + stage: build + script: + - mkdir -p bld/debug + - cd bld/debug + - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ + - ninja -k0 + - meson test --print-errorlogs diff --git a/.woodpecker.yaml b/.woodpecker.yaml deleted file mode 100644 index e7c9151..0000000 --- a/.woodpecker.yaml +++ /dev/null @@ -1,132 +0,0 @@ -steps: - - name: codespell - when: - - event: [manual, pull_request] - - event: [push, tag] - branch: [master, releases/*] - image: alpine:latest - commands: - - apk add openssl - - apk add python3 - - apk add py3-pip - - python3 -m venv codespell-venv - - source codespell-venv/bin/activate - - pip install codespell - - codespell README.md CHANGELOG.md *.c *.h doc/*.scd bar decorations modules particles examples - - deactivate - - - name: subprojects - when: - - event: [manual, pull_request] - - event: [push, tag] - branch: [master, releases/*] - image: alpine:latest - commands: - - apk add git - - mkdir -p subprojects && cd subprojects - - git clone https://codeberg.org/dnkl/tllist.git - - git clone https://codeberg.org/dnkl/fcft.git - - cd .. - - - name: x64 - when: - - event: [manual, pull_request] - - event: [push, tag] - branch: [master, releases/*] - depends_on: [subprojects] - image: alpine:latest - commands: - - apk update - - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc - - apk add pixman-dev freetype-dev fontconfig-dev - - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev pipewire-dev - - apk add ttf-dejavu - - apk add git - - apk add flex bison - - # Debug - - apk add gcovr - - mkdir -p bld/debug-x64 - - cd bld/debug-x64 - - meson --buildtype=debug -Db_coverage=true ../.. - - ninja -k0 - - meson test --print-errorlogs - - ninja coverage-html - - mv meson-logs/coveragereport ../../coverage - - ninja coverage-text - - tail -2 meson-logs/coverage.txt - - ./yambar --version - - cd ../.. - - # Release - - mkdir -p bld/release-x64 - - cd bld/release-x64 - - meson --buildtype=minsize ../../ - - ninja -k0 - - meson test --print-errorlogs - - ./yambar --version - - cd ../.. - - # X11 only - - mkdir -p bld/x11-only - - cd bld/x11-only - - meson --buildtype=debug -Dbackend-x11=enabled -Dbackend-wayland=disabled ../../ - - ninja -k0 - - meson test --print-errorlogs - - ./yambar --version - - cd ../.. - - # Wayland only - - mkdir -p bld/wayland-only - - cd bld/wayland-only - - meson --buildtype=debug -Dbackend-x11=disabled -Dbackend-wayland=enabled ../../ - - ninja -k0 - - meson test --print-errorlogs - - ./yambar --version - - cd ../.. - - - name: x86 - when: - - event: [manual, pull_request] - - event: [push, tag] - branch: [master, releases/*] - depends_on: [subprojects] - image: i386/alpine:latest - commands: - - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc - - apk add pixman-dev freetype-dev fontconfig-dev - - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev - - apk add wayland-dev wayland-protocols wlroots-dev - - apk add json-c-dev libmpdclient-dev alsa-lib-dev pulseaudio-dev pipewire-dev - - apk add ttf-dejavu - - apk add git - - apk add flex bison - - # Debug - - mkdir -p bld/debug-x86 - - cd bld/debug-x86 - - meson --buildtype=debug ../../ - - ninja -k0 - - meson test --print-errorlogs - - ./yambar --version - - cd ../.. - - # Release - - mkdir -p bld/release-x86 - - cd bld/release-x86 - - meson --buildtype=minsize ../../ - - ninja -k0 - - meson test --print-errorlogs - - ./yambar --version - - cd ../.. - - # Plugins as shared modules - - mkdir -p bld/shared-modules - - cd bld/shared-modules - - meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../ - - ninja -k0 - - meson test --print-errorlogs - - ./yambar --version - - cd ../.. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e0e772..c7d7c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,550 +1,17 @@ # Changelog -* [Unreleased](#unreleased) -* [1.11.0](#1-11-0) -* [1.10.0](#1-10-0) -* [1.9.0](#1-9-0) -* [1.8.0](#1-8-0) -* [1.7.0](#1-7-0) -* [1.6.2](#1-6-2) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) -## Unreleased -### Added - -* environment variable substitution in config files ([#96][96]). -* Log output now respects the [`NO_COLOR`](http://no-color.org/) - environment variable. -* network: `type` tag ([#380][380]). -* network: `type` and `kind` tags ([#380][380]). -* tags: `/` tag formatter: divides the tag's decimal value with `N` - ([#392][392]). -* i3/sway: `output` tag, reflecting the output (monitor) a workspace - is on. -* Added "string like" `~~` operator to Map particle. Allows glob-style - matching on strings using `*` and `?` characters ([#400][400]). -* Added "single" mode flag to the `mpd` module ([#428][428]). -* niri: add a new module for niri-workspaces and niri-language - ([#404][404]). -* pipewire: added `spacing`, `left-spacing` and `right-spacing` - attributes. -* mpris: new module ([#53][53]). - -[96]: https://codeberg.org/dnkl/yambar/issues/96 -[380]: https://codeberg.org/dnkl/yambar/issues/380 -[392]: https://codeberg.org/dnkl/yambar/issues/392 -[400]: https://codeberg.org/dnkl/yambar/pulls/400 -[428]: https://codeberg.org/dnkl/yambar/pulls/428 -[404]: https://codeberg.org/dnkl/yambar/issues/404 -[53]: https://codeberg.org/dnkl/yambar/issues/53 - - -### Changed - -* `river`: expand to an empty list of particles when river is not - running ([#384][384]). - -[384]: https://codeberg.org/dnkl/yambar/issues/384 - - -### Deprecated -### Removed -### Fixed - -* network: fix missing break in switch statement ([#377][377]). -* i3/sway: crash when output is turned off an on ([#300][300]). -* mpd: yambar never attempting to reconnect after MPD closed the - connection (for example, when MPD is restarted). -* Bar positioning on multi-monitor setups, when `location=bottom`. -* pipewire: Improve handling of node switching ([#424][424]). - -[377]: https://codeberg.org/dnkl/yambar/issues/377 -[300]: https://codeberg.org/dnkl/yambar/issues/300 -[424]: https://codeberg.org/dnkl/yambar/pulls/424 - - -### Security -### Contributors - - -## 1.11.0 - -### Added - -* battery: current smoothing, for improved discharge estimates. -* battery: scale option, for batteries that report 'charge' at a - different scale than 'current'. -* network: new `quality` tag (Wi-Fi only). -* Read alternative config from pipes and FIFOs (e.g. `--config - /dev/stdin`) ([#340][340]). -* Added `overlay` and `background` as possible `layer` values - ([#372][372]). - -[340]: https://codeberg.org/dnkl/yambar/pulls/340 -[372]: https://codeberg.org/dnkl/yambar/issues/372 - - -### Changed - -* log-level: default to `warning` -* network: use dynlist instead of fixed name ([#355][355]) - -[355]: https://codeberg.org/dnkl/yambar/pulls/355 - - -### Fixed - -* Compiler error _‘fmt’ may be used uninitialized_ ([#311][311]). -* map: conditions failing to match when they contain multiple, quoted - tag values ([#302][302]). -* Crash when hidden by an opaque window. -* Bar not resizing itself when the screen resolution is changed - ([#330][330]). -* i3/sway: incorrect empty/title state of workspaces ([#343][343]). -* mem: state updated on each bar redraw ([#352][352]). -* script: buffer overflow when reading large amounts of data. -* i3/sway: module fails when reloading config file ([#361][361]). -* Worked around bug in gcc causing a compilation error ([#350][350]). -* Miscalculation of list width in presence of empty particles ([#369][369]). -* Log-level not respected by syslog. - -[311]: https://codeberg.org/dnkl/yambar/issues/311 -[302]: https://codeberg.org/dnkl/yambar/issues/302 -[330]: https://codeberg.org/dnkl/yambar/issues/330 -[343]: https://codeberg.org/dnkl/yambar/issues/343 -[352]: https://codeberg.org/dnkl/yambar/issues/352 -[361]: https://codeberg.org/dnkl/yambar/issues/361 -[350]: https://codeberg.org/dnkl/yambar/issues/350 -[369]: https://codeberg.org/dnkl/yambar/issues/369 - - -### Contributors - -* Delgan -* Haden Collins -* Jordan Isaacs -* kotyk -* Leonardo Hernández Hernández -* oob -* rdbo -* Sertonix -* steovd -* Väinö Mäkelä -* Yiyu Zhou - - -## 1.10.0 - -### Added - -* Field width tag format option ([#246][246]) -* river: support for ‘layout’ events. -* dwl: support for specifying name of tags ([#256][256]) -* i3/sway: extend option `sort`; use `native` to sort numbered workspaces only. -* modules/dwl: handle the appid status ([#284][284]) -* battery: also show estimation for time to full ([#303][303]). -* on-click: tilde expansion ([#307][307]) -* script: tilde expansion of `path` ([#307][307]). - -[246]: https://codeberg.org/dnkl/yambar/issues/246 -[256]: https://codeberg.org/dnkl/yambar/pulls/256 -[284]: https://codeberg.org/dnkl/yambar/pulls/284 -[307]: https://codeberg.org/dnkl/yambar/issues/307 - - -### Changed - -* disk-io: `interval` renamed to `poll-interval` -* mem: `interval` renamed to `poll-interval` -* battery/network/script: `poll-interval` unit changed from seconds to - milliseconds ([#244][244]). -* all modules: minimum poll interval changed from 500ms to 250ms. -* network: do not use IPv6 link-local ([#281][281]) - -[244]: https://codeberg.org/dnkl/yambar/issues/244 -[281]: https://codeberg.org/dnkl/yambar/pulls/281 - - -### Fixed - -* Build failures for certain combinations of enabled and disabled - plugins ([#239][239]). -* Documentation for the `cpu` module; `interval` has been renamed to - `poll-interval` ([#241][241]). -* battery: module was not thread safe. -* dwl module reporting only the last part of the title ([#251][251]) -* i3/sway: regression; persistent workspaces shown twice - ([#253][253]). -* pipewire: use `roundf()` instead of `ceilf()` for more accuracy - ([#262][262]) -* Crash when a yaml anchor has a value that already exists in the - target yaml node ([#286][286]). -* battery: Fix time conversion in battery estimation ([#303][303]). -* battery: poll timeout being reset when receiving irrelevant udev - notification (leading to battery status never updating, in worst - case) ([#305][305]). - -[239]: https://codeberg.org/dnkl/yambar/issues/239 -[241]: https://codeberg.org/dnkl/yambar/issues/241 -[251]: https://codeberg.org/dnkl/yambar/pulls/251 -[253]: https://codeberg.org/dnkl/yambar/issues/253 -[262]: https://codeberg.org/dnkl/yambar/issues/262 -[286]: https://codeberg.org/dnkl/yambar/issues/286 -[305]: https://codeberg.org/dnkl/yambar/issues/305 - - -### Contributors - -* Leonardo Gibrowski Faé (Horus) -* Armin Fisslthaler -* Ben Brown -* David Bimmler -* Leonardo Hernández Hernández -* Ogromny -* Oleg Hahm -* Stanislav Ochotnický -* tiosgz -* Yutaro Ohno - - -## 1.9.0 - -### Added - -* Support for specifying number of decimals when printing a float tag - ([#200][200]). -* Support for custom font fallbacks ([#153][153]). -* overline: new decoration ([#153][153]). -* i3/sway: boolean option `strip-workspace-numbers`. -* font-shaping: new inheritable configuration option, allowing you to - configure whether strings should be _shaped_ using HarfBuzz, or not - ([#159][159]). -* river: support for the new “mode” event present in version 3 of the - river status manager protocol, in the form of a new tag, _”mode”_, - in the `title` particle. -* network: request link stats and expose under tags `dl-speed` and - `ul-speed` when `poll-interval` is set. -* new module: disk-io. -* new module: pulse ([#223][223]). -* alsa: `dB` tag ([#202][202]). -* mpd: `file` tag ([#219][219]). -* pipewire: add a new module for pipewire ([#224][224]) -* on-click: support `next`/`previous` mouse buttons ([#228][228]). -* dwl: add a new module for DWL ([#218][218]) -* sway: support for workspace ‘rename’ and ‘move’ events - ([#216][216]). - -[153]: https://codeberg.org/dnkl/yambar/issues/153 -[159]: https://codeberg.org/dnkl/yambar/issues/159 -[200]: https://codeberg.org/dnkl/yambar/issues/200 -[202]: https://codeberg.org/dnkl/yambar/issues/202 -[218]: https://codeberg.org/dnkl/yambar/pulls/218 -[219]: https://codeberg.org/dnkl/yambar/pulls/219 -[223]: https://codeberg.org/dnkl/yambar/pulls/223 -[224]: https://codeberg.org/dnkl/yambar/pulls/224 -[228]: https://codeberg.org/dnkl/yambar/pulls/228 -[216]: https://codeberg.org/dnkl/yambar/issues/216 - - -### Changed - -* All modules are now compile-time optional. -* Minimum required meson version is now 0.59. -* Float tags are now treated as floats instead of integers when - formatted with the `kb`/`kib`/`mb`/`mib`/`gb`/`gib` string particle - formatters. -* network: `tx-bitrate` and `rx-bitrate` are now in bits/s instead of - Mb/s. Use the `mb` string formatter to render these tags as before - (e.g. `string: {text: "{tx-bitrate:mb}"}`). -* i3: newly created, and **unfocused** workspaces are now considered - non-empty ([#191][191]) -* alsa: use dB instead of raw volume values, if possible, when - calculating the `percent` tag ([#202][202]) -* cpu: `content` particle is now a template instantiated once for each - core, and once for the total CPU usage. See - **yambar-modules-cpu**(5) for more information ([#207][207]). -* **BREAKING CHANGE**: overhaul of the `map` particle. Instead of - specifying a `tag` and then an array of `values`, you must now - simply use an array of `conditions`, that consist of: - - ` ` - - where `` is one of: - - `== != < <= > >=` - - Note that boolean tags must be used as is: - - `online` - - `~online # use '~' to match for their falsehood` - - As an example, if you previously had something like: - - ``` - map: - tag: State - values: - unrecognized: - ... - ``` - - You would now write it as: - - ``` - map: - conditions: - State == unrecognized: - ... - ``` - - Note that if `` contains any non-alphanumerical characters, - it **must** be surrounded by `""`: - - `State == "very confused!!!"` - - Finally, you can mix and match conditions using the boolean - operators `&&` and `||`: - - ``` - && - && ( || ) # parenthesis work - ~( && ) # '~' can be applied to any condition - ``` - - For a more thorough explanation, see the updated map section in the - man page for yambar-particles([#137][137], [#175][175] and [#][182]). - -[137]: https://codeberg.org/dnkl/yambar/issues/137 -[175]: https://codeberg.org/dnkl/yambar/issues/172 -[182]: https://codeberg.org/dnkl/yambar/issues/182 -[191]: https://codeberg.org/dnkl/yambar/issues/191 -[202]: https://codeberg.org/dnkl/yambar/issues/202 -[207]: https://codeberg.org/dnkl/yambar/issues/207 - - -### Fixed - -* i3: fixed “missing workspace indicator” (_err: modules/i3.c:94: - workspace reply/event without 'name' and/or 'output', and/or 'focus' - properties_). -* Slow/laggy behavior when quickly spawning many `on-click` handlers, - e.g. when handling mouse wheel events ([#169][169]). -* cpu: don’t error out on systems where SMT has been disabled - ([#172][172]). -* examples/dwl-tags: updated parsing of `output` name ([#178][178]). -* sway-xkb: don’t crash when Sway sends an _”added”_ event for a - device yambar is already tracking ([#177][177]). -* Crash when a particle is “too wide”, and tries to render outside the - bar ([#198][198]). -* string: crash when failing to convert string to UTF-32. -* script: only first transaction processed when receiving multiple - transactions in a single batch ([#221][221]). -* network: missing SSID (recent kernels, or possibly wireless drivers, - no longer provide the SSID in the `NL80211_CMD_NEW_STATION` - response) ([#226][226]). -* sway-xkb: crash when compositor presents multiple inputs with - identical IDs ([#229][229]). - -[169]: https://codeberg.org/dnkl/yambar/issues/169 -[172]: https://codeberg.org/dnkl/yambar/issues/172 -[178]: https://codeberg.org/dnkl/yambar/issues/178 -[177]: https://codeberg.org/dnkl/yambar/issues/177 -[198]: https://codeberg.org/dnkl/yambar/issues/198 -[221]: https://codeberg.org/dnkl/yambar/issues/221 -[226]: https://codeberg.org/dnkl/yambar/issues/226 -[229]: https://codeberg.org/dnkl/yambar/issues/229 - - -### Contributors - -* Baptiste Daroussin -* Horus -* Johannes -* Leonardo Gibrowski Faé -* Leonardo Neumann -* Midgard -* Ogromny -* Peter Rice -* Timur Celik -* Willem van de Krol -* hiog - - -## 1.8.0 - -### Added - -* ramp: can now have custom min and max values - ([#103](https://codeberg.org/dnkl/yambar/issues/103)). -* border: new decoration. -* i3/sway: new boolean tag: `empty` - ([#139](https://codeberg.org/dnkl/yambar/issues/139)). -* mem: a module handling system memory monitoring -* cpu: a module offering cpu usage monitoring -* removables: support for audio CDs - ([#146](https://codeberg.org/dnkl/yambar/issues/146)). -* removables: new boolean tag: `audio`. - - -### Changed - -* fcft >= 3.0 is now required. -* Made `libmpdclient` an optional dependency -* battery: unknown battery states are now mapped to ‘unknown’, instead - of ‘discharging’. -* Wayland: the bar no longer exits when the monitor is - disabled/unplugged ([#106](https://codeberg.org/dnkl/yambar/issues/106)). - - -### Fixed - -* `left-margin` and `right-margin` from being rejected as invalid - options. -* Crash when `udev_monitor_receive_device()` returned `NULL`. This - affected the “backlight”, “battery” and “removables” modules - ([#109](https://codeberg.org/dnkl/yambar/issues/109)). -* foreign-toplevel: update bar when a top-level is closed. -* Bar not being mapped on an output before at least one module has - “refreshed” it ([#116](https://codeberg.org/dnkl/yambar/issues/116)). -* network: failure to retrieve wireless attributes (SSID, RX/TX - bitrate, signal strength etc). -* Integer options that were supposed to be >= 0 were incorrectly - allowed, leading to various bad things; including yambar crashing, - or worse, the compositor crashing - ([#129](https://codeberg.org/dnkl/yambar/issues/129)). -* kib/kb, mib/mb and gib/gb formatters were inverted. - - -### Contributors - -* [sochotnicky](https://codeberg.org/sochotnicky) -* Alexandre Acebedo -* anb -* Baptiste Daroussin -* Catterwocky -* horus645 -* Jan Beich -* mz -* natemaia -* nogerine -* Soc Virnyl S. Estela -* Vincent Fischer - - -## 1.7.0 - -### Added - -* i3: `persistent` attribute, allowing persistent workspaces - ([#72](https://codeberg.org/dnkl/yambar/issues/72)). -* bar: `border.{left,right,top,bottom}-width`, allowing the width of - each side of the border to be configured - individually. `border.width` is now a short-hand for setting all - four borders to the same value - ([#77](https://codeberg.org/dnkl/yambar/issues/77)). -* bar: `layer: top|bottom`, allowing the layer which the bar is - rendered on to be changed. Wayland only - ignored on X11. -* river: `all-monitors: false|true`. -* `-d,--log-level=info|warning|error|none` command line option - ([#84](https://codeberg.org/dnkl/yambar/issues/84)). -* river: support for the river-status protocol, version 2 (‘urgent’ - views). -* `online` tag to the `alsa` module. -* alsa: `volume` and `muted` options, allowing you to configure which - channels to use as source for the volume level and muted state. -* foreign-toplevel: Wayland module that provides information about - currently opened windows. -* alsa: support for capture devices. -* network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags. -* network: `poll-interval` option (for the new `signal` and - `*-bitrate` tags). -* tags: percentage tag formatter, for range tags: `{tag_name:%}`. -* tags: kb/mb/gb, and kib/mib/gib tag formatters. -* clock: add a config option to show UTC time. - -### Changed - -* bar: do not add `spacing` around empty (zero-width) modules. -* alsa: do not error out if we fail to connect to the ALSA device, or - if we get disconnected. Instead, keep retrying until we succeed - ([#86](https://codeberg.org/dnkl/yambar/issues/86)). - - -### Fixed - -* `yambar --backend=wayland` always erroring out with _”yambar was - compiled without the Wayland backend”_. -* Regression: `{where}` tag not being expanded in progress-bar - `on-click` handlers. -* `alsa` module causing yambar to use 100% CPU if the ALSA device is - disconnected ([#61](https://codeberg.org/dnkl/yambar/issues/61)). - - -### Contributors - -* [paemuri](https://codeberg.org/paemuri) -* [ericonr](https://codeberg.org/ericonr) -* [Nulo](https://nulo.in) - - -## 1.6.2 - -### Added - -* Text shaping support. -* Support for middle and right mouse buttons, mouse wheel and trackpad - scrolling ([#39](https://codeberg.org/dnkl/yambar/issues/39)). -* script: polling mode. See the new `poll-interval` option - ([#67](https://codeberg.org/dnkl/yambar/issues/67)). - - -### Changed - -* doc: split up **yambar-modules**(5) into multiple man pages, one for - each module ([#15](https://codeberg.org/dnkl/yambar/issues/15)). -* fcft >= 2.4.0 is now required. -* sway-xkb: non-keyboard inputs are now ignored - ([#51](https://codeberg.org/dnkl/yambar/issues/51)). -* battery: don’t terminate (causing last status to “freeze”) when - failing to update; retry again later - ([#44](https://codeberg.org/dnkl/yambar/issues/44)). -* battery: differentiate "Not Charging" and "Discharging" in state - tag of battery module. - ([#57](https://codeberg.org/dnkl/yambar/issues/57)). -* string: use HORIZONTAL ELLIPSIS instead of three regular periods - when truncating a string - ([#73](https://codeberg.org/dnkl/yambar/issues/73)). - - -### Fixed - -* Crash when merging non-dictionary anchors in the YAML configuration - ([#32](https://codeberg.org/dnkl/yambar/issues/32)). -* Crash in the `ramp` particle when the tag’s value was out-of-bounds - ([#45](https://codeberg.org/dnkl/yambar/issues/45)). -* Crash when a string particle contained `{}` - ([#48](https://codeberg.org/dnkl/yambar/issues/48)). -* `script` module rejecting range tag end values containing the digit - `9` ([#60](https://codeberg.org/dnkl/yambar/issues/60)). - - -### Contributors - -* [novakane](https://codeberg.org/novakane) -* [mz](https://codeberg.org/mz) - - ## 1.6.1 ### Changed * i3: workspaces with numerical names are sorted separately from non-numerically named workspaces - ([#30](https://codeberg.org/dnkl/yambar/issues/30)). + (https://codeberg.org/dnkl/yambar/issues/30). ### Fixed @@ -552,7 +19,7 @@ * mpd: `elapsed` tag not working (regression, introduced in 1.6.0). * Wrong background color for (semi-) transparent backgrounds. * battery: stats sometimes getting stuck at 0, or impossibly large - values ([#25](https://codeberg.org/dnkl/yambar/issues/25)). + values (https://codeberg.org/dnkl/yambar/issues/25). ## 1.6.0 @@ -561,17 +28,17 @@ * alsa: `percent` tag. This is an integer tag that represents the current volume as a percentage value - ([#10](https://codeberg.org/dnkl/yambar/issues/10)). + (https://codeberg.org/dnkl/yambar/issues/10). * river: added documentation - ([#9](https://codeberg.org/dnkl/yambar/issues/9)). + (https://codeberg.org/dnkl/yambar/issues/9). * script: new module, adds support for custom user scripts - ([#11](https://codeberg.org/dnkl/yambar/issues/11)). + (https://codeberg.org/dnkl/yambar/issues/11). * mpd: `volume` tag. This is a range tag that represents MPD's current volume in percentage (0-100) * i3: `sort` configuration option, that controls how the workspace list is sorted. Can be set to one of `none`, `ascending` or `descending`. Default is `none` - ([#17](https://codeberg.org/dnkl/yambar/issues/17)). + (https://codeberg.org/dnkl/yambar/issues/17). * i3: `mode` tag: the name of the currently active mode @@ -581,12 +48,12 @@ error”_. * Memory leak when a YAML parsing error was encountered. * clock: update every second when necessary - ([#12](https://codeberg.org/dnkl/yambar/issues/12)). + (https://codeberg.org/dnkl/yambar/issues/12). * mpd: fix compilation with clang - ([#16](https://codeberg.org/dnkl/yambar/issues/16)). + (https://codeberg.org/dnkl/yambar/issues/16). * Crash when the alpha component in a color value was 0. * XCB: Fallback to non-primary monitor when the primary monitor is - disconnected ([#20](https://codeberg.org/dnkl/yambar/issues/20)) + disconnected (https://codeberg.org/dnkl/yambar/issues/20) ### Contributors diff --git a/PKGBUILD b/PKGBUILD index 6fc2d69..12e6642 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,8 +1,7 @@ pkgname=yambar -pkgver=1.11.0 +pkgver=1.6.1 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" -changelog=CHANGELOG.md arch=('x86_64' 'aarch64') url=https://codeberg.org/dnkl/yambar license=(mit) @@ -16,9 +15,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'libpulse' - 'pipewire' - 'fcft>=3.0.0' 'fcft<4.0.0') + 'fcft>=2.0.0') optdepends=('xcb-util-errors: better X error messages') source=() diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 266fc06..10a44ea 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.11.0 +pkgver=1.6.1 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for Wayland" arch=('x86_64' 'aarch64') @@ -16,11 +16,8 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'libpulse' - 'pipewire' - 'fcft>=3.0.0' 'fcft<4.0.0') + 'fcft>=2.0.0') source=() -changelog=CHANGELOG.md pkgver() { cd ../.git &> /dev/null && git describe --tags --long | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || diff --git a/README.md b/README.md index 4e825b3..8052784 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,5 @@ -[![CI status](https://ci.codeberg.org/api/badges/dnkl/yambar/status.svg)](https://ci.codeberg.org/dnkl/yambar) - # Yambar -**This project is not developed anymore, and this repository will be -archived in a near future**. - -I do not have neither the time nor the will to work on this -anymore. Mainly because I do not use a bar myself anymore. - -There are also technical difficulties, caused by the fact that yambar -was initially X11, and only later was Wayland support added. - -This means I have to maintain a backend (X11) I have not used myself -in many years, as well as trying to work around technical limitations -imposed by the way both X11 and Wayland is supported, As my own use of -a bar has dwindled, the will to refactor and improve the backends has -disappeared. - -Yambar has seen a lot of contributions, for which I am very -grateful. I hope that means someone is willing to pick up where I left -of, and continue working on yambar. If not, we at least had a good -run; the first commit was in late 2018, roughly 6½ years ago! - - -[![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg?columns=4)](https://repology.org/project/yambar/versions) - - ## Index 1. [Introduction](#introduction) @@ -80,9 +54,9 @@ bar: right: - clock: content: - - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} + - string: {text: , font: "Font Awesome 5 Free:style=solid:size=12"} - string: {text: "{date}", right-margin: 5} - - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} + - string: {text: , font: "Font Awesome 5 Free:style=solid:size=12"} - string: {text: "{time}"} ``` @@ -99,17 +73,10 @@ Available modules: * backlight * battery * clock -* cpu -* disk-io -* dwl -* foreign-toplevel * i3 (and Sway) * label -* mem * mpd * network -* pipewire -* pulse * removables * river * script (see script [examples](examples/scripts)) @@ -120,6 +87,18 @@ 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 @@ -128,7 +107,7 @@ mkdir -p bld/release && cd bld/release Second, configure the build (if you intend to install it globally, you might also want `--prefix=/usr`): ```sh -meson setup --buildtype=release ../.. +meson --buildtype=release ../.. ``` Optionally, explicitly disable a backend (or enable, if you want a diff --git a/bar/backend.h b/bar/backend.h index b7a9fcb..f2681a8 100644 --- a/bar/backend.h +++ b/bar/backend.h @@ -7,10 +7,11 @@ struct backend { bool (*setup)(struct bar *bar); void (*cleanup)(struct bar *bar); - void (*loop)(struct bar *bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)); + void (*loop)(struct bar *bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, + 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); - const char *(*output_name)(const struct bar *bar); }; diff --git a/bar/bar.c b/bar/bar.c index 109f210..526f80f 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -1,14 +1,12 @@ #include "bar.h" #include "private.h" -#include -#include -#include -#include -#include #include #include +#include +#include #include +#include #include #include @@ -18,17 +16,15 @@ #include "../log.h" #if defined(ENABLE_X11) -#include "xcb.h" + #include "xcb.h" #endif #if defined(ENABLE_WAYLAND) -#include "wayland.h" + #include "wayland.h" #endif -#define max(x, y) ((x) > (y) ? (x) : (y)) - /* - * Calculate total width of left/center/right groups. + * Calculate total width of left/center/rigth groups. * Note: begin_expose() must have been called */ static void @@ -40,33 +36,23 @@ calculate_widths(const struct private *b, int *left, int *center, int *right) for (size_t i = 0; i < b->left.count; i++) { struct exposable *e = b->left.exps[i]; - if (e->width > 0) - *left += b->left_spacing + e->width + b->right_spacing; + *left += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->center.count; i++) { struct exposable *e = b->center.exps[i]; - if (e->width > 0) - *center += b->left_spacing + e->width + b->right_spacing; + *center += b->left_spacing + e->width + b->right_spacing; } for (size_t i = 0; i < b->right.count; i++) { struct exposable *e = b->right.exps[i]; - if (e->width > 0) - *right += b->left_spacing + e->width + b->right_spacing; + *right += b->left_spacing + e->width + b->right_spacing; } /* No spacing on the edges (that's what the margins are for) */ - if (*left > 0) - *left -= b->left_spacing + b->right_spacing; - if (*center > 0) - *center -= b->left_spacing + b->right_spacing; - if (*right > 0) - *right -= b->left_spacing + b->right_spacing; - - assert(*left >= 0); - assert(*center >= 0); - assert(*right >= 0); + *left -= b->left_spacing + b->right_spacing; + *center -= b->left_spacing + b->right_spacing; + *right -= b->left_spacing + b->right_spacing; } static void @@ -75,26 +61,20 @@ expose(const struct bar *_bar) const struct private *bar = _bar->private; pixman_image_t *pix = bar->pix; - pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, &bar->background, 1, - &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); - pixman_image_fill_rectangles( - PIXMAN_OP_OVER, pix, &bar->border.color, 4, - (pixman_rectangle16_t[]){ - /* Left */ - {0, 0, bar->border.left_width, bar->height_with_border}, + PIXMAN_OP_SRC, pix, &bar->background, 1, + &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); - /* Right */ - {bar->width - bar->border.right_width, 0, bar->border.right_width, bar->height_with_border}, - - /* Top */ - {bar->border.left_width, 0, bar->width - bar->border.left_width - bar->border.right_width, - bar->border.top_width}, - - /* Bottom */ - {bar->border.left_width, bar->height_with_border - bar->border.bottom_width, - bar->width - bar->border.left_width - bar->border.right_width, bar->border.bottom_width}, - }); + if (bar->border.width > 0) { + pixman_image_fill_rectangles( + PIXMAN_OP_OVER, pix, &bar->border.color, 4, + (pixman_rectangle16_t[]){ + {0, 0, bar->width, bar->border.width}, + {0, 0, bar->border.width, bar->height_with_border}, + {bar->width - bar->border.width, 0, bar->border.width, bar->height_with_border}, + {0, bar->height_with_border - bar->border.width, bar->width, bar->border.width}, + }); + } for (size_t i = 0; i < bar->left.count; i++) { struct module *m = bar->left.mods[i]; @@ -102,7 +82,6 @@ expose(const struct bar *_bar) if (e != NULL) e->destroy(e); bar->left.exps[i] = module_begin_expose(m); - assert(bar->left.exps[i]->width >= 0); } for (size_t i = 0; i < bar->center.count; i++) { @@ -111,7 +90,6 @@ expose(const struct bar *_bar) if (e != NULL) e->destroy(e); bar->center.exps[i] = module_begin_expose(m); - assert(bar->center.exps[i]->width >= 0); } for (size_t i = 0; i < bar->right.count; i++) { @@ -120,49 +98,42 @@ expose(const struct bar *_bar) if (e != NULL) e->destroy(e); bar->right.exps[i] = module_begin_expose(m); - assert(bar->right.exps[i]->width >= 0); } int left_width, center_width, right_width; calculate_widths(bar, &left_width, ¢er_width, &right_width); - int y = bar->border.top_width; - int x = bar->border.left_width + bar->left_margin - bar->left_spacing; - pixman_region32_t clip; - pixman_region32_init_rect( - &clip, bar->border.left_width + bar->left_margin, bar->border.top_width, - (bar->width - bar->left_margin - bar->right_margin - bar->border.left_width - bar->border.right_width), - bar->height); - pixman_image_set_clip_region32(pix, &clip); - pixman_region32_fini(&clip); - + int y = bar->border.width; + int x = bar->border.width + bar->left_margin - bar->left_spacing; for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); - if (e->width > 0) - x += bar->left_spacing + e->width + bar->right_spacing; + x += bar->left_spacing + e->width + bar->right_spacing; } x = bar->width / 2 - center_width / 2 - bar->left_spacing; for (size_t i = 0; i < bar->center.count; i++) { const struct exposable *e = bar->center.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); - if (e->width > 0) - x += bar->left_spacing + e->width + bar->right_spacing; + x += bar->left_spacing + e->width + bar->right_spacing; } - x = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width); + x = bar->width - ( + right_width + + bar->left_spacing + + bar->right_margin + + bar->border.width); for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); - if (e->width > 0) - x += bar->left_spacing + e->width + bar->right_spacing; + x += bar->left_spacing + e->width + bar->right_spacing; } bar->backend.iface->commit(_bar); } + static void refresh(const struct bar *bar) { @@ -177,20 +148,15 @@ set_cursor(struct bar *bar, const char *cursor) b->backend.iface->set_cursor(bar, cursor); } -static const char * -output_name(const struct bar *bar) -{ - const struct private *b = bar->private; - return b->backend.iface->output_name(bar); -} - static void -on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct bar *_bar, enum mouse_event event, int x, int y) { struct private *bar = _bar->private; - if ((y < bar->border.top_width || y >= (bar->height_with_border - bar->border.bottom_width)) - || (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) { + if ((y < bar->border.width || + y >= (bar->height_with_border - bar->border.width)) || + (x < bar->border.width || x >= (bar->width - bar->border.width))) + { set_cursor(_bar, "left_ptr"); return; } @@ -198,17 +164,14 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, int left_width, center_width, right_width; calculate_widths(bar, &left_width, ¢er_width, &right_width); - int mx = bar->border.left_width + bar->left_margin - bar->left_spacing; + int mx = bar->border.width + bar->left_margin - bar->left_spacing; for (size_t i = 0; i < bar->left.count; i++) { struct exposable *e = bar->left.exps[i]; - if (e->width == 0) - continue; - mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) - e->on_mouse(e, _bar, event, btn, x - mx, y); + e->on_mouse(e, _bar, event, x - mx, y); return; } @@ -219,31 +182,28 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, for (size_t i = 0; i < bar->center.count; i++) { struct exposable *e = bar->center.exps[i]; - if (e->width == 0) - continue; - mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) - e->on_mouse(e, _bar, event, btn, x - mx, y); + e->on_mouse(e, _bar, event, x - mx, y); return; } mx += e->width + bar->right_spacing; } - mx = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width); + mx = bar->width - (right_width + + bar->left_spacing + + bar->right_margin + + bar->border.width); for (size_t i = 0; i < bar->right.count; i++) { struct exposable *e = bar->right.exps[i]; - if (e->width == 0) - continue; - mx += bar->left_spacing; if (x >= mx && x < mx + e->width) { if (e->on_mouse != NULL) - e->on_mouse(e, _bar, event, btn, x - mx, y); + e->on_mouse(e, _bar, event, x - mx, y); return; } @@ -253,27 +213,13 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, set_cursor(_bar, "left_ptr"); } -static void -set_module_thread_name(thrd_t id, struct module *mod) -{ - char title[16]; - if (mod->description != NULL) - strncpy(title, mod->description(mod), sizeof(title)); - 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) { struct private *bar = _bar->private; - bar->height_with_border = bar->height + bar->border.top_width + bar->border.bottom_width; + bar->height_with_border = bar->height + 2 * bar->border.width; if (!bar->backend.iface->setup(_bar)) { bar->backend.iface->cleanup(_bar); @@ -283,33 +229,29 @@ run(struct bar *_bar) } set_cursor(_bar, "left_ptr"); - expose(_bar); /* Start modules */ - thrd_t thrd_left[max(bar->left.count, 1)]; - thrd_t thrd_center[max(bar->center.count, 1)]; - thrd_t thrd_right[max(bar->right.count, 1)]; + thrd_t thrd_left[bar->left.count]; + thrd_t thrd_center[bar->center.count]; + thrd_t thrd_right[bar->right.count]; for (size_t i = 0; i < bar->left.count; i++) { struct module *mod = bar->left.mods[i]; 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"); @@ -323,26 +265,20 @@ run(struct bar *_bar) int mod_ret; for (size_t i = 0; i < bar->left.count; i++) { thrd_join(thrd_left[i], &mod_ret); - if (mod_ret != 0) { - const struct module *m = bar->left.mods[i]; - LOG_ERR("module: LEFT #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret); - } + if (mod_ret != 0) + LOG_ERR("module: LEFT #%zu: non-zero exit value: %d", i, mod_ret); ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } for (size_t i = 0; i < bar->center.count; i++) { thrd_join(thrd_center[i], &mod_ret); - if (mod_ret != 0) { - const struct module *m = bar->center.mods[i]; - LOG_ERR("module: CENTER #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret); - } + if (mod_ret != 0) + LOG_ERR("module: CENTER #%zu: non-zero exit value: %d", i, mod_ret); ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } for (size_t i = 0; i < bar->right.count; i++) { thrd_join(thrd_right[i], &mod_ret); - if (mod_ret != 0) { - const struct module *m = bar->right.mods[i]; - LOG_ERR("module: RIGHT #%zu (%s): non-zero exit value: %d", i, m->description(m), mod_ret); - } + if (mod_ret != 0) + LOG_ERR("module: RIGHT #%zu: non-zero exit value: %d", i, mod_ret); ret = ret == 0 && mod_ret != 0 ? mod_ret : ret; } @@ -430,7 +366,7 @@ bar_new(const struct bar_config *config) break; case BAR_BACKEND_WAYLAND: -#if defined(ENABLE_WAYLAND) +#if defined(BAR_WAYLAND) backend_data = bar_backend_wayland_new(); backend_iface = &wayland_backend_iface; #else @@ -445,7 +381,6 @@ bar_new(const struct bar_config *config) struct private *priv = calloc(1, sizeof(*priv)); priv->monitor = config->monitor != NULL ? strdup(config->monitor) : NULL; - priv->layer = config->layer; priv->location = config->location; priv->height = config->height; priv->background = config->background; @@ -453,11 +388,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.left_width = config->border.left_width; - priv->border.right_width = config->border.right_width; - priv->border.top_width = config->border.top_width; - priv->border.bottom_width = config->border.bottom_width; + priv->border.width = config->border.width; priv->border.color = config->border.color; priv->border.left_margin = config->border.left_margin; priv->border.right_margin = config->border.right_margin; @@ -488,7 +419,6 @@ bar_new(const struct bar_config *config) bar->destroy = &destroy; bar->refresh = &refresh; bar->set_cursor = &set_cursor; - bar->output_name = &output_name; for (size_t i = 0; i < priv->left.count; i++) priv->left.mods[i]->bar = bar; diff --git a/bar/bar.h b/bar/bar.h index ce91247..78e2414 100644 --- a/bar/bar.h +++ b/bar/bar.h @@ -1,7 +1,6 @@ #pragma once #include "../color.h" -#include "../font-shaping.h" #include "../module.h" struct bar { @@ -13,31 +12,24 @@ struct bar { void (*refresh)(const struct bar *bar); void (*set_cursor)(struct bar *bar, const char *cursor); - - const char *(*output_name)(const struct bar *bar); }; enum bar_location { BAR_TOP, BAR_BOTTOM }; -enum bar_layer { BAR_LAYER_OVERLAY, BAR_LAYER_TOP, BAR_LAYER_BOTTOM, BAR_LAYER_BACKGROUND }; enum bar_backend { BAR_BACKEND_AUTO, BAR_BACKEND_XCB, BAR_BACKEND_WAYLAND }; struct bar_config { enum bar_backend backend; const char *monitor; - enum bar_layer layer; enum bar_location location; - enum font_shaping font_shaping; int height; int left_spacing, right_spacing; int left_margin, right_margin; - int trackpad_sensitivity; pixman_color_t background; struct { - int left_width, right_width; - int top_width, bottom_width; + int width; pixman_color_t color; int left_margin, right_margin; int top_margin, bottom_margin; diff --git a/bar/meson.build b/bar/meson.build index 6ca5ec9..90f050b 100644 --- a/bar/meson.build +++ b/bar/meson.build @@ -7,11 +7,11 @@ endif if backend_wayland wayland_protocols = dependency('wayland-protocols') - wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir') + wayland_protocols_datadir = wayland_protocols.get_pkgconfig_variable('pkgdatadir') wscanner = dependency('wayland-scanner', native: true) wscanner_prog = find_program( - wscanner.get_variable('wayland_scanner'), native: true) + wscanner.get_pkgconfig_variable('wayland_scanner'), native: true) wl_proto_headers = [] wl_proto_src = [] diff --git a/bar/private.h b/bar/private.h index d48b07f..b5d888f 100644 --- a/bar/private.h +++ b/bar/private.h @@ -3,22 +3,18 @@ #include "../bar/bar.h" #include "backend.h" -struct private -{ +struct private { /* From bar_config */ char *monitor; - enum bar_layer layer; enum bar_location location; int height; int left_spacing, right_spacing; int left_margin, right_margin; - int trackpad_sensitivity; pixman_color_t background; struct { - int left_width, right_width; - int top_width, bottom_width; + int width; pixman_color_t color; int left_margin, right_margin; int top_margin, bottom_margin; diff --git a/bar/wayland.c b/bar/wayland.c index 86ab252..f4b3cce 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1,25 +1,22 @@ #include "wayland.h" -#include -#include -#include -#include #include #include #include +#include #include +#include -#include -#include #include +#include #include #include #include #include -#include #include +#include #define LOG_MODULE "bar:wayland" #define LOG_ENABLE_DBG 0 @@ -28,10 +25,6 @@ #include "private.h" -#if !defined(MFD_NOEXEC_SEAL) -#define MFD_NOEXEC_SEAL 0 -#endif - struct buffer { bool busy; size_t width; @@ -50,7 +43,6 @@ struct monitor { struct wl_output *output; struct zxdg_output_v1 *xdg; char *name; - uint32_t wl_name; int x; int y; @@ -101,7 +93,6 @@ struct wayland_backend { tll(struct monitor) monitors; const struct monitor *monitor; - char *last_mapped_monitor; int scale; @@ -116,15 +107,12 @@ struct wayland_backend { /* We're already waiting for a frame done callback */ bool render_scheduled; - tll(struct buffer) buffers; /* List of SHM buffers */ - struct buffer *next_buffer; /* Bar is rendering to this one */ - struct buffer *pending_buffer; /* Finished, but not yet rendered */ - struct wl_callback *frame_callback; + tll(struct buffer) buffers; /* List of SHM buffers */ + struct buffer *next_buffer; /* Bar is rendering to this one */ + struct buffer *pending_buffer; /* Finished, but not yet rendered */ - double aggregated_scroll; - bool have_discrete; - - void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y); + void (*bar_expose)(const struct bar *bar); + void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, int x, int y); }; static void @@ -156,7 +144,7 @@ bar_backend_wayland_new(void) static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { - // printf("SHM format: 0x%08x\n", format); + //printf("SHM format: 0x%08x\n", format); } static const struct wl_shm_listener shm_listener = { @@ -166,7 +154,10 @@ static const struct wl_shm_listener shm_listener = { static void update_cursor_surface(struct wayland_backend *backend, struct seat *seat) { - if (seat->pointer.serial == 0 || seat->pointer.cursor == NULL || seat->pointer.surface == NULL) { + if (seat->pointer.serial == 0 || + seat->pointer.cursor == NULL || + seat->pointer.surface == NULL) + { return; } @@ -175,12 +166,17 @@ update_cursor_surface(struct wayland_backend *backend, struct seat *seat) const int scale = seat->pointer.scale; wl_surface_set_buffer_scale(seat->pointer.surface, scale); - wl_surface_attach(seat->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); + wl_surface_attach( + seat->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); - wl_pointer_set_cursor(seat->wl_pointer, seat->pointer.serial, seat->pointer.surface, image->hotspot_x / scale, - image->hotspot_y / scale); + wl_pointer_set_cursor( + seat->wl_pointer, seat->pointer.serial, + seat->pointer.surface, + image->hotspot_x / scale, image->hotspot_y / scale); - wl_surface_damage_buffer(seat->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); + + wl_surface_damage_buffer( + seat->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(seat->pointer.surface); wl_display_flush(backend->display); @@ -210,9 +206,11 @@ reload_cursor_theme(struct seat *seat, int new_scale) } } - LOG_INFO("%s: cursor theme: %s, size: %u, scale: %d", seat->name, cursor_theme, cursor_size, new_scale); + LOG_INFO("%s: cursor theme: %s, size: %u, scale: %d", + seat->name, cursor_theme, cursor_size, new_scale); - struct wl_cursor_theme *theme = wl_cursor_theme_load(cursor_theme, cursor_size * new_scale, seat->backend->shm); + struct wl_cursor_theme *theme = wl_cursor_theme_load( + cursor_theme, cursor_size * new_scale, seat->backend->shm); if (theme == NULL) { LOG_ERR("%s: failed to load cursor theme", seat->name); @@ -224,7 +222,8 @@ reload_cursor_theme(struct seat *seat, int new_scale) } static void -wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, +wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct seat *seat = data; @@ -240,19 +239,19 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, str } static void -wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) +wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { struct seat *seat = data; struct wayland_backend *backend = seat->backend; - backend->have_discrete = false; - if (backend->active_seat == seat) backend->active_seat = NULL; } static void -wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) +wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct seat *seat = data; struct wayland_backend *backend = seat->backend; @@ -261,119 +260,52 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_f seat->pointer.y = wl_fixed_to_int(surface_y) * backend->scale; backend->active_seat = seat; - backend->bar_on_mouse(backend->bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse( + backend->bar, ON_MOUSE_MOTION, seat->pointer.x, seat->pointer.y); } static void -wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, - uint32_t state) +wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - struct seat *seat = data; - struct wayland_backend *backend = seat->backend; - - if (state == WL_POINTER_BUTTON_STATE_PRESSED) - backend->active_seat = seat; - else { - enum mouse_button btn; - - switch (button) { - case BTN_LEFT: - btn = MOUSE_BTN_LEFT; - break; - case BTN_MIDDLE: - btn = MOUSE_BTN_MIDDLE; - break; - case BTN_RIGHT: - btn = MOUSE_BTN_RIGHT; - break; - case BTN_SIDE: - btn = MOUSE_BTN_PREVIOUS; - break; - case BTN_EXTRA: - btn = MOUSE_BTN_NEXT; - break; - default: - return; - } - - backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); - } -} - -static void -wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) -{ - if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) + if (state != WL_POINTER_BUTTON_STATE_PRESSED) return; struct seat *seat = data; struct wayland_backend *backend = seat->backend; - struct private *bar = backend->bar->private; backend->active_seat = seat; + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, seat->pointer.x, seat->pointer.y); +} - 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 = bar->trackpad_sensitivity; - const double adjust = backend->aggregated_scroll > 0 ? -step : step; - - while (fabs(backend->aggregated_scroll) >= step) { - backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); - backend->aggregated_scroll += adjust; - } +static void +wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ } 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 -wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) +wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) { } static void -wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) +wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) { - if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) - return; - - 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) +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; - - 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); - } } static const struct wl_pointer_listener pointer_listener = { @@ -389,14 +321,16 @@ static const struct wl_pointer_listener pointer_listener = { }; static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { struct seat *seat = data; if (caps & WL_SEAT_CAPABILITY_POINTER) { if (seat->wl_pointer == NULL) { assert(seat->pointer.surface == NULL); - seat->pointer.surface = wl_compositor_create_surface(seat->backend->compositor); + seat->pointer.surface = wl_compositor_create_surface( + seat->backend->compositor); if (seat->pointer.surface == NULL) { LOG_ERR("%s: failed to create pointer surface", seat->name); @@ -436,8 +370,10 @@ static const struct wl_seat_listener seat_listener = { }; static void -output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, - int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform) +output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, + int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { struct monitor *mon = data; mon->width_mm = physical_width; @@ -445,29 +381,19 @@ output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, i } static void -output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) +output_mode(void *data, struct wl_output *wl_output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) +{ +} + +static void +output_done(void *data, struct wl_output *wl_output) { } static bool update_size(struct wayland_backend *backend); static void refresh(const struct bar *_bar); -static void -output_done(void *data, struct wl_output *wl_output) -{ - struct monitor *mon = data; - - if (mon->backend->monitor == mon) { - int old_scale = mon->backend->scale; - int old_width = mon->backend->width; - - update_size(mon->backend); - - if (mon->backend->scale != old_scale || mon->backend->width != old_width) - refresh(mon->backend->bar); - } -} - static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) { @@ -476,40 +402,28 @@ output_scale(void *data, struct wl_output *wl_output, int32_t factor) return; mon->scale = factor; + + if (mon->backend->monitor == mon) { + int old_scale = mon->backend->scale; + update_size(mon->backend); + + if (mon->backend->scale != old_scale) + refresh(mon->backend->bar); + } } -#if defined(WL_OUTPUT_NAME_SINCE_VERSION) -static void -output_name(void *data, struct wl_output *wl_output, const char *name) -{ - struct monitor *mon = data; - free(mon->name); - mon->name = name != NULL ? strdup(name) : NULL; -} -#endif - -#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) -static void -output_description(void *data, struct wl_output *wl_output, const char *description) -{ -} -#endif static const struct wl_output_listener output_listener = { .geometry = &output_geometry, .mode = &output_mode, .done = &output_done, .scale = &output_scale, -#if defined(WL_OUTPUT_NAME_SINCE_VERSION) - .name = &output_name, -#endif -#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) - .description = &output_description, -#endif }; static void -xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) +xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, + int32_t x, int32_t y) { struct monitor *mon = data; mon->x = x; @@ -517,69 +431,46 @@ xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output } static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, + int32_t width, int32_t height) { struct monitor *mon = data; mon->width_px = width; mon->height_px = height; } -static bool create_surface(struct wayland_backend *backend); -static void destroy_surface(struct wayland_backend *backend); - static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { const struct monitor *mon = data; - LOG_INFO("monitor: %s: %dx%d+%d+%d (%dx%dmm)", mon->name, mon->width_px, mon->height_px, mon->x, mon->y, - mon->width_mm, mon->height_mm); + LOG_INFO("monitor: %s: %dx%d+%d+%d (%dx%dmm)", + mon->name, mon->width_px, mon->height_px, + mon->x, mon->y, mon->width_mm, mon->height_mm); struct wayland_backend *backend = mon->backend; struct private *bar = backend->bar->private; - const bool is_mapped = backend->monitor != NULL; - if (is_mapped) { - assert(backend->surface != NULL); - assert(backend->last_mapped_monitor == NULL); - return; - } - - const bool output_is_our_configured_monitor - = (bar->monitor != NULL && mon->name != NULL && strcmp(bar->monitor, mon->name) == 0); - - const bool output_is_last_mapped = (backend->last_mapped_monitor != NULL && mon->name != NULL - && strcmp(backend->last_mapped_monitor, mon->name) == 0); - - if (output_is_our_configured_monitor) - LOG_DBG("%s: using this monitor (user configured)", mon->name); - else if (output_is_last_mapped) - LOG_DBG("%s: using this monitor (last mapped)", mon->name); - - if (output_is_our_configured_monitor || output_is_last_mapped) { + if (bar->monitor != NULL && mon->name != NULL && + strcmp(bar->monitor, mon->name) == 0) + { /* User specified a monitor, and this is one */ backend->monitor = mon; - - free(backend->last_mapped_monitor); - backend->last_mapped_monitor = NULL; - - if (create_surface(backend) && update_size(backend)) { - if (backend->pipe_fds[1] >= 0) - refresh(backend->bar); - } } + } static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, + const char *name) { struct monitor *mon = data; - free(mon->name); mon->name = strdup(name); } static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, + const char *description) { } @@ -597,12 +488,14 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", + iface, wanted, version); return false; } static void -handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { LOG_DBG("global: 0x%08x, interface=%s, version=%u", name, interface, version); struct wayland_backend *backend = data; @@ -612,7 +505,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - backend->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, required); + backend->compositor = wl_registry_bind( + registry, name, &wl_compositor_interface, required); } else if (strcmp(interface, wl_shm_interface.name) == 0) { @@ -620,7 +514,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - backend->shm = wl_registry_bind(registry, name, &wl_shm_interface, required); + backend->shm = wl_registry_bind( + registry, name, &wl_shm_interface, required); wl_shm_add_listener(backend->shm, &shm_listener, backend); } @@ -629,9 +524,12 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, required); + struct wl_output *output = wl_registry_bind( + registry, name, &wl_output_interface, required); - tll_push_back(backend->monitors, ((struct monitor){.backend = backend, .wl_name = name, .output = output})); + tll_push_back(backend->monitors, ((struct monitor){ + .backend = backend, + .output = output})); struct monitor *mon = &tll_back(backend->monitors); wl_output_add_listener(output, &output_listener, mon); @@ -644,7 +542,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha assert(backend->xdg_output_manager != NULL); if (backend->xdg_output_manager != NULL) { - mon->xdg = zxdg_output_manager_v1_get_xdg_output(backend->xdg_output_manager, mon->output); + mon->xdg = zxdg_output_manager_v1_get_xdg_output( + backend->xdg_output_manager, mon->output); zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon); } @@ -655,18 +554,21 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - backend->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, required); + backend->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, required); } else if (strcmp(interface, wl_seat_interface.name) == 0) { - const uint32_t required = 5; + const uint32_t required = 3; if (!verify_iface_version(interface, version, required)) return; - struct wl_seat *seat = wl_registry_bind(registry, name, &wl_seat_interface, required); + struct wl_seat *seat = wl_registry_bind( + registry, name, &wl_seat_interface, required); assert(seat != NULL); - tll_push_back(backend->seats, ((struct seat){.backend = backend, .seat = seat, .id = name})); + tll_push_back( + backend->seats, ((struct seat){.backend = backend, .seat = seat, .id = name})); wl_seat_add_listener(seat, &seat_listener, &tll_back(backend->seats)); } @@ -676,7 +578,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - backend->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, required); + backend->xdg_output_manager = wl_registry_bind( + registry, name, &zxdg_output_manager_v1_interface, required); } } @@ -685,8 +588,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { struct wayland_backend *backend = data; - tll_foreach(backend->seats, it) - { + tll_foreach(backend->seats, it) { if (it->item.id == name) { if (backend->active_seat == &it->item) backend->active_seat = NULL; @@ -696,24 +598,9 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) } } - tll_foreach(backend->monitors, it) - { - struct monitor *mon = &it->item; - if (mon->wl_name == name) { - LOG_INFO("%s disconnected/disabled", mon->name); - - if (mon == backend->monitor) { - assert(backend->last_mapped_monitor == NULL); - backend->last_mapped_monitor = strdup(mon->name); - backend->monitor = NULL; - } - - tll_remove(backend->monitors, it); - return; - } - } - LOG_WARN("unknown global removed: 0x%08x", name); + + /* TODO: need to handle displays and seats */ } static const struct wl_registry_listener registry_listener = { @@ -722,7 +609,8 @@ static const struct wl_registry_listener registry_listener = { }; static void -layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) +layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t w, uint32_t h) { struct wayland_backend *backend = data; backend->width = w * backend->scale; @@ -734,10 +622,22 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint3 static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { - LOG_DBG("layer surface closed by compositor"); - struct wayland_backend *backend = data; - destroy_surface(backend); + + /* + * Called e.g. when an output is disabled. We don't get a + * corresponding event if/when that same output re-appears. So, + * for now, we simply shut down. In the future, we _could_ maybe + * destroy the surface, listen for output events and re-create the + * surface if the same output re-appears. + */ + LOG_WARN("compositor requested surface be closed - shutting down"); + + if (write(backend->bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) + != sizeof(uint64_t)) + { + LOG_ERRNO("failed to signal abort to modules"); + } } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -745,96 +645,10 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = &layer_surface_closed, }; -static const struct wl_surface_listener surface_listener; - -static bool -create_surface(struct wayland_backend *backend) -{ - assert(tll_length(backend->monitors) > 0); - assert(backend->surface == NULL); - assert(backend->layer_surface == NULL); - - struct bar *_bar = backend->bar; - struct private *bar = _bar->private; - - backend->surface = wl_compositor_create_surface(backend->compositor); - if (backend->surface == NULL) { - LOG_ERR("failed to create panel surface"); - return false; - } - - wl_surface_add_listener(backend->surface, &surface_listener, backend); - - enum zwlr_layer_shell_v1_layer layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - - switch (bar->layer) { - case BAR_LAYER_BACKGROUND: - layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; - break; - - case BAR_LAYER_BOTTOM: - layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - break; - - case BAR_LAYER_TOP: - layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; - break; - - case BAR_LAYER_OVERLAY: - layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; - break; - } - - backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - backend->layer_shell, backend->surface, backend->monitor != NULL ? backend->monitor->output : NULL, layer, - "panel"); - - if (backend->layer_surface == NULL) { - LOG_ERR("failed to create layer shell surface"); - return false; - } - - zwlr_layer_surface_v1_add_listener(backend->layer_surface, &layer_surface_listener, backend); - - /* Aligned to top, maximum width */ - enum zwlr_layer_surface_v1_anchor top_or_bottom - = bar->location == BAR_TOP ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - - zwlr_layer_surface_v1_set_anchor(backend->layer_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | top_or_bottom); - - return true; -} - -static void -destroy_surface(struct wayland_backend *backend) -{ - if (backend->layer_surface != NULL) - zwlr_layer_surface_v1_destroy(backend->layer_surface); - if (backend->surface != NULL) - wl_surface_destroy(backend->surface); - if (backend->frame_callback != NULL) - wl_callback_destroy(backend->frame_callback); - - if (backend->pending_buffer != NULL) - backend->pending_buffer->busy = false; - if (backend->next_buffer != NULL) - backend->next_buffer->busy = false; - - backend->layer_surface = NULL; - backend->surface = NULL; - backend->frame_callback = NULL; - backend->pending_buffer = NULL; - backend->next_buffer = NULL; - - backend->scale = 0; - backend->render_scheduled = false; -} - static void buffer_release(void *data, struct wl_buffer *wl_buffer) { - // printf("buffer release\n"); + //printf("buffer release\n"); struct buffer *buffer = data; assert(buffer->busy); buffer->busy = false; @@ -847,8 +661,7 @@ static const struct wl_buffer_listener buffer_listener = { static struct buffer * get_buffer(struct wayland_backend *backend) { - tll_foreach(backend->buffers, it) - { + tll_foreach(backend->buffers, it) { if (!it->item.busy && it->item.width == backend->width && it->item.height == backend->height) { it->item.busy = true; return &it->item; @@ -875,32 +688,15 @@ get_buffer(struct wayland_backend *backend) pixman_image_t *pix = NULL; /* Backing memory for SHM */ -#if defined(MEMFD_CREATE) - /* - * Older kernels reject MFD_NOEXEC_SEAL with EINVAL. Try first - * *with* it, and if that fails, try again *without* it. - */ - errno = 0; - pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL); - - if (pool_fd < 0 && errno == EINVAL) { - pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); - } -#elif defined(__FreeBSD__) - // memfd_create on FreeBSD 13 is SHM_ANON without sealing support - pool_fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); -#else - char name[] = "/tmp/yambar-wayland-shm-buffer-pool-XXXXXX"; - pool_fd = mkostemp(name, O_CLOEXEC); - unlink(name); -#endif + pool_fd = memfd_create("yambar-wayland-shm-buffer-pool", MFD_CLOEXEC); if (pool_fd == -1) { LOG_ERRNO("failed to create SHM backing memory file"); goto err; } /* Total size */ - const uint32_t stride = stride_for_format_and_width(PIXMAN_a8r8g8b8, backend->width); + const uint32_t stride = stride_for_format_and_width( + PIXMAN_a8r8g8b8, backend->width); size = stride * backend->height; if (ftruncate(pool_fd, size) == -1) { @@ -914,50 +710,43 @@ get_buffer(struct wayland_backend *backend) goto err; } -#if defined(MEMFD_CREATE) - /* Seal file - we no longer allow any kind of resizing */ - /* TODO: wayland mmaps(PROT_WRITE), for some unknown reason, hence we cannot use F_SEAL_FUTURE_WRITE */ - if (fcntl(pool_fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) { - LOG_ERRNO("failed to seal SHM backing memory file"); - /* This is not a fatal error */ - } -#endif - pool = wl_shm_create_pool(backend->shm, pool_fd, size); if (pool == NULL) { LOG_ERR("failed to create SHM pool"); goto err; } - buf = wl_shm_pool_create_buffer(pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888); + buf = wl_shm_pool_create_buffer( + pool, 0, backend->width, backend->height, stride, WL_SHM_FORMAT_ARGB8888); if (buf == NULL) { LOG_ERR("failed to create SHM buffer"); goto err; } /* We use the entire pool for our single buffer */ - wl_shm_pool_destroy(pool); - pool = NULL; - close(pool_fd); - pool_fd = -1; + wl_shm_pool_destroy(pool); pool = NULL; + close(pool_fd); pool_fd = -1; - pix = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, backend->width, backend->height, (uint32_t *)mmapped, - stride); + pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, backend->width, backend->height, (uint32_t *)mmapped, stride); if (pix == NULL) { LOG_ERR("failed to create pixman image"); goto err; } /* Push to list of available buffers, but marked as 'busy' */ - tll_push_back(backend->buffers, ((struct buffer){ - .busy = true, - .width = backend->width, - .height = backend->height, - .size = size, - .mmapped = mmapped, - .wl_buf = buf, - .pix = pix, - })); + tll_push_back( + backend->buffers, + ((struct buffer){ + .busy = true, + .width = backend->width, + .height = backend->height, + .size = size, + .mmapped = mmapped, + .wl_buf = buf, + .pix = pix, + }) + ); struct buffer *ret = &tll_back(backend->buffers); wl_buffer_add_listener(ret->wl_buf, &buffer_listener, ret); @@ -987,8 +776,7 @@ guess_scale(const struct wayland_backend *backend) bool all_have_same_scale = true; int last_scale = -1; - tll_foreach(backend->monitors, it) - { + tll_foreach(backend->monitors, it) { if (last_scale == -1) last_scale = it->item.scale; else if (last_scale != it->item.scale) { @@ -1014,31 +802,40 @@ update_size(struct wayland_backend *backend) const struct monitor *mon = backend->monitor; const int scale = mon != NULL ? mon->scale : guess_scale(backend); - assert(backend->surface != NULL); + if (backend->scale == scale) + return true; backend->scale = scale; int height = bar->height_with_border; height /= scale; height *= scale; - bar->height = height - bar->border.top_width - bar->border.bottom_width; + bar->height = height - 2 * bar->border.width; bar->height_with_border = height; - zwlr_layer_surface_v1_set_size(backend->layer_surface, 0, bar->height_with_border / scale); + zwlr_layer_surface_v1_set_size( + backend->layer_surface, 0, bar->height_with_border / scale); zwlr_layer_surface_v1_set_exclusive_zone( backend->layer_surface, - (bar->height_with_border + (bar->location == BAR_TOP ? bar->border.bottom_margin : bar->border.top_margin)) - / scale); + (bar->height_with_border + (bar->location == BAR_TOP + ? bar->border.bottom_margin + : bar->border.top_margin)) + / scale); - zwlr_layer_surface_v1_set_margin(backend->layer_surface, bar->border.top_margin / scale, - bar->border.right_margin / scale, bar->border.bottom_margin / scale, - bar->border.left_margin / scale); + zwlr_layer_surface_v1_set_margin( + backend->layer_surface, + bar->border.top_margin / scale, + bar->border.right_margin / scale, + bar->border.bottom_margin / scale, + bar->border.left_margin / scale + ); /* Trigger a 'configure' event, after which we'll have the width */ wl_surface_commit(backend->surface); wl_display_roundtrip(backend->display); - if (backend->width == -1 || backend->height != bar->height_with_border) { + if (backend->width == -1 || + backend->height != bar->height_with_border) { LOG_ERR("failed to get panel width"); return false; } @@ -1055,6 +852,8 @@ update_size(struct wayland_backend *backend) return true; } +static const struct wl_surface_listener surface_listener; + static bool setup(struct bar *_bar) { @@ -1100,17 +899,44 @@ setup(struct bar *_bar) /* Trigger listeners registered in previous roundtrip */ wl_display_roundtrip(backend->display); - if (backend->surface == NULL && backend->layer_surface == NULL) { - if (!create_surface(backend)) - return false; - - if (!update_size(backend)) - return false; + backend->surface = wl_compositor_create_surface(backend->compositor); + if (backend->surface == NULL) { + LOG_ERR("failed to create panel surface"); + return false; } - assert(backend->monitor == NULL || backend->width / backend->monitor->scale <= backend->monitor->width_px); + wl_surface_add_listener(backend->surface, &surface_listener, backend); - if (pipe2(backend->pipe_fds, O_CLOEXEC | O_NONBLOCK) == -1) { + backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + backend->layer_shell, backend->surface, + backend->monitor != NULL ? backend->monitor->output : NULL, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); + + if (backend->layer_surface == NULL) { + LOG_ERR("failed to create layer shell surface"); + return false; + } + + zwlr_layer_surface_v1_add_listener( + backend->layer_surface, &layer_surface_listener, backend); + + /* Aligned to top, maximum width */ + enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP + ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + zwlr_layer_surface_v1_set_anchor( + backend->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + top_or_bottom); + + update_size(backend); + + assert(backend->monitor == NULL || + backend->width / backend->monitor->scale <= backend->monitor->width_px); + + if (pipe(backend->pipe_fds) == -1) { LOG_ERRNO("failed to create pipe"); return false; } @@ -1130,29 +956,7 @@ cleanup(struct bar *_bar) if (backend->pipe_fds[1] >= 0) close(backend->pipe_fds[1]); - tll_foreach(backend->monitors, it) - { - struct monitor *mon = &it->item; - free(mon->name); - - if (mon->xdg != NULL) - zxdg_output_v1_destroy(mon->xdg); - if (mon->output != NULL) - wl_output_release(mon->output); - tll_remove(backend->monitors, it); - } - free(backend->last_mapped_monitor); - - if (backend->xdg_output_manager != NULL) - zxdg_output_manager_v1_destroy(backend->xdg_output_manager); - - tll_foreach(backend->seats, it) seat_destroy(&it->item); - tll_free(backend->seats); - - destroy_surface(backend); - - tll_foreach(backend->buffers, it) - { + tll_foreach(backend->buffers, it) { if (it->item.wl_buf != NULL) wl_buffer_destroy(it->item.wl_buf); if (it->item.pix != NULL) @@ -1162,8 +966,30 @@ cleanup(struct bar *_bar) tll_remove(backend->buffers, it); } + tll_foreach(backend->monitors, it) { + struct monitor *mon = &it->item; + free(mon->name); + + if (mon->xdg != NULL) + zxdg_output_v1_destroy(mon->xdg); + if (mon->output != NULL) + wl_output_release(mon->output); + tll_remove(backend->monitors, it); + } + + if (backend->xdg_output_manager != NULL) + zxdg_output_manager_v1_destroy(backend->xdg_output_manager); + + tll_foreach(backend->seats, it) + seat_destroy(&it->item); + tll_free(backend->seats); + + if (backend->layer_surface != NULL) + zwlr_layer_surface_v1_destroy(backend->layer_surface); if (backend->layer_shell != NULL) zwlr_layer_shell_v1_destroy(backend->layer_shell); + if (backend->surface != NULL) + wl_surface_destroy(backend->surface); if (backend->compositor != NULL) wl_compositor_destroy(backend->compositor); if (backend->shm != NULL) @@ -1177,27 +1003,22 @@ cleanup(struct bar *_bar) /* Destroyed when freeing buffer list */ bar->pix = NULL; + } static void -loop(struct bar *_bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)) +loop(struct bar *_bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y)) { struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; - bool send_abort_to_modules = true; - - pthread_setname_np(pthread_self(), "bar(wayland)"); + backend->bar_expose = expose; backend->bar_on_mouse = on_mouse; - while (wl_display_prepare_read(backend->display) != 0) { - if (wl_display_dispatch_pending(backend->display) < 0) { - LOG_ERRNO("failed to dispatch pending Wayland events"); - goto out; - } - } - + while (wl_display_prepare_read(backend->display) != 0) + wl_display_dispatch_pending(backend->display); wl_display_flush(backend->display); while (true) { @@ -1209,82 +1030,51 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (fds[0].revents & POLLIN) { - /* Already done by the bar */ - send_abort_to_modules = false; break; } if (fds[1].revents & POLLHUP) { LOG_INFO("disconnected from wayland"); + if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) + != sizeof(uint64_t)) + { + LOG_ERRNO("failed to signal abort to modules"); + } break; } if (fds[2].revents & POLLIN) { - bool do_expose = false; - - /* Coalesce “refresh” commands */ - __attribute__((unused)) size_t count = 0; - while (true) { - uint8_t command; - ssize_t r = read(backend->pipe_fds[0], &command, sizeof(command)); - if (r < 0 && errno == EAGAIN) - break; - - if (r != sizeof(command)) { - LOG_ERRNO("failed to read from command pipe"); - goto out; - } - - assert(command == 1); - if (command == 1) { - count++; - do_expose = true; - } + uint8_t command; + if (read(backend->pipe_fds[0], &command, sizeof(command)) + != sizeof(command)) + { + LOG_ERRNO("failed to read from command pipe"); + break; } - LOG_DBG("coalesced %zu expose commands", count); - if (do_expose) - expose(_bar); + assert(command == 1); + expose(_bar); } if (fds[1].revents & POLLIN) { - if (wl_display_read_events(backend->display) < 0) { - LOG_ERRNO("failed to read events from the Wayland socket"); - goto out; - } - - while (wl_display_prepare_read(backend->display) != 0) { - if (wl_display_dispatch_pending(backend->display) < 0) { - LOG_ERRNO("failed to dispatch pending Wayland events"); - goto out; - } - } + wl_display_read_events(backend->display); + while (wl_display_prepare_read(backend->display) != 0) + wl_display_dispatch_pending(backend->display); wl_display_flush(backend->display); } } -out: - if (!send_abort_to_modules) - return; - - if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { - LOG_ERRNO("failed to signal abort to modules"); - } - - // wl_display_cancel_read(backend->display); + wl_display_cancel_read(backend->display); } static void -surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) +surface_enter(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { struct wayland_backend *backend = data; - free(backend->last_mapped_monitor); - backend->last_mapped_monitor = NULL; - - tll_foreach(backend->monitors, it) - { + tll_foreach(backend->monitors, it) { struct monitor *mon = &it->item; if (mon->output != wl_output) @@ -1304,18 +1094,11 @@ surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_ou } static void -surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) +surface_leave(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { struct wayland_backend *backend = data; - const struct monitor *mon = backend->monitor; - - assert(mon != NULL); - assert(mon->output == wl_output); - backend->monitor = NULL; - - assert(backend->last_mapped_monitor == NULL); - backend->last_mapped_monitor = mon->name != NULL ? strdup(mon->name) : NULL; } static const struct wl_surface_listener surface_listener = { @@ -1323,7 +1106,8 @@ static const struct wl_surface_listener surface_listener = { .leave = &surface_leave, }; -static void frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data); +static void frame_callback( + void *data, struct wl_callback *wl_callback, uint32_t callback_data); static const struct wl_callback_listener frame_listener = { .done = &frame_callback, @@ -1332,15 +1116,13 @@ static const struct wl_callback_listener frame_listener = { static void frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data) { - // printf("frame callback\n"); + //printf("frame callback\n"); struct private *bar = data; struct wayland_backend *backend = bar->backend.data; backend->render_scheduled = false; - assert(wl_callback == backend->frame_callback); wl_callback_destroy(wl_callback); - backend->frame_callback = NULL; if (backend->pending_buffer != NULL) { struct buffer *buffer = backend->pending_buffer; @@ -1355,11 +1137,10 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da wl_surface_commit(backend->surface); wl_display_flush(backend->display); - backend->frame_callback = cb; backend->pending_buffer = NULL; backend->render_scheduled = true; } else - ; // printf("nothing more to do\n"); + ;//printf("nothing more to do\n"); } static void @@ -1368,16 +1149,13 @@ commit(const struct bar *_bar) struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; - // printf("commit: %dxl%d\n", backend->width, backend->height); - - if (backend->next_buffer == NULL) - return; + //printf("commit: %dxl%d\n", backend->width, backend->height); assert(backend->next_buffer != NULL); assert(backend->next_buffer->busy); if (backend->render_scheduled) { - // printf("already scheduled\n"); + //printf("already scheduled\n"); if (backend->pending_buffer != NULL) backend->pending_buffer->busy = false; @@ -1386,7 +1164,7 @@ commit(const struct bar *_bar) backend->next_buffer = NULL; } else { - // printf("scheduling new frame callback\n"); + //printf("scheduling new frame callback\n"); struct buffer *buffer = backend->next_buffer; assert(buffer->busy); @@ -1400,7 +1178,6 @@ commit(const struct bar *_bar) wl_display_flush(backend->display); backend->render_scheduled = true; - backend->frame_callback = cb; } backend->next_buffer = get_buffer(backend); @@ -1414,7 +1191,9 @@ refresh(const struct bar *_bar) const struct private *bar = _bar->private; const struct wayland_backend *backend = bar->backend.data; - if (write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t)) != sizeof(uint8_t)) { + if (write(backend->pipe_fds[1], &(uint8_t){1}, sizeof(uint8_t)) + != sizeof(uint8_t)) + { LOG_ERRNO("failed to signal 'refresh' to main thread"); } } @@ -1434,7 +1213,8 @@ set_cursor(struct bar *_bar, const char *cursor) seat->pointer.xcursor = cursor; - seat->pointer.cursor = wl_cursor_theme_get_cursor(seat->pointer.theme, cursor); + seat->pointer.cursor = wl_cursor_theme_get_cursor( + seat->pointer.theme, cursor); if (seat->pointer.cursor == NULL) { LOG_ERR("%s: failed to load cursor '%s'", seat->name, cursor); @@ -1444,15 +1224,6 @@ set_cursor(struct bar *_bar, const char *cursor) update_cursor_surface(backend, seat); } -static const char * -bar_output_name(const struct bar *_bar) -{ - const struct private *bar = _bar->private; - const struct wayland_backend *backend = bar->backend.data; - - return backend->monitor != NULL ? backend->monitor->name : NULL; -} - const struct backend wayland_backend_iface = { .setup = &setup, .cleanup = &cleanup, @@ -1460,5 +1231,4 @@ const struct backend wayland_backend_iface = { .commit = &commit, .refresh = &refresh, .set_cursor = &set_cursor, - .output_name = &bar_output_name, }; diff --git a/bar/xcb.c b/bar/xcb.c index ae52bf3..f7a3ee1 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -1,16 +1,15 @@ #include "xcb.h" -#include #include +#include -#include -#include #include +#include #include +#include #include #include -#include #include #include #include @@ -39,6 +38,7 @@ struct xcb_backend { void *client_pixmap; size_t client_pixmap_size; pixman_image_t *pix; + }; void * @@ -54,8 +54,11 @@ setup(struct bar *_bar) struct private *bar = _bar->private; struct xcb_backend *backend = bar->backend.data; - if (bar->border.left_margin != 0 || bar->border.right_margin != 0 || bar->border.top_margin != 0 - || bar->border.bottom_margin) { + if (bar->border.left_margin != 0 || + bar->border.right_margin != 0 || + bar->border.top_margin != 0 || + bar->border.bottom_margin) + { LOG_WARN("non-zero border margins ignored in X11 backend"); } @@ -72,8 +75,10 @@ setup(struct bar *_bar) xcb_screen_t *screen = xcb_aux_get_screen(backend->conn, default_screen); - xcb_randr_get_monitors_reply_t *monitors - = xcb_randr_get_monitors_reply(backend->conn, xcb_randr_get_monitors(backend->conn, screen->root, 0), &e); + xcb_randr_get_monitors_reply_t *monitors = xcb_randr_get_monitors_reply( + backend->conn, + xcb_randr_get_monitors(backend->conn, screen->root, 0), + &e); if (e != NULL) { LOG_ERR("failed to get monitor list: %s", xcb_error(e)); @@ -84,13 +89,17 @@ setup(struct bar *_bar) /* Find monitor coordinates and width/height */ bool found_monitor = false; - for (xcb_randr_monitor_info_iterator_t it = xcb_randr_get_monitors_monitors_iterator(monitors); it.rem > 0; - xcb_randr_monitor_info_next(&it)) { + for (xcb_randr_monitor_info_iterator_t it = + xcb_randr_get_monitors_monitors_iterator(monitors); + it.rem > 0; + xcb_randr_monitor_info_next(&it)) + { const xcb_randr_monitor_info_t *mon = it.data; char *name = get_atom_name(backend->conn, mon->name); - LOG_INFO("monitor: %s: %ux%u+%u+%u (%ux%umm)", name, mon->width, mon->height, mon->x, mon->y, - mon->width_in_millimeters, mon->height_in_millimeters); + LOG_INFO("monitor: %s: %ux%u+%u+%u (%ux%umm)", name, + mon->width, mon->height, mon->x, mon->y, + mon->width_in_millimeters, mon->height_in_millimeters); /* User wants a specific monitor, and this is not the one */ if (bar->monitor != NULL && strcmp(bar->monitor, name) != 0) { @@ -101,11 +110,14 @@ setup(struct bar *_bar) backend->x = mon->x; backend->y = mon->y; bar->width = mon->width; - backend->y += bar->location == BAR_TOP ? 0 : mon->height - bar->height_with_border; + backend->y += bar->location == BAR_TOP ? 0 + : screen->height_in_pixels - bar->height_with_border; found_monitor = true; - if ((bar->monitor != NULL && strcmp(bar->monitor, name) == 0) || (bar->monitor == NULL && mon->primary)) { + if ((bar->monitor != NULL && strcmp(bar->monitor, name) == 0) || + (bar->monitor == NULL && mon->primary)) + { /* Exact match */ free(name); break; @@ -142,47 +154,74 @@ setup(struct bar *_bar) LOG_DBG("using a %hhu-bit visual", depth); backend->colormap = xcb_generate_id(backend->conn); - xcb_create_colormap(backend->conn, 0, backend->colormap, screen->root, vis->visual_id); + xcb_create_colormap( + backend->conn, 0, backend->colormap, screen->root, vis->visual_id); backend->win = xcb_generate_id(backend->conn); xcb_create_window( - backend->conn, depth, backend->win, screen->root, backend->x, backend->y, bar->width, bar->height_with_border, - 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, vis->visual_id, - (XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP), - (const uint32_t[]){screen->black_pixel, screen->white_pixel, - (XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS - | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_STRUCTURE_NOTIFY), - backend->colormap}); + backend->conn, + depth, backend->win, screen->root, + backend->x, backend->y, bar->width, bar->height_with_border, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, vis->visual_id, + (XCB_CW_BACK_PIXEL | + XCB_CW_BORDER_PIXEL | + XCB_CW_EVENT_MASK | + XCB_CW_COLORMAP), + (const uint32_t []){ + screen->black_pixel, + screen->white_pixel, + (XCB_EVENT_MASK_EXPOSURE | + XCB_EVENT_MASK_BUTTON_RELEASE | + XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_STRUCTURE_NOTIFY), + backend->colormap} + ); const char *title = "yambar"; - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - strlen(title), title); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + strlen(title), title); - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, - (const uint32_t[]){getpid()}); - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, 1, - (const uint32_t[]){_NET_WM_WINDOW_TYPE_DOCK}); - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_STATE, XCB_ATOM_ATOM, 32, 2, - (const uint32_t[]){_NET_WM_STATE_ABOVE, _NET_WM_STATE_STICKY}); - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, - (const uint32_t[]){0xffffffff}); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){getpid()}); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, + 1, (const uint32_t []){_NET_WM_WINDOW_TYPE_DOCK}); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + _NET_WM_STATE, XCB_ATOM_ATOM, 32, + 2, (const uint32_t []){_NET_WM_STATE_ABOVE, _NET_WM_STATE_STICKY}); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){0xffffffff}); /* Always on top */ - xcb_configure_window(backend->conn, backend->win, XCB_CONFIG_WINDOW_STACK_MODE, - (const uint32_t[]){XCB_STACK_MODE_ABOVE}); + xcb_configure_window( + backend->conn, backend->win, XCB_CONFIG_WINDOW_STACK_MODE, + (const uint32_t []){XCB_STACK_MODE_ABOVE}); uint32_t top_strut, bottom_strut; uint32_t top_pair[2], bottom_pair[2]; if (bar->location == BAR_TOP) { - top_strut = bar->height_with_border; + top_strut = backend->y + bar->height_with_border; top_pair[0] = backend->x; top_pair[1] = backend->x + bar->width - 1; bottom_strut = 0; bottom_pair[0] = bottom_pair[1] = 0; } else { - bottom_strut = bar->height_with_border; + bottom_strut = screen->height_in_pixels - backend->y; bottom_pair[0] = backend->x; bottom_pair[1] = backend->x + bar->width - 1; @@ -192,38 +231,42 @@ setup(struct bar *_bar) uint32_t strut[] = { /* left/right/top/bottom */ - 0, - 0, + 0, 0, top_strut, bottom_strut, /* start/end pairs for left/right/top/bottom */ - 0, - 0, - 0, - 0, - top_pair[0], - top_pair[1], - bottom_pair[0], - bottom_pair[1], + 0, 0, + 0, 0, + top_pair[0], top_pair[1], + bottom_pair[0], bottom_pair[1], }; - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, 4, - strut); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, + 4, strut); - xcb_change_property(backend->conn, XCB_PROP_MODE_REPLACE, backend->win, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, - 32, 12, strut); + xcb_change_property( + backend->conn, + XCB_PROP_MODE_REPLACE, backend->win, + _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, + 12, strut); backend->gc = xcb_generate_id(backend->conn); - xcb_create_gc(backend->conn, backend->gc, backend->win, XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, - (const uint32_t[]){screen->white_pixel, 0}); + xcb_create_gc(backend->conn, backend->gc, backend->win, + XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, + (const uint32_t []){screen->white_pixel, 0}); - const uint32_t stride = stride_for_format_and_width(PIXMAN_a8r8g8b8, bar->width); + const uint32_t stride = stride_for_format_and_width( + PIXMAN_a8r8g8b8, bar->width); backend->client_pixmap_size = stride * bar->height_with_border; backend->client_pixmap = malloc(backend->client_pixmap_size); - backend->pix = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, bar->width, bar->height_with_border, - (uint32_t *)backend->client_pixmap, stride); + backend->pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, bar->width, bar->height_with_border, + (uint32_t *)backend->client_pixmap, stride); bar->pix = backend->pix; xcb_map_window(backend->conn, backend->win); @@ -266,18 +309,20 @@ cleanup(struct bar *_bar) } static void -loop(struct bar *_bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)) +loop(struct bar *_bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y)) { 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) { - struct pollfd fds[] = {{.fd = _bar->abort_fd, .events = POLLIN}, {.fd = fd, .events = POLLIN}}; + struct pollfd fds[] = { + {.fd = _bar->abort_fd, .events = POLLIN}, + {.fd = fd, .events = POLLIN} + }; poll(fds, sizeof(fds) / sizeof(fds[0]), -1); @@ -286,14 +331,18 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), if (fds[1].revents & POLLHUP) { LOG_WARN("disconnected from XCB"); - if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { + if (write(_bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) + != sizeof(uint64_t)) + { LOG_ERRNO("failed to signal abort to modules"); } break; } - for (xcb_generic_event_t *e = xcb_wait_for_event(backend->conn); e != NULL; - e = xcb_poll_for_event(backend->conn)) { + for (xcb_generic_event_t *e = xcb_wait_for_event(backend->conn); + e != NULL; + e = xcb_poll_for_event(backend->conn)) + { switch (XCB_EVENT_RESPONSE_TYPE(e)) { case 0: LOG_ERR("XCB: %s", xcb_error((const xcb_generic_error_t *)e)); @@ -305,7 +354,7 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), case XCB_MOTION_NOTIFY: { const xcb_motion_notify_event_t *evt = (void *)e; - on_mouse(_bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, evt->event_x, evt->event_y); + on_mouse(_bar, ON_MOUSE_MOTION, evt->event_x, evt->event_y); break; } @@ -314,16 +363,7 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), case XCB_BUTTON_RELEASE: { const xcb_button_release_event_t *evt = (void *)e; - - switch (evt->detail) { - case 1: - case 2: - case 3: - case 4: - case 5: - on_mouse(_bar, ON_MOUSE_CLICK, evt->detail, evt->event_x, evt->event_y); - break; - } + on_mouse(_bar, ON_MOUSE_CLICK, evt->event_x, evt->event_y); break; } @@ -355,9 +395,10 @@ commit(const struct bar *_bar) const struct private *bar = _bar->private; const struct xcb_backend *backend = bar->backend.data; - xcb_put_image(backend->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, backend->win, backend->gc, bar->width, - bar->height_with_border, 0, 0, 0, backend->depth, backend->client_pixmap_size, - backend->client_pixmap); + xcb_put_image( + backend->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, backend->win, backend->gc, + bar->width, bar->height_with_border, 0, 0, 0, + backend->depth, backend->client_pixmap_size, backend->client_pixmap); xcb_flush(backend->conn); } @@ -369,19 +410,23 @@ refresh(const struct bar *_bar) /* Send an event to handle refresh from main thread */ - /* Note: docs say that all X11 events are 32 bytes, regardless of + /* Note: docs say that all X11 events are 32 bytes, reglardless of * the size of the event structure */ xcb_expose_event_t *evt = calloc(32, 1); - *evt = (xcb_expose_event_t){.response_type = XCB_EXPOSE, - .window = backend->win, - .x = 0, - .y = 0, - .width = bar->width, - .height = bar->height, - .count = 1}; + *evt = (xcb_expose_event_t){ + .response_type = XCB_EXPOSE, + .window = backend->win, + .x = 0, + .y = 0, + .width = bar->width, + .height = bar->height, + .count = 1 + }; - xcb_send_event(backend->conn, false, backend->win, XCB_EVENT_MASK_EXPOSURE, (char *)evt); + xcb_send_event( + backend->conn, false, backend->win, XCB_EVENT_MASK_EXPOSURE, + (char *)evt); xcb_flush(backend->conn); free(evt); @@ -403,14 +448,8 @@ set_cursor(struct bar *_bar, const char *cursor) xcb_free_cursor(backend->conn, backend->cursor); backend->cursor = xcb_cursor_load_cursor(backend->cursor_ctx, cursor); - xcb_change_window_attributes(backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor); -} - -static const char * -output_name(const struct bar *_bar) -{ - /* Not implemented */ - return NULL; + xcb_change_window_attributes( + backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor); } const struct backend xcb_backend_iface = { @@ -420,5 +459,4 @@ const struct backend xcb_backend_iface = { .commit = &commit, .refresh = &refresh, .set_cursor = &set_cursor, - .output_name = &output_name, }; diff --git a/char32.c b/char32.c deleted file mode 100644 index eb3abb4..0000000 --- a/char32.c +++ /dev/null @@ -1,83 +0,0 @@ -#include "char32.h" - -#include -#include -#include - -#include - -#if defined __has_include -#if __has_include() -#include -#endif -#endif - -#define LOG_MODULE "char32" -#define LOG_ENABLE_DBG 0 -#include "log.h" - -/* - * For now, assume we can map directly to the corresponding wchar_t - * functions. This is true if: - * - * - both data types have the same size - * - both use the same encoding (though we require that encoding to be UTF-32) - */ - -_Static_assert(sizeof(wchar_t) == sizeof(char32_t), "wchar_t vs. char32_t size mismatch"); - -#if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__ -#error "char32_t does not use UTF-32" -#endif -#if (!defined(__STDC_ISO_10646__) || !__STDC_ISO_10646__) && !defined(__FreeBSD__) -#error "wchar_t does not use UTF-32" -#endif - -size_t -c32len(const char32_t *s) -{ - return wcslen((const wchar_t *)s); -} - -char32_t * -ambstoc32(const char *src) -{ - if (src == NULL) - return NULL; - - const size_t src_len = strlen(src); - - char32_t *ret = malloc((src_len + 1) * sizeof(ret[0])); - if (ret == NULL) - return NULL; - - mbstate_t ps = {0}; - char32_t *out = ret; - const char *in = src; - const char *const end = src + src_len + 1; - - size_t chars = 0; - size_t rc; - - while ((rc = mbrtoc32(out, in, end - in, &ps)) != 0) { - switch (rc) { - case (size_t)-1: - case (size_t)-2: - case (size_t)-3: - goto err; - } - - in += rc; - out++; - chars++; - } - - *out = U'\0'; - - ret = realloc(ret, (chars + 1) * sizeof(ret[0])); - return ret; - -err: - free(ret); - return NULL; -} diff --git a/char32.h b/char32.h deleted file mode 100644 index 1e7a9de..0000000 --- a/char32.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include - -size_t c32len(const char32_t *s); -char32_t *ambstoc32(const char *src); diff --git a/completions/zsh/_yambar b/completions/zsh/_yambar index 90b1117..aa8172f 100644 --- a/completions/zsh/_yambar +++ b/completions/zsh/_yambar @@ -8,6 +8,5 @@ _arguments \ '(-c --config)'{-c,--config}'[alternative configuration file]:filename:_files' \ '(-C --validate)'{-C,--validate}'[verify configuration then quit]' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running]:pidfile:_files' \ - '(-d --log-level)'{-d,--log-level}'[log level (warning)]:loglevel:(info warning error none)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-s --log-no-syslog)'{-s,--log-no-syslog}'[disable syslog logging]' diff --git a/config-verify.c b/config-verify.c index ed7d2f5..7e87d42 100644 --- a/config-verify.c +++ b/config-verify.c @@ -1,7 +1,7 @@ #include "config.h" -#include #include +#include #include @@ -16,9 +16,11 @@ conf_err_prefix(const keychain_t *chain, const struct yml_node *node) static char msg[4096]; int idx = 0; - idx += snprintf(&msg[idx], sizeof(msg) - idx, "%zu:%zu: ", yml_source_line(node), yml_source_column(node)); + idx += snprintf(&msg[idx], sizeof(msg) - idx, "%zu:%zu: ", + yml_source_line(node), yml_source_column(node)); - tll_foreach(*chain, key) idx += snprintf(&msg[idx], sizeof(msg) - idx, "%s.", key->item); + tll_foreach(*chain, key) + idx += snprintf(&msg[idx], sizeof(msg) - idx, "%s.", key->item); /* Remove trailing "." */ msg[idx - 1] = '\0'; @@ -43,27 +45,8 @@ conf_verify_int(keychain_t *chain, const struct yml_node *node) if (yml_value_is_int(node)) return true; - LOG_ERR("%s: value is not an integer: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); - return false; -} - -bool -conf_verify_unsigned(keychain_t *chain, const struct yml_node *node) -{ - if (yml_value_is_int(node) && yml_value_as_int(node) >= 0) - return true; - - LOG_ERR("%s: value is not a positive integer: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); - return false; -} - -bool -conf_verify_bool(keychain_t *chain, const struct yml_node *node) -{ - if (yml_value_is_bool(node)) - return true; - - LOG_ERR("%s: value is not a boolean: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); + LOG_ERR("%s: value is not an integer: '%s'", + conf_err_prefix(chain, node), yml_value_as_string(node)); return false; } @@ -76,7 +59,10 @@ conf_verify_list(keychain_t *chain, const struct yml_node *node, return false; } - for (struct yml_list_iter iter = yml_list_iter(node); iter.node != NULL; yml_list_next(&iter)) { + for (struct yml_list_iter iter = yml_list_iter(node); + iter.node != NULL; + yml_list_next(&iter)) + { if (!verify(chain, iter.node)) return false; } @@ -85,7 +71,8 @@ conf_verify_list(keychain_t *chain, const struct yml_node *node, } bool -conf_verify_enum(keychain_t *chain, const struct yml_node *node, const char *values[], size_t count) +conf_verify_enum(keychain_t *chain, const struct yml_node *node, + const char *values[], size_t count) { const char *s = yml_value_as_string(node); if (s == NULL) { @@ -106,7 +93,8 @@ conf_verify_enum(keychain_t *chain, const struct yml_node *node, const char *val } bool -conf_verify_dict(keychain_t *chain, const struct yml_node *node, const struct attr_info info[]) +conf_verify_dict(keychain_t *chain, const struct yml_node *node, + const struct attr_info info[]) { if (!yml_is_dict(node)) { LOG_ERR("%s: must be a dictionary", conf_err_prefix(chain, node)); @@ -121,7 +109,10 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node, const struct at bool exists[count]; memset(exists, 0, sizeof(exists)); - for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) { + for (struct yml_dict_iter it = yml_dict_iter(node); + it.key != NULL; + yml_dict_next(&it)) + { const char *key = yml_value_as_string(it.key); if (key == NULL) { LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key)); @@ -161,48 +152,6 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node, const struct at return true; } -static bool -verify_on_click_path(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_string(chain, node)) - return false; - -#if 1 - /* We allow non-absolute paths in on-click handlers */ - return true; -#else - const char *path = yml_value_as_string(node); - - const bool is_absolute = path[0] == '/'; - const bool is_tilde = path[0] == '~' && path[1] == '/'; - - if (!is_absolute && !is_tilde) { - LOG_ERR("%s: path must be either absolute, or begin with '~/", conf_err_prefix(chain, node)); - return false; - } - - return true; -#endif -} - -bool -conf_verify_on_click(keychain_t *chain, const struct yml_node *node) -{ - /* on-click: */ - const char *s = yml_value_as_string(node); - if (s != NULL) - return verify_on_click_path(chain, node); - - static const struct attr_info info[] = { - {"left", false, &verify_on_click_path}, {"middle", false, &verify_on_click_path}, - {"right", false, &verify_on_click_path}, {"wheel-up", false, &verify_on_click_path}, - {"wheel-down", false, &verify_on_click_path}, {"previous", false, &verify_on_click_path}, - {"next", false, &verify_on_click_path}, {NULL, false, NULL}, - }; - - return conf_verify_dict(chain, node, info); -} - bool conf_verify_color(keychain_t *chain, const struct yml_node *node) { @@ -216,30 +165,27 @@ conf_verify_color(keychain_t *chain, const struct yml_node *node) int v = sscanf(s, "%02x%02x%02x%02x", &r, &g, &b, &a); if (strlen(s) != 8 || v != 4) { - LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)", conf_err_prefix(chain, node)); + LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)", + conf_err_prefix(chain, node)); return false; } return true; } + bool conf_verify_font(keychain_t *chain, const struct yml_node *node) { if (!yml_is_scalar(node)) { - LOG_ERR("%s: font must be a fontconfig-formatted string", conf_err_prefix(chain, node)); + LOG_ERR("%s: font must be a fontconfig-formatted string", + conf_err_prefix(chain, node)); return false; } return true; } -bool -conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node) -{ - return conf_verify_enum(chain, node, (const char *[]){"full", /*"graphemes",*/ "none"}, 2); -} - bool conf_verify_decoration(keychain_t *chain, const struct yml_node *node) { @@ -247,8 +193,7 @@ conf_verify_decoration(keychain_t *chain, const struct yml_node *node) if (yml_dict_length(node) != 1) { LOG_ERR("%s: decoration must be a dictionary with a single key; " - "the name of the particle", - conf_err_prefix(chain, node)); + "the name of the particle", conf_err_prefix(chain, node)); return false; } @@ -264,7 +209,8 @@ conf_verify_decoration(keychain_t *chain, const struct yml_node *node) const struct deco_iface *iface = plugin_load_deco(deco_name); if (iface == NULL) { - LOG_ERR("%s: invalid decoration name: %s", conf_err_prefix(chain, deco), deco_name); + LOG_ERR("%s: invalid decoration name: %s", + conf_err_prefix(chain, deco), deco_name); return false; } @@ -281,7 +227,10 @@ conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node) { assert(yml_is_list(node)); - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it)) + { if (!conf_verify_particle(chain, it.node)) return false; } @@ -296,8 +245,7 @@ conf_verify_particle_dictionary(keychain_t *chain, const struct yml_node *node) if (yml_dict_length(node) != 1) { LOG_ERR("%s: particle must be a dictionary with a single key; " - "the name of the particle", - conf_err_prefix(chain, node)); + "the name of the particle", conf_err_prefix(chain, node)); return false; } @@ -313,7 +261,8 @@ conf_verify_particle_dictionary(keychain_t *chain, const struct yml_node *node) const struct particle_iface *iface = plugin_load_particle(particle_name); if (iface == NULL) { - LOG_ERR("%s: invalid particle name: %s", conf_err_prefix(chain, particle), particle_name); + LOG_ERR("%s: invalid particle name: %s", + conf_err_prefix(chain, particle), particle_name); return false; } @@ -335,18 +284,19 @@ conf_verify_particle(keychain_t *chain, const struct yml_node *node) else if (yml_is_list(node)) return conf_verify_particle_list_items(chain, node); else { - LOG_ERR("%s: particle must be either a dictionary or a list", conf_err_prefix(chain, node)); + LOG_ERR("%s: particle must be either a dictionary or a list", + conf_err_prefix(chain, node)); return false; } } + static bool verify_module(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node) || yml_dict_length(node) != 1) { LOG_ERR("%s: module must be a dictionary with a single key; " - "the name of the module", - conf_err_prefix(chain, node)); + "the name of the module", conf_err_prefix(chain, node)); return false; } @@ -362,7 +312,8 @@ verify_module(keychain_t *chain, const struct yml_node *node) const struct module_iface *iface = plugin_load_module(mod_name); if (iface == NULL) { - LOG_ERR("%s: invalid module name: %s", conf_err_prefix(chain, node), mod_name); + LOG_ERR( + "%s: invalid module name: %s", conf_err_prefix(chain, node), mod_name); return false; } @@ -384,7 +335,10 @@ verify_module_list(keychain_t *chain, const struct yml_node *node) return false; } - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it)) + { if (!verify_module(chain, it.node)) return false; } @@ -396,12 +350,14 @@ static bool verify_bar_border(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"width", false, &conf_verify_unsigned}, {"left-width", false, &conf_verify_unsigned}, - {"right-width", false, &conf_verify_unsigned}, {"top-width", false, &conf_verify_unsigned}, - {"bottom-width", false, &conf_verify_unsigned}, {"color", false, &conf_verify_color}, - {"margin", false, &conf_verify_unsigned}, {"left-margin", false, &conf_verify_unsigned}, - {"right-margin", false, &conf_verify_unsigned}, {"top-margin", false, &conf_verify_unsigned}, - {"bottom-margin", false, &conf_verify_unsigned}, {NULL, false, NULL}, + {"width", false, &conf_verify_int}, + {"color", false, &conf_verify_color}, + {"margin", false, &conf_verify_int}, + {"left-margin", false, &conf_verify_int}, + {"right-margin", false, &conf_verify_int}, + {"top-margin", false, &conf_verify_int}, + {"bottom-margin", false, &conf_verify_int}, + {NULL, false, NULL}, }; return conf_verify_dict(chain, node, attrs); @@ -413,12 +369,6 @@ verify_bar_location(keychain_t *chain, const struct yml_node *node) return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2); } -static bool -verify_bar_layer(keychain_t *chain, const struct yml_node *node) -{ - return conf_verify_enum(chain, node, (const char *[]){"overlay", "top", "bottom", "background"}, 4); -} - bool conf_verify_bar(const struct yml_node *bar) { @@ -431,32 +381,28 @@ conf_verify_bar(const struct yml_node *bar) chain_push(&chain, "bar"); static const struct attr_info attrs[] = { - {"height", true, &conf_verify_unsigned}, + {"height", true, &conf_verify_int}, {"location", true, &verify_bar_location}, {"background", true, &conf_verify_color}, {"monitor", false, &conf_verify_string}, - {"layer", false, &verify_bar_layer}, - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, + {"spacing", false, &conf_verify_int}, + {"left-spacing", false, &conf_verify_int}, + {"right-spacing", false, &conf_verify_int}, - {"margin", false, &conf_verify_unsigned}, - {"left-margin", false, &conf_verify_unsigned}, - {"right-margin", false, &conf_verify_unsigned}, + {"margin", false, &conf_verify_int}, + {"left_margin", false, &conf_verify_int}, + {"right_margin", false, &conf_verify_int}, {"border", false, &verify_bar_border}, {"font", false, &conf_verify_font}, - {"font-shaping", false, &conf_verify_font_shaping}, {"foreground", false, &conf_verify_color}, {"left", false, &verify_module_list}, {"center", false, &verify_module_list}, {"right", false, &verify_module_list}, - {"trackpad-sensitivity", false, &conf_verify_unsigned}, - {NULL, false, NULL}, }; diff --git a/config-verify.h b/config-verify.h index 8ad44ce..44a6a66 100644 --- a/config-verify.h +++ b/config-verify.h @@ -26,23 +26,22 @@ chain_pop(keychain_t *chain) tll_pop_back(*chain); } -const char *conf_err_prefix(const keychain_t *chain, const struct yml_node *node); +const char *conf_err_prefix( + const keychain_t *chain, const struct yml_node *node); + bool conf_verify_string(keychain_t *chain, const struct yml_node *node); bool conf_verify_int(keychain_t *chain, const struct yml_node *node); -bool conf_verify_unsigned(keychain_t *chain, const struct yml_node *node); -bool conf_verify_bool(keychain_t *chain, const struct yml_node *node); -bool conf_verify_enum(keychain_t *chain, const struct yml_node *node, const char *values[], size_t count); +bool conf_verify_enum(keychain_t *chain, const struct yml_node *node, + const char *values[], size_t count); bool conf_verify_list(keychain_t *chain, const struct yml_node *node, bool (*verify)(keychain_t *chain, const struct yml_node *node)); bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, 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); -bool conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node); diff --git a/config.c b/config.c index 0f80364..aaa62f9 100644 --- a/config.c +++ b/config.c @@ -1,10 +1,9 @@ #include "config.h" -#include -#include -#include #include +#include #include +#include #include @@ -21,7 +20,9 @@ static uint8_t hex_nibble(char hex) { - assert((hex >= '0' && hex <= '9') || (hex >= 'a' && hex <= 'f') || (hex >= 'A' && hex <= 'F')); + assert((hex >= '0' && hex <= '9') || + (hex >= 'a' && hex <= 'f') || + (hex >= 'A' && hex <= 'F')); if (hex >= '0' && hex <= '9') return hex - '0'; @@ -55,9 +56,9 @@ conf_to_color(const struct yml_node *node) alpha |= alpha << 8; return (pixman_color_t){ - .red = (uint32_t)(red << 8 | red) * alpha / 0xffff, + .red = (uint32_t)(red << 8 | red) * alpha / 0xffff, .green = (uint32_t)(green << 8 | green) * alpha / 0xffff, - .blue = (uint32_t)(blue << 8 | blue) * alpha / 0xffff, + .blue = (uint32_t)(blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; } @@ -65,69 +66,7 @@ conf_to_color(const struct yml_node *node) struct fcft_font * conf_to_font(const struct yml_node *node) { - const char *font_spec = yml_value_as_string(node); - - size_t count = 0; - size_t size = 0; - const char **fonts = NULL; - - char *copy = strdup(font_spec); - for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) { - /* Trim spaces, strictly speaking not necessary, but looks nice :) */ - while (isspace(font[0])) - font++; - - if (font[0] == '\0') - continue; - - if (count + 1 > size) { - size += 4; - fonts = realloc(fonts, size * sizeof(fonts[0])); - } - - assert(count + 1 <= size); - fonts[count++] = font; - } - - struct fcft_font *ret = fcft_from_name(count, fonts, NULL); - - free(fonts); - free(copy); - return ret; -} - -enum font_shaping -conf_to_font_shaping(const struct yml_node *node) -{ - const char *v = yml_value_as_string(node); - - if (strcmp(v, "none") == 0) - return FONT_SHAPE_NONE; - - else if (strcmp(v, "graphemes") == 0) { - static bool have_warned = false; - - if (!have_warned && !(fcft_capabilities() & FCFT_CAPABILITY_GRAPHEME_SHAPING)) { - have_warned = true; - LOG_WARN("cannot enable grapheme shaping; no support in fcft"); - } - return FONT_SHAPE_GRAPHEMES; - } - - else if (strcmp(v, "full") == 0) { - static bool have_warned = false; - - if (!have_warned && !(fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING)) { - have_warned = true; - LOG_WARN("cannot enable full text shaping; no support in fcft"); - } - return FONT_SHAPE_FULL; - } - - else { - assert(false); - return FONT_SHAPE_NONE; - } + return fcft_from_name(1, &(const char *){yml_value_as_string(node)}, NULL); } struct deco * @@ -145,20 +84,25 @@ conf_to_deco(const struct yml_node *node) } static struct particle * -particle_simple_list_from_config(const struct yml_node *node, struct conf_inherit inherited) +particle_simple_list_from_config(const struct yml_node *node, + struct conf_inherit inherited) { size_t count = yml_list_length(node); struct particle *parts[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it), idx++) + { parts[idx] = conf_to_particle(it.node, inherited); } /* Lazy-loaded function pointer to particle_list_new() */ - static struct particle *(*particle_list_new)(struct particle *common, struct particle *particles[], size_t count, - int left_spacing, int right_spacing) - = NULL; + static struct particle *(*particle_list_new)( + struct particle *common, + struct particle *particles[], size_t count, + int left_spacing, int right_spacing) = NULL; if (particle_list_new == NULL) { const struct plugin *plug = plugin_load("list", PLUGIN_PARTICLE); @@ -167,8 +111,8 @@ particle_simple_list_from_config(const struct yml_node *node, struct conf_inheri assert(particle_list_new != NULL); } - struct particle *common = particle_common_new(0, 0, NULL, fcft_clone(inherited.font), inherited.font_shaping, - inherited.foreground, NULL); + struct particle *common = particle_common_new( + 0, 0, NULL, fcft_clone(inherited.font), inherited.foreground, NULL); return particle_list_new(common, parts, count, 0, 2); } @@ -187,74 +131,16 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_margin = yml_get_value(pair.value, "right-margin"); const struct yml_node *on_click = yml_get_value(pair.value, "on-click"); const struct yml_node *font_node = yml_get_value(pair.value, "font"); - const struct yml_node *font_shaping_node = yml_get_value(pair.value, "font-shaping"); const struct yml_node *foreground_node = yml_get_value(pair.value, "foreground"); const struct yml_node *deco_node = yml_get_value(pair.value, "deco"); - int left = margin != NULL ? yml_value_as_int(margin) : left_margin != NULL ? yml_value_as_int(left_margin) : 0; - int right = margin != NULL ? yml_value_as_int(margin) : right_margin != NULL ? yml_value_as_int(right_margin) : 0; - - char *on_click_templates[MOUSE_BTN_COUNT] = {NULL}; - if (on_click != NULL) { - const char *yml_legacy = yml_value_as_string(on_click); - - if (yml_legacy != NULL) { - char *legacy = NULL; - - if (yml_legacy[0] == '~' && yml_legacy[1] == '/') { - const char *home_dir = getenv("HOME"); - - if (home_dir != NULL) - if (asprintf(&legacy, "%s/%s", home_dir, yml_legacy + 2) < 0) - legacy = NULL; - - if (legacy == NULL) - legacy = strdup(yml_legacy); - } else - legacy = strdup(yml_legacy); - - on_click_templates[MOUSE_BTN_LEFT] = legacy; - } - - else if (yml_is_dict(on_click)) { - for (struct yml_dict_iter it = yml_dict_iter(on_click); it.key != NULL; yml_dict_next(&it)) { - const char *key = yml_value_as_string(it.key); - const char *yml_template = yml_value_as_string(it.value); - - char *template = NULL; - - if (yml_template[0] == '~' && yml_template[1] == '/') { - const char *home_dir = getenv("HOME"); - - if (home_dir != NULL) - if (asprintf(&template, "%s/%s", home_dir, yml_template + 2) < 0) - template = NULL; - - if (template == NULL) - template = strdup(yml_template); - } else - template = strdup(yml_template); - - 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 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 if (strcmp(key, "previous") == 0) - on_click_templates[MOUSE_BTN_PREVIOUS] = template; - else if (strcmp(key, "next") == 0) - on_click_templates[MOUSE_BTN_NEXT] = template; - else - assert(false); - } - } - } + int left = margin != NULL ? yml_value_as_int(margin) : + left_margin != NULL ? yml_value_as_int(left_margin) : 0; + int right = margin != NULL ? yml_value_as_int(margin) : + right_margin != NULL ? yml_value_as_int(right_margin) : 0; + const char *on_click_template + = on_click != NULL ? yml_value_as_string(on_click) : NULL; struct deco *deco = deco_node != NULL ? conf_to_deco(deco_node) : NULL; /* @@ -266,14 +152,14 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) * clone the font, since each particle takes ownership of its own * font. */ - struct fcft_font *font = font_node != NULL ? conf_to_font(font_node) : fcft_clone(inherited.font); - enum font_shaping font_shaping - = font_shaping_node != NULL ? conf_to_font_shaping(font_shaping_node) : inherited.font_shaping; - pixman_color_t foreground = foreground_node != NULL ? conf_to_color(foreground_node) : inherited.foreground; + struct fcft_font *font = font_node != NULL + ? conf_to_font(font_node) : fcft_clone(inherited.font); + pixman_color_t foreground = foreground_node != NULL + ? conf_to_color(foreground_node) : inherited.foreground; /* Instantiate base/common particle */ - struct particle *common - = particle_common_new(left, right, on_click_templates, font, font_shaping, foreground, deco); + struct particle *common = particle_common_new( + left, right, on_click_template, font, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); @@ -289,8 +175,6 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) struct bar_config conf = { .backend = backend, - .layer = BAR_LAYER_BOTTOM, - .font_shaping = FONT_SHAPE_FULL, }; /* @@ -301,7 +185,8 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) conf.height = yml_value_as_int(height); const struct yml_node *location = yml_get_value(bar, "location"); - conf.location = strcmp(yml_value_as_string(location), "top") == 0 ? BAR_TOP : BAR_BOTTOM; + conf.location = strcmp(yml_value_as_string(location), "top") == 0 + ? BAR_TOP : BAR_BOTTOM; const struct yml_node *background = yml_get_value(bar, "background"); conf.background = conf_to_color(background); @@ -314,21 +199,6 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) if (monitor != NULL) conf.monitor = yml_value_as_string(monitor); - const struct yml_node *layer = yml_get_value(bar, "layer"); - if (layer != NULL) { - const char *tmp = yml_value_as_string(layer); - if (strcmp(tmp, "overlay") == 0) - conf.layer = BAR_LAYER_OVERLAY; - else if (strcmp(tmp, "top") == 0) - conf.layer = BAR_LAYER_TOP; - else if (strcmp(tmp, "bottom") == 0) - conf.layer = BAR_LAYER_BOTTOM; - else if (strcmp(tmp, "background") == 0) - conf.layer = BAR_LAYER_BACKGROUND; - else - assert(false); - } - const struct yml_node *spacing = yml_get_value(bar, "spacing"); if (spacing != NULL) conf.left_spacing = conf.right_spacing = yml_value_as_int(spacing); @@ -353,16 +223,9 @@ 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"); - const struct yml_node *left_width = yml_get_value(border, "left-width"); - const struct yml_node *right_width = yml_get_value(border, "right-width"); - const struct yml_node *top_width = yml_get_value(border, "top-width"); - const struct yml_node *bottom_width = yml_get_value(border, "bottom-width"); const struct yml_node *color = yml_get_value(border, "color"); const struct yml_node *margin = yml_get_value(border, "margin"); const struct yml_node *left_margin = yml_get_value(border, "left-margin"); @@ -371,24 +234,16 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *bottom_margin = yml_get_value(border, "bottom-margin"); if (width != NULL) - conf.border.left_width = conf.border.right_width = conf.border.top_width = conf.border.bottom_width - = yml_value_as_int(width); - - if (left_width != NULL) - conf.border.left_width = yml_value_as_int(left_width); - if (right_width != NULL) - conf.border.right_width = yml_value_as_int(right_width); - if (top_width != NULL) - conf.border.top_width = yml_value_as_int(top_width); - if (bottom_width != NULL) - conf.border.bottom_width = yml_value_as_int(bottom_width); + conf.border.width = yml_value_as_int(width); if (color != NULL) conf.border.color = conf_to_color(color); if (margin != NULL) - conf.border.left_margin = conf.border.right_margin = conf.border.top_margin = conf.border.bottom_margin - = yml_value_as_int(margin); + conf.border.left_margin = + conf.border.right_margin = + conf.border.top_margin = + conf.border.bottom_margin = yml_value_as_int(margin); if (left_margin != NULL) conf.border.left_margin = yml_value_as_int(left_margin); @@ -408,7 +263,6 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) * foreground color at top-level. */ struct fcft_font *font = fcft_from_name(1, &(const char *){"sans"}, NULL); - enum font_shaping font_shaping = FONT_SHAPE_FULL; pixman_color_t foreground = {0xffff, 0xffff, 0xffff, 0xffff}; /* White */ const struct yml_node *font_node = yml_get_value(bar, "font"); @@ -417,17 +271,12 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) font = conf_to_font(font_node); } - const struct yml_node *font_shaping_node = yml_get_value(bar, "font-shaping"); - if (font_shaping_node != NULL) - font_shaping = conf_to_font_shaping(font_shaping_node); - const struct yml_node *foreground_node = yml_get_value(bar, "foreground"); if (foreground_node != NULL) foreground = conf_to_color(foreground_node); struct conf_inherit inherited = { .font = font, - .font_shaping = font_shaping, .foreground = foreground, }; @@ -443,7 +292,10 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) struct module **mods = calloc(count, sizeof(*mods)); size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it), idx++) + { struct yml_dict_iter m = yml_dict_iter(it.node); const char *mod_name = yml_value_as_string(m.key); @@ -454,14 +306,14 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) * applied to all its particles. */ const struct yml_node *mod_font = yml_get_value(m.value, "font"); - const struct yml_node *mod_font_shaping = yml_get_value(m.value, "font-shaping"); - const struct yml_node *mod_foreground = yml_get_value(m.value, "foreground"); + const struct yml_node *mod_foreground = yml_get_value( + m.value, "foreground"); struct conf_inherit mod_inherit = { - .font = mod_font != NULL ? conf_to_font(mod_font) : inherited.font, - .font_shaping - = mod_font_shaping != NULL ? conf_to_font_shaping(mod_font_shaping) : inherited.font_shaping, - .foreground = mod_foreground != NULL ? conf_to_color(mod_foreground) : inherited.foreground, + .font = mod_font != NULL + ? conf_to_font(mod_font) : inherited.font, + .foreground = mod_foreground != NULL + ? conf_to_color(mod_foreground) : inherited.foreground, }; const struct module_iface *iface = plugin_load_module(mod_name); diff --git a/config.h b/config.h index 86c2f4e..56f5b2e 100644 --- a/config.h +++ b/config.h @@ -1,9 +1,8 @@ #pragma once -#include "bar/bar.h" -#include "font-shaping.h" -#include "yml.h" #include +#include "yml.h" +#include "bar/bar.h" struct bar; struct particle; @@ -17,13 +16,12 @@ struct bar *conf_to_bar(const struct yml_node *bar, enum bar_backend backend); pixman_color_t conf_to_color(const struct yml_node *node); struct fcft_font *conf_to_font(const struct yml_node *node); -enum font_shaping conf_to_font_shaping(const struct yml_node *node); struct conf_inherit { const struct fcft_font *font; - enum font_shaping font_shaping; pixman_color_t foreground; }; -struct particle *conf_to_particle(const struct yml_node *node, struct conf_inherit inherited); +struct particle *conf_to_particle( + const struct yml_node *node, struct conf_inherit inherited); struct deco *conf_to_deco(const struct yml_node *node); diff --git a/decoration.h b/decoration.h index ba44e43..efb79e2 100644 --- a/decoration.h +++ b/decoration.h @@ -4,11 +4,10 @@ struct deco { void *private; - void (*expose)(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height); + void (*expose)(const struct deco *deco, pixman_image_t *pix, + int x, int y, int width, int height); void (*destroy)(struct deco *deco); }; -#define DECORATION_COMMON_ATTRS \ - { \ - NULL, false, NULL \ - } +#define DECORATION_COMMON_ATTRS \ + {NULL, false, NULL} diff --git a/decorations/background.c b/decorations/background.c index f1430f1..b3b9ed2 100644 --- a/decorations/background.c +++ b/decorations/background.c @@ -1,13 +1,12 @@ #include -#include "../config-verify.h" #include "../config.h" +#include "../config-verify.h" #include "../decoration.h" #include "../plugin.h" -struct private -{ - // struct rgba color; +struct private { + //struct rgba color; pixman_color_t color; }; @@ -23,7 +22,9 @@ static void expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) { const struct private *d = deco->private; - pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 1, &(pixman_rectangle16_t){x, y, width, height}); + pixman_image_fill_rectangles( + PIXMAN_OP_OVER, pix, &d->color, 1, + &(pixman_rectangle16_t){x, y, width, height}); } static struct deco * diff --git a/decorations/border.c b/decorations/border.c deleted file mode 100644 index e93fc4e..0000000 --- a/decorations/border.c +++ /dev/null @@ -1,91 +0,0 @@ -#include - -#include "../config-verify.h" -#include "../config.h" -#include "../decoration.h" -#include "../plugin.h" - -#define LOG_MODULE "border" -#define LOG_ENABLE_DBG 0 -#include "../log.h" - -#define min(x, y) ((x) < (y) ? (x) : (y)) -#define max(x, y) ((x) > (y) ? (x) : (y)) - -struct private -{ - pixman_color_t color; - int size; -}; - -static void -destroy(struct deco *deco) -{ - struct private *d = deco->private; - free(d); - free(deco); -} - -static void -expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) -{ - const struct private *d = deco->private; - pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 4, - (pixman_rectangle16_t[]){ - /* Top */ - {x, y, width, min(d->size, height)}, - - /* Bottom */ - {x, max(y + height - d->size, y), width, min(d->size, height)}, - - /* Left */ - {x, y, min(d->size, width), height}, - - /* Right */ - {max(x + width - d->size, x), y, min(d->size, width), height}, - }); -} - -static struct deco * -border_new(pixman_color_t color, int size) -{ - struct private *priv = calloc(1, sizeof(*priv)); - priv->color = color; - priv->size = size; - - struct deco *deco = calloc(1, sizeof(*deco)); - deco->private = priv; - deco->expose = &expose; - deco->destroy = &destroy; - - return deco; -} - -static struct deco * -from_conf(const struct yml_node *node) -{ - const struct yml_node *color = yml_get_value(node, "color"); - const struct yml_node *size = yml_get_value(node, "size"); - return border_new(conf_to_color(color), size != NULL ? yml_value_as_int(size) : 1); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"color", true, &conf_verify_color}, - {"size", false, &conf_verify_unsigned}, - DECORATION_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct deco_iface deco_border_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct deco_iface iface __attribute__((weak, alias("deco_border_iface"))); -#endif diff --git a/decorations/meson.build b/decorations/meson.build index c64164c..708267e 100644 --- a/decorations/meson.build +++ b/decorations/meson.build @@ -1,7 +1,7 @@ deco_sdk = declare_dependency(dependencies: [pixman, tllist, fcft]) decorations = [] -foreach deco : ['background', 'border', 'stack', 'underline', 'overline'] +foreach deco : ['background', 'stack', 'underline'] if plugs_as_libs shared_module('@0@'.format(deco), '@0@.c'.format(deco), dependencies: deco_sdk, diff --git a/decorations/overline.c b/decorations/overline.c deleted file mode 100644 index e9ff8be..0000000 --- a/decorations/overline.c +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#include "../config-verify.h" -#include "../config.h" -#include "../decoration.h" -#include "../plugin.h" - -struct private -{ - int size; - pixman_color_t color; -}; - -static void -destroy(struct deco *deco) -{ - struct private *d = deco->private; - free(d); - free(deco); -} - -static void -expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) -{ - const struct private *d = deco->private; - pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 1, &(pixman_rectangle16_t){x, y, width, d->size}); -} - -static struct deco * -overline_new(int size, pixman_color_t color) -{ - struct private *priv = calloc(1, sizeof(*priv)); - priv->size = size; - priv->color = color; - - struct deco *deco = calloc(1, sizeof(*deco)); - deco->private = priv; - deco->expose = &expose; - deco->destroy = &destroy; - - return deco; -} - -static struct deco * -from_conf(const struct yml_node *node) -{ - const struct yml_node *size = yml_get_value(node, "size"); - const struct yml_node *color = yml_get_value(node, "color"); - return overline_new(yml_value_as_int(size), conf_to_color(color)); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"size", true, &conf_verify_unsigned}, - {"color", true, &conf_verify_color}, - DECORATION_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct deco_iface deco_overline_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct deco_iface iface __attribute__((weak, alias("deco_overline_iface"))); -#endif diff --git a/decorations/stack.c b/decorations/stack.c index d8420d2..16c58ee 100644 --- a/decorations/stack.c +++ b/decorations/stack.c @@ -1,14 +1,13 @@ #include #define LOG_MODULE "stack" -#include "../config-verify.h" -#include "../config.h" -#include "../decoration.h" #include "../log.h" +#include "../config.h" +#include "../config-verify.h" +#include "../decoration.h" #include "../plugin.h" -struct private -{ +struct private { struct deco **decos; size_t count; }; @@ -58,7 +57,10 @@ from_conf(const struct yml_node *node) struct deco *decos[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it), idx++) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it), idx++) + { decos[idx] = conf_to_deco(it.node); } @@ -73,7 +75,10 @@ verify_conf(keychain_t *chain, const struct yml_node *node) return false; } - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it)) + { if (!conf_verify_decoration(chain, it.node)) return false; } diff --git a/decorations/underline.c b/decorations/underline.c index 0175116..a700bec 100644 --- a/decorations/underline.c +++ b/decorations/underline.c @@ -1,12 +1,11 @@ #include -#include "../config-verify.h" #include "../config.h" +#include "../config-verify.h" #include "../decoration.h" #include "../plugin.h" -struct private -{ +struct private { int size; pixman_color_t color; }; @@ -23,8 +22,9 @@ static void expose(const struct deco *deco, pixman_image_t *pix, int x, int y, int width, int height) { const struct private *d = deco->private; - pixman_image_fill_rectangles(PIXMAN_OP_OVER, pix, &d->color, 1, - &(pixman_rectangle16_t){x, y + height - d->size, width, d->size}); + pixman_image_fill_rectangles( + PIXMAN_OP_OVER, pix, &d->color, 1, + &(pixman_rectangle16_t){x, y + height - d->size, width, d->size}); } static struct deco * @@ -54,7 +54,7 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"size", true, &conf_verify_unsigned}, + {"size", true, &conf_verify_int}, {"color", true, &conf_verify_color}, DECORATION_COMMON_ATTRS, }; diff --git a/doc/meson.build b/doc/meson.build index e801bf1..b4dccb3 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -1,86 +1,11 @@ sh = find_program('sh', native: true) scdoc = dependency('scdoc', native: true) -scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) +scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) -plugin_pages = [] -if plugin_alsa_enabled - plugin_pages += ['yambar-modules-alsa.5.scd'] -endif -if plugin_backlight_enabled - plugin_pages += ['yambar-modules-backlight.5.scd'] -endif -if plugin_battery_enabled - plugin_pages += ['yambar-modules-battery.5.scd'] -endif -if plugin_clock_enabled - plugin_pages += ['yambar-modules-clock.5.scd'] -endif -if plugin_cpu_enabled - plugin_pages += ['yambar-modules-cpu.5.scd'] -endif -if plugin_disk_io_enabled - plugin_pages += ['yambar-modules-disk-io.5.scd'] -endif -if plugin_dwl_enabled - plugin_pages += ['yambar-modules-dwl.5.scd'] -endif -if plugin_foreign_toplevel_enabled - plugin_pages += ['yambar-modules-foreign-toplevel.5.scd'] -endif -if plugin_mem_enabled - plugin_pages += ['yambar-modules-mem.5.scd'] -endif -if plugin_mpd_enabled - plugin_pages += ['yambar-modules-mpd.5.scd'] -endif -if plugin_mpris_enabled - plugin_pages += ['yambar-modules-mpris.5.scd'] -endif -if plugin_i3_enabled - plugin_pages += ['yambar-modules-i3.5.scd'] - plugin_pages += ['yambar-modules-sway.5.scd'] -endif -if plugin_label_enabled - plugin_pages += ['yambar-modules-label.5.scd'] -endif -if plugin_network_enabled - plugin_pages += ['yambar-modules-network.5.scd'] -endif -if plugin_niri_language_enabled - plugin_pages += ['yambar-modules-niri-language.5.scd'] -endif -if plugin_niri_workspaces_enabled - plugin_pages += ['yambar-modules-niri-workspaces.5.scd'] -endif -if plugin_pipewire_enabled - plugin_pages += ['yambar-modules-pipewire.5.scd'] -endif -if plugin_pulse_enabled - plugin_pages += ['yambar-modules-pulse.5.scd'] -endif -if plugin_removables_enabled - plugin_pages += ['yambar-modules-removables.5.scd'] -endif -if plugin_river_enabled - plugin_pages += ['yambar-modules-river.5.scd'] -endif -if plugin_script_enabled - plugin_pages += ['yambar-modules-script.5.scd'] -endif -if plugin_sway_xkb_enabled - plugin_pages += ['yambar-modules-sway-xkb.5.scd'] -endif -if plugin_xkb_enabled - plugin_pages += ['yambar-modules-xkb.5.scd'] -endif - -foreach man_src : ['yambar.1.scd', - 'yambar.5.scd', - 'yambar-decorations.5.scd', - 'yambar-modules.5.scd', - 'yambar-particles.5.scd', - 'yambar-tags.5.scd'] + plugin_pages +foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd', + 'yambar-modules.5.scd', 'yambar-particles.5.scd', + 'yambar-tags.5.scd'] parts = man_src.split('.') name = parts[-3] section = parts[-2] @@ -90,7 +15,7 @@ foreach man_src : ['yambar.1.scd', out, output: out, input: man_src, - command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.full_path())], + command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.path())], capture: true, install: true, install_dir: join_paths(get_option('mandir'), 'man@0@'.format(section))) diff --git a/doc/yambar-decorations.5.scd b/doc/yambar-decorations.5.scd index 3d7c379..48dcc87 100644 --- a/doc/yambar-decorations.5.scd +++ b/doc/yambar-decorations.5.scd @@ -23,7 +23,7 @@ This decoration sets the particles background color. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | color : color : yes @@ -49,7 +49,7 @@ bottom of the particle. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | size : int : yes @@ -70,74 +70,9 @@ content: color: ff0000ff ``` - -# OVERLINE - -Similar to _underline_, this decoration renders a line of configurable -size and color at the top of the particle. - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| size -: int -: yes -: The size (height/thickness) of the line, in pixels -| color -: color -: yes -: The color of the line. See *yambar*(5) for format. - -## EXAMPLES - -``` -content: - string: - deco: - overline: - size: 2 - color: ff0000ff -``` - - -# BORDER - -This decoration renders a border of configurable size (i.e border -width) around the particle. - -## CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| color -: color -: yes -: The color of the line. See *yambar*(5) for format. -| size -: int -: no -: Border width, in pixels. Defaults to 1px. - -## EXAMPLES - -``` -content: - string: - deco: - border: - size: 2 - color: ff0000ff -``` - - # STACK -This particle combines multiple decorations. +This particles combines multiple decorations. ## CONFIGURATION diff --git a/doc/yambar-modules-alsa.5.scd b/doc/yambar-modules-alsa.5.scd deleted file mode 100644 index 14804e5..0000000 --- a/doc/yambar-modules-alsa.5.scd +++ /dev/null @@ -1,70 +0,0 @@ -yambar-modules-alsa(5) - -# NAME -alsa - Monitors an alsa soundcard for volume and mute/unmute changes - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| online -: bool -: True when the ALSA device has successfully been opened -| dB -: range -: Volume level (in dB), with min and max as start and end range - values. -| volume -: range -: Volume level (raw), with min and max as start and end range values -| percent -: range -: Volume level, as a percentage. This value is based on the *dB* tag - if available, otherwise the *volume* tag. -| muted -: bool -: True if muted, otherwise false - - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| card -: string -: yes -: The soundcard name. *default* might work. -| mixer -: string -: yes -: Mixer channel to monitor. _Master_ might work. -| volume -: string -: no -: The name of the channel to use as source for the volume level - (default: first available channel, usually "Front Left"). -| muted -: string -: no -: The name of the channel to use as source for the muted state - (default: first available channel, usually "Front Left"). - - -# 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 deleted file mode 100644 index 6fa3a9a..0000000 --- a/doc/yambar-modules-backlight.5.scd +++ /dev/null @@ -1,47 +0,0 @@ -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 deleted file mode 100644 index aab2106..0000000 --- a/doc/yambar-modules-battery.5.scd +++ /dev/null @@ -1,90 +0,0 @@ -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. - -Note that it is common (and "normal") for batteries to be in the state -*unknown* under certain conditions. - -For example, some have been seen to enter the *unknown* state when -charging and the capacity reaches ~90%. The battery then stays in -*unknown*, rather than *charging*, until it has been fully charged and -enters the state *full*. - -This does not happen with all batteries, and other batteries may enter -the state *unknown* under other conditions. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| name -: string -: Battery device name -| manufacturer -: string -: Name of the battery manufacturer -| model -: string -: Battery model name -| state -: string -: One of *full*, *not charging*, *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 milliseconds, to poll for capacity changes - (default=*60000*). Set to `0` to disable polling (*warning*: many - batteries do not support asynchronous reporting). Cannot be less - than 250ms. -| battery-scale -: int -: no -: How much to scale down the battery charge amount. Some batteries - report too high resulting in bad discharge estimates. Default=1. -| smoothing-secs -: int -: no -: How many seconds to perform smoothing over for battery discharge - estimates. Default=100s. - -# EXAMPLES - -``` -bar: - left: - - battery: - name: BAT0 - poll-interval: 30000 - 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 deleted file mode 100644 index bf3506b..0000000 --- a/doc/yambar-modules-clock.5.scd +++ /dev/null @@ -1,51 +0,0 @@ -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*) -| utc -: bool -: no -: Use GMT instead of local timezone (default=false) - -# 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-cpu.5.scd b/doc/yambar-modules-cpu.5.scd deleted file mode 100644 index 090ccdd..0000000 --- a/doc/yambar-modules-cpu.5.scd +++ /dev/null @@ -1,79 +0,0 @@ -yambar-modules-cpu(5) - -# NAME -cpu - This module provides the CPU usage - -# DESCRIPTION - -This module reports CPU usage, in percent. The _content_ particle is a -template that is instantiated once for each core, and once for the -total CPU usage. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| id -: int -: Core ID. 0..n represents individual cores, and -1 represents the - total usage -| cpu -: range -: Current usage of CPU core {id}, in percent - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| poll-interval -: int -: no -: Refresh interval of the CPU usage stats in milliseconds - (default=500). Cannot be less then 250ms. - -# EXAMPLES - -## Display total CPU usage as a number -``` -bar: - left: - - cpu: - poll-interval: 2500 - content: - map: - conditions: - id < 0: - - string: {text: , font: Font Awesome 6 Free:style=solid} - - string: {text: "{cpu}%"} -``` - -## Display a vertical bar for each core -``` -bar: - left: - - cpu: - poll-interval: 2500 - content: - map: - conditions: - id >= 0: - - ramp: - tag: cpu - items: - - string: {text: ▁} - - string: {text: ▂} - - string: {text: ▃} - - string: {text: ▄} - - string: {text: ▅} - - string: {text: ▆} - - string: {text: ▇} - - string: {text: █} -``` - - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-disk-io.5.scd b/doc/yambar-modules-disk-io.5.scd deleted file mode 100644 index 3f51e79..0000000 --- a/doc/yambar-modules-disk-io.5.scd +++ /dev/null @@ -1,85 +0,0 @@ -yambar-modules-disk-io(5) - -# NAME -disk-io - This module keeps track of the amount of bytes being -read/written from/to disk. It can distinguish between all partitions -currently present in the machine. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| device -: string -: Name of the device being tracked (use the command *lsblk* to see these). - There is a special device, "Total", that reports the total stats - for the machine -| is_disk -: boolean -: whether or not the device is a disk (e.g., sda, sdb) or a partition - (e.g., sda1, sda2, ...). "Total" is advertised as a disk. -| read_speed -: int -: bytes read, in bytes/s -| write_speed -: int -: bytes written, in bytes/s -| ios_in_progress -: int -: number of ios that are happening at the time of polling - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| poll-interval -: int -: no -: Refresh interval of disk's stats in milliseconds (default=500). - Cannot be less then 250ms. - -# EXAMPLES - -This reports the total amount of bytes being read and written every second, -formatting in b/s, kb/s, mb/s, or gb/s, as appropriate. - -``` -bar: - left: - - disk-io: - poll-interval: 1000 - content: - map: - conditions: - device == Total: - list: - items: - - string: {text: "Total read: "} - - map: - default: {string: {text: "{read_speed} B/s"}} - conditions: - read_speed > 1073741824: - string: {text: "{read_speed:gib} GB/s"} - read_speed > 1048576: - string: {text: "{read_speed:mib} MB/s"} - read_speed > 1024: - string: {text: "{read_speed:kib} KB/s"} - - string: {text: " | "} - - string: {text: "Total written: "} - - map: - default: {string: {text: "{write_speed} B/s"}} - conditions: - write_speed > 1073741824: - string: {text: "{write_speed:gib} GB/s"} - write_speed > 1048576: - string: {text: "{write_speed:mib} MB/s"} - write_speed > 1024: - string: {text: "{write_speed:kib} KB/s"} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-dwl.5.scd b/doc/yambar-modules-dwl.5.scd deleted file mode 100644 index 1562e56..0000000 --- a/doc/yambar-modules-dwl.5.scd +++ /dev/null @@ -1,107 +0,0 @@ -yambar-modules-dwl(5) - -# NAME -dwl - This module provides information about dwl tags, and information. - -# DESCRIPTION - -This module provides a map of each tags present in dwl. - -Each tags has its _id_, its _name_, its status (_selected_, _empty_, _urgent_) -and the global data like _title_, _appid_, _fullscreen_, _floating_, -_selmon_, and _layout_). The tags start a 1. For needs where -you only want information about the global data and not the _tags_, -there is a tag with the id _0_ that contains only the global data. - -This module will track *only* the monitor where yambar was launched on. -If you have a multi monitor setup, please launch yambar on each of your -monitors. - -Please, be aware that only *one instance* of this module is supported. -Running multiple instances at the same time may result in -*undefined behavior*. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| id -: int -: dwl tag id. -| name -: string -: The name of the tag (defaults to _id_ if not set). -| selected -: bool -: True if the tag is currently selected. -| empty -: bool -: True if there are no windows in the tag. -| urgent -: bool -: True if the tag has the urgent flag set. -| title -: string -: The currently focused window's title. -| appid -: string -: The currently focused window's application id. -| fullscreen -: bool -: True if there is a fullscreen window in the current tag. -| floating -: bool -: True if there is a floating window in the current tag. -| selmon -: bool -: True if the monitor is actually focused. -| layout -: string -: The actual layout name of the tag. - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| number-of-tags -: int -: yes -: The number of defined tags in the dwl `config.def.h`. -| name-of-tags -: list -: false -: The name of the tags (must have the same length that _number-of-tags_). -| dwl-info-filename -: string -: yes -: The filepath to the log emitted by dwl when running. - -# EXAMPLES - -``` -bar: - left: - - dwl: - number-of-tags: 9 - dwl-info-filename: "/home/ogromny/dwl_info" - name-of-tags: [ , , , , , , , ,  ] - content: - list: - items: - - map: - conditions: - # default tag - id == 0: {string: {text: "{layout} {title}"}} - - selected: {string: {text: "-> {name}"}} - ~empty: {string: {text: "{name}"}} - urgent: {string: {text: "=> {name} <="}} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-foreign-toplevel.5.scd b/doc/yambar-modules-foreign-toplevel.5.scd deleted file mode 100644 index 8b1d576..0000000 --- a/doc/yambar-modules-foreign-toplevel.5.scd +++ /dev/null @@ -1,78 +0,0 @@ -yambar-modules-foreign-toplevel(5) - -# NAME -foreign-toplevel - This module provides information about toplevel windows in Wayland - -# DESCRIPTION - -This module uses the _wlr foreign toplevel management_ Wayland -protocol to provide information about currently open windows, such as -their application ID, window title, and their current state -(maximized/minimized/fullscreen/activated). - -The configuration for the foreign-toplevel module specifies a -_template_ particle. This particle will be instantiated once for each -window. - -Note: Wayland only. - - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| app-id -: string -: The application ID (typically the application name) -| title -: string -: The window title -| maximized -: bool -: True if the window is currently maximized -| minimized -: bool -: True if the window is currently minimized -| fullscreen -: bool -: True if the window is currently fullscreened -| activated -: bool -: True if the window is currently activated (i.e. has focus) - - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| content -: particle -: yes -: Template particle that will be instantiated once for each window -| all-monitors -: bool -: no -: When set to true, only windows on the same monitor the bar will be - used. The default is false. - - -# EXAMPLES - -``` -bar: - left: - - foreign-toplevel: - content: - map: - conditions: - ~activated: {empty: {}} - activated: - - string: {text: "{app-id}: {title}"} -``` - -# 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 deleted file mode 100644 index 2014a3c..0000000 --- a/doc/yambar-modules-i3.5.scd +++ /dev/null @@ -1,117 +0,0 @@ -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 -| output -: string -: The output (monitor) the workspace is on -| 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 -| empty -: bool -: True if the workspace is empty (Sway only) -| 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_, _native_, _ascending_ or _descending_, defaults to _none_. Use _native_ to sort numbered workspaces only. -| strip-workspace-numbers -: bool -: no -: If true, *N:* prefixes will be stripped from workspace names. Useful together with *sort*, to have the workspace order fixed. -| persistent -: list of strings -: no -: Persistent workspaces. I.e. workspaces that are never removed, even if empty. -| 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: - default: {string: {text: "{name}"}} - conditions: - state == 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 deleted file mode 100644 index a6516f1..0000000 --- a/doc/yambar-modules-label.5.scd +++ /dev/null @@ -1,32 +0,0 @@ -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-mem.5.scd b/doc/yambar-modules-mem.5.scd deleted file mode 100644 index fc0a9eb..0000000 --- a/doc/yambar-modules-mem.5.scd +++ /dev/null @@ -1,52 +0,0 @@ -yambar-modules-mem(5) - -# NAME -mem - This module provides the memory usage - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| free -: int -: Free memory in bytes -| used -: int -: Used memory in bytes -| total -: int -: Total memory in bytes -| percent_free -: range -: Free memory in percent -| percent_used -: range -: Used memory in percent - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| poll-interval -: string -: no -: Refresh interval of the memory usage stats in milliseconds - (default=500). Cannot be less then 250ms. - -# EXAMPLES - -``` -bar: - left: - - mem: - poll-interval: 2500 - content: - string: {text: "{used:mb}MB"} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) diff --git a/doc/yambar-modules-mpd.5.scd b/doc/yambar-modules-mpd.5.scd deleted file mode 100644 index d89407a..0000000 --- a/doc/yambar-modules-mpd.5.scd +++ /dev/null @@ -1,86 +0,0 @@ -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 -| single -: bool -: True if the *single* 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) -| file -: string -: Filename or URL of currently playing song (also valid in *paused* state) -| pos -: string -: *%M:%S*-formatted string describing the song's current position - (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-mpris.5.scd b/doc/yambar-modules-mpris.5.scd deleted file mode 100644 index 510dc8f..0000000 --- a/doc/yambar-modules-mpris.5.scd +++ /dev/null @@ -1,101 +0,0 @@ -yambar-modules-mpris(5) - -# NAME -mpris - This module provides MPRIS status such as currently playing artist/album/song - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| state -: string -: One of *offline*, *stopped*, *paused* or *playing* -| shuffle -: bool -: True if the *shuffle* flag is set -| repeat -: string -: One of *none*, *track* or *paylist* -| volume -: range -: Volume in percentage -| album -: string -: Currently playing album -| artist -: string -: Artist of currently playing song -| title -: string -: Title of currently playing song -| file -: string -: Filename or URL of currently playing song -| pos -: string -: *%M:%S*-formatted string describing the song's current position - (also see _elapsed_) -| end -: string -: *%M:%S*-formatted string describing the song's total length (also - see _duration_) -| elapsed -: realtime -: Position in currently playing song, in milliseconds. Can be used - with a _progress-bar_ particle. - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| identities -: list of string -: yes -: A list of MPRIS client identities -| query_timeout -: int -: no -: Dbus/MPRIS client connection timeout in ms. Try setting/incrementing - this value if the module reports a timeout error. Defaults to 500. - -# EXAMPLES - -``` -bar: - center: - - mpris: - identities: - - "spotify" - - "firefox" - content: - map: - conditions: - state != offline && state != stopped: - - string: {text: "{artist}", max: 30 } - - string: {text: "-" } - - string: {text: "{title}", max: 30 } -``` - -# NOTE - -The 'identity' refers a part of your clients DBus bus name. -You can obtain a list of active client names using: - -``` -Systemd: > busctl --user --list -Playerctl: > playerctl --list-all -Libdbus: > dbus-send --session --print-reply --type=method_call \ - --dest='org.freedesktop.DBus' /org org.freedesktop.DBus.ListNames -``` - -MPRIS client bus names start with 'org.mpris.MediaPlayer2.'. -For example, firefox may use the bus name: -'org.mpris.MediaPlayer2.firefox.instance_1_7' which -gives us the identity 'firefox' - -# SEE ALSO - -*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 deleted file mode 100644 index afbbae3..0000000 --- a/doc/yambar-modules-network.5.scd +++ /dev/null @@ -1,126 +0,0 @@ -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. It instantiates the provided _content_ -particle for each network interface. - -Note: while the module internally tracks all assigned IPv4/IPv6 -addresses, it currently exposes only a single IPv4 and a single IPv6 -address per network interface. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| name -: string -: Network interface name -| type -: string -: Interface type (*ether*, *wlan*, *loopback*, or *ARPHRD_NNN*, where - *N* is a number). -| kind -: string -: Interface kind. Empty for non-virtual interfaces. For virtual - interfaces, this value is taken from the _IFLA\_INFO\_KIND_ netlink - attribute. Examples of valid values are *bond*, *bridge*, *gre*, *tun* - and *veth*. -| index -: int -: Network interface index -| 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 -| ssid -: string -: SSID the adapter is connected to (Wi-Fi only) -| signal -: int -: Signal strength, in dBm (Wi-Fi only) -| quality -: range -: Quality of the signal, in percent (Wi-Fi only) -| rx-bitrate -: int -: RX bitrate, in bits/s -| tx-bitrate -: int -: TX bitrate in bits/s -| dl-speed -: int -: Download speed in bits/s -| ul-speed -: int -: Upload speed in bits/s - - -# CONFIGURATION - -[[ *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_ -| poll-interval -: int -: no -: Periodically (in milliseconds) update the signal, quality, rx+tx bitrate, and - ul+dl speed tags (default=0). Setting it to 0 disables updates. Cannot be less - than 250ms. - - -# EXAMPLES - -Display all Ethernet (including WLAN) devices. This excludes loopback, -bridges etc. - -``` -bar: - left: - - network: - content: - map: - conditions: - type == ether || type == wlan: - map: - default: - string: {text: "{name}: {state} ({ipv4})"} - conditions: - ipv4 == "": - string: {text: "{name}: {state}"} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-niri-language.5.scd b/doc/yambar-modules-niri-language.5.scd deleted file mode 100644 index befa41e..0000000 --- a/doc/yambar-modules-niri-language.5.scd +++ /dev/null @@ -1,34 +0,0 @@ -yambar-modules-niri-language(5) - -# NAME -niri-language - This module provides information about niri's currently -selected language. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| language -: string -: The currently selected language. - -# CONFIGURATION - -No additional attributes supported, only the generic ones (see -*GENERIC CONFIGURATION* in *yambar-modules*(5)) - -# EXAMPLES - -``` -bar: - left: - - niri-language: - content: - string: {text: "{language}"} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-niri-workspaces.5.scd b/doc/yambar-modules-niri-workspaces.5.scd deleted file mode 100644 index 812bade..0000000 --- a/doc/yambar-modules-niri-workspaces.5.scd +++ /dev/null @@ -1,60 +0,0 @@ -yambar-modules-niri-workspaces(5) - -# NAME -niri-workspaces - This module provides information about niri workspaces. - -# DESCRIPTION - -This module provides a map of each workspace present in niri. - -Each workspace has its _id_, _name_, and its status (_focused_, -_active_, _empty_). The workspaces are sorted by their ids. - -This module will *only* track the monitor where yambar was launched. -If you have a multi monitor setup, please launch yambar on each -individual monitor to track its workspaces. - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| id -: int -: The workspace id. -| name -: string -: The name of the workspace. -| active -: bool -: True if the workspace is currently visible on the current output. -| focused -: bool -: True if the workspace is currently focused. -| empty -: bool -: True if the workspace contains no window. - -# CONFIGURATION - -No additional attributes supported, only the generic ones (see -*GENERIC CONFIGURATION* in *yambar-modules*(5)) - -# EXAMPLES - -``` -bar: - left: - - niri-workspaces: - content: - map: - default: {string: {text: "| {id}"}} - conditions: - active: {string: {text: "-> {id}"}} - ~empty: {string: {text: "@ {id}"}} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-pipewire.5.scd b/doc/yambar-modules-pipewire.5.scd deleted file mode 100644 index 8010449..0000000 --- a/doc/yambar-modules-pipewire.5.scd +++ /dev/null @@ -1,103 +0,0 @@ -yambar-modules-pipewire(5) - -# NAME -pipewire - Monitors pipewire for volume, mute/unmute, device change - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| type -: string -: Either "source" (capture) or "sink" (speaker) -| name -: string -: Current device name -| description -: string -: Current device description -| form_factor -: string -: Current device form factor (headset, speaker, mic, etc.) -| bus -: string -: Current device bus (bluetooth, alsa, etc.) -| icon -: string -: Current device icon name -| muted -: bool -: True if muted, otherwise false -| linear_volume -: range -: Linear volume in percentage (with 0 as min and 100 as max) -| cubic_volume -: range -: Cubic volume (used by pulseaudio) in percentage (with 0 as min and 100 as max) - - -# CONFIGURATION - -[[ *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_ -| content -: particle -: yes -: Unlike other modules, _content_ is a template particle that will be - expanded twice (i.e. into a list of two elements). The first - element is the 'sink', and the second element the 'source'. - - -# EXAMPLES - -``` -bar: - left: - - pipewire: - anchors: - volume: &volume - conditions: - muted: {string: {text: "{linear_volume}%", foreground: ff0000ff}} - ~muted: {string: {text: "{linear_volume}%"}} - content: - list: - items: - - map: - conditions: - type == "sink": - map: - conditions: - icon == "audio-headset-bluetooth": - string: {text: "🎧 "} - default: - - ramp: - tag: linear_volume - items: - - string: {text: "🔈 "} - - string: {text: "🔉 "} - - string: {text: "🔊 "} - type == "source": - - string: {text: "🎙 "} - - map: - <<: *volume -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-pulse.5.scd b/doc/yambar-modules-pulse.5.scd deleted file mode 100644 index 008ec78..0000000 --- a/doc/yambar-modules-pulse.5.scd +++ /dev/null @@ -1,67 +0,0 @@ -yambar-modules-pulse(5) - -# NAME -pulse - Monitors a PulseAudio source and/or sink - -# TAGS - -[[ *Name* -:[ *Type* -:< *Description* -| online -: bool -: True when connected to the PulseAudio server -| sink_online -: bool -: True when the sink is present -| source_online -: bool -: True when the source is present -| sink_percent -: range -: Sink volume level, as a percentage -| source_percent -: range -: Source volume level, as a percentage -| sink_muted -: bool -: True if the sink is muted, otherwise false -| source_muted -: bool -: True if the source is muted, otherwise false -| sink_port -: string -: Description of the active sink port -| source_port -: string -: Description of the active source port - -# CONFIGURATION - -[[ *Name* -:[ *Type* -:[ *Req* -:< *Description* -| sink -: string -: no -: Name of sink to monitor (default: _@DEFAULT\_SINK@_). -| source -: string -: no -: Name of source to monitor (default: _@DEFAULT\_SOURCE@_). - -# EXAMPLES - -``` -bar: - left: - - pulse: - content: - string: {text: "{sink_percent}% ({sink_port})"} -``` - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-removables.5.scd b/doc/yambar-modules-removables.5.scd deleted file mode 100644 index 0a193d4..0000000 --- a/doc/yambar-modules-removables.5.scd +++ /dev/null @@ -1,92 +0,0 @@ -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) -| audio -: bool -: True if an optical drive has an audio CD inserted (i.e. this - property is always false for non-optical drives). -| device -: string -: Volume device name (typically */dev/sd?*) -| 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: - conditions: - ~mounted: - string: - on-click: udisksctl mount -b {device} - text: "{label}" - mounted: - 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 deleted file mode 100644 index 3bf3b61..0000000 --- a/doc/yambar-modules-river.5.scd +++ /dev/null @@ -1,109 +0,0 @@ -yambar-modules-river(5) - -# NAME -river - This module provides 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 once for each seat, with tags representing -the seats' name, the title of the seats' currently focused view, and -its current river "mode". - -It also specifies a _content_ template particle, which is instantiated -once for all 32 river tags. This means you probably want to use a -*map* particle to hide unused river tags. - -# TAGS (for the "content" particle) - -[[ *Name* -:[ *Type* -:< *Description* -| id -: int -: River tag number -| urgent -: bool -: True if the river tag has at least one urgent view. -| visible -: bool -: True if the river tag is focused by at least one output (i.e. visible on at least one monitor). -| 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 *urgent* if _urgent_ is true, *focused* if _focused_ is true, - *unfocused* if _visible_ is true, but _focused_ is false, or - *invisible* if the river tag is not visible on any monitors. - - -# TAGS (for the "title" particle) - -[[ *Name* -:[ *Type* -:< *Description* -| seat -: string -: The name of the seat. -| title -: string -: The seat's focused view's title. -| mode -: string -: The seat's current mode (entered with e.g. *riverctl enter-mode foobar*). -| layout -: string -: Current layout of the output currently focused by the seat. - -# CONFIGURATION - -[[ *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. -| all-monitors -: bool -: no -: When set to false (the default), tags reflect river tags and seats - for the monitor yambar is on only. When set to true, tags reflect - the union of all monitors. - -# EXAMPLES - -``` -bar: - left: - - river: - title: {string: { text: "{seat} - {title} ({layout}/{mode})" }} - content: - map: - conditions: - ~occupied: {empty: {}} - occupied: - 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 deleted file mode 100644 index 48722cf..0000000 --- a/doc/yambar-modules-script.5.scd +++ /dev/null @@ -1,151 +0,0 @@ -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. - -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 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 depend -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 -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 either be an absolute path, - or start with *~/*. -| args -: list of strings -: no -: Arguments to pass to the script/binary. -| poll-interval -: integer -: no -: Number of milliseconds between each script run. If unset, or set to - 0, continuous mode is used. - -# 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 runs in continuous 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: - -``` -bar: - left: - - script: - path: /path/to/script.sh - args: [] - content: {string: {text: "{test}"}} -``` - -Another example use case of this module could be to display currently playing -song or other media from players that support MPRIS (Media Player Remote -Interfacing Specification): - -``` -bar: - center: - - script: - path: /usr/bin/playerctl - args: - - "--follow" - - "metadata" - - "-f" - - | - status|string|{{status}} - artist|string|{{artist}} - title|string|{{title}} - content: - map: - conditions: - status == Paused: {empty: {}} - status == Playing: - content: {string: {text: "{artist} - {title}"}} -``` - -The above snippet runs a _playerctl_ utility in _--follow_ mode, reacting to -media updates on DBUS and outputting status, artist and title of media being -played in a format that is recognized by yambar. See _playerctl_ documentation -for more available metadata fields and control over which players get used. - -# SEE ALSO - -*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-modules-sway-xkb.5.scd b/doc/yambar-modules-sway-xkb.5.scd deleted file mode 100644 index bcf948c..0000000 --- a/doc/yambar-modules-sway-xkb.5.scd +++ /dev/null @@ -1,70 +0,0 @@ -yambar-modules-sway-xkb(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-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 deleted file mode 100644 index c440322..0000000 --- a/doc/yambar-modules-sway.5.scd +++ /dev/null @@ -1,10 +0,0 @@ -yambar-modules-sway(5) - -# DESCRIPTION - -Please use the i3 (*yambar-modules-i3*(5)) module, as it is fully compatible with Sway - -# SEE ALSO - -*yambar-modules*(5), *yambar-modules-i3*(5) - diff --git a/doc/yambar-modules-xkb.5.scd b/doc/yambar-modules-xkb.5.scd deleted file mode 100644 index ef03097..0000000 --- a/doc/yambar-modules-xkb.5.scd +++ /dev/null @@ -1,52 +0,0 @@ -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-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 deleted file mode 100644 index 1aadbf7..0000000 --- a/doc/yambar-modules-xwindow.5.scd +++ /dev/null @@ -1,45 +0,0 @@ -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 1ec4871..14aed8f 100644 --- a/doc/yambar-modules.5.scd +++ b/doc/yambar-modules.5.scd @@ -38,7 +38,7 @@ For example, to render _backlight_ as " 20%", you could use: ``` content: - string: - font: Font Awesome 6 Free:style=solid:pixelsize=14 + font: Font Awesome 5 Free:style=solid:pixelsize=14 text:  - string: font: Adobe Helvetica:pixelsize=12 @@ -68,17 +68,20 @@ in red. ``` content: map: - conditions: - ~carrier: {empty: {}} - carrier: + tag: carrier + values: + false: {empty: {}} + true: map: + tag: state default: {string: {text: , font: *awesome, foreground: ffffff66}} - conditions: - state == up: + values: + up: map: + tag: ipv4 default: {string: {text: , font: *awesome}} - conditions: - ipv4 == "": {string: {text: , font: *awesome, foreground: ffffff66}} + values: + "": {string: {text: , font: *awesome, foreground: ffffff66}} ``` ## Use yaml anchors @@ -91,7 +94,7 @@ In these cases, you can define an anchor point, either at top-level, or in a module's _anchors_ attribute: ``` -awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14 +awesome: &awesome Font Awesome 5 Free:style=solid:pixelsize=14 ``` @@ -110,7 +113,7 @@ following attributes are supported by all modules: [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | content : particle : yes @@ -130,59 +133,835 @@ following attributes are supported by all modules: : Foreground (text) color of the content particle. This is an inherited attribute. -# BUILT-IN MODULES +# ALSA -Available modules have their own pages: +Monitors an alsa soundcard for volume and mute/unmute changes. -*yambar-modules-alsa*(5) +## TAGS -*yambar-modules-backlight*(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-battery*(5) -*yambar-modules-clock*(5) +## CONFIGURATION -*yambar-modules-cpu*(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-disk-io*(5) +## EXAMPLES -*yambar-modules-dwl*(5) +``` +bar: + left: + - alsa: + card: hw:PCH + mixer: Master + content: {string: {text: "{volume}"}} +``` -*yambar-modules-foreign-toplevel*(5) +# BACKLIGHT -*yambar-modules-i3*(5) +This module reads monitor backlight status from +_/sys/class/backlight_, and uses *udev* to monitor for changes. -*yambar-modules-label*(5) +## TAGS -*yambar-modules-mem*(5) +[[ *Name* +:[ *Type* +:[ *Description* +| brightness +: range +: The current brightness level, in absolute value +| percent +: range +: The current brightness level, in percent -*yambar-modules-mpd*(5) +## CONFIGURATION -*yambar-modules-network*(5) +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| name +: string +: yes +: The backlight device's name (one of the names in */sys/class/backlight*) -*yambar-modules-pipewire*(5) +## EXAMPLES -*yambar-modules-pulse*(5) +``` +bar: + left: + - backlight: + name: intel_backlight + content: + string: {text: "backlight: {percent}%"} +``` -*yambar-modules-removables*(5) +# BATTERY -*yambar-modules-river*(5) +This module reads battery status from _/sys/class/power_supply_ and +uses *udev* to monitor for changes. -*yambar-modules-script*(5) +## TAGS -*yambar-modules-sway-xkb*(5) +[[ *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. -*yambar-modules-sway*(5) +## CONFIGURATION -*yambar-modules-niri-language*(5) +[[ *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). -*yambar-modules-niri-workspaces*(5) +## EXAMPLES -*yambar-modules-xkb*(5) +``` +bar: + left: + - battery: + name: BAT0 + poll-interval: 30 + content: + string: {text: "BAT: {capacity}% {estimate}"} +``` -*yambar-modules-xwindow*(5) +# 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. Each +_tag_ is a single line on the format: + +``` +name|type|value +``` + +Where _name_ is what you also use to refer to the tag in the yambar +configuration, _type_ is one of the tag types defined in +*yambar-tags*(5), and _value_ is the tag’s value. + +Example: + +``` +var1|string|hello +var2|int|13 + +var1|string|world +var2|int|37 +``` + +The example above consists of two transactions. Each transaction has +two tags: one string tag and one integer tag. The second transaction +replaces the tags from the first transaction. + +Supported _types_ are: + +- string +- int +- bool +- float +- range:n-m (e.g. *var|range:0-100|57*) + +## TAGS + +User defined. + +## CONFIGURATION + +[[ *Name* +:[ *Type* +:[ *Req* +:[ *Description* +| path +: string +: yes +: Path to script/binary to execute. Must be an absolute path. +| args +: list of strings +: no +: Arguments to pass to the script/binary. + +## EXAMPLES + +Here is an "hello world" example script: + +``` +#!/bin/sh + +while true; do + echo "test|string|hello" + echo "" + sleep 3 + + echo "test|string|world" + echo "" + sleep 3 +done +``` + +This script will emit a single string tag, _test_, and alternate its +value between *hello* and *world* every three seconds. + +A corresponding yambar configuration could look like this: + +``` +bar: + left: + - script: + path: /path/to/script.sh + args: [] + content: {string: {text: "{test}"}} +``` + +# SWAY-XKB + +This module uses *Sway* 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}"} +``` # SEE ALSO *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5) - diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 325ef89..08977c5 100644 --- a/doc/yambar-particles.5.scd +++ b/doc/yambar-particles.5.scd @@ -12,7 +12,7 @@ following attributes are supported by all particles: [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | left-margin : int : no @@ -31,76 +31,20 @@ following attributes are supported by all particles: : Font to use. Note that this is an inherited attribute; i.e. you can set it on e.g. a _list_ particle, and it will apply to all particles in the list. -| font-shaping -: enum -: no -: font-shaping; one of _full_ or _none_. When set to _full_ (the - default), strings will be "shaped" using HarfBuzz. Requires support - in fcft. | foreground : color : no : Foreground (text) color. Just like _font_, this is an inherited attribute. | on-click -: associative array/string -: no -: When set to a string, executes the string as a command when the - particle is left-clicked. Tags can be used. Note that the string is - *not* executed in a shell. Environment variables are not expanded. - *~/* is expanded, but only in the first argument. The same applies - to all attributes associated with it, below. -| on-click.left : string : no -: Command to execute when the particle is left-clicked. -| on-click.right -: string -: no -: Command to execute when the particle is right-clicked. -| on-click.middle -: string -: no -: Command to execute when the particle is middle-clicked. -| on-click.wheel-up -: string -: no -: Command to execute every time a 'wheel-up' event is triggered. -| on-click.wheel-down -: string -: no -: Command to execute every time a 'wheel-down' event is triggered. -| on-click.previous -: string -: no -: Command to execute when the particle is clicked with the 'previous' button. -| on-click.next -: string -: no -: Command to execute when the particle is clicked with the 'next' button. +: Command to execute when the particle is clicked. Tags can be + used. Note that the string is *not* executed in a shell. | deco : decoration : no : Decoration to apply to the particle. See *yambar-decorations*(5) -## EXAMPLES: - -*on-click* as a string (handles left click): -``` -content: - : - on-click: command args -``` - -*on-click* as an associative array (handles other buttons): -``` -content: - : - on-click: - left: command-1 - wheel-up: command-3 - wheel-down: command-4 -``` - # STRING This is the most basic particle. It takes a format string, consisting @@ -115,7 +59,7 @@ of free text mixed with tag specifiers. | text : string : yes -: Format string. Tags are specified with _{tag_name}_. Some tag types +: Format string. Tags are spcified with _{tag_name}_. Some tag types have suffixes that can be appended (e.g. _{tag_name:suffix}_). See *yambar-modules*(5)). | max @@ -123,9 +67,9 @@ of free text mixed with tag specifiers. : no : Sets the rendered string's maximum length. If the final string's length exceeds this, the rendered string will be truncated, and - "…" will be appended. Note that the trailing "…" is + "..." will be appended. Note that the trailing "..." are *included* in the maximum length. I.e. if you set _max_ to '5', you - will only get *4* characters from the string. + will only get *2* characters from the string. ## EXAMPLES @@ -138,7 +82,7 @@ content: # EMPTY This particle is a place-holder. While it does not render any tags, -margins and decorations are rendered. +margins and decortions are rendered. ## CONFIGURATION @@ -155,7 +99,7 @@ content: This particle is a list (or sequence, if you like) of other particles. It can be used to render e.g. _string_ particles with -different font and/or color formatting. Or any other particle +different font and/or color formatting. Or ay other particle combinations. But note that this means you *cannot* set any attributes on the _list_ @@ -166,7 +110,7 @@ particle itself. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | items : list : yes @@ -214,165 +158,51 @@ content: - string: ... ``` -Note that the short form has a hard-coded *right-spacing* of 2. This -cannot be changed. If you want a different spacing, you must use an -explicit list particle (i.e. the long form). - - # MAP This particle maps the values of a specific tag to different -particles based on conditions. A condition takes either the form of: - -``` - -``` - -Or, for boolean tags: - -``` - -``` - -Where is the tag you would like to map, is one of: - -[- == -:- != -:- >= -:- > -:- <= -:- < - -and is the value you would like to compare it to. *If the -value contains any non-alphanumerical characters, you must -surround it with ' \" ' *: - -``` -"hello world" -"@#$%" -``` - -Negation is done with a preceding '~': - -``` -~ -~ -``` - -To match for empty strings, use ' "" ': - -``` - == "" -``` - -String glob matching - -To perform string matching using globbing with "\*" & "?" characters: -\* Match any zero or more characters. ? Match exactly any one -character. - -``` - ~~ "hello*" -``` - -Will match any string starting with "hello", including "hello", -"hello1", "hello123", etc. - -``` - ~~ "hello?" -``` - -Will match any string starting with "hello" followed by any single -character, including "hello1", "hello-", but not "hello". - -Furthermore, you may use the boolean operators: - -[- && -:- || - -in order to create more complex conditions: - -``` - && -``` - -You may surround with parenthesis for clarity or -specifying precedence: - -``` -() - && ( || ) -``` - -In addition to explicit tag values, you can also specify a +particles. In addition to explicit tag values, you can also specify a default/fallback particle. -Note that conditions are evaluated in the order they appear. *If -multiple conditions are true, the first one will be used*. This means -that in a configuration such as: - -``` -tx-bitrate > 1000: -tx-bitrate > 1000000: -``` - -the second condition would never run, since whenever the second -condition is true, the first is also true. The correct way of doing -this would be to invert the order of the conditions: - -``` -tx-bitrate > 1000000: -tx-bitrate > 1000: -``` - - ## CONFIGURATION [[ *Name* :[ *Type* :[ *Req* -:< *Description* -| conditions +:[ *Description* +| tag +: string +: yes +: The tag (name of) which values should be mapped +| values : associative array : yes -: An associative array of conditions (see above) mapped to particles +: An associative array of tag values mapped to particles | default : particle : no -: Default particle to use, none of the conditions are true +: Default particle to use, when tag's value does not match any of the + mapped values. ## EXAMPLES ``` content: map: + tag: tag_name default: string: text: this is the default particle; the tag's value is now {tag_name} - conditions: - tag == one_value: + values: + one_value: string: text: tag's value is now one_value - tag == another_value: + another_value: string: text: tag's value is now another_value ``` -For a boolean tag: - -``` -content: - map: - conditions: - tag: - string: - text: tag is true - ~tag: - string: - text: tag is false -``` - # RAMP This particle uses a range tag to index into an array of @@ -385,7 +215,7 @@ indicator. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | tag : string : yes @@ -396,18 +226,6 @@ indicator. : List of particles. Note that the tag value is *not* used as-is; its minimum and maximum values are used to map the tag's range to the particle list's range. -| min -: int -: no -: If present this will be used as a lower bound instead of the tags - minimum value. Tag values falling outside the defined range will - get clamped to min/max. -| max -: int -: no -: If present this will be used as an upper bound instead of the tags - maximum value. Tag values falling outside the defined range will - get clamped to min/max. ## EXAMPLES @@ -440,7 +258,7 @@ itself when needed. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | tag : string : yes @@ -476,7 +294,7 @@ itself when needed. ``` content: - progress-bar: + progres-bar: tag: tag_name length: 20 start: {string: {text: ├}} diff --git a/doc/yambar-tags.5.scd b/doc/yambar-tags.5.scd index b6b8b56..1e3c138 100644 --- a/doc/yambar-tags.5.scd +++ b/doc/yambar-tags.5.scd @@ -11,7 +11,7 @@ their information. Each module defines its own set of tags. The available tag *types* are: [[ *Type* -:< *Description* +:[ *Description* | string : Value is a string. Rendered as-is by the _string_ particle. | int @@ -38,97 +38,9 @@ The available tag *types* are: # FORMATTING -A tag may be followed by one or more formatters that alter the tags -rendition. +As mentioned above, each tag type has a default representation that is +used when the tag is rendered by a string particle. -Formatters are added by appending a ':' separated list of formatter -names: - - "{tag_name:max:hex}" - -In the table below, "kind" describes the type of action performed by -the formatter: - -- *format*: changes the representation of the tag's value -- *selector*: changes what to render - -In general, formatters of the same kind cannot be combined; if -multiple formatters of the same kind are specified, the last one will -be used. - -[[ *Formatter* -:[ *Kind* -:[ *Applies to* -:< *Description* -| [0][.] -: format -: Numeric tags (integer and floats) -: The width reserved to the field. The leading '0' is optional and - indicates zero padding, as opposed to space padding. The trailing - '.' is also optional -| . -: format -: Float tags -: How many decimals to print -| [0][.] -: format -: N: numeric tags, M: float tags -: Combined version of the two previous formatters -| hex -: format -: All tag types -: Renders a tag's value in hex -| oct -: format -: All tag types -: Renders a tag's value in octal -| % -: format -: Range tags -: Renders a range tag's value as a percentage value -| /N -: format -: All tag types -: Renders a tag's value (in decimal) divided by N -| kb, mb, gb -: format -: All tag types -: Renders a tag's value (in decimal) divided by 1000, 1000^2 or - 1000^3. Note: no unit suffix is appended -| kib, mib, gib -: format -: All tag types -: Same as *kb*, *mb* and *gb*, but divide by 1024^n instead of 1000^n. -| min -: selector -: Range tags -: Renders a range tag's minimum value -| max -: selector -: Range tags -: Renders a range tag's maximum value -| unit -: selector -: Realtime tags -: Renders a realtime tag's unit (e.g. "s", or "ms") - -# EXAMPLES - -- A numeric (float or int) tag with at least 3 digits, zero-padded if - necessary: - -``` -{tag:03} -``` - -- A float tag with 2 decimals: - -``` -{tag:.2} -``` - -- A "byte count" tag in gigabytes: - -``` -{tag:gib}GB -``` +All integer, floating point and boolean tag types can be modified to +instead be rendered in hexadecimal or octal form, by appending either +the *:hex* or *:oct* suffixes. For example, _\"{tag_name:hex}\"_. \ No newline at end of file diff --git a/doc/yambar.1.scd b/doc/yambar.1.scd index 2aaa46f..f2526a2 100644 --- a/doc/yambar.1.scd +++ b/doc/yambar.1.scd @@ -25,11 +25,7 @@ yambar - modular status panel for X11 and Wayland *-p*,*--print-pid*=_FILE_|_FD_ Print PID to this file, or FD, when successfully started. The file (or FD) is closed immediately after writing the PID. When a _FILE_ - as been specified, the file is unlinked upon exiting. - -*-d*,*--log-level*={*info*,*warning*,*error*,*none*} - Log level, used both for log output on stderr as well as - syslog. Default: _warning_. + as been specified, the file is unlinked exit. *-l*,*--log-colorize*=[{*never*,*always*,*auto*}] Enables or disables colorization of log output on stderr. diff --git a/doc/yambar.5.scd b/doc/yambar.5.scd index 38c521d..6b4a5ff 100644 --- a/doc/yambar.5.scd +++ b/doc/yambar.5.scd @@ -12,10 +12,9 @@ and reference them using anchors. Besides the normal yaml types, there are a couple of yambar specific types that are frequently used: -- *font*: this is a comma separated list of fonts in _fontconfig_ - format. Example of valid values: - - Font Awesome 6 Brands - - Font Awesome 6 Free:style=solid +- *font*: this is a string in _fontconfig_ format. Example of valid values: + - Font Awesome 5 Brands + - Font Awesome 5 Free:style=solid - Dina:pixelsize=10:slant=italic - Dina:pixelsize=10:weight=bold - *color*: an rgba hexstring; _RRGGBBAA_. Examples: @@ -23,17 +22,12 @@ types that are frequently used: - 000000ff: black, no transparency - 00ff00ff: green, no transparency - ff000099: red, semi-transparent -- *environment reference*: a string that contains format ${VAR}. This will be - replaced by the value of the environment variable VAR. Example: - - ${HOME} - - ${HOME}/.config/yambar - - ENV is ${ENV}, ENV2 is ${ENV2} # FORMAT [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | height : int : yes @@ -51,11 +45,6 @@ types that are frequently used: : no : Monitor to place the bar on. If not specified, the primary monitor will be used -| layer -: string -: no -: Layer to put bar on. One of _overlay_, _top_, _bottom_ or - _background_. Wayland only. Default: _bottom_. | left-spacing : int : no @@ -84,26 +73,10 @@ types that are frequently used: : associative array : no : Configures the border around the status bar -| border.left-width -: int -: no -: Width of the border on the left side, in pixels -| border.right-width -: int -: no -: Width of the border on the right side, in pixels -| border.top-width -: int -: no -: Width of the border on the top side, in pixels -| border.bottom-width -: int -: no -: Width of the border on the bottom side, in pixels | border.width : int : no -: Short-hand for setting _border.left/right/top/bottom-width_ +: Width, in pixels, of the border | border.color : color : no @@ -131,27 +104,11 @@ types that are frequently used: | font : font : no -: Default font to use in modules and particles. May also be a comma - separated list of several fonts, in which case the first font is - the primary font, and the rest fallback fonts. These are yambar - custom fallback fonts that will be searched before the fontconfig - provided fallback list. -| font-shaping -: enum -: no -: Default setting for font-shaping, for use in particles. One of - _full_ or _none_. When set to _full_ (the default), strings will be - "shaped" using HarfBuzz. Requires support in fcft. +: Default font to use in modules and particles | foreground : 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 @@ -178,8 +135,8 @@ bar: right: - clock: - content: - - string: {text: "{time}"} + content: + - string: {text: "{time}"} ``` # FILES diff --git a/examples/configurations/laptop.conf b/examples/configurations/laptop.conf index 1bdd16c..d124e72 100644 --- a/examples/configurations/laptop.conf +++ b/examples/configurations/laptop.conf @@ -4,9 +4,9 @@ # For X11/i3, you'll want to replace calls to swaymsg with i3-msg, and # the sway-xkb module with the xkb module. -# fonts we'll be reusing here and there -awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14 -awesome_brands: &awesome_brands Font Awesome 6 Brands:pixelsize=16 +# fonts we'll be re-using here and there +awesome: &awesome Font Awesome 5 Free:style=solid:pixelsize=14 +awesome_brands: &awesome_brands Font Awesome 5 Brands:pixelsize=16 std_underline: &std_underline {underline: { size: 2, color: ff0000ff}} @@ -45,68 +45,70 @@ bar: - urgent: &urgent foreground: 000000ff deco: {stack: [background: {color: bc2b3fff}, <<: *std_underline]} - - map: &i3_mode - default: - - string: - margin: 5 - text: "{mode}" - deco: {background: {color: cc421dff}} - - empty: {right-margin: 7} - conditions: - mode == default: {empty: {}} content: "": map: - conditions: - state == focused: {string: {<<: [*default, *focused]}} - state == unfocused: {string: {<<: *default}} - state == invisible: {string: {<<: [*default, *invisible]}} - state == urgent: {string: {<<: [*default, *urgent]}} + tag: state + values: + focused: {string: {<<: [*default, *focused]}} + unfocused: {string: {<<: *default}} + invisible: {string: {<<: [*default, *invisible]}} + urgent: {string: {<<: [*default, *urgent]}} main: map: - conditions: - state == focused: {string: {<<: [*main, *focused]}} - state == unfocused: {string: {<<: *main}} - state == invisible: {string: {<<: [*main, *invisible]}} - state == urgent: {string: {<<: [*main, *urgent]}} + tag: state + values: + focused: {string: {<<: [*main, *focused]}} + unfocused: {string: {<<: *main}} + invisible: {string: {<<: [*main, *invisible]}} + urgent: {string: {<<: [*main, *urgent]}} surfing: map: - conditions: - state == focused: {string: {<<: [*surfing, *focused]}} - state == unfocused: {string: {<<: *surfing}} - state == invisible: {string: {<<: [*surfing, *invisible]}} - state == urgent: {string: {<<: [*surfing, *urgent]}} + tag: state + values: + focused: {string: {<<: [*surfing, *focused]}} + unfocused: {string: {<<: *surfing}} + invisible: {string: {<<: [*surfing, *invisible]}} + urgent: {string: {<<: [*surfing, *urgent]}} misc: map: - conditions: - state == focused: {string: {<<: [*misc, *focused]}} - state == unfocused: {string: {<<: *misc}} - state == invisible: {string: {<<: [*misc, *invisible]}} - state == urgent: {string: {<<: [*misc, *urgent]}} + tag: state + values: + focused: {string: {<<: [*misc, *focused]}} + unfocused: {string: {<<: *misc}} + invisible: {string: {<<: [*misc, *invisible]}} + urgent: {string: {<<: [*misc, *urgent]}} mail: map: - conditions: - state == focused: {string: {<<: [*mail, *focused]}} - state == unfocused: {string: {<<: *mail}} - state == invisible: {string: {<<: [*mail, *invisible]}} - state == urgent: {string: {<<: [*mail, *urgent]}} + tag: state + values: + focused: {string: {<<: [*mail, *focused]}} + unfocused: {string: {<<: *mail}} + invisible: {string: {<<: [*mail, *invisible]}} + urgent: {string: {<<: [*mail, *urgent]}} music: map: - conditions: - state == focused: {string: {<<: [*music, *focused]}} - state == unfocused: {string: {<<: *music}} - state == invisible: {string: {<<: [*music, *invisible]}} - state == urgent: {string: {<<: [*music, *urgent]}} + tag: state + values: + focused: {string: {<<: [*music, *focused]}} + unfocused: {string: {<<: *music}} + invisible: {string: {<<: [*music, *invisible]}} + urgent: {string: {<<: [*music, *urgent]}} + current: + map: + left-margin: 7 + tag: application + values: + "": {string: {text: "{title}"}} + default: + list: + spacing: 0 + items: + - string: {text: "{application}", max: 10, foreground: ffa0a0ff} + - string: {text: ": "} + - string: {text: "{title}", max: 35} - - foreign-toplevel: - content: - map: - conditions: - ~activated: {empty: {}} - activated: - - string: {text: "{app-id}", foreground: ffa0a0ff} - - string: {text: ": {title}"} center: - mpd: host: /run/mpd/socket @@ -115,28 +117,32 @@ bar: spacing: 0 items: - map: - conditions: - state == playing: {string: {text: "{artist}"}} - state == paused: {string: {text: "{artist}", foreground: ffffff66}} + tag: state + values: + playing: {string: {text: "{artist}"}} + paused: {string: {text: "{artist}", foreground: ffffff66}} - string: {text: " | ", foreground: ffffff66} - map: - conditions: - state == playing: {string: {text: "{album}"}} - state == paused: {string: {text: "{album}", foreground: ffffff66}} + tag: state + values: + playing: {string: {text: "{album}"}} + paused: {string: {text: "{album}", foreground: ffffff66}} - string: {text: " | ", foreground: ffffff66} - map: - conditions: - state == playing: {string: {text: "{title}", foreground: ffa0a0ff}} - state == paused: {string: {text: "{title}", foreground: ffffff66}} + tag: state + values: + playing: {string: {text: "{title}", foreground: ffa0a0ff}} + paused: {string: {text: "{title}", foreground: ffffff66}} content: map: margin: 10 - conditions: - state == offline: {string: {text: offline, foreground: ff0000ff}} - state == stopped: {string: {text: stopped}} - state == paused: {list: *artist_album_title} - state == playing: {list: *artist_album_title} + tag: state + values: + offline: {string: {text: offline, foreground: ff0000ff}} + stopped: {string: {text: stopped}} + paused: {list: *artist_album_title} + playing: {list: *artist_album_title} right: - removables: @@ -146,21 +152,24 @@ bar: spacing: 5 content: map: - conditions: - ~mounted: + tag: mounted + values: + false: map: + tag: optical on-click: udisksctl mount -b {device} - conditions: - ~optical: [{string: *drive}, {string: {text: "{label}"}}] - optical: [{string: *optical}, {string: {text: "{label}"}}] - mounted: + values: + false: [{string: *drive}, {string: {text: "{label}"}}] + true: [{string: *optical}, {string: {text: "{label}"}}] + true: map: + tag: optical on-click: udisksctl unmount -b {device} - conditions: - ~optical: + values: + false: - string: {<<: *drive, deco: *std_underline} - string: {text: "{label}"} - optical: + true: - string: {<<: *optical, deco: *std_underline} - string: {text: "{label}"} - sway-xkb: @@ -169,69 +178,66 @@ bar: - string: {text: , font: *awesome} - string: {text: "{layout}"} - network: + name: enp1s0 content: map: - default: {empty: {}} - conditions: - name == enp1s0: + tag: carrier + values: + false: {empty: {}} + true: map: - conditions: - ~carrier: {empty: {}} - carrier: + tag: state + default: {string: {text: , font: *awesome, foreground: ffffff66}} + values: + up: map: - default: {string: {text: , font: *awesome, foreground: ffffff66}} - conditions: - state == up && ipv4 != "": {string: {text: , font: *awesome}} + tag: ipv4 + default: {string: {text: , font: *awesome}} + values: + "": {string: {text: , font: *awesome, foreground: ffffff66}} - network: - poll-interval: 1000 + name: wlp2s0 content: map: - default: {empty: {}} - conditions: - name == wlp2s0: + tag: state + default: {string: {text: , font: *awesome, foreground: ffffff66}} + values: + down: {string: {text: , font: *awesome, foreground: ff0000ff}} + up: map: - default: {string: {text: , font: *awesome, foreground: ffffff66}} - conditions: - state == down: {string: {text: , font: *awesome, foreground: ff0000ff}} - state == up: - map: - default: - - string: {text: , font: *awesome} - - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s"} - - conditions: - ipv4 == "": - - string: {text: , font: *awesome, foreground: ffffff66} - - string: {text: "{ssid} {dl-speed:mb}/{ul-speed:mb} Mb/s", foreground: ffffff66} + tag: ipv4 + default: {string: {text: , font: *awesome}} + values: + "": {string: {text: , font: *awesome, foreground: ffffff66}} - alsa: card: hw:PCH mixer: Master content: map: - conditions: - ~online: {string: {text: , font: *awesome, foreground: ff0000ff}} - online: - map: - on-click: /bin/sh -c "amixer -q sset Speaker unmute && amixer -q sset Headphone unmute && amixer -q sset Master toggle" - conditions: - muted: {string: {text: , font: *awesome, foreground: ffffff66}} - ~muted: - ramp: - tag: percent - items: - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} - - string: {text: , font: *awesome} + on-click: /bin/sh -c "amixer -q sset Speaker unmute && amixer -q sset Headphone unmute && amixer -q sset Master toggle" + tag: muted + values: + true: {string: {text: , font: *awesome, foreground: ffffff66}} + false: + ramp: + tag: volume + items: + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} + - string: {text: , font: *awesome} - backlight: name: intel_backlight content: [ string: {text: , font: *awesome}, string: {text: "{percent}%"}] - battery: name: BAT0 - poll-interval: 30000 - anchors: - discharging: &discharging - list: - items: + poll-interval: 30 + content: + map: + tag: state + values: + discharging: - ramp: tag: capacity items: @@ -246,34 +252,12 @@ bar: - string: {text: , font: *awesome} - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% {estimate}"} - content: - map: - conditions: - state == unknown: - <<: *discharging - state == discharging: - <<: *discharging - state == charging: + charging: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% {estimate}"} - state == full: + full: - string: {text: , foreground: 00ff00ff, font: *awesome} - string: {text: "{capacity}% full"} - state == "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: @@ -284,6 +268,6 @@ bar: - label: content: string: - on-click: systemctl poweroff + on-click: loginctl poweroff text:  font: *awesome diff --git a/examples/configurations/river-tags.conf b/examples/configurations/river-tags.conf deleted file mode 100644 index 28bc0b9..0000000 --- a/examples/configurations/river-tags.conf +++ /dev/null @@ -1,58 +0,0 @@ -hack: &hack Hack Nerd Font:pixelsize=13 -bg_default: &bg_default {stack: [{background: {color: 81A1C1ff}}, {underline: {size: 4, color: D8DEE9ff}}]} -bar: - height: 40 - location: top - font: JuliaMono:pixelsize=10 - spacing: 2 - margin: 0 - layer: bottom - foreground: eeeeeeff - background: 2E3440dd - - left: - - river: - anchors: - - base: &river_base - left-margin: 10 - right-margin: 13 - default: {string: {text: , font: *hack}} - conditions: - id == 1: {string: {text: ﳐ, font: *hack}} - id == 2: {string: {text: , font: *hack}} - id == 3: {string: {text: , font: *hack}} - id == 4: {string: {text: , font: *hack}} - id == 5: {string: {text: , font: *hack}} - id == 10: {string: {text: "scratchpad", font: *hack}} - id == 11: {string: {text: "work", font: *hack}} - - content: - map: - on-click: - left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))" - right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))" - middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))" - conditions: - state == urgent: - map: - <<: *river_base - deco: {background: {color: D08770ff}} - state == focused: - map: - <<: *river_base - deco: *bg_default - state == visible && ~occupied: - map: - <<: *river_base - state == visible && occupied: - map: - <<: *river_base - deco: *bg_default - state == unfocused: - map: - <<: *river_base - state == invisible && ~occupied: {empty: {}} - state == invisible && occupied: - map: - <<: *river_base - deco: {underline: {size: 3, color: ea6962ff}} diff --git a/examples/river-minimal.yml b/examples/river-minimal.yml deleted file mode 100644 index 2ca225f..0000000 --- a/examples/river-minimal.yml +++ /dev/null @@ -1,69 +0,0 @@ -bg_default: &bg_default {stack: [{background: {color: 81A1C1ff}}, {underline: {size: 4, color: D8DEE9ff}}]} -bar: - height: 32 - location: top - background: 000000ff - font: NotoSans:pixelsize=16 - - right: - - clock: - content: - - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} - - string: {text: "{date}", right-margin: 5} - - string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"} - - string: {text: "{time} "} - left: - - river: - anchors: - - base: &river_base - left-margin: 10 - right-margin: 13 - default: {string: {text: }} - conditions: - id == 1: {string: {text: 1}} - id == 2: {string: {text: 2}} - id == 3: {string: {text: 3}} - id == 4: {string: {text: 4}} - id == 5: {string: {text: 5}} - - content: - map: - on-click: - left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))" - right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))" - middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))" - conditions: - state == urgent: - map: - <<: *river_base - deco: {background: {color: D08770ff}} - state == focused: - map: - <<: *river_base - deco: *bg_default - state == visible && ~occupied: - map: - <<: *river_base - state == visible && occupied: - map: - <<: *river_base - deco: *bg_default - state == unfocused: - map: - <<: *river_base - state == invisible && ~occupied: {empty: {}} - state == invisible && occupied: - map: - <<: *river_base - deco: {underline: {size: 3, color: ea6962ff}} - - - center: - - foreign-toplevel: - content: - map: - conditions: - ~activated: {empty: {}} - activated: - - string: {text: " {app-id}", foreground: ffa0a0ff} - - string: {text: ": {title}"} diff --git a/examples/scripts/dwl-tags.sh b/examples/scripts/dwl-tags.sh deleted file mode 100755 index 4999548..0000000 --- a/examples/scripts/dwl-tags.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env bash -# -# dwl-tags.sh - display dwl tags -# -# USAGE: dwl-tags.sh 1 -# -# REQUIREMENTS: -# - inotifywait ( 'inotify-tools' on arch ) -# - Launch dwl with `dwl > ~.cache/dwltags` or change $fname -# -# TAGS: -# Name Type Return -# ---------------------------------------------------- -# {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 -# -# Example configuration: -# -# - 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 -# conditions: -# tag_0_occupied: -# map: -# conditions: -# tag_0_focused: {string: {text: "{tag_0}", <<: *focused}} -# ~tag_0_focused: {string: {text: "{tag_0}", <<: *occupied}} -# ~tag_0_occupied: -# map: -# conditions: -# tag_0_focused: {string: {text: "{tag_0}", <<: *focused}} -# ~tag_0_focused: {string: {text: "{tag_0}", <<: *default}} -# ... -# ... -# ... -# - map: -# margin: 4 -# conditions: -# tag_8_occupied: -# map: -# conditions: -# tag_8_focused: {string: {text: "{tag_8}", <<: *focused}} -# ~tag_8_focused: {string: {text: "{tag_8}", <<: *occupied}} -# ~tag_8_occupied: -# map: -# values: -# tag_8_focused: {string: {text: "{tag_8}", <<: *focused}} -# ~tag_8_focused: {string: {text: "{tag_8}", <<: *default}} -# - list: -# spacing: 3 -# items: -# - string: {text: "{layout}"} -# - string: {text: "{title}"} - - -# Variables -declare output title layout activetags selectedtags -declare -a tags name -readonly fname="$HOME"/.cache/dwltags - - -_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</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 - done - - printf -- '%s\n' "layout|string|${layout}" - printf -- '%s\n' "" - -} - -# Call the function here so the tags are displayed at dwl launch -_cycle - -while true; do - - [[ ! -f "${fname}" ]] && printf -- '%s\n' \ - "You need to redirect dwl stdout to ~/.cache/dwltags" >&2 - - inotifywait -qq --event modify "${fname}" - - # Get info from the file - output="$(tail -n6 "${fname}")" - title="$(echo "${output}" | grep title | cut -d ' ' -f 3- )" - #selmon="$(echo "${output}" | grep 'selmon')" - layout="$(echo "${output}" | grep layout | cut -d ' ' -f 3- )" - - # 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 output title layout activetags selectedtags -unset -v tags name diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh deleted file mode 100755 index 5026b5a..0000000 --- a/examples/scripts/pacman.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/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} int number of pacman packages -# {aur} int number of aur packages -# {pkg} int sum of both -# -# Examples configuration: -# - script: -# 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: -# default: { string: { text: "{pacman} + {aur} = {pkg}" } } -# conditions: -# pkg == 0: {string: {text: no updates}} - - -declare interval aur_helper pacman_num aur_num pkg_num - -# Error message in STDERR -_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 - # NUMBER[SUFFIXE] - # Possible suffix: - # "s" seconds / "m" minutes / "h" hours / "d" days - interval="1h" - - # 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" - exit 1 - else - aur_num=$("${aur_helper}" -Qmu | wc -l) - fi - - pkg_num=$(( pacman_num + aur_num )) - - 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 aur_helper pacman_num aur_num pkg_num -unset -f _err diff --git a/external/river-status-unstable-v1.xml b/external/river-status-unstable-v1.xml index e9629dd..a4d6f4e 100644 --- a/external/river-status-unstable-v1.xml +++ b/external/river-status-unstable-v1.xml @@ -1,7 +1,7 @@ - Copyright 2020 The River Developers + Copyright 2020 Isaac Freund Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -47,7 +47,7 @@ - + This interface allows clients to receive information about the current windowing state of an output. @@ -75,36 +75,12 @@ - - - - Sent once on binding the interface and again whenever the set of - tags with at least one urgent view changes. - - - - - - - Sent once on binding the interface should a layout name exist and again - whenever the name changes. - - - - - - - Sent when the current layout name has been removed without a new one - being set, for example when the active layout generator disconnects. - - - + This interface allows clients to receive information about the current - focus of a seat. Note that (un)focused_output events will only be sent - if the client has bound the relevant wl_output globals. + focus of a seat. @@ -136,13 +112,5 @@ - - - - Sent once on binding the interface and again whenever a new mode - is entered (e.g. with riverctl enter-mode foobar). - - - diff --git a/external/wlr-foreign-toplevel-management-unstable-v1.xml b/external/wlr-foreign-toplevel-management-unstable-v1.xml deleted file mode 100644 index 1081337..0000000 --- a/external/wlr-foreign-toplevel-management-unstable-v1.xml +++ /dev/null @@ -1,270 +0,0 @@ - - - - Copyright © 2018 Ilia Bozhinov - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - The purpose of this protocol is to enable the creation of taskbars - and docks by providing them with a list of opened applications and - letting them request certain actions on them, like maximizing, etc. - - After a client binds the zwlr_foreign_toplevel_manager_v1, each opened - toplevel window will be sent via the toplevel event - - - - - This event is emitted whenever a new toplevel window is created. It - is emitted for all toplevels, regardless of the app that has created - them. - - All initial details of the toplevel(title, app_id, states, etc.) will - be sent immediately after this event via the corresponding events in - zwlr_foreign_toplevel_handle_v1. - - - - - - - Indicates the client no longer wishes to receive events for new toplevels. - However the compositor may emit further toplevel_created events, until - the finished event is emitted. - - The client must not send any more requests after this one. - - - - - - This event indicates that the compositor is done sending events to the - zwlr_foreign_toplevel_manager_v1. The server will destroy the object - immediately after sending this request, so it will become invalid and - the client should free any resources associated with it. - - - - - - - A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel - window. Each app may have multiple opened toplevels. - - Each toplevel has a list of outputs it is visible on, conveyed to the - client with the output_enter and output_leave events. - - - - - This event is emitted whenever the title of the toplevel changes. - - - - - - - This event is emitted whenever the app-id of the toplevel changes. - - - - - - - This event is emitted whenever the toplevel becomes visible on - the given output. A toplevel may be visible on multiple outputs. - - - - - - - This event is emitted whenever the toplevel stops being visible on - the given output. It is guaranteed that an entered-output event - with the same output has been emitted before this event. - - - - - - - Requests that the toplevel be maximized. If the maximized state actually - changes, this will be indicated by the state event. - - - - - - Requests that the toplevel be unmaximized. If the maximized state actually - changes, this will be indicated by the state event. - - - - - - Requests that the toplevel be minimized. If the minimized state actually - changes, this will be indicated by the state event. - - - - - - Requests that the toplevel be unminimized. If the minimized state actually - changes, this will be indicated by the state event. - - - - - - Request that this toplevel be activated on the given seat. - There is no guarantee the toplevel will be actually activated. - - - - - - - The different states that a toplevel can have. These have the same meaning - as the states with the same names defined in xdg-toplevel - - - - - - - - - - - This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 - is created and each time the toplevel state changes, either because of a - compositor action or because of a request in this protocol. - - - - - - - - This event is sent after all changes in the toplevel state have been - sent. - - This allows changes to the zwlr_foreign_toplevel_handle_v1 properties - to be seen as atomic, even if they happen via multiple events. - - - - - - Send a request to the toplevel to close itself. The compositor would - typically use a shell-specific method to carry out this request, for - example by sending the xdg_toplevel.close event. However, this gives - no guarantees the toplevel will actually be destroyed. If and when - this happens, the zwlr_foreign_toplevel_handle_v1.closed event will - be emitted. - - - - - - The rectangle of the surface specified in this request corresponds to - the place where the app using this protocol represents the given toplevel. - It can be used by the compositor as a hint for some operations, e.g - minimizing. The client is however not required to set this, in which - case the compositor is free to decide some default value. - - If the client specifies more than one rectangle, only the last one is - considered. - - The dimensions are given in surface-local coordinates. - Setting width=height=0 removes the already-set rectangle. - - - - - - - - - - - - - - - - This event means the toplevel has been destroyed. It is guaranteed there - won't be any more events for this zwlr_foreign_toplevel_handle_v1. The - toplevel itself becomes inert so any requests will be ignored except the - destroy request. - - - - - - Destroys the zwlr_foreign_toplevel_handle_v1 object. - - This request should be called either when the client does not want to - use the toplevel anymore or after the closed event to finalize the - destruction of the object. - - - - - - - - Requests that the toplevel be fullscreened on the given output. If the - fullscreen state and/or the outputs the toplevel is visible on actually - change, this will be indicated by the state and output_enter/leave - events. - - The output parameter is only a hint to the compositor. Also, if output - is NULL, the compositor should decide which output the toplevel will be - fullscreened on, if at all. - - - - - - - Requests that the toplevel be unfullscreened. If the fullscreen state - actually changes, this will be indicated by the state event. - - - - - - - - This event is emitted whenever the parent of the toplevel changes. - - No event is emitted when the parent handle is destroyed by the client. - - - - - diff --git a/external/wlr-layer-shell-unstable-v1.xml b/external/wlr-layer-shell-unstable-v1.xml index d62fd51..fa67001 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,12 +47,6 @@ 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. @@ -100,7 +94,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 @@ -109,14 +103,6 @@ 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. @@ -203,85 +189,21 @@ - - - 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 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. + 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. 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. - Keyboard interactivity is double-buffered, see wl_surface.commit. + Events is double-buffered, see wl_surface.commit. - + @@ -366,7 +288,6 @@ - diff --git a/external/wlr-protocols b/external/wlr-protocols index d1598e8..16a2888 160000 --- a/external/wlr-protocols +++ b/external/wlr-protocols @@ -1 +1 @@ -Subproject commit d1598e82240d6e8ca57729495a94d4e11d222033 +Subproject commit 16a28885bc92869d8e589e725e7bf018432c47e4 diff --git a/font-shaping.h b/font-shaping.h deleted file mode 100644 index 3ae3817..0000000 --- a/font-shaping.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -enum font_shaping { - FONT_SHAPE_NONE, - FONT_SHAPE_GRAPHEMES, - FONT_SHAPE_FULL, -}; diff --git a/generate-version.sh b/generate-version.sh index 8ac3b03..d17cd40 100755 --- a/generate-version.sh +++ b/generate-version.sh @@ -13,18 +13,11 @@ out_file=${3} if [ -d "${src_dir}/.git" ] && command -v git > /dev/null; then workdir=$(pwd) cd "${src_dir}" - - if git describe --tags > /dev/null 2>&1; then - git_version=$(git describe --always --tags) - else - # No tags available, happens in e.g. CI builds - git_version="${default_version}" - fi - + git_version=$(git describe --always --tags) git_branch=$(git rev-parse --abbrev-ref HEAD) cd "${workdir}" - new_version="${git_version} ($(date "+%b %d %Y"), branch '${git_branch}')" + new_version="${git_version} ($(env LC_TIME=C date "+%b %d %Y"), branch '${git_branch}')" else new_version="${default_version}" fi diff --git a/log.c b/log.c index ba4ebd9..c5d9093 100644 --- a/log.c +++ b/log.c @@ -1,60 +1,41 @@ #include "log.h" -#include -#include -#include -#include -#include -#include #include +#include +#include #include -#include +#include +#include +#include #include -#define ALEN(v) (sizeof(v) / sizeof((v)[0])) -#define UNUSED __attribute__((unused)) +#include static bool colorize = false; -static bool do_syslog = false; -static enum log_class log_level = LOG_CLASS_NONE; - -static const struct { - const char name[8]; - const char log_prefix[7]; - uint8_t color; - int syslog_equivalent; -} log_level_map[] = { - [LOG_CLASS_NONE] = {"none", "none", 5, -1}, - [LOG_CLASS_ERROR] = {"error", " err", 31, LOG_ERR}, - [LOG_CLASS_WARNING] = {"warning", "warn", 33, LOG_WARNING}, - [LOG_CLASS_INFO] = {"info", "info", 97, LOG_INFO}, - [LOG_CLASS_DEBUG] = {"debug", " dbg", 36, LOG_DEBUG}, -}; +static bool do_syslog = true; void -log_init(enum log_colorize _colorize, bool _do_syslog, enum log_facility syslog_facility, enum log_class _log_level) +log_init(enum log_colorize _colorize, bool _do_syslog, + enum log_facility syslog_facility, enum log_class syslog_level) { static const int facility_map[] = { [LOG_FACILITY_USER] = LOG_USER, [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - /* Don't use colors if NO_COLOR is defined and not empty */ - const char *no_color_str = getenv("NO_COLOR"); - const bool no_color = no_color_str != NULL && no_color_str[0] != '\0'; + static const int level_map[] = { + [LOG_CLASS_ERROR] = LOG_ERR, + [LOG_CLASS_WARNING] = LOG_WARNING, + [LOG_CLASS_INFO] = LOG_INFO, + [LOG_CLASS_DEBUG] = LOG_DEBUG, + }; - colorize = _colorize == LOG_COLORIZE_NEVER - ? false - : _colorize == LOG_COLORIZE_ALWAYS - ? true - : !no_color && isatty(STDERR_FILENO); + colorize = _colorize == LOG_COLORIZE_NEVER ? false : _colorize == LOG_COLORIZE_ALWAYS ? true : isatty(STDERR_FILENO); do_syslog = _do_syslog; - log_level = _log_level; - int slvl = log_level_map[_log_level].syslog_equivalent; - if (do_syslog && slvl != -1) { - openlog(NULL, /*LOG_PID*/ 0, facility_map[syslog_facility]); - setlogmask(LOG_UPTO(slvl)); + if (do_syslog) { + openlog(NULL, /*LOG_PID*/0, facility_map[syslog_facility]); + setlogmask(LOG_UPTO(level_map[syslog_level])); } } @@ -66,153 +47,120 @@ log_deinit(void) } static void -_log(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, int sys_errno, - va_list va) +_log(enum log_class log_class, const char *module, const char *file, int lineno, + const char *fmt, int sys_errno, va_list va) { - assert(log_class > LOG_CLASS_NONE); - assert(log_class < ALEN(log_level_map)); - - if (log_class > log_level) - return; - - const char *prefix = log_level_map[log_class].log_prefix; - unsigned int class_clr = log_level_map[log_class].color; + const char *class = "abcd"; + int class_clr = 0; + switch (log_class) { + case LOG_CLASS_ERROR: class = " err"; class_clr = 31; break; + case LOG_CLASS_WARNING: class = "warn"; class_clr = 33; break; + case LOG_CLASS_INFO: class = "info"; class_clr = 97; break; + case LOG_CLASS_DEBUG: class = " dbg"; class_clr = 36; break; + } char clr[16]; - snprintf(clr, sizeof(clr), "\033[%um", class_clr); - fprintf(stderr, "%s%s%s: ", colorize ? clr : "", prefix, colorize ? "\033[0m" : ""); + snprintf(clr, sizeof(clr), "\e[%dm", class_clr); + fprintf(stderr, "%s%s%s: ", colorize ? clr : "", class, colorize ? "\e[0m" : ""); if (colorize) - fputs("\033[2m", stderr); + fprintf(stderr, "\e[2m"); fprintf(stderr, "%s:%d: ", file, lineno); if (colorize) - fputs("\033[0m", stderr); + fprintf(stderr, "\e[0m"); vfprintf(stderr, fmt, va); if (sys_errno != 0) - fprintf(stderr, ": %s (%d)", strerror(sys_errno), sys_errno); + fprintf(stderr, ": %s", strerror(sys_errno)); - fputc('\n', stderr); + fprintf(stderr, "\n"); } static void -_sys_log(enum log_class log_class, const char *module, const char UNUSED *file, int UNUSED lineno, const char *fmt, - int sys_errno, va_list va) +_sys_log(enum log_class log_class, const char *module, + const char *file __attribute__((unused)), + int lineno __attribute__((unused)), + const char *fmt, int sys_errno, va_list va) { - assert(log_class > LOG_CLASS_NONE); - assert(log_class < ALEN(log_level_map)); - if (!do_syslog) return; - if (log_class > log_level) - return; - /* Map our log level to syslog's level */ - int level = log_level_map[log_class].syslog_equivalent; - - char msg[4096]; - int n = vsnprintf(msg, sizeof(msg), fmt, va); - assert(n >= 0); - - if (sys_errno != 0 && (size_t)n < sizeof(msg)) - snprintf(msg + n, sizeof(msg) - n, ": %s", strerror(sys_errno)); - - syslog(level, "%s: %s", module, msg); -} - -void -log_msg_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) -{ - va_list va2; - va_copy(va2, va); - _log(log_class, module, file, lineno, fmt, 0, va); - _sys_log(log_class, module, file, lineno, fmt, 0, va2); - va_end(va2); -} - -void -log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - log_msg_va(log_class, module, file, lineno, fmt, va); - va_end(va); -} - -void -log_errno_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) -{ - log_errno_provided_va(log_class, module, file, lineno, errno, fmt, va); -} - -void -log_errno(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - log_errno_va(log_class, module, file, lineno, fmt, va); - va_end(va); -} - -void -log_errno_provided_va(enum log_class log_class, const char *module, const char *file, int lineno, int errno_copy, - const char *fmt, va_list va) -{ - va_list va2; - va_copy(va2, va); - _log(log_class, module, file, lineno, fmt, errno_copy, va); - _sys_log(log_class, module, file, lineno, fmt, errno_copy, va2); - va_end(va2); -} - -void -log_errno_provided(enum log_class log_class, const char *module, const char *file, int lineno, int errno_copy, - const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - log_errno_provided_va(log_class, module, file, lineno, errno_copy, fmt, va); - va_end(va); -} - -static size_t -map_len(void) -{ - size_t len = ALEN(log_level_map); -#ifndef _DEBUG - /* Exclude "debug" entry for non-debug builds */ - len--; -#endif - return len; -} - -int -log_level_from_string(const char *str) -{ - if (str[0] == '\0') - return -1; - - for (int i = 0, n = map_len(); i < n; i++) - if (strcmp(str, log_level_map[i].name) == 0) - return i; - - return -1; -} - -const char * -log_level_string_hint(void) -{ - static char buf[64]; - if (buf[0] != '\0') - return buf; - - for (size_t i = 0, pos = 0, n = map_len(); i < n; i++) { - const char *entry = log_level_map[i].name; - const char *delim = (i + 1 < n) ? ", " : ""; - pos += snprintf(buf + pos, sizeof(buf) - pos, "'%s'%s", entry, delim); + int level = -1; + switch (log_class) { + case LOG_CLASS_ERROR: level = LOG_ERR; break; + case LOG_CLASS_WARNING: level = LOG_WARNING; break; + case LOG_CLASS_INFO: level = LOG_INFO; break; + case LOG_CLASS_DEBUG: level = LOG_DEBUG; break; } - return buf; + assert(level != -1); + + const char *sys_err = sys_errno != 0 ? strerror(sys_errno) : NULL; + + va_list va2; + va_copy(va2, va); + + /* Calculate required size of buffer holding the entire log message */ + int required_len = 0; + required_len += strlen(module) + 2; /* "%s: " */ + required_len += vsnprintf(NULL, 0, fmt, va2); va_end(va2); + + if (sys_errno != 0) + required_len += strlen(sys_err) + 2; /* ": %s" */ + + /* Format the msg */ + char *msg = malloc(required_len + 1); + int idx = 0; + + idx += snprintf(&msg[idx], required_len + 1 - idx, "%s: ", module); + idx += vsnprintf(&msg[idx], required_len + 1 - idx, fmt, va); + + if (sys_errno != 0) { + snprintf( + &msg[idx], required_len + 1 - idx, ": %s", strerror(sys_errno)); + } + + syslog(level, "%s", msg); + free(msg); +} + +void +log_msg(enum log_class log_class, const char *module, + const char *file, int lineno, const char *fmt, ...) +{ + va_list ap1, ap2; + va_start(ap1, fmt); + va_copy(ap2, ap1); + _log(log_class, module, file, lineno, fmt, 0, ap1); + _sys_log(log_class, module, file, lineno, fmt, 0, ap2); + va_end(ap1); + va_end(ap2); +} + +void log_errno(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) +{ + va_list ap1, ap2; + va_start(ap1, fmt); + va_copy(ap2, ap1); + _log(log_class, module, file, lineno, fmt, errno, ap1); + _sys_log(log_class, module, file, lineno, fmt, errno, ap2); + va_end(ap1); + va_end(ap2); +} + +void log_errno_provided(enum log_class log_class, const char *module, + const char *file, int lineno, int _errno, + const char *fmt, ...) +{ + va_list ap1, ap2; + va_start(ap1, fmt); + va_copy(ap2, ap1); + _log(log_class, module, file, lineno, fmt, _errno, ap1); + _sys_log(log_class, module, file, lineno, fmt, _errno, ap2); + va_end(ap1); + va_end(ap2); } diff --git a/log.h b/log.h index 48f16fe..dfddd76 100644 --- a/log.h +++ b/log.h @@ -1,43 +1,42 @@ #pragma once -#include #include enum log_colorize { LOG_COLORIZE_NEVER, LOG_COLORIZE_ALWAYS, LOG_COLORIZE_AUTO }; enum log_facility { LOG_FACILITY_USER, LOG_FACILITY_DAEMON }; +enum log_class { LOG_CLASS_ERROR, LOG_CLASS_WARNING, LOG_CLASS_INFO, LOG_CLASS_DEBUG }; -enum log_class { LOG_CLASS_NONE, LOG_CLASS_ERROR, LOG_CLASS_WARNING, LOG_CLASS_INFO, LOG_CLASS_DEBUG }; - -void log_init(enum log_colorize colorize, bool do_syslog, enum log_facility syslog_facility, enum log_class log_level); +void log_init(enum log_colorize colorize, bool do_syslog, + enum log_facility syslog_facility, enum log_class syslog_level); void log_deinit(void); -void log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) - __attribute__((format(printf, 5, 6))); +void log_msg(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) __attribute__((format (printf, 5, 6))); -void log_errno(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) - __attribute__((format(printf, 5, 6))); +void log_errno(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) __attribute__((format (printf, 5, 6))); -void log_errno_provided(enum log_class log_class, const char *module, const char *file, int lineno, int _errno, - const char *fmt, ...) __attribute__((format(printf, 6, 7))); +void log_errno_provided( + enum log_class log_class, const char *module, + const char *file, int lineno, int _errno, + const char *fmt, ...) __attribute__((format (printf, 6, 7))); -void log_msg_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) - __attribute__((format(printf, 5, 0))); -void log_errno_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, - va_list va) __attribute__((format(printf, 5, 0))); -void log_errno_provided_va(enum log_class log_class, const char *module, const char *file, int lineno, int _errno, - const char *fmt, va_list va) __attribute__((format(printf, 6, 0))); - -int log_level_from_string(const char *str); -const char *log_level_string_hint(void); - -#define LOG_ERR(...) log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERRNO(...) log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERRNO_P(_errno, ...) \ - log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, _errno, __VA_ARGS__) -#define LOG_WARN(...) log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_INFO(...) log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERR(fmt, ...) \ + log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) +#define LOG_ERRNO(fmt, ...) \ + log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) +#define LOG_ERRNO_P(fmt, _errno, ...) \ + log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, \ + _errno, fmt, ## __VA_ARGS__) +#define LOG_WARN(fmt, ...) \ + log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) +#define LOG_INFO(fmt, ...) \ + log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG -#define LOG_DBG(...) log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) + #define LOG_DBG(fmt, ...) \ + log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, fmt, ## __VA_ARGS__) #else -#define LOG_DBG(...) + #define LOG_DBG(fmt, ...) #endif diff --git a/main.c b/main.c index c355843..f8a8453 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,4 @@ #include -#include -#include #include #include #include @@ -11,12 +9,14 @@ #include #include #include +#include +#include -#include -#include +#include #include #include -#include +#include +#include #include "bar/bar.h" #include "config.h" @@ -87,7 +87,7 @@ get_config_path(void) static struct bar * load_bar(const char *config_path, enum bar_backend backend) { - FILE *conf_file = fopen(config_path, "re"); + FILE *conf_file = fopen(config_path, "r"); if (conf_file == NULL) { LOG_ERRNO("%s: failed to open", config_path); return NULL; @@ -127,14 +127,13 @@ print_usage(const char *prog_name) printf("Usage: %s [OPTION]...\n", prog_name); printf("\n"); printf("Options:\n"); - printf(" -b,--backend={xcb,wayland,auto} backend to use (default: auto)\n" - " -c,--config=FILE alternative configuration file\n" - " -C,--validate verify configuration then quit\n" - " -p,--print-pid=FILE|FD print PID to file or FD\n" - " -d,--log-level={info|warning|error|none} log level (warning)\n" - " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" - " -s,--log-no-syslog disable syslog logging\n" - " -v,--version show the version number and quit\n"); + printf(" -b,--backend={xcb,wayland,auto} backend to use (default: auto)\n" + " -c,--config=FILE alternative configuration file\n" + " -C,--validate verify configuration then quit\n" + " -p,--print-pid=FILE|FD print PID to file or FD\n" + " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" + " -s,--log-no-syslog disable syslog logging\n" + " -v,--version show the version number and quit\n"); } static bool @@ -147,8 +146,9 @@ print_pid(const char *pid_file, bool *unlink_at_exit) int pid_fd = strtoul(pid_file, &end, 10); if (errno != 0 || *end != '\0') { - if ((pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) - < 0) { + if ((pid_fd = open(pid_file, + O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { LOG_ERRNO("%s: failed to open", pid_file); return false; } else @@ -177,16 +177,15 @@ int main(int argc, char *const *argv) { static const struct option longopts[] = { - {"backend", required_argument, 0, 'b'}, - {"config", required_argument, 0, 'c'}, - {"validate", no_argument, 0, 'C'}, - {"print-pid", required_argument, 0, 'p'}, - {"log-level", required_argument, 0, 'd'}, - {"log-colorize", optional_argument, 0, 'l'}, - {"log-no-syslog", no_argument, 0, 's'}, - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {NULL, no_argument, 0, 0}, + {"backend", required_argument, 0, 'b'}, + {"config", required_argument, 0, 'c'}, + {"validate", no_argument, 0, 'C'}, + {"print-pid", required_argument, 0, 'p'}, + {"log-colorize", optional_argument, 0, 'l'}, + {"log-no-syslog", no_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {NULL, no_argument, 0, 0}, }; bool unlink_pid_file = false; @@ -196,12 +195,11 @@ main(int argc, char *const *argv) char *config_path = NULL; enum bar_backend backend = BAR_BACKEND_AUTO; - enum log_class log_level = LOG_CLASS_WARNING; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; bool log_syslog = true; while (true) { - int c = getopt_long(argc, argv, ":b:c:Cp:d:l::svh", longopts, NULL); + int c = getopt_long(argc, argv, ":b:c:Cp:l::svh", longopts, NULL); if (c == -1) break; @@ -222,8 +220,9 @@ main(int argc, char *const *argv) if (stat(optarg, &st) == -1) { fprintf(stderr, "%s: invalid configuration file: %s\n", optarg, strerror(errno)); return EXIT_FAILURE; - } else if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode)) { - fprintf(stderr, "%s: invalid configuration file: neither a regular file nor a pipe or FIFO\n", optarg); + } else if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "%s: invalid configuration file: not a regular file\n", + optarg); return EXIT_FAILURE; } @@ -239,16 +238,6 @@ main(int argc, char *const *argv) pid_file = optarg; break; - case 'd': { - int lvl = log_level_from_string(optarg); - if (lvl < 0) { - fprintf(stderr, "-d,--log-level: %s: argument must be one of %s\n", optarg, log_level_string_hint()); - return EXIT_FAILURE; - } - log_level = lvl; - break; - } - case 'l': if (optarg == NULL || strcmp(optarg, "auto") == 0) log_colorize = LOG_COLORIZE_AUTO; @@ -284,12 +273,7 @@ main(int argc, char *const *argv) } } - log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, log_level); - - _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, "fcft log level enum offset"); - _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, "fcft colorize enum mismatch"); - fcft_init((enum fcft_log_colorize)log_colorize, log_syslog, (enum fcft_log_class)log_level); - atexit(&fcft_fini); + log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_WARNING); const struct sigaction sa = {.sa_handler = &signal_handler}; sigaction(SIGINT, &sa, NULL); @@ -366,7 +350,7 @@ main(int argc, char *const *argv) } if (aborted) - LOG_INFO("aborted: %s (%ld)", strsignal(aborted), (long)aborted); + LOG_INFO("aborted: %s (%d)", strsignal(aborted), aborted); done: /* Signal abort to other threads */ @@ -376,7 +360,7 @@ done: int res; int r = thrd_join(bar_thread, &res); if (r != 0) - LOG_ERRNO_P(r, "failed to join bar thread"); + LOG_ERRNO_P("failed to join bar thread", r); bar->destroy(bar); close(abort_fd); diff --git a/meson.build b/meson.build index 67d3096..9309064 100644 --- a/meson.build +++ b/meson.build @@ -1,28 +1,20 @@ project('yambar', 'c', - version: '1.11.0', + version: '1.6.1', license: 'MIT', - meson_version: '>=0.60.0', + meson_version: '>=0.53.0', default_options: ['c_std=c18', 'warning_level=1', + 'werror=true', 'b_ndebug=if-release']) is_debug_build = get_option('buildtype').startswith('debug') plugs_as_libs = get_option('core-plugins-as-shared-libraries') cc = meson.get_compiler('c') -cc_flags = [ - '-Werror=all' - ] - -if cc.has_function('memfd_create', - args: ['-D_GNU_SOURCE=200809L'], - prefix: '#include ') - add_project_arguments('-DMEMFD_CREATE', language: 'c') -endif # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') -build_root = meson.global_build_root().split('/') +build_root = meson.build_root().split('/') relative_dir_parts = [] i = 0 in_prefix = true @@ -51,9 +43,7 @@ endif # Common dependencies dl = cc.find_library('dl') m = cc.find_library('m') -threads = [dependency('threads'), cc.find_library('stdthreads', required: false)] -libepoll = dependency('epoll-shim', required: false) -libinotify = dependency('libinotify', required: false) +threads = dependency('threads') pixman = dependency('pixman-1') yaml = dependency('yaml-0.1') @@ -75,10 +65,9 @@ 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: ['>=3.0.0', '<4.0.0'], fallback: 'fcft') +fcft = dependency('fcft', version: ['>=2.0.0', '<3.0.0'], fallback: 'fcft') add_project_arguments( - cc_flags + ['-D_GNU_SOURCE'] + (is_debug_build ? ['-D_DEBUG'] : []) + (backend_x11 ? ['-DENABLE_X11'] : []) + @@ -95,37 +84,30 @@ if backend_x11 c_args: xcb_errors.found() ? '-DHAVE_XCB_ERRORS' : [], pic: plugs_as_libs) - xcb_stuff = declare_dependency( - link_with: xcb_stuff_lib, - dependencies: [xcb_aux, xcb_cursor, xcb_event, xcb_ewmh, xcb_randr, - xcb_render, xcb_errors], - ) + xcb_stuff = declare_dependency(link_with: xcb_stuff_lib) install_headers('xcb.h', subdir: 'yambar') endif subdir('completions') +subdir('doc') subdir('bar') subdir('decorations') subdir('particles') subdir('modules') -subdir('doc') -env = find_program('env', native: true) generate_version_sh = files('generate-version.sh') version = custom_target( 'generate_version', build_always_stale: true, output: 'version.h', - command: [env, 'LC_ALL=C', generate_version_sh, meson.project_version(), '@CURRENT_SOURCE_DIR@', '@OUTPUT@']) + command: [generate_version_sh, meson.project_version(), '@SOURCE_DIR@', '@OUTPUT@']) yambar = executable( 'yambar', - 'char32.c', 'char32.h', 'color.h', 'config-verify.c', 'config-verify.h', 'config.c', 'config.h', 'decoration.h', - 'font-shaping.h', 'log.c', 'log.h', 'main.c', 'module.c', 'module.h', @@ -134,7 +116,7 @@ yambar = executable( 'tag.c', 'tag.h', 'yml.c', 'yml.h', version, - dependencies: [bar, libepoll, libinotify, pixman, yaml, threads, dl, tllist, fcft] + + dependencies: [bar, pixman, yaml, threads, dl, tllist, fcft] + decorations + particles + modules, build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles', export_dynamic: true, @@ -170,34 +152,3 @@ summary( }, bool_yn: true ) - -summary( - { - 'ALSA': plugin_alsa_enabled, - 'Backlight': plugin_backlight_enabled, - 'Battery': plugin_battery_enabled, - 'Clock': plugin_clock_enabled, - 'CPU monitoring': plugin_cpu_enabled, - 'Disk I/O monitoring': plugin_disk_io_enabled, - 'dwl (dwm for Wayland)': plugin_dwl_enabled, - 'Foreign toplevel (window tracking for Wayland)': plugin_foreign_toplevel_enabled, - 'Memory monitoring': plugin_mem_enabled, - 'Music Player Daemon (MPD)': plugin_mpd_enabled, - 'Media Player Remote Interface Specificaion (MPRIS)': plugin_mpris_enabled, - 'i3+Sway': plugin_i3_enabled, - 'Label': plugin_label_enabled, - 'Network monitoring': plugin_network_enabled, - 'Pipewire': plugin_pipewire_enabled, - 'PulseAudio': plugin_pulse_enabled, - 'Removables monitoring': plugin_removables_enabled, - 'River': plugin_river_enabled, - 'Script': plugin_script_enabled, - 'Sway XKB keyboard': plugin_sway_xkb_enabled, - 'Niri language': plugin_niri_language_enabled, - 'Niri workspaces': plugin_niri_workspaces_enabled, - 'XKB keyboard (for X11)': plugin_xkb_enabled, - 'XWindow (window tracking for X11)': plugin_xwindow_enabled, - }, - section: 'Optional modules', - bool_yn: true -) diff --git a/meson_options.txt b/meson_options.txt index 23a8e11..f293c6f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,52 +5,3 @@ option( option( 'core-plugins-as-shared-libraries', type: 'boolean', value: false, description: 'Compiles modules, particles and decorations as shared libraries, which are loaded on-demand') - -option('plugin-alsa', type: 'feature', value: 'auto', - description: 'ALSA support') -option('plugin-backlight', type: 'feature', value: 'auto', - description: 'Backlight support') -option('plugin-battery', type: 'feature', value: 'auto', - description: 'Battery support') -option('plugin-clock', type: 'feature', value: 'auto', - description: 'Clock support') -option('plugin-cpu', type: 'feature', value: 'auto', - description: 'CPU monitoring support') -option('plugin-disk-io', type: 'feature', value: 'auto', - description: 'Disk I/O support') -option('plugin-dwl', type: 'feature', value: 'auto', - description: 'dwl (dwm for wayland) support') -option('plugin-foreign-toplevel', type: 'feature', value: 'auto', - description: 'Foreign toplevel (window tracking for Wayland) support') -option('plugin-mem', type: 'feature', value: 'auto', - description: 'Memory monitoring support') -option('plugin-mpd', type: 'feature', value: 'auto', - description: 'Music Player Daemon (MPD) support') -option('plugin-mpris', type: 'feature', value: 'enabled', - description: 'Media Player Remote Interface Specificaion (MPRIS) support') -option('plugin-i3', type: 'feature', value: 'auto', - description: 'i3+Sway support') -option('plugin-label', type: 'feature', value: 'auto', - description: 'Label support') -option('plugin-network', type: 'feature', value: 'auto', - description: 'Network monitoring support') -option('plugin-pipewire', type: 'feature', value: 'auto', - description: 'Pipewire support') -option('plugin-pulse', type: 'feature', value: 'auto', - description: 'PulseAudio support') -option('plugin-removables', type: 'feature', value: 'auto', - description: 'Removables (USB sticks, CD-ROM etc) monitoring support') -option('plugin-river', type: 'feature', value: 'auto', - description: 'River support') -option('plugin-script', type: 'feature', value: 'auto', - description: 'Script support') -option('plugin-sway-xkb', type: 'feature', value: 'auto', - description: 'keyboard support for Sway') -option('plugin-niri-language', type: 'feature', value: 'auto', - description: 'language support for Niri') -option('plugin-niri-workspaces', type: 'feature', value: 'auto', - description: 'workspaces support for Niri') -option('plugin-xkb', type: 'feature', value: 'auto', - description: 'keyboard support for X11') -option('plugin-xwindow', type: 'feature', value: 'auto', - description: 'XWindow (window tracking for X11) support') diff --git a/module.c b/module.c index d3bde3b..1e80c32 100644 --- a/module.c +++ b/module.c @@ -1,6 +1,6 @@ #include "module.h" -#include #include +#include #include struct module * diff --git a/module.h b/module.h index 3a83cdc..b757a04 100644 --- a/module.h +++ b/module.h @@ -26,8 +26,6 @@ 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)(const struct module *mod); }; struct module *module_common_new(void); @@ -35,9 +33,9 @@ void module_default_destroy(struct module *mod); struct exposable *module_begin_expose(struct module *mod); /* List of attributes *all* modules implement */ -#define MODULE_COMMON_ATTRS \ - {"content", true, &conf_verify_particle}, {"anchors", false, NULL}, {"font", false, &conf_verify_font}, \ - {"foreground", false, &conf_verify_color}, \ - { \ - NULL, false, NULL \ - } +#define MODULE_COMMON_ATTRS \ + {"content", true, &conf_verify_particle}, \ + {"anchors", false, NULL}, \ + {"font", false, &conf_verify_font}, \ + {"foreground", false, &conf_verify_color}, \ + {NULL, false, NULL} diff --git a/modules/alsa.c b/modules/alsa.c index 8d7cd25..9e410df 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -1,8 +1,6 @@ -#include #include #include -#include -#include +#include #include @@ -10,149 +8,54 @@ #define LOG_MODULE "alsa" #define LOG_ENABLE_DBG 0 +#include "../log.h" #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" -#include "../log.h" #include "../plugin.h" -enum channel_type { CHANNEL_PLAYBACK, CHANNEL_CAPTURE }; - -struct channel { - snd_mixer_selem_channel_id_t id; - enum channel_type type; - char *name; - - bool use_db; - long vol_cur; - long db_cur; - bool muted; -}; - -struct private -{ +struct private { char *card; char *mixer; - char *volume_name; - char *muted_name; struct particle *label; - tll(struct channel) channels; + tll(snd_mixer_selem_channel_id_t) channels; - bool online; - - bool has_playback_volume; - long playback_vol_min; - long playback_vol_max; - - bool has_playback_db; - long playback_db_min; - long playback_db_max; - - bool has_capture_volume; - long capture_vol_min; - long capture_vol_max; - - long has_capture_db; - long capture_db_min; - long capture_db_max; - - const struct channel *volume_chan; - const struct channel *muted_chan; + long vol_min; + long vol_max; + long vol_cur; + bool muted; }; -static void -channel_free(struct channel *chan) -{ - free(chan->name); -} - static void destroy(struct module *mod) { struct private *m = mod->private; - tll_foreach(m->channels, it) - { - channel_free(&it->item); - tll_remove(m->channels, it); - } + tll_free(m->channels); m->label->destroy(m->label); free(m->card); free(m->mixer); - free(m->volume_name); - free(m->muted_name); free(m); module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - static char desc[32]; - const struct private *m = mod->private; - snprintf(desc, sizeof(desc), "alsa(%s)", m->card); - return desc; -} - static struct exposable * content(struct module *mod) { struct private *m = mod->private; + int percent = m->vol_max - m->vol_min > 0 + ? round(100. * m->vol_cur / (m->vol_max - m->vol_min)) + : 0; + mtx_lock(&mod->lock); - - const struct channel *volume_chan = m->volume_chan; - const struct channel *muted_chan = m->muted_chan; - - bool muted = muted_chan != NULL ? muted_chan->muted : false; - long vol_min = 0, vol_max = 0, vol_cur = 0; - long db_min = 0, db_max = 0, db_cur = 0; - bool use_db = false; - - if (volume_chan != NULL) { - if (volume_chan->type == CHANNEL_PLAYBACK) { - db_min = m->playback_db_min; - db_max = m->playback_db_max; - vol_min = m->playback_vol_min; - vol_max = m->playback_vol_max; - } else { - db_min = m->capture_db_min; - db_max = m->capture_db_max; - vol_min = m->capture_vol_min; - vol_max = m->capture_vol_max; - } - vol_cur = volume_chan->vol_cur; - db_cur = volume_chan->db_cur; - use_db = volume_chan->use_db; - } - - int percent; - - if (use_db) { - bool use_linear = db_max - db_min <= 24 * 100; - if (use_linear) { - percent = db_min - db_max > 0 ? round(100. * (db_cur - db_min) / (db_max - db_min)) : 0; - } else { - double normalized = pow(10, (double)(db_cur - db_max) / 6000.); - if (db_min != SND_CTL_TLV_DB_GAIN_MUTE) { - double min_norm = pow(10, (double)(db_min - db_max) / 6000.); - normalized = (normalized - min_norm) / (1. - min_norm); - } - percent = round(100. * normalized); - } - } else { - percent = vol_max - vol_min > 0 ? round(100. * (vol_cur - vol_min) / (vol_max - vol_min)) : 0; - } - struct tag_set tags = { .tags = (struct tag *[]){ - tag_new_bool(mod, "online", m->online), - tag_new_int_range(mod, "volume", vol_cur, vol_min, vol_max), - tag_new_int_range(mod, "dB", db_cur, db_min, db_max), + tag_new_int_range(mod, "volume", m->vol_cur, m->vol_min, m->vol_max), tag_new_int_range(mod, "percent", percent, 0, 100), - tag_new_bool(mod, "muted", muted), + tag_new_bool(mod, "muted", m->muted), }, - .count = 5, + .count = 3, }; mtx_unlock(&mod->lock); @@ -167,137 +70,136 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) { struct private *m = mod->private; - mtx_lock(&mod->lock); + int idx = 0; + + /* Get min/max volume levels */ + long min = 0, max = 0; + int r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + + if (r < 0) { + LOG_DBG("%s,%s: failed to get volume min/max (mixer is digital?)", + m->card, m->mixer); + } + + /* Make sure min <= max */ + if (min > max) { + LOG_WARN( + "%s,%s: indicated minimum volume is greater than the maximum: " + "%ld > %ld", m->card, m->mixer, min, max); + min = max; + } + + long cur[tll_length(m->channels)]; + memset(cur, 0, sizeof(cur)); /* If volume level can be changed (i.e. this isn't just a switch; - * e.g. a digital channel), get current channel levels */ - tll_foreach(m->channels, it) - { - struct channel *chan = &it->item; - - const bool has_volume = chan->type == CHANNEL_PLAYBACK ? m->has_playback_volume : m->has_capture_volume; - const bool has_db = chan->type == CHANNEL_PLAYBACK ? m->has_playback_db : m->has_capture_db; - - if (!has_volume && !has_db) - continue; - - if (has_db) { - chan->use_db = true; - - const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_db_min : m->capture_db_min; - const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_db_max : m->capture_db_max; - assert(min <= max); - - int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_dB(elem, chan->id, &chan->db_cur) - : snd_mixer_selem_get_capture_dB(elem, chan->id, &chan->db_cur); + * e.g. a digital channel), get current level */ + if (max > 0) { + tll_foreach(m->channels, it) { + int r = snd_mixer_selem_get_playback_volume( + elem, it->item, &cur[idx]); if (r < 0) { - LOG_ERR("%s,%s: %s: failed to get current dB", m->card, m->mixer, chan->name); + LOG_WARN("%s,%s: %s: failed to get current volume", + m->card, m->mixer, + snd_mixer_selem_channel_name(it->item)); } - if (chan->db_cur < min) { - LOG_WARN("%s,%s: %s: current dB is less than the indicated minimum: " - "%ld < %ld", - m->card, m->mixer, chan->name, chan->db_cur, min); - chan->db_cur = min; - } - - if (chan->db_cur > max) { - LOG_WARN("%s,%s: %s: current dB is greater than the indicated maximum: " - "%ld > %ld", - m->card, m->mixer, chan->name, chan->db_cur, max); - chan->db_cur = max; - } - - assert(chan->db_cur >= min); - assert(chan->db_cur <= max); - - LOG_DBG("%s,%s: %s: dB: %ld", m->card, m->mixer, chan->name, chan->db_cur); - } else - chan->use_db = false; - - const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_min : m->capture_vol_min; - const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_max : m->capture_vol_max; - assert(min <= max); - - int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_volume(elem, chan->id, &chan->vol_cur) - : snd_mixer_selem_get_capture_volume(elem, chan->id, &chan->vol_cur); - - if (r < 0) { - LOG_ERR("%s,%s: %s: failed to get current volume", m->card, m->mixer, chan->name); + LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, + snd_mixer_selem_channel_name(it->item), cur[idx]); + idx++; } - - if (chan->vol_cur < min) { - LOG_WARN("%s,%s: %s: current volume is less than the indicated minimum: " - "%ld < %ld", - m->card, m->mixer, chan->name, chan->vol_cur, min); - chan->vol_cur = min; - } - - if (chan->vol_cur > max) { - LOG_WARN("%s,%s: %s: current volume is greater than the indicated maximum: " - "%ld > %ld", - m->card, m->mixer, chan->name, chan->vol_cur, max); - chan->vol_cur = max; - } - - assert(chan->vol_cur >= min); - assert(chan->vol_cur <= max); - - LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, chan->name, chan->vol_cur); } - /* Get channels’ muted state */ - tll_foreach(m->channels, it) - { - struct channel *chan = &it->item; + int unmuted[tll_length(m->channels)]; + memset(unmuted, 0, sizeof(unmuted)); - int unmuted; - - int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_switch(elem, chan->id, &unmuted) - : snd_mixer_selem_get_capture_switch(elem, chan->id, &unmuted); + /* Get muted state */ + idx = 0; + tll_foreach(m->channels, it) { + int r = snd_mixer_selem_get_playback_switch( + elem, it->item, &unmuted[idx]); if (r < 0) { - LOG_WARN("%s,%s: %s: failed to get muted state", m->card, m->mixer, chan->name); - unmuted = 1; + LOG_WARN("%s,%s: %s: failed to get muted state", + m->card, m->mixer, snd_mixer_selem_channel_name(it->item)); + unmuted[idx] = 1; } - chan->muted = !unmuted; - LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, chan->name, !unmuted); + LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, + snd_mixer_selem_channel_name(it->item), !unmuted[idx]); + + idx++; } - m->online = true; + /* Warn if volume level is inconsistent across the channels */ + for (size_t i = 1; i < tll_length(m->channels); i++) { + if (cur[i] != cur[i - 1]) { + LOG_WARN("%s,%s: channel volume mismatch, using value from %s", + m->card, m->mixer, + snd_mixer_selem_channel_name(tll_front(m->channels))); + break; + } + } + /* Warn if muted state is inconsistent across the channels */ + for (size_t i = 1; i < tll_length(m->channels); i++) { + if (unmuted[i] != unmuted[i - 1]) { + LOG_WARN("%s,%s: channel muted mismatch, using value from %s", + m->card, m->mixer, + snd_mixer_selem_channel_name(tll_front(m->channels))); + break; + } + } + + /* Make sure min <= cur <= max */ + if (cur[0] < min) { + LOG_WARN( + "%s,%s: current volume is less than the indicated minimum: " + "%ld < %ld", m->card, m->mixer, cur[0], min); + cur[0] = min; + } + + if (cur[0] > max) { + LOG_WARN( + "%s,%s: current volume is greater than the indicated maximum: " + "%ld > %ld", m->card, m->mixer, cur[0], max); + cur[0] = max; + } + + assert(cur[0] >= min); + assert(cur[0] <= max); + + LOG_DBG( + "muted=%d, cur=%ld, min=%ld, max=%ld", !unmuted[0], cur[0], min, max); + + mtx_lock(&mod->lock); + m->vol_min = min; + m->vol_max = max; + m->vol_cur = cur[0]; + m->muted = !unmuted[0]; mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); } -enum run_state { - RUN_ERROR, - RUN_FAILED_CONNECT, - RUN_DISCONNECTED, - RUN_DONE, -}; - -static enum run_state -run_while_online(struct module *mod) +static int +run(struct module *mod) { struct private *m = mod->private; - enum run_state ret = RUN_ERROR; - - /* Make sure we aren’t still tracking channels from previous connects */ - tll_free(m->channels); + int ret = 1; snd_mixer_t *handle; if (snd_mixer_open(&handle, 0) != 0) { LOG_ERR("failed to open handle"); - return ret; + return 1; } - if (snd_mixer_attach(handle, m->card) != 0 || snd_mixer_selem_register(handle, NULL, NULL) != 0 - || snd_mixer_load(handle) != 0) { + if (snd_mixer_attach(handle, m->card) != 0 || + snd_mixer_selem_register(handle, NULL, NULL) != 0 || + snd_mixer_load(handle) != 0) + { LOG_ERR("failed to attach to card"); - ret = RUN_FAILED_CONNECT; goto err; } @@ -306,142 +208,37 @@ run_while_online(struct module *mod) snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, m->mixer); - snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); if (elem == NULL) { LOG_ERR("failed to find mixer"); goto err; } - /* Get playback volume range */ - m->has_playback_volume = snd_mixer_selem_has_playback_volume(elem) > 0; - if (m->has_playback_volume) { - if (snd_mixer_selem_get_playback_volume_range(elem, &m->playback_vol_min, &m->playback_vol_max) < 0) { - LOG_ERR("%s,%s: failed to get playback volume range", m->card, m->mixer); - assert(m->playback_vol_min == 0); - assert(m->playback_vol_max == 0); - } - - if (m->playback_vol_min > m->playback_vol_max) { - LOG_WARN("%s,%s: indicated minimum playback volume is greater than the " - "maximum: %ld > %ld", - m->card, m->mixer, m->playback_vol_min, m->playback_vol_max); - m->playback_vol_min = m->playback_vol_max; - } - } - - if (snd_mixer_selem_get_playback_dB_range(elem, &m->playback_db_min, &m->playback_db_max) < 0) { - LOG_WARN("%s,%s: failed to get playback dB range, " - "will use raw volume values instead", - m->card, m->mixer); - m->has_playback_db = false; - } else - m->has_playback_db = true; - - /* Get capture volume range */ - m->has_capture_volume = snd_mixer_selem_has_capture_volume(elem) > 0; - if (m->has_capture_volume) { - if (snd_mixer_selem_get_capture_volume_range(elem, &m->capture_vol_min, &m->capture_vol_max) < 0) { - LOG_ERR("%s,%s: failed to get capture volume range", m->card, m->mixer); - assert(m->capture_vol_min == 0); - assert(m->capture_vol_max == 0); - } - - if (m->capture_vol_min > m->capture_vol_max) { - LOG_WARN("%s,%s: indicated minimum capture volume is greater than the " - "maximum: %ld > %ld", - m->card, m->mixer, m->capture_vol_min, m->capture_vol_max); - m->capture_vol_min = m->capture_vol_max; - } - } - - if (snd_mixer_selem_get_capture_dB_range(elem, &m->capture_db_min, &m->capture_db_max) < 0) { - LOG_WARN("%s,%s: failed to get capture dB range, " - "will use raw volume values instead", - m->card, m->mixer); - m->has_capture_db = false; - } else - m->has_capture_db = true; - /* Get available channels */ for (size_t i = 0; i < SND_MIXER_SCHN_LAST; i++) { - bool is_playback = snd_mixer_selem_has_playback_channel(elem, i) == 1; - bool is_capture = snd_mixer_selem_has_capture_channel(elem, i) == 1; - - if (is_playback || is_capture) { - struct channel chan = { - .id = i, - .type = is_playback ? CHANNEL_PLAYBACK : CHANNEL_CAPTURE, - .name = strdup(snd_mixer_selem_channel_name(i)), - }; - tll_push_back(m->channels, chan); + if (snd_mixer_selem_has_playback_channel(elem, i)) { + tll_push_back(m->channels, i); } } - if (tll_length(m->channels) == 0) { - LOG_ERR("%s,%s: no channels", m->card, m->mixer); - goto err; - } - char channels_str[1024]; int channels_idx = 0; - tll_foreach(m->channels, it) - { - const struct channel *chan = &it->item; - - channels_idx += snprintf(&channels_str[channels_idx], sizeof(channels_str) - channels_idx, - channels_idx == 0 ? "%s (%s)" : ", %s (%s)", chan->name, - chan->type == CHANNEL_PLAYBACK ? "🔊" : "🎤"); + tll_foreach(m->channels, it) { + channels_idx += snprintf( + &channels_str[channels_idx], sizeof(channels_str) - channels_idx, + channels_idx == 0 ? "%s" : ", %s", + snd_mixer_selem_channel_name(it->item)); assert(channels_idx <= sizeof(channels_str)); } LOG_INFO("%s,%s: channels: %s", m->card, m->mixer, channels_str); - /* Verify volume/muted channel names are valid and exists */ - bool volume_channel_is_valid = m->volume_name == NULL; - bool muted_channel_is_valid = m->muted_name == NULL; - - tll_foreach(m->channels, it) - { - const struct channel *chan = &it->item; - if (m->volume_name != NULL && strcmp(chan->name, m->volume_name) == 0) { - m->volume_chan = chan; - volume_channel_is_valid = true; - } - if (m->muted_name != NULL && strcmp(chan->name, m->muted_name) == 0) { - m->muted_chan = chan; - muted_channel_is_valid = true; - } - } - - if (m->volume_name == NULL) - m->volume_chan = &tll_front(m->channels); - if (m->muted_name == NULL) - m->muted_chan = &tll_front(m->channels); - - if (!volume_channel_is_valid) { - assert(m->volume_name != NULL); - LOG_ERR("volume: invalid channel name: %s", m->volume_name); - goto err; - } - - if (!muted_channel_is_valid) { - assert(m->muted_name != NULL); - LOG_ERR("muted: invalid channel name: %s", m->muted_name); - goto err; - } - /* Initial state */ update_state(mod, elem); - LOG_INFO( - "%s,%s: %s range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", m->card, m->mixer, - m->volume_chan->use_db ? "dB" : "volume", - (m->volume_chan->type == CHANNEL_PLAYBACK ? (m->volume_chan->use_db ? m->playback_db_min : m->playback_vol_min) - : (m->volume_chan->use_db ? m->capture_db_min : m->capture_vol_min)), - (m->volume_chan->type == CHANNEL_PLAYBACK ? (m->volume_chan->use_db ? m->playback_db_max : m->playback_vol_max) - : (m->volume_chan->use_db ? m->capture_db_max : m->capture_vol_max)), - m->volume_chan->use_db ? m->volume_chan->db_cur : m->volume_chan->vol_cur, - m->muted_chan->muted ? " (muted)" : "", m->volume_chan->name, m->muted_chan->name); + LOG_INFO("%s,%s: volume min=%ld, max=%ld, current=%ld%s", + m->card, m->mixer, m->vol_min, m->vol_max, m->vol_cur, + m->muted ? ", muted" : ""); mod->bar->refresh(mod->bar); @@ -454,188 +251,42 @@ run_while_online(struct module *mod) fds[0] = (struct pollfd){.fd = mod->abort_fd, .events = POLLIN}; snd_mixer_poll_descriptors(handle, &fds[1], fd_count); - int r = poll(fds, fd_count + 1, -1); - if (r < 0) { - if (errno == EINTR) - continue; + poll(fds, fd_count + 1, -1); - LOG_ERRNO("failed to poll"); + if (fds[0].revents & POLLIN) break; - } - if (fds[0].revents & POLLIN) { - ret = RUN_DONE; + if (fds[1].revents & POLLHUP) { + /* Don't know if this can happen */ + LOG_ERR("disconnected from alsa"); break; } - for (size_t i = 0; i < fd_count; i++) { - if (fds[1 + i].revents & (POLLHUP | POLLERR | POLLNVAL)) { - LOG_ERR("disconnected from alsa"); - - mtx_lock(&mod->lock); - m->online = false; - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); - - ret = RUN_DISCONNECTED; - goto err; - } - } - snd_mixer_handle_events(handle); update_state(mod, elem); } + ret = 0; + err: snd_mixer_close(handle); snd_config_update_free_global(); return ret; } -static int -run(struct module *mod) -{ - int ret = 1; - - int ifd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (ifd < 0) { - LOG_ERRNO("failed to inotify"); - return 1; - } - - int wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE); - if (wd < 0) { - LOG_ERRNO("failed to create inotify watcher for /dev/snd"); - close(ifd); - return 1; - } - - while (true) { - enum run_state state = run_while_online(mod); - - switch (state) { - case RUN_DONE: - ret = 0; - goto out; - - case RUN_ERROR: - ret = 1; - goto out; - - case RUN_FAILED_CONNECT: - break; - - case RUN_DISCONNECTED: - /* - * We’ve been connected - drain the watcher - * - * We don’t want old, un-releated events (for other - * soundcards, for example) to trigger a storm of - * re-connect attempts. - */ - while (true) { - uint8_t buf[1024]; - ssize_t amount = read(ifd, buf, sizeof(buf)); - if (amount < 0) { - if (errno == EAGAIN) - break; - - LOG_ERRNO("failed to drain inotify watcher"); - ret = 1; - goto out; - } - - if (amount == 0) - break; - } - - break; - } - - bool have_create_event = false; - - while (!have_create_event) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = ifd, .events = POLLIN}}; - int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); - - if (r < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - ret = 1; - goto out; - } - - if (fds[0].revents & (POLLIN | POLLHUP)) { - ret = 0; - goto out; - } - - if (fds[1].revents & POLLHUP) { - LOG_ERR("inotify socket closed"); - ret = 1; - goto out; - } - - assert(fds[1].revents & POLLIN); - - while (true) { - char buf[1024]; - ssize_t len = read(ifd, buf, sizeof(buf)); - - if (len < 0) { - if (errno == EAGAIN) - break; - - LOG_ERRNO("failed to read inotify events"); - ret = 1; - goto out; - } - - if (len == 0) - break; - - /* Consume inotify data */ - for (const char *ptr = buf; ptr < buf + len;) { - const struct inotify_event *e = (const struct inotify_event *)ptr; - - if (e->mask & IN_CREATE) { - LOG_DBG("inotify: CREATED: /dev/snd/%.*s", e->len, e->name); - have_create_event = true; - } - - ptr += sizeof(*e) + e->len; - } - } - } - } - -out: - if (wd >= 0) - inotify_rm_watch(ifd, wd); - if (ifd >= 0) - close(ifd); - return ret; -} - static struct module * -alsa_new(const char *card, const char *mixer, const char *volume_channel_name, const char *muted_channel_name, - struct particle *label) +alsa_new(const char *card, const char *mixer, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->card = strdup(card); priv->mixer = strdup(mixer); - priv->volume_name = volume_channel_name != NULL ? strdup(volume_channel_name) : NULL; - priv->muted_name = muted_channel_name != NULL ? strdup(muted_channel_name) : NULL; struct module *mod = module_common_new(); mod->private = priv; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -644,13 +295,12 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *card = yml_get_value(node, "card"); const struct yml_node *mixer = yml_get_value(node, "mixer"); - const struct yml_node *volume = yml_get_value(node, "volume"); - const struct yml_node *muted = yml_get_value(node, "muted"); const struct yml_node *content = yml_get_value(node, "content"); - return alsa_new(yml_value_as_string(card), yml_value_as_string(mixer), - volume != NULL ? yml_value_as_string(volume) : NULL, - muted != NULL ? yml_value_as_string(muted) : NULL, conf_to_particle(content, inherited)); + return alsa_new( + yml_value_as_string(card), + yml_value_as_string(mixer), + conf_to_particle(content, inherited)); } static bool @@ -659,8 +309,6 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"card", true, &conf_verify_string}, {"mixer", true, &conf_verify_string}, - {"volume", false, &conf_verify_string}, - {"muted", false, &conf_verify_string}, MODULE_COMMON_ATTRS, }; diff --git a/modules/backlight.c b/modules/backlight.c index 1495c5c..053a998 100644 --- a/modules/backlight.c +++ b/modules/backlight.c @@ -1,26 +1,24 @@ -#include -#include -#include -#include #include #include #include +#include +#include #include +#include -#include #include +#include #include #define LOG_MODULE "backlight" -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../plugin.h" -struct private -{ +struct private { struct particle *label; char *device; @@ -40,12 +38,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "backlight"; -} - static struct exposable * content(struct module *mod) { @@ -112,13 +104,13 @@ readint_from_fd(int fd) static int initialize(struct private *m) { - int backlight_fd = open("/sys/class/backlight", O_RDONLY | O_CLOEXEC); + int backlight_fd = open("/sys/class/backlight", O_RDONLY); if (backlight_fd == -1) { LOG_ERRNO("/sys/class/backlight"); return -1; } - int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY | O_CLOEXEC); + int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY); close(backlight_fd); if (base_dir_fd == -1) { @@ -126,7 +118,7 @@ initialize(struct private *m) return -1; } - int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY | O_CLOEXEC); + int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY); if (max_fd == -1) { LOG_ERRNO("/sys/class/backlight/%s/max_brightness", m->device); close(base_dir_fd); @@ -136,7 +128,7 @@ initialize(struct private *m) m->max_brightness = readint_from_fd(max_fd); close(max_fd); - int current_fd = openat(base_dir_fd, "brightness", O_RDONLY | O_CLOEXEC); + int current_fd = openat(base_dir_fd, "brightness", O_RDONLY); close(base_dir_fd); if (current_fd == -1) { @@ -146,7 +138,8 @@ initialize(struct private *m) m->current_brightness = readint_from_fd(current_fd); - LOG_INFO("%s: brightness: %ld (max: %ld)", m->device, m->current_brightness, m->max_brightness); + LOG_INFO("%s: brightness: %ld (max: %ld)", m->device, m->current_brightness, + m->max_brightness); return current_fd; } @@ -179,31 +172,20 @@ run(struct module *mod) bar->refresh(bar); - int ret = 1; while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, }; - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { - if (errno == EINTR) - continue; + poll(fds, 2, -1); - LOG_ERRNO("failed to poll"); + if (fds[0].revents & POLLIN) break; - } - - if (fds[0].revents & POLLIN) { - ret = 0; - break; - } struct udev_device *dev = udev_monitor_receive_device(mon); - if (dev == NULL) - continue; - const char *sysname = udev_device_get_sysname(dev); - bool is_us = sysname != NULL && strcmp(sysname, m->device) == 0; + + bool is_us = strcmp(sysname, m->device) == 0; udev_device_unref(dev); if (!is_us) @@ -219,7 +201,7 @@ run(struct module *mod) udev_unref(udev); close(current_fd); - return ret; + return 0; } static struct module * @@ -234,7 +216,6 @@ backlight_new(const char *device, struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -244,7 +225,8 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *c = yml_get_value(node, "content"); - return backlight_new(yml_value_as_string(name), conf_to_particle(c, inherited)); + return backlight_new( + yml_value_as_string(name), conf_to_particle(c, inherited)); } static bool diff --git a/modules/battery.c b/modules/battery.c index 34b98c8..7dcfc45 100644 --- a/modules/battery.c +++ b/modules/battery.c @@ -1,49 +1,31 @@ -#include -#include -#include #include #include #include -#include #include +#include +#include #include -#include #include +#include #include -#include #define LOG_MODULE "battery" #define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../plugin.h" -#define max(x, y) ((x) > (y) ? (x) : (y)) +enum state { STATE_FULL, STATE_CHARGING, STATE_DISCHARGING }; -static const long min_poll_interval = 250; -static const long default_poll_interval = 60 * 1000; -static const long one_sec_in_ns = 1000000000; - -enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING, STATE_UNKNOWN }; - -struct current_state { - long ema; - long current; - struct timespec time; -}; - -struct private -{ +struct private { struct particle *label; - long poll_interval; - int battery_scale; - long smoothing_scale; + int poll_interval; char *battery; char *manufacturer; char *model; @@ -57,64 +39,10 @@ struct private long energy; long power; long charge; - struct current_state ema_current; + long current; long time_to_empty; - long time_to_full; }; -static int64_t -difftimespec_ns(const struct timespec after, const struct timespec before) -{ - return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * (int64_t)one_sec_in_ns - + ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec); -} - -// Linear Exponential Moving Average (unevenly spaced time series) -// http://www.eckner.com/papers/Algorithms%20for%20Unevenly%20Spaced%20Time%20Series.pdf -// Adapted from: https://github.com/andreas50/utsAlgorithms/blob/master/ema.c -static void -ema_linear(struct current_state *state, struct current_state curr, long tau) -{ - double w, w2, tmp; - - if (state->current == -1) { - *state = curr; - return; - } - - long time = difftimespec_ns(curr.time, state->time); - tmp = time / (double)tau; - w = exp(-tmp); - if (tmp > 1e-6) { - w2 = (1 - w) / tmp; - } else { - // Use taylor expansion for numerical stability - w2 = 1 - tmp / 2 + tmp * tmp / 6 - tmp * tmp * tmp / 24; - } - - double ema = state->ema * w + curr.current * (1 - w2) + state->current * (w2 - w); - - state->ema = ema; - state->current = curr.current; - state->time = curr.time; - - LOG_DBG("ema current: %ld", (long)ema); -} - -static void -timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res) -{ - - res->tv_sec = a->tv_sec - b->tv_sec; - res->tv_nsec = a->tv_nsec - b->tv_nsec; - - /* tv_nsec may be negative */ - if (res->tv_nsec < 0) { - res->tv_sec--; - res->tv_nsec += one_sec_in_ns; - } -} - static void destroy(struct module *mod) { @@ -129,15 +57,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - static char desc[32]; - const struct private *m = mod->private; - snprintf(desc, sizeof(desc), "bat(%s)", m->battery); - return desc; -} - static struct exposable * content(struct module *mod) { @@ -145,25 +64,22 @@ content(struct module *mod) mtx_lock(&mod->lock); - assert(m->state == STATE_FULL || m->state == STATE_NOTCHARGING || m->state == STATE_CHARGING - || m->state == STATE_DISCHARGING || m->state == STATE_UNKNOWN); + assert(m->state == STATE_FULL || + m->state == STATE_CHARGING || + m->state == STATE_DISCHARGING); unsigned long hours; unsigned long minutes; - if (m->time_to_empty > 0) { - minutes = m->time_to_empty / 60; - hours = minutes / 60; - minutes = minutes % 60; - } else if (m->time_to_full > 0) { - minutes = m->time_to_full / 60; - hours = minutes / 60; - minutes = minutes % 60; - } else if (m->energy_full >= 0 && m->charge && m->power >= 0) { - unsigned long energy = m->state == STATE_CHARGING ? m->energy_full - m->energy : m->energy; + if (m->time_to_empty >= 0) { + hours = m->time_to_empty / 60; + minutes = m->time_to_empty % 60; + } else if (m->energy_full >= 0 && m->charge && m->power >= 0) { + unsigned long energy = m->state == STATE_CHARGING + ? m->energy_full - m->energy : m->energy; double hours_as_float; - if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) + if (m->state == STATE_FULL) hours_as_float = 0.0; else if (m->power > 0) hours_as_float = (double)energy / m->power; @@ -172,14 +88,15 @@ content(struct module *mod) hours = hours_as_float; minutes = (hours_as_float - (double)hours) * 60; - } else if (m->charge_full >= 0 && m->charge >= 0 && m->ema_current.current >= 0) { - unsigned long charge = m->state == STATE_CHARGING ? m->charge_full - m->charge : m->charge; + } else if (m->charge_full >= 0 && m->charge >= 0 && m->current >= 0) { + unsigned long charge = m->state == STATE_CHARGING + ? m->charge_full - m->charge : m->charge; double hours_as_float; - if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING) + if (m->state == STATE_FULL) hours_as_float = 0.0; - else if (m->ema_current.current > 0) - hours_as_float = (double)charge / m->ema_current.current; + else if (m->current > 0) + hours_as_float = (double)charge / m->current; else hours_as_float = 99.0; @@ -200,7 +117,6 @@ 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"), @@ -219,18 +135,20 @@ content(struct module *mod) } static const char * -readline_from_fd(int fd, size_t sz, char buf[static sz]) +readline_from_fd(int fd) { - ssize_t bytes = read(fd, buf, sz - 1); + static char buf[4096]; + + ssize_t sz = read(fd, buf, sizeof(buf) - 1); lseek(fd, 0, SEEK_SET); - if (bytes < 0) { + if (sz < 0) { LOG_WARN("failed to read from FD=%d", fd); return NULL; } - buf[bytes] = '\0'; - for (ssize_t i = bytes - 1; i >= 0 && buf[i] == '\n'; bytes--) + buf[sz] = '\0'; + for (ssize_t i = sz - 1; i >= 0 && buf[i] == '\n'; sz--) buf[i] = '\0'; return buf; @@ -239,8 +157,7 @@ readline_from_fd(int fd, size_t sz, char buf[static sz]) static long readint_from_fd(int fd) { - char buf[512]; - const char *s = readline_from_fd(fd, sizeof(buf), buf); + const char *s = readline_from_fd(fd); if (s == NULL) return 0; @@ -257,15 +174,13 @@ readint_from_fd(int fd) static bool initialize(struct private *m) { - char line_buf[512]; - - int pw_fd = open("/sys/class/power_supply", O_RDONLY | O_CLOEXEC); + 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 | O_CLOEXEC); + int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); close(pw_fd); if (base_dir_fd < 0) { @@ -274,31 +189,34 @@ initialize(struct private *m) } { - int fd = openat(base_dir_fd, "manufacturer", O_RDONLY | O_CLOEXEC); + int fd = openat(base_dir_fd, "manufacturer", O_RDONLY); if (fd == -1) { - LOG_WARN("/sys/class/power_supply/%s/manufacturer: %s", m->battery, strerror(errno)); + LOG_WARN("/sys/class/power_supply/%s/manufacturer: %s", + m->battery, strerror(errno)); m->manufacturer = NULL; } else { - m->manufacturer = strdup(readline_from_fd(fd, sizeof(line_buf), line_buf)); + m->manufacturer = strdup(readline_from_fd(fd)); close(fd); } } { - int fd = openat(base_dir_fd, "model_name", O_RDONLY | O_CLOEXEC); + int fd = openat(base_dir_fd, "model_name", O_RDONLY); if (fd == -1) { - LOG_WARN("/sys/class/power_supply/%s/model_name: %s", m->battery, strerror(errno)); + LOG_WARN("/sys/class/power_supply/%s/model_name: %s", + m->battery, strerror(errno)); m->model = NULL; } else { - m->model = strdup(readline_from_fd(fd, sizeof(line_buf), line_buf)); + m->model = strdup(readline_from_fd(fd)); close(fd); } } - if (faccessat(base_dir_fd, "energy_full_design", O_RDONLY, 0) == 0 - && faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) { + if (faccessat(base_dir_fd, "energy_full_design", O_RDONLY, 0) == 0 && + faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) + { { - int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY | O_CLOEXEC); + int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/energy_full_design", m->battery); goto err; @@ -309,7 +227,7 @@ initialize(struct private *m) } { - int fd = openat(base_dir_fd, "energy_full", O_RDONLY | O_CLOEXEC); + int fd = openat(base_dir_fd, "energy_full", O_RDONLY); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/energy_full", m->battery); goto err; @@ -322,27 +240,28 @@ initialize(struct private *m) m->energy_full = m->energy_full_design = -1; } - if (faccessat(base_dir_fd, "charge_full_design", O_RDONLY, 0) == 0 - && faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) { + if (faccessat(base_dir_fd, "charge_full_design", O_RDONLY, 0) == 0 && + faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) + { { - int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY | O_CLOEXEC); + int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/charge_full_design", m->battery); goto err; } - m->charge_full_design = readint_from_fd(fd) / m->battery_scale; + m->charge_full_design = readint_from_fd(fd); close(fd); } { - int fd = openat(base_dir_fd, "charge_full", O_RDONLY | O_CLOEXEC); + int fd = openat(base_dir_fd, "charge_full", O_RDONLY); if (fd == -1) { LOG_ERRNO("/sys/class/power_supply/%s/charge_full", m->battery); goto err; } - m->charge_full = readint_from_fd(fd) / m->battery_scale; + m->charge_full = readint_from_fd(fd); close(fd); } } else { @@ -362,13 +281,13 @@ update_status(struct module *mod) { struct private *m = mod->private; - int pw_fd = open("/sys/class/power_supply", O_RDONLY | O_CLOEXEC); + 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 | O_CLOEXEC); + int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY); close(pw_fd); if (base_dir_fd < 0) { @@ -376,14 +295,14 @@ update_status(struct module *mod) return false; } - int status_fd = openat(base_dir_fd, "status", O_RDONLY | O_CLOEXEC); + int status_fd = openat(base_dir_fd, "status", O_RDONLY); if (status_fd < 0) { LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery); close(base_dir_fd); return false; } - int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY | O_CLOEXEC); + int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY); if (capacity_fd < 0) { LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery); close(status_fd); @@ -391,12 +310,11 @@ update_status(struct module *mod) return false; } - int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY | O_CLOEXEC); - int power_fd = openat(base_dir_fd, "power_now", O_RDONLY | O_CLOEXEC); - int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY | O_CLOEXEC); - int current_fd = openat(base_dir_fd, "current_now", O_RDONLY | O_CLOEXEC); - int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY | O_CLOEXEC); - int time_to_full_fd = openat(base_dir_fd, "time_to_full_now", O_RDONLY | O_CLOEXEC); + 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; @@ -404,14 +322,8 @@ update_status(struct module *mod) long charge = charge_fd >= 0 ? readint_from_fd(charge_fd) : -1; long current = current_fd >= 0 ? readint_from_fd(current_fd) : -1; long time_to_empty = time_to_empty_fd >= 0 ? readint_from_fd(time_to_empty_fd) : -1; - long time_to_full = time_to_full_fd >= 0 ? readint_from_fd(time_to_full_fd) : -1; - if (charge >= -1) { - charge /= m->battery_scale; - } - - char buf[512]; - const char *status = readline_from_fd(status_fd, sizeof(buf), buf); + const char *status = readline_from_fd(status_fd); if (status_fd >= 0) close(status_fd); @@ -427,8 +339,6 @@ update_status(struct module *mod) close(current_fd); if (time_to_empty_fd >= 0) close(time_to_empty_fd); - if (time_to_full_fd >= 0) - close(time_to_full_fd); if (base_dir_fd >= 0) close(base_dir_fd); @@ -436,42 +346,34 @@ update_status(struct module *mod) if (status == NULL) { LOG_WARN("failed to read battery state"); - state = STATE_UNKNOWN; + 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_UNKNOWN; + state = STATE_DISCHARGING; else { LOG_ERR("unrecognized battery state: %s", status); - state = STATE_UNKNOWN; + state = STATE_DISCHARGING; } LOG_DBG("capacity: %ld, energy: %ld, power: %ld, charge=%ld, current=%ld, " - "time-to-empty: %ld, time-to-full: %ld", - capacity, energy, power, charge, current, time_to_empty, time_to_full); + "time-to-empty: %ld", capacity, energy, power, charge, current, + time_to_empty); mtx_lock(&mod->lock); - if (m->state != state) { - m->ema_current = (struct current_state){-1, 0, (struct timespec){0, 0}}; - } m->state = state; m->capacity = capacity; m->energy = energy; m->power = power; m->charge = charge; - if (current != -1) { - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - ema_linear(&m->ema_current, (struct current_state){current, current, t}, m->smoothing_scale); - } + m->current = current; m->time_to_empty = time_to_empty; - m->time_to_full = time_to_full; mtx_unlock(&mod->lock); return true; } @@ -485,10 +387,13 @@ run(struct module *mod) if (!initialize(m)) return -1; - LOG_INFO("%s: %s %s (at %.1f%% of original capacity)", m->battery, m->manufacturer, m->model, - (m->energy_full > 0 ? 100.0 * m->energy_full / m->energy_full_design - : m->charge_full > 0 ? 100.0 * m->charge_full / m->charge_full_design - : 0.0)); + LOG_INFO("%s: %s %s (at %.1f%% of original capacity)", + m->battery, m->manufacturer, m->model, + (m->energy_full > 0 + ? 100.0 * m->energy_full / m->energy_full_design + : m->charge_full > 0 + ? 100.0 * m->charge_full / m->charge_full_design + : 0.0)); int ret = 1; @@ -506,82 +411,32 @@ run(struct module *mod) bar->refresh(bar); - int timeout_left_ms = m->poll_interval; - while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, {.fd = udev_monitor_get_fd(mon), .events = POLLIN}, }; - - int timeout = m->poll_interval > 0 ? timeout_left_ms : -1; - - struct timespec time_before_poll; - if (clock_gettime(CLOCK_BOOTTIME, &time_before_poll) < 0) { - LOG_ERRNO("failed to get current time"); - break; - } - - const int poll_ret = poll(fds, sizeof(fds) / sizeof(fds[0]), timeout); - - if (poll_ret < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } + poll(fds, 2, m->poll_interval > 0 ? m->poll_interval * 1000 : -1); if (fds[0].revents & POLLIN) { ret = 0; break; } - bool udev_for_us = false; - if (fds[1].revents & POLLIN) { struct udev_device *dev = udev_monitor_receive_device(mon); - if (dev != NULL) { - const char *sysname = udev_device_get_sysname(dev); - udev_for_us = sysname != NULL && strcmp(sysname, m->battery) == 0; + const char *sysname = udev_device_get_sysname(dev); - if (!udev_for_us) { - LOG_DBG("udev notification not for us (%s != %s)", m->battery, - sysname != sysname ? sysname : "NULL"); - } else - LOG_DBG("triggering update due to udev notification"); + bool is_us = sysname != NULL && strcmp(sysname, m->battery) == 0; + udev_device_unref(dev); - udev_device_unref(dev); - } + if (!is_us) + continue; } - if (udev_for_us || poll_ret == 0) { - if (update_status(mod)) - bar->refresh(bar); - } - - if (poll_ret == 0 || udev_for_us) { - LOG_DBG("resetting timeout-left to %ldms", m->poll_interval); - timeout_left_ms = m->poll_interval; - } else { - struct timespec time_after_poll; - if (clock_gettime(CLOCK_BOOTTIME, &time_after_poll) < 0) { - LOG_ERRNO("failed to get current time"); - break; - } - - struct timespec timeout_consumed; - timespec_sub(&time_after_poll, &time_before_poll, &timeout_consumed); - - const int timeout_consumed_ms = timeout_consumed.tv_sec * 1000 + timeout_consumed.tv_nsec / 1000000; - - LOG_DBG("timeout-left before: %dms, consumed: %dms, updated: %dms", timeout_left_ms, timeout_consumed_ms, - max(timeout_left_ms - timeout_consumed_ms, 0)); - - timeout_left_ms -= timeout_consumed_ms; - if (timeout_left_ms < 0) - timeout_left_ms = 0; - } + if (!update_status(mod)) + break; + bar->refresh(bar); } out: @@ -593,24 +448,19 @@ out: } static struct module * -battery_new(const char *battery, struct particle *label, long poll_interval_msecs, int battery_scale, - long smoothing_secs) +battery_new(const char *battery, struct particle *label, int poll_interval_secs) { struct private *m = calloc(1, sizeof(*m)); m->label = label; - m->poll_interval = poll_interval_msecs; - m->battery_scale = battery_scale; - m->smoothing_scale = smoothing_secs * one_sec_in_ns; + m->poll_interval = poll_interval_secs; m->battery = strdup(battery); - m->state = STATE_UNKNOWN; - m->ema_current = (struct current_state){-1, 0, (struct timespec){0, 0}}; + m->state = STATE_DISCHARGING; struct module *mod = module_common_new(); mod->private = m; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -620,29 +470,11 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *poll_interval = yml_get_value(node, "poll-interval"); - const struct yml_node *battery_scale = yml_get_value(node, "battery-scale"); - const struct yml_node *smoothing_secs = yml_get_value(node, "smoothing-secs"); - return battery_new(yml_value_as_string(name), conf_to_particle(c, inherited), - (poll_interval != NULL ? yml_value_as_int(poll_interval) : default_poll_interval), - (battery_scale != NULL ? yml_value_as_int(battery_scale) : 1), - (smoothing_secs != NULL ? yml_value_as_int(smoothing_secs) : 100)); -} - -static bool -conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_unsigned(chain, node)) - return false; - - const long value = yml_value_as_int(node); - - if (value != 0 && value < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); - return false; - } - - return true; + return battery_new( + yml_value_as_string(name), + conf_to_particle(c, inherited), + poll_interval != NULL ? yml_value_as_int(poll_interval) : 60); } static bool @@ -650,9 +482,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"name", true, &conf_verify_string}, - {"poll-interval", false, &conf_verify_poll_interval}, - {"battery-scale", false, &conf_verify_unsigned}, - {"smoothing-secs", false, &conf_verify_unsigned}, + {"poll-interval", false, &conf_verify_int}, MODULE_COMMON_ATTRS, }; diff --git a/modules/clock.c b/modules/clock.c index 1758c0a..e9f1694 100644 --- a/modules/clock.c +++ b/modules/clock.c @@ -1,22 +1,20 @@ -#include -#include #include #include #include +#include #include #include #define LOG_MODULE "clock" #define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../plugin.h" -struct private -{ +struct private { struct particle *label; enum { UPDATE_GRANULARITY_SECONDS, @@ -24,7 +22,6 @@ struct private } update_granularity; char *date_format; char *time_format; - bool utc; }; static void @@ -38,18 +35,12 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "clock"; -} - static struct exposable * content(struct module *mod) { const struct private *m = mod->private; time_t t = time(NULL); - struct tm *tm = m->utc ? gmtime(&t) : localtime(&t); + struct tm *tm = localtime(&t); char date_str[1024]; strftime(date_str, sizeof(date_str), m->date_format, tm); @@ -58,7 +49,8 @@ content(struct module *mod) strftime(time_str, sizeof(time_str), m->time_format, tm); struct tag_set tags = { - .tags = (struct tag *[]){tag_new_string(mod, "time", time_str), tag_new_string(mod, "date", date_str)}, + .tags = (struct tag *[]){tag_new_string(mod, "time", time_str), + tag_new_string(mod, "date", date_str)}, .count = 2, }; @@ -75,8 +67,6 @@ run(struct module *mod) const struct bar *bar = mod->bar; bar->refresh(bar); - int ret = 1; - while (true) { struct timespec _now; clock_gettime(CLOCK_REALTIME, &_now); @@ -90,12 +80,15 @@ run(struct module *mod) switch (m->update_granularity) { case UPDATE_GRANULARITY_SECONDS: { - const struct timeval next_second = {.tv_sec = now.tv_sec + 1, .tv_usec = 0}; + const struct timeval next_second = { + .tv_sec = now.tv_sec + 1, + .tv_usec = 0}; struct timeval _timeout; timersub(&next_second, &now, &_timeout); - assert(_timeout.tv_sec == 0 || (_timeout.tv_sec == 1 && _timeout.tv_usec == 0)); + assert(_timeout.tv_sec == 0 || + (_timeout.tv_sec == 1 && _timeout.tv_usec == 0)); timeout_ms = _timeout.tv_usec / 1000; break; } @@ -115,44 +108,44 @@ run(struct module *mod) /* Add 1ms to account for rounding errors */ timeout_ms++; - LOG_DBG("now: %lds %ldµs -> timeout: %dms", now.tv_sec, now.tv_usec, timeout_ms); + LOG_DBG("now: %lds %ldµs -> timeout: %dms", + now.tv_sec, now.tv_usec, timeout_ms); struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - if (poll(fds, 1, timeout_ms) < 0) { - if (errno == EINTR) - continue; + poll(fds, 1, timeout_ms); - LOG_ERRNO("failed to poll"); + if (fds[0].revents & POLLIN) break; - } - - if (fds[0].revents & POLLIN) { - ret = 0; - break; - } bar->refresh(bar); } - return ret; + return 0; } static struct module * -clock_new(struct particle *label, const char *date_format, const char *time_format, bool utc) +clock_new(struct particle *label, const char *date_format, const char *time_format) { struct private *m = calloc(1, sizeof(*m)); m->label = label; m->date_format = strdup(date_format); m->time_format = strdup(time_format); - m->utc = utc; static const char *const seconds_formatters[] = { - "%c", "%s", "%S", "%T", "%r", "%X", + "%c", + "%s", + "%S", + "%T", + "%r", + "%X", }; m->update_granularity = UPDATE_GRANULARITY_MINUTES; - for (size_t i = 0; i < sizeof(seconds_formatters) / sizeof(seconds_formatters[0]); i++) { + for (size_t i = 0; + i < sizeof(seconds_formatters) / sizeof(seconds_formatters[0]); + i++) + { if (strstr(time_format, seconds_formatters[i]) != NULL) { m->update_granularity = UPDATE_GRANULARITY_SECONDS; break; @@ -160,14 +153,14 @@ clock_new(struct particle *label, const char *date_format, const char *time_form } LOG_DBG("using %s update granularity", - (m->update_granularity == UPDATE_GRANULARITY_MINUTES ? "minutes" : "seconds")); + (m->update_granularity == UPDATE_GRANULARITY_MINUTES + ? "minutes" : "seconds")); struct module *mod = module_common_new(); mod->private = m; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -177,11 +170,11 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *date_format = yml_get_value(node, "date-format"); const struct yml_node *time_format = yml_get_value(node, "time-format"); - const struct yml_node *utc = yml_get_value(node, "utc"); - return clock_new(conf_to_particle(c, inherited), date_format != NULL ? yml_value_as_string(date_format) : "%x", - time_format != NULL ? yml_value_as_string(time_format) : "%H:%M", - utc != NULL ? yml_value_as_bool(utc) : false); + return clock_new( + conf_to_particle(c, inherited), + date_format != NULL ? yml_value_as_string(date_format) : "%x", + time_format != NULL ? yml_value_as_string(time_format) : "%H:%M"); } static bool @@ -190,7 +183,6 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"date-format", false, &conf_verify_string}, {"time-format", false, &conf_verify_string}, - {"utc", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; diff --git a/modules/cpu.c b/modules/cpu.c deleted file mode 100644 index 118361e..0000000 --- a/modules/cpu.c +++ /dev/null @@ -1,297 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_MODULE "cpu" -#define LOG_ENABLE_DBG 0 -#include "../log.h" - -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" -#include "../particles/dynlist.h" -#include "../plugin.h" - -static const long min_poll_interval = 250; - -struct cpu_stats { - uint32_t *prev_cores_idle; - uint32_t *prev_cores_nidle; - - uint32_t *cur_cores_idle; - uint32_t *cur_cores_nidle; -}; - -struct private -{ - struct particle *template; - uint16_t interval; - size_t core_count; - struct cpu_stats cpu_stats; -}; - -static void -destroy(struct module *mod) -{ - struct private *m = mod->private; - - m->template->destroy(m->template); - free(m->cpu_stats.prev_cores_idle); - free(m->cpu_stats.prev_cores_nidle); - free(m->cpu_stats.cur_cores_idle); - free(m->cpu_stats.cur_cores_nidle); - free(m); - - module_default_destroy(mod); -} - -static const char * -description(const struct module *mod) -{ - return "cpu"; -} - -static uint32_t -get_cpu_nb_cores() -{ - int nb_cores = sysconf(_SC_NPROCESSORS_ONLN); - LOG_DBG("CPU count: %d", nb_cores); - - return nb_cores; -} - -static bool -parse_proc_stat_line(const char *line, uint32_t *user, uint32_t *nice, uint32_t *system, uint32_t *idle, - uint32_t *iowait, uint32_t *irq, uint32_t *softirq, uint32_t *steal, uint32_t *guest, - uint32_t *guestnice) -{ - int32_t core_id; - if (line[sizeof("cpu") - 1] == ' ') { - int read = sscanf(line, - "cpu %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32, - user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); - return read == 10; - } else { - int read = sscanf(line, - "cpu%" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32, - &core_id, user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice); - return read == 11; - } -} - -static uint8_t -get_cpu_usage_percent(const struct cpu_stats *cpu_stats, int8_t core_idx) -{ - uint32_t prev_total = cpu_stats->prev_cores_idle[core_idx + 1] + cpu_stats->prev_cores_nidle[core_idx + 1]; - - uint32_t cur_total = cpu_stats->cur_cores_idle[core_idx + 1] + cpu_stats->cur_cores_nidle[core_idx + 1]; - - double totald = cur_total - prev_total; - double nidled = cpu_stats->cur_cores_nidle[core_idx + 1] - cpu_stats->prev_cores_nidle[core_idx + 1]; - - double percent = (nidled * 100) / (totald + 1); - return round(percent); -} - -static void -refresh_cpu_stats(struct cpu_stats *cpu_stats, size_t core_count) -{ - int32_t core = 0; - uint32_t user = 0; - uint32_t nice = 0; - uint32_t system = 0; - uint32_t idle = 0; - uint32_t iowait = 0; - uint32_t irq = 0; - uint32_t softirq = 0; - uint32_t steal = 0; - uint32_t guest = 0; - uint32_t guestnice = 0; - - FILE *fp = NULL; - char *line = NULL; - size_t len = 0; - ssize_t read; - - fp = fopen("/proc/stat", "re"); - if (NULL == fp) { - LOG_ERRNO("unable to open /proc/stat"); - return; - } - - while ((read = getline(&line, &len, fp)) != -1 && core <= core_count) { - if (strncmp(line, "cpu", sizeof("cpu") - 1) == 0) { - if (!parse_proc_stat_line(line, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, - &guestnice)) { - LOG_ERR("unable to parse /proc/stat line"); - goto exit; - } - - cpu_stats->prev_cores_idle[core] = cpu_stats->cur_cores_idle[core]; - cpu_stats->prev_cores_nidle[core] = cpu_stats->cur_cores_nidle[core]; - - cpu_stats->cur_cores_idle[core] = idle + iowait; - cpu_stats->cur_cores_nidle[core] = user + nice + system + irq + softirq + steal; - - core++; - } - } - -exit: - fclose(fp); - free(line); -} - -static struct exposable * -content(struct module *mod) -{ - const struct private *m = mod->private; - - mtx_lock(&mod->lock); - - const size_t list_count = m->core_count + 1; - struct exposable *parts[list_count]; - - { - uint8_t total_usage = get_cpu_usage_percent(&m->cpu_stats, -1); - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_int(mod, "id", -1), - tag_new_int_range(mod, "cpu", total_usage, 0, 100), - }, - .count = 2, - }; - - parts[0] = m->template->instantiate(m->template, &tags); - tag_set_destroy(&tags); - } - - for (size_t i = 0; i < m->core_count; i++) { - uint8_t core_usage = get_cpu_usage_percent(&m->cpu_stats, i); - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_int(mod, "id", i), - tag_new_int_range(mod, "cpu", core_usage, 0, 100), - }, - .count = 2, - }; - - parts[i + 1] = m->template->instantiate(m->template, &tags); - tag_set_destroy(&tags); - } - - mtx_unlock(&mod->lock); - return dynlist_exposable_new(parts, list_count, 0, 0); -} - -static int -run(struct module *mod) -{ - const struct bar *bar = mod->bar; - bar->refresh(bar); - - struct private *p = mod->private; - while (true) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - - int res = poll(fds, sizeof(fds) / sizeof(*fds), p->interval); - - if (res < 0) { - if (EINTR == errno) - continue; - LOG_ERRNO("unable to poll abort fd"); - return -1; - } - - if (fds[0].revents & POLLIN) - break; - - mtx_lock(&mod->lock); - refresh_cpu_stats(&p->cpu_stats, p->core_count); - mtx_unlock(&mod->lock); - bar->refresh(bar); - } - - return 0; -} - -static struct module * -cpu_new(uint16_t interval, struct particle *template) -{ - uint32_t nb_cores = get_cpu_nb_cores(); - - struct private *p = calloc(1, sizeof(*p)); - p->template = template; - p->interval = interval; - p->core_count = nb_cores; - - p->cpu_stats.prev_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_nidle)); - p->cpu_stats.prev_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.prev_cores_idle)); - - p->cpu_stats.cur_cores_nidle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_nidle)); - p->cpu_stats.cur_cores_idle = calloc(nb_cores + 1, sizeof(*p->cpu_stats.cur_cores_idle)); - - struct module *mod = module_common_new(); - mod->private = p; - mod->run = &run; - mod->destroy = &destroy; - mod->content = &content; - mod->description = &description; - return mod; -} - -static struct module * -from_conf(const struct yml_node *node, struct conf_inherit inherited) -{ - const struct yml_node *interval = yml_get_value(node, "poll-interval"); - const struct yml_node *c = yml_get_value(node, "content"); - - return cpu_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); -} - -static bool -conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_unsigned(chain, node)) - return false; - - if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); - return false; - } - - return true; -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"poll-interval", false, &conf_verify_poll_interval}, - MODULE_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_cpu_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_cpu_iface"))); -#endif diff --git a/modules/dbus.h b/modules/dbus.h deleted file mode 100644 index 6517cef..0000000 --- a/modules/dbus.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -// This header provides an generic interface for different versions of -// systemd-sdbus. - -#if defined(HAVE_LIBSYSTEMD) -#include -#elif defined(HAVE_LIBELOGIND) -#include -#elif defined(HAVE_BASU) -#include -#endif - diff --git a/modules/disk-io.c b/modules/disk-io.c deleted file mode 100644 index c33cbef..0000000 --- a/modules/disk-io.c +++ /dev/null @@ -1,350 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#define LOG_MODULE "disk-io" -#define LOG_ENABLE_DBG 0 -#include "../log.h" - -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" -#include "../particles/dynlist.h" -#include "../plugin.h" - -static const long min_poll_interval = 250; - -struct device_stats { - char *name; - bool is_disk; - - uint64_t prev_sectors_read; - uint64_t cur_sectors_read; - - uint64_t prev_sectors_written; - uint64_t cur_sectors_written; - - uint32_t ios_in_progress; - - bool exists; -}; - -struct private -{ - struct particle *label; - uint16_t interval; - tll(struct device_stats *) devices; -}; - -static bool -is_disk(char const *name) -{ - DIR *dir = opendir("/sys/block"); - if (dir == NULL) { - LOG_ERRNO("failed to read /sys/block directory"); - return false; - } - - struct dirent *entry; - - bool found = false; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(name, entry->d_name) == 0) { - found = true; - break; - } - } - - closedir(dir); - return found; -} - -static struct device_stats * -new_device_stats(char const *name) -{ - struct device_stats *dev = malloc(sizeof(*dev)); - dev->name = strdup(name); - dev->is_disk = is_disk(name); - return dev; -} - -static void -free_device_stats(struct device_stats *dev) -{ - free(dev->name); - free(dev); -} - -static void -destroy(struct module *mod) -{ - struct private *m = mod->private; - m->label->destroy(m->label); - tll_foreach(m->devices, it) { free_device_stats(it->item); } - tll_free(m->devices); - free(m); - module_default_destroy(mod); -} - -static const char * -description(const struct module *mod) -{ - return "disk-io"; -} - -static void -refresh_device_stats(struct private *m) -{ - FILE *fp = NULL; - char *line = NULL; - size_t len = 0; - ssize_t read; - - fp = fopen("/proc/diskstats", "re"); - if (NULL == fp) { - LOG_ERRNO("unable to open /proc/diskstats"); - return; - } - - /* - * Devices may be added or removed during the bar's lifetime, as external - * block devices are connected or disconnected from the machine. /proc/diskstats - * reports data only for the devices that are currently connected. - * - * This means that if we have a device that ISN'T in /proc/diskstats, it was - * disconnected, and we need to remove it from the list. - * - * On the other hand, if a device IS in /proc/diskstats, but not in our list, we - * must create a new device_stats struct and add it to the list. - * - * The 'exists' variable is what keep tracks of whether or not /proc/diskstats - * is still reporting the device (i.e., it is still connected). - */ - tll_foreach(m->devices, it) { it->item->exists = false; } - - while ((read = getline(&line, &len, fp)) != -1) { - /* - * For an explanation of the fields below, see - * https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats - */ - uint8_t major_number = 0; - uint8_t minor_number = 0; - char *device_name = NULL; - uint32_t completed_reads = 0; - uint32_t merged_reads = 0; - uint64_t sectors_read = 0; - uint32_t reading_time = 0; - uint32_t completed_writes = 0; - uint32_t merged_writes = 0; - uint64_t sectors_written = 0; - uint32_t writting_time = 0; - uint32_t ios_in_progress = 0; - uint32_t io_time = 0; - uint32_t io_weighted_time = 0; - uint32_t completed_discards = 0; - uint32_t merged_discards = 0; - uint32_t sectors_discarded = 0; - uint32_t discarding_time = 0; - uint32_t completed_flushes = 0; - uint32_t flushing_time = 0; - if (!sscanf(line, - " %" SCNu8 " %" SCNu8 " %ms %" SCNu32 " %" SCNu32 " %" SCNu64 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu64 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 " %" SCNu32 - " %" SCNu32 " %" SCNu32 " %" SCNu32, - &major_number, &minor_number, &device_name, &completed_reads, &merged_reads, §ors_read, - &reading_time, &completed_writes, &merged_writes, §ors_written, &writting_time, - &ios_in_progress, &io_time, &io_weighted_time, &completed_discards, &merged_discards, - §ors_discarded, &discarding_time, &completed_flushes, &flushing_time)) { - LOG_ERR("unable to parse /proc/diskstats line"); - free(device_name); - goto exit; - } - - bool found = false; - tll_foreach(m->devices, it) - { - struct device_stats *dev = it->item; - if (strcmp(dev->name, device_name) == 0) { - dev->prev_sectors_read = dev->cur_sectors_read; - dev->prev_sectors_written = dev->cur_sectors_written; - dev->ios_in_progress = ios_in_progress; - dev->cur_sectors_read = sectors_read; - dev->cur_sectors_written = sectors_written; - dev->exists = true; - found = true; - break; - } - } - - if (!found) { - struct device_stats *new_dev = new_device_stats(device_name); - new_dev->ios_in_progress = ios_in_progress; - new_dev->prev_sectors_read = sectors_read; - new_dev->cur_sectors_read = sectors_read; - new_dev->prev_sectors_written = sectors_written; - new_dev->cur_sectors_written = sectors_written; - new_dev->exists = true; - tll_push_back(m->devices, new_dev); - } - - free(device_name); - } - - tll_foreach(m->devices, it) - { - if (!it->item->exists) { - free_device_stats(it->item); - tll_remove(m->devices, it); - } - } -exit: - fclose(fp); - free(line); -} - -static struct exposable * -content(struct module *mod) -{ - const struct private *p = mod->private; - uint64_t total_bytes_read = 0; - uint64_t total_bytes_written = 0; - uint32_t total_ios_in_progress = 0; - mtx_lock(&mod->lock); - struct exposable *tag_parts[p->devices.length + 1]; - int i = 0; - tll_foreach(p->devices, it) - { - struct device_stats *dev = it->item; - uint64_t bytes_read = (dev->cur_sectors_read - dev->prev_sectors_read) * 512; - uint64_t bytes_written = (dev->cur_sectors_written - dev->prev_sectors_written) * 512; - - if (dev->is_disk) { - total_bytes_read += bytes_read; - total_bytes_written += bytes_written; - total_ios_in_progress += dev->ios_in_progress; - } - - struct tag_set tags = { - .tags = (struct tag *[]) { - tag_new_string(mod, "device", dev->name), - tag_new_bool(mod, "is_disk", dev->is_disk), - tag_new_int(mod, "read_speed", (bytes_read * 1000) / p->interval), - tag_new_int(mod, "write_speed", (bytes_written * 1000) / p->interval), - tag_new_int(mod, "ios_in_progress", dev->ios_in_progress), - }, - .count = 5, - }; - tag_parts[i++] = p->label->instantiate(p->label, &tags); - tag_set_destroy(&tags); - } - struct tag_set tags = { - .tags = (struct tag *[]) { - tag_new_string(mod, "device", "Total"), - tag_new_bool(mod, "is_disk", true), - tag_new_int(mod, "read_speed", (total_bytes_read * 1000) / p->interval), - tag_new_int(mod, "write_speed", (total_bytes_written * 1000) / p->interval), - tag_new_int(mod, "ios_in_progress", total_ios_in_progress), - }, - .count = 5, - }; - tag_parts[i] = p->label->instantiate(p->label, &tags); - tag_set_destroy(&tags); - mtx_unlock(&mod->lock); - - return dynlist_exposable_new(tag_parts, p->devices.length + 1, 0, 0); -} - -static int -run(struct module *mod) -{ - const struct bar *bar = mod->bar; - bar->refresh(bar); - struct private *p = mod->private; - while (true) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - - int res = poll(fds, sizeof(fds) / sizeof(*fds), p->interval); - - if (res < 0) { - if (EINTR == errno) - continue; - LOG_ERRNO("unable to poll abort fd"); - return -1; - } - - if (fds[0].revents & POLLIN) - break; - - mtx_lock(&mod->lock); - refresh_device_stats(p); - mtx_unlock(&mod->lock); - bar->refresh(bar); - } - - return 0; -} - -static struct module * -disk_io_new(uint16_t interval, struct particle *label) -{ - struct private *p = calloc(1, sizeof(*p)); - p->label = label; - p->interval = interval; - - struct module *mod = module_common_new(); - mod->private = p; - mod->run = &run; - mod->destroy = &destroy; - mod->content = &content; - mod->description = &description; - return mod; -} - -static struct module * -from_conf(const struct yml_node *node, struct conf_inherit inherited) -{ - const struct yml_node *interval = yml_get_value(node, "poll-interval"); - const struct yml_node *c = yml_get_value(node, "content"); - - return disk_io_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), - conf_to_particle(c, inherited)); -} - -static bool -conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_unsigned(chain, node)) - return false; - - if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: poll-interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); - return false; - } - - return true; -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"poll-interval", false, &conf_verify_poll_interval}, - MODULE_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_disk_io_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_disk_io_iface"))); -#endif diff --git a/modules/dwl.c b/modules/dwl.c deleted file mode 100644 index 3b1bdcc..0000000 --- a/modules/dwl.c +++ /dev/null @@ -1,550 +0,0 @@ -#include -#include -#include -#include -#include - -#define ARR_LEN(x) (sizeof((x)) / sizeof((x)[0])) - -#include "../config-verify.h" -#include "../config.h" -#include "../log.h" -#include "../module.h" -#include "../particles/dynlist.h" -#include "../plugin.h" - -#define LOG_MODULE "dwl" -#define LOG_ENABLE_DBG 0 - -struct dwl_tag { - int id; - char *name; - bool selected; - bool empty; - bool urgent; -}; - -struct private -{ - struct particle *label; - - char const *monitor; - - unsigned int number_of_tags; - char *dwl_info_filename; - - /* dwl data */ - char *title; - char *appid; - bool fullscreen; - bool floating; - bool selmon; - tll(struct dwl_tag *) tags; - char *layout; -}; - -enum LINE_MODE { - LINE_MODE_0, - LINE_MODE_TITLE, - LINE_MODE_APPID, - LINE_MODE_FULLSCREEN, - LINE_MODE_FLOATING, - LINE_MODE_SELMON, - LINE_MODE_TAGS, - LINE_MODE_LAYOUT, -}; - -static void -free_dwl_tag(struct dwl_tag *tag) -{ - free(tag->name); - free(tag); -} - -static void -destroy(struct module *module) -{ - struct private *private = module->private; - private->label->destroy(private->label); - - tll_free_and_free(private->tags, free_dwl_tag); - free(private->dwl_info_filename); - free(private->title); - free(private->layout); - free(private); - - module_default_destroy(module); -} - -static char const * -description(const struct module *module) -{ - return "dwl"; -} - -static struct exposable * -content(struct module *module) -{ - struct private const *private = module->private; - mtx_lock(&module->lock); - - size_t i = 0; - /* + 1 for `default` tag */ - struct exposable *exposable[tll_length(private->tags) + 1]; - tll_foreach(private->tags, it) - { - struct tag_set tags = { - .tags = (struct tag*[]){ - tag_new_string(module, "title", private->title), - tag_new_string(module, "appid", private->appid), - tag_new_bool(module, "fullscreen", private->fullscreen), - tag_new_bool(module, "floating", private->floating), - tag_new_bool(module, "selmon", private->selmon), - tag_new_string(module, "layout", private->layout), - tag_new_int(module, "id", it->item->id), - tag_new_string(module, "name", it->item->name), - tag_new_bool(module, "selected", it->item->selected), - tag_new_bool(module, "empty", it->item->empty), - tag_new_bool(module, "urgent", it->item->urgent), - }, - .count = 11, - }; - exposable[i++] = private->label->instantiate(private->label, &tags); - tag_set_destroy(&tags); - } - - /* default tag (used for title, layout, etc) */ - struct tag_set tags = { - .tags = (struct tag*[]){ - tag_new_string(module, "title", private->title), - tag_new_string(module, "appid", private->appid), - tag_new_bool(module, "fullscreen", private->fullscreen), - tag_new_bool(module, "floating", private->floating), - tag_new_bool(module, "selmon", private->selmon), - tag_new_string(module, "layout", private->layout), - tag_new_int(module, "id", 0), - tag_new_string(module, "name", "0"), - tag_new_bool(module, "selected", false), - tag_new_bool(module, "empty", true), - tag_new_bool(module, "urgent", false), - }, - .count = 11, - }; - exposable[i++] = private->label->instantiate(private->label, &tags); - tag_set_destroy(&tags); - - mtx_unlock(&module->lock); - return dynlist_exposable_new(exposable, i, 0, 0); -} - -static struct dwl_tag * -dwl_tag_from_id(struct private *private, uint32_t id) -{ - tll_foreach(private->tags, it) - { - if (it->item->id == id) - return it->item; - } - - assert(false); /* unreachable */ - return NULL; -} - -static void -process_line(char *line, struct module *module) -{ - struct private *private = module->private; - enum LINE_MODE line_mode = LINE_MODE_0; - - /* Remove \n */ - line[strcspn(line, "\n")] = '\0'; - - /* Split line by space */ - size_t index = 1; - char *save_pointer = NULL; - char *string = strtok_r(line, " ", &save_pointer); - while (string != NULL) { - /* dwl logs are formatted like this - * $1 -> monitor - * $2 -> action - * $3 -> arg1 - * $4 -> arg2 - * ... */ - - /* monitor */ - if (index == 1) { - /* Not our monitor */ - if (strcmp(string, private->monitor) != 0) - break; - } - /* action */ - else if (index == 2) { - if (strcmp(string, "title") == 0) { - line_mode = LINE_MODE_TITLE; - /* Update the title here, to avoid allocate and free memory on - * every iteration (the line is separated by spaces, then we - * join it again) a bit suboptimal, isn't it?) */ - free(private->title); - private->title = strdup(save_pointer); - break; - } else if (strcmp(string, "appid") == 0) { - line_mode = LINE_MODE_APPID; - /* Update the appid here, same as the title. */ - free(private->appid); - private->appid = strdup(save_pointer); - break; - } else if (strcmp(string, "fullscreen") == 0) - line_mode = LINE_MODE_FULLSCREEN; - else if (strcmp(string, "floating") == 0) - line_mode = LINE_MODE_FLOATING; - else if (strcmp(string, "selmon") == 0) - line_mode = LINE_MODE_SELMON; - else if (strcmp(string, "tags") == 0) - line_mode = LINE_MODE_TAGS; - else if (strcmp(string, "layout") == 0) - line_mode = LINE_MODE_LAYOUT; - else { - LOG_WARN("UNKNOWN action, please open an issue on https://codeberg.org/dnkl/yambar"); - return; - } - } - /* args */ - else { - if (line_mode == LINE_MODE_TAGS) { - static uint32_t occupied, selected, client_tags, urgent; - static uint32_t *target = NULL; - - /* dwl tags action log are formatted like this - * $3 -> occupied - * $4 -> tags - * $5 -> clientTags (not needed) - * $6 -> urgent */ - if (index == 3) - target = &occupied; - else if (index == 4) - target = &selected; - else if (index == 5) - target = &client_tags; - else if (index == 6) - target = &urgent; - - /* No need to check error IMHO */ - *target = strtoul(string, NULL, 10); - - /* Populate information */ - if (index == 6) { - for (size_t id = 1; id <= private->number_of_tags; ++id) { - uint32_t mask = 1 << (id - 1); - - struct dwl_tag *dwl_tag = dwl_tag_from_id(private, id); - dwl_tag->selected = mask & selected; - dwl_tag->empty = !(mask & occupied); - dwl_tag->urgent = mask & urgent; - } - } - } else - switch (line_mode) { - case LINE_MODE_TITLE: - case LINE_MODE_APPID: - assert(false); /* unreachable */ - break; - case LINE_MODE_FULLSCREEN: - private - ->fullscreen = (strcmp(string, "0") != 0); - break; - case LINE_MODE_FLOATING: - private - ->floating = (strcmp(string, "0") != 0); - break; - case LINE_MODE_SELMON: - private - ->selmon = (strcmp(string, "0") != 0); - break; - case LINE_MODE_LAYOUT: - free(private->layout); - private->layout = strdup(string); - break; - default:; - assert(false); /* unreachable */ - } - } - - string = strtok_r(NULL, " ", &save_pointer); - ++index; - } -} - -static int -file_read_content(FILE *file, struct module *module) -{ - static char buffer[1024]; - - errno = 0; - while (fgets(buffer, ARR_LEN(buffer), file) != NULL) - process_line(buffer, module); - - fseek(file, 0, SEEK_END); - - /* Check whether error has been */ - if (ferror(file) != 0) { - LOG_ERRNO("unable to read file's content."); - return 1; - } - - return 0; -} - -static void -file_seek_to_last_n_lines(FILE *file, int number_of_lines) -{ - if (number_of_lines == 0 || file == NULL) - return; - - fseek(file, 0, SEEK_END); - - long position = ftell(file); - while (position > 0) { - /* Cannot go less than position 0 */ - if (fseek(file, --position, SEEK_SET) == EINVAL) - break; - - if (fgetc(file) == '\n') - if (number_of_lines-- == 0) - break; - } -} - -static int -run_init(int *inotify_fd, int *inotify_wd, FILE **file, char *dwl_info_filename) -{ - *inotify_fd = inotify_init(); - if (*inotify_fd == -1) { - LOG_ERRNO("unable to create inotify fd."); - return -1; - } - - *inotify_wd = inotify_add_watch(*inotify_fd, dwl_info_filename, IN_MODIFY); - if (*inotify_wd == -1) { - close(*inotify_fd); - LOG_ERRNO("unable to add watch to inotify fd."); - return 1; - } - - *file = fopen(dwl_info_filename, "re"); - if (*file == NULL) { - inotify_rm_watch(*inotify_fd, *inotify_wd); - close(*inotify_fd); - LOG_ERRNO("unable to open file."); - return 1; - } - - return 0; -} - -static int -run_clean(int inotify_fd, int inotify_wd, FILE *file) -{ - if (inotify_fd != -1) { - if (inotify_wd != -1) - inotify_rm_watch(inotify_fd, inotify_wd); - close(inotify_fd); - } - - if (file != NULL) { - if (fclose(file) == EOF) { - LOG_ERRNO("unable to close file."); - return 1; - } - } - - return 0; -}; - -static int -run(struct module *module) -{ - struct private *private = module->private; - - /* Ugly, but I didn't find better way for waiting - * the monitor's name to be set */ - do { - private->monitor = module->bar->output_name(module->bar); - usleep(50); - } while (private->monitor == NULL); - - int inotify_fd = -1, inotify_wd = -1; - FILE *file = NULL; - if (run_init(&inotify_fd, &inotify_wd, &file, private->dwl_info_filename) != 0) - return 1; - - /* Dwl output is 6 lines per monitor, so let's assume that nobody has - * more than 5 monitors (6 * 5 = 30) */ - mtx_lock(&module->lock); - file_seek_to_last_n_lines(file, 30); - if (file_read_content(file, module) != 0) { - mtx_unlock(&module->lock); - return run_clean(inotify_fd, inotify_wd, file); - } - mtx_unlock(&module->lock); - - module->bar->refresh(module->bar); - - while (true) { - struct pollfd fds[] = { - (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, - (struct pollfd){.fd = inotify_fd, .events = POLLIN}, - }; - - if (poll(fds, ARR_LEN(fds), -1) == -1) { - if (errno == EINTR) - continue; - - LOG_ERRNO("unable to poll."); - break; - } - - if (fds[0].revents & POLLIN) - break; - - /* fds[1] (inotify_fd) must be POLLIN otherwise issue happen'd */ - if (!(fds[1].revents & POLLIN)) { - LOG_ERR("expected POLLIN revent"); - break; - } - - /* Block until event */ - static char buffer[1024]; - ssize_t length = read(inotify_fd, buffer, ARR_LEN(buffer)); - - if (length == 0) - break; - - if (length == -1) { - if (errno == EAGAIN) - continue; - - LOG_ERRNO("unable to read %s", private->dwl_info_filename); - break; - } - - mtx_lock(&module->lock); - if (file_read_content(file, module) != 0) { - mtx_unlock(&module->lock); - break; - } - mtx_unlock(&module->lock); - - module->bar->refresh(module->bar); - } - - return run_clean(inotify_fd, inotify_wd, file); -} - -static struct module * -dwl_new(struct particle *label, int number_of_tags, struct yml_node const *name_of_tags, char const *dwl_info_filename) -{ - struct private *private = calloc(1, sizeof(struct private)); - private->label = label; - private->number_of_tags = number_of_tags; - private->dwl_info_filename = strdup(dwl_info_filename); - - struct yml_list_iter list = {0}; - if (name_of_tags) - list = yml_list_iter(name_of_tags); - - for (int i = 1; i <= number_of_tags; i++) { - struct dwl_tag *dwl_tag = calloc(1, sizeof(struct dwl_tag)); - dwl_tag->id = i; - if (list.node) { - dwl_tag->name = strdup(yml_value_as_string(list.node)); - yml_list_next(&list); - } else if (asprintf(&dwl_tag->name, "%d", i) < 0) { - LOG_ERRNO("asprintf"); - } - tll_push_back(private->tags, dwl_tag); - } - - struct module *module = module_common_new(); - module->private = private; - module->run = &run; - module->destroy = &destroy; - module->content = &content; - module->description = &description; - - return module; -} - -static struct module * -from_conf(struct yml_node const *node, struct conf_inherit inherited) -{ - struct yml_node const *content = yml_get_value(node, "content"); - struct yml_node const *number_of_tags = yml_get_value(node, "number-of-tags"); - struct yml_node const *name_of_tags = yml_get_value(node, "name-of-tags"); - struct yml_node const *dwl_info_filename = yml_get_value(node, "dwl-info-filename"); - - return dwl_new(conf_to_particle(content, inherited), yml_value_as_int(number_of_tags), name_of_tags, - yml_value_as_string(dwl_info_filename)); -} - -static bool -verify_names(keychain_t *keychain, const struct yml_node *node) -{ - if (!yml_is_list(node)) { - LOG_ERR("%s: %s is not a list", conf_err_prefix(keychain, node), yml_value_as_string(node)); - return false; - } - return conf_verify_list(keychain, node, &conf_verify_string); -} - -static bool -verify_conf(keychain_t *keychain, struct yml_node const *node) -{ - - static struct attr_info const attrs[] = { - {"number-of-tags", true, &conf_verify_unsigned}, - {"name-of-tags", false, &verify_names}, - {"dwl-info-filename", true, &conf_verify_string}, - MODULE_COMMON_ATTRS, - }; - - if (!conf_verify_dict(keychain, node, attrs)) - return false; - - /* No need to check whether is `number_of_tags` is a int - * because `conf_verify_unsigned` already did it */ - struct yml_node const *ntags_key = yml_get_key(node, "number-of-tags"); - struct yml_node const *value = yml_get_value(node, "number-of-tags"); - int number_of_tags = yml_value_as_int(value); - if (number_of_tags == 0) { - LOG_ERR("%s: %s must not be 0", conf_err_prefix(keychain, ntags_key), yml_value_as_string(ntags_key)); - return false; - } - - struct yml_node const *key = yml_get_key(node, "name-of-tags"); - value = yml_get_value(node, "name-of-tags"); - if (value && yml_list_length(value) != number_of_tags) { - LOG_ERR("%s: %s must have the same number of elements that %s", conf_err_prefix(keychain, key), - yml_value_as_string(key), yml_value_as_string(ntags_key)); - return false; - } - - /* No need to check whether is `dwl_info_filename` is a string - * because `conf_verify_string` already did it */ - key = yml_get_key(node, "dwl-info-filename"); - value = yml_get_value(node, "dwl-info-filename"); - if (strlen(yml_value_as_string(value)) == 0) { - LOG_ERR("%s: %s must not be empty", conf_err_prefix(keychain, key), yml_value_as_string(key)); - return false; - } - - return true; -} - -struct module_iface const module_dwl_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern struct module_iface const iface __attribute__((weak, alias("module_dwl_iface"))); -#endif diff --git a/modules/foreign-toplevel.c b/modules/foreign-toplevel.c deleted file mode 100644 index ccd6d5b..0000000 --- a/modules/foreign-toplevel.c +++ /dev/null @@ -1,666 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#include - -#define LOG_MODULE "foreign-toplevel" -#define LOG_ENABLE_DBG 0 -#include "../log.h" -#include "../particles/dynlist.h" -#include "../plugin.h" - -#include "wlr-foreign-toplevel-management-unstable-v1.h" -#include "xdg-output-unstable-v1.h" - -#define min(x, y) ((x) < (y) ? (x) : (y)) - -static const int required_manager_interface_version = 2; - -struct output { - struct module *mod; - - uint32_t wl_name; - struct wl_output *wl_output; - struct zxdg_output_v1 *xdg_output; - - char *name; -}; - -struct toplevel { - struct module *mod; - struct zwlr_foreign_toplevel_handle_v1 *handle; - - char *app_id; - char *title; - - bool maximized; - bool minimized; - bool activated; - bool fullscreen; - - tll(const struct output *) outputs; -}; - -struct private -{ - struct particle *template; - uint32_t manager_wl_name; - struct zwlr_foreign_toplevel_manager_v1 *manager; - struct zxdg_output_manager_v1 *xdg_output_manager; - - bool all_monitors; - tll(struct toplevel) toplevels; - tll(struct output) outputs; -}; - -static void -output_free(struct output *output) -{ - free(output->name); - if (output->xdg_output != NULL) - zxdg_output_v1_destroy(output->xdg_output); - if (output->wl_output != NULL) - wl_output_release(output->wl_output); -} - -static void -toplevel_free(struct toplevel *top) -{ - if (top->handle != NULL) - zwlr_foreign_toplevel_handle_v1_destroy(top->handle); - - free(top->app_id); - free(top->title); - tll_free(top->outputs); -} - -static void -destroy(struct module *mod) -{ - struct private *m = mod->private; - m->template->destroy(m->template); - - assert(tll_length(m->toplevels) == 0); - assert(tll_length(m->outputs) == 0); - - free(m); - module_default_destroy(mod); -} - -static const char * -description(const struct module *mod) -{ - return "toplevel"; -} - -static struct exposable * -content(struct module *mod) -{ - const struct private *m = mod->private; - - mtx_lock(&mod->lock); - - const size_t toplevel_count = tll_length(m->toplevels); - size_t show_count = 0; - struct exposable *toplevels[toplevel_count]; - - const char *current_output = mod->bar->output_name(mod->bar); - - tll_foreach(m->toplevels, it) - { - const struct toplevel *top = &it->item; - - bool show = false; - - if (m->all_monitors) - show = true; - else if (current_output != NULL) { - tll_foreach(top->outputs, it2) - { - const struct output *output = it2->item; - if (output->name != NULL && strcmp(output->name, current_output) == 0) { - show = true; - break; - } - } - } - - if (!show) - continue; - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_string(mod, "app-id", it->item.app_id), - tag_new_string(mod, "title", it->item.title), - tag_new_bool(mod, "maximized", it->item.maximized), - tag_new_bool(mod, "minimized", it->item.minimized), - tag_new_bool(mod, "activated", it->item.activated), - tag_new_bool(mod, "fullscreen", it->item.fullscreen), - }, - .count = 6, - }; - - toplevels[show_count++] = m->template->instantiate(m->template, &tags); - tag_set_destroy(&tags); - } - - mtx_unlock(&mod->lock); - return dynlist_exposable_new(toplevels, show_count, 0, 0); -} - -static bool -verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) -{ - if (version >= wanted) - return true; - - LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); - - return false; -} - -static void -xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) -{ -} - -static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) -{ -} - -static void -xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) -{ -} - -static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) -{ - struct output *output = data; - struct module *mod = output->mod; - - mtx_lock(&mod->lock); - { - free(output->name); - output->name = name != NULL ? strdup(name) : NULL; - } - mtx_unlock(&mod->lock); -} - -static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) -{ -} - -static struct zxdg_output_v1_listener xdg_output_listener = { - .logical_position = xdg_output_handle_logical_position, - .logical_size = xdg_output_handle_logical_size, - .done = xdg_output_handle_done, - .name = xdg_output_handle_name, - .description = xdg_output_handle_description, -}; - -static void -title(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *title) -{ - struct toplevel *top = data; - - mtx_lock(&top->mod->lock); - { - free(top->title); - top->title = title != NULL ? strdup(title) : NULL; - } - mtx_unlock(&top->mod->lock); -} - -static void -app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *app_id) -{ - struct toplevel *top = data; - - mtx_lock(&top->mod->lock); - { - free(top->app_id); - top->app_id = app_id != NULL ? strdup(app_id) : NULL; - } - mtx_unlock(&top->mod->lock); -} - -static void -output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *wl_output) -{ - struct toplevel *top = data; - struct module *mod = top->mod; - struct private *m = mod->private; - - mtx_lock(&mod->lock); - - const struct output *output = NULL; - tll_foreach(m->outputs, it) - { - if (it->item.wl_output == wl_output) { - output = &it->item; - break; - } - } - - if (output == NULL) { - LOG_ERR("output-enter event on untracked output"); - goto out; - } - - tll_foreach(top->outputs, it) - { - if (it->item == output) { - LOG_ERR("output-enter event on output we're already on"); - goto out; - } - } - - LOG_DBG("mapped: %s:%s on %s", top->app_id, top->title, output->name); - tll_push_back(top->outputs, output); - -out: - mtx_unlock(&mod->lock); -} - -static void -output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *wl_output) -{ - struct toplevel *top = data; - struct module *mod = top->mod; - struct private *m = mod->private; - - mtx_lock(&mod->lock); - - const struct output *output = NULL; - tll_foreach(m->outputs, it) - { - if (it->item.wl_output == wl_output) { - output = &it->item; - break; - } - } - - if (output == NULL) { - LOG_ERR("output-leave event on untracked output"); - goto out; - } - - bool output_removed = false; - tll_foreach(top->outputs, it) - { - if (it->item == output) { - LOG_DBG("unmapped: %s:%s from %s", top->app_id, top->title, output->name); - tll_remove(top->outputs, it); - output_removed = true; - break; - } - } - - if (!output_removed) { - LOG_ERR("output-leave event on an output we're not on"); - goto out; - } - -out: - mtx_unlock(&mod->lock); -} - -static void -state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *states) -{ - struct toplevel *top = data; - - bool maximized = false; - bool minimized = false; - bool activated = false; - bool fullscreen = false; - - enum zwlr_foreign_toplevel_handle_v1_state *state; - wl_array_for_each(state, states) - { - switch (*state) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: - maximized = true; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: - minimized = true; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: - activated = true; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: - fullscreen = true; - break; - } - } - - mtx_lock(&top->mod->lock); - { - top->maximized = maximized; - top->minimized = minimized; - top->activated = activated; - top->fullscreen = fullscreen; - } - mtx_unlock(&top->mod->lock); -} - -static void -done(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) -{ - struct toplevel *top = data; - const struct bar *bar = top->mod->bar; - bar->refresh(bar); -} - -static void -closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) -{ - struct toplevel *top = data; - struct module *mod = top->mod; - struct private *m = mod->private; - - mtx_lock(&mod->lock); - tll_foreach(m->toplevels, it) - { - if (it->item.handle == handle) { - toplevel_free(top); - tll_remove(m->toplevels, it); - break; - } - } - mtx_unlock(&mod->lock); - - const struct bar *bar = mod->bar; - bar->refresh(bar); -} - -static void -parent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct zwlr_foreign_toplevel_handle_v1 *parent) -{ -} - -static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_listener = { - .title = &title, - .app_id = &app_id, - .output_enter = &output_enter, - .output_leave = &output_leave, - .state = &state, - .done = &done, - .closed = &closed, - .parent = &parent, -}; - -static void -toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zwlr_foreign_toplevel_handle_v1 *handle) -{ - struct module *mod = data; - struct private *m = mod->private; - - struct toplevel toplevel = { - .mod = mod, - .handle = handle, - }; - - mtx_lock(&mod->lock); - { - tll_push_back(m->toplevels, toplevel); - - zwlr_foreign_toplevel_handle_v1_add_listener(handle, &toplevel_listener, &tll_back(m->toplevels)); - } - mtx_unlock(&mod->lock); -} - -static void -finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager) -{ - struct module *mod = data; - struct private *m = mod->private; - - assert(m->manager == manager); - zwlr_foreign_toplevel_manager_v1_destroy(m->manager); - m->manager = NULL; -} - -static const struct zwlr_foreign_toplevel_manager_v1_listener manager_listener = { - .toplevel = &toplevel, - .finished = &finished, -}; - -static void -output_xdg_output(struct output *output) -{ - struct private *m = output->mod->private; - - if (m->xdg_output_manager == NULL) - return; - if (output->xdg_output != NULL) - return; - - output->xdg_output = zxdg_output_manager_v1_get_xdg_output(m->xdg_output_manager, output->wl_output); - zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); -} - -static void -handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) -{ - struct module *mod = data; - struct private *m = mod->private; - - if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { - if (!verify_iface_version(interface, version, required_manager_interface_version)) - return; - - m->manager_wl_name = name; - } - - else if (strcmp(interface, wl_output_interface.name) == 0) { - const uint32_t required = 3; - if (!verify_iface_version(interface, version, required)) - return; - - struct output output = { - .mod = mod, - .wl_name = name, - .wl_output = wl_registry_bind(registry, name, &wl_output_interface, required), - }; - - mtx_lock(&mod->lock); - tll_push_back(m->outputs, output); - output_xdg_output(&tll_back(m->outputs)); - mtx_unlock(&mod->lock); - } - - else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { - const uint32_t required = 2; - if (!verify_iface_version(interface, version, required)) - return; - - m->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, required); - - mtx_lock(&mod->lock); - tll_foreach(m->outputs, it) output_xdg_output(&it->item); - mtx_unlock(&mod->lock); - } -} - -static void -handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) -{ - struct module *mod = data; - struct private *m = mod->private; - - mtx_lock(&mod->lock); - - tll_foreach(m->outputs, it) - { - const struct output *output = &it->item; - if (output->wl_name == name) { - - /* Loop all toplevels */ - tll_foreach(m->toplevels, it2) - { - - /* And remove this output from their list of tracked - * outputs */ - tll_foreach(it2->item.outputs, it3) - { - if (it3->item == output) { - tll_remove(it2->item.outputs, it3); - break; - } - } - } - - tll_remove(m->outputs, it); - goto out; - } - } - -out: - mtx_unlock(&mod->lock); -} - -static const struct wl_registry_listener registry_listener = { - .global = &handle_global, - .global_remove = &handle_global_remove, -}; - -static int -run(struct module *mod) -{ - struct private *m = mod->private; - int ret = -1; - - struct wl_display *display = NULL; - struct wl_registry *registry = NULL; - - if ((display = wl_display_connect(NULL)) == NULL) { - LOG_ERR("no Wayland compositor running"); - goto out; - } - - if ((registry = wl_display_get_registry(display)) == NULL - || wl_registry_add_listener(registry, ®istry_listener, mod) != 0) { - LOG_ERR("failed to get Wayland registry"); - goto out; - } - - wl_display_roundtrip(display); - - if (m->manager_wl_name == 0) { - LOG_ERR("compositor does not implement the foreign-toplevel-manager interface"); - goto out; - } - - m->manager = wl_registry_bind(registry, m->manager_wl_name, &zwlr_foreign_toplevel_manager_v1_interface, - required_manager_interface_version); - - zwlr_foreign_toplevel_manager_v1_add_listener(m->manager, &manager_listener, mod); - - while (true) { - wl_display_flush(display); - - struct pollfd fds[] = { - {.fd = mod->abort_fd, .events = POLLIN}, - {.fd = wl_display_get_fd(display), .events = POLLIN}, - }; - - int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); - if (r < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } - - if (fds[0].revents & (POLLIN | POLLHUP)) { - ret = 0; - break; - } - - if (fds[1].revents & POLLHUP) { - LOG_ERR("disconnected from the Wayland compositor"); - break; - } - - assert(fds[1].revents & POLLIN); - wl_display_dispatch(display); - } - -out: - tll_foreach(m->toplevels, it) - { - toplevel_free(&it->item); - tll_remove(m->toplevels, it); - } - - tll_foreach(m->outputs, it) - { - output_free(&it->item); - tll_remove(m->outputs, it); - } - - if (m->xdg_output_manager != NULL) - zxdg_output_manager_v1_destroy(m->xdg_output_manager); - if (m->manager != NULL) - zwlr_foreign_toplevel_manager_v1_destroy(m->manager); - if (registry != NULL) - wl_registry_destroy(registry); - if (display != NULL) - wl_display_disconnect(display); - return ret; -} - -static struct module * -ftop_new(struct particle *label, bool all_monitors) -{ - struct private *m = calloc(1, sizeof(*m)); - m->template = label; - m->all_monitors = all_monitors; - - struct module *mod = module_common_new(); - mod->private = m; - mod->run = &run; - mod->destroy = &destroy; - mod->content = &content; - mod->description = &description; - return mod; -} - -static struct module * -from_conf(const struct yml_node *node, struct conf_inherit inherited) -{ - const struct yml_node *c = yml_get_value(node, "content"); - const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); - - return ftop_new(conf_to_particle(c, inherited), all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"all-monitors", false, &conf_verify_bool}, - MODULE_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_foreign_toplevel_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_foreign_toplevel_iface"))); -#endif diff --git a/modules/i3-common.c b/modules/i3-common.c index 957a4d2..589bfb8 100644 --- a/modules/i3-common.c +++ b/modules/i3-common.c @@ -1,15 +1,15 @@ #include "i3-common.h" -#include #include #include #include +#include #include #if defined(ENABLE_X11) -#include -#include + #include + #include #endif #include @@ -19,7 +19,7 @@ #include "../log.h" #if defined(ENABLE_X11) -#include "../xcb.h" + #include "../xcb.h" #endif #include "i3-ipc.h" @@ -41,11 +41,14 @@ get_socket_address_x11(struct sockaddr_un *addr) xcb_atom_t atom = get_atom(conn, "I3_SOCKET_PATH"); assert(atom != XCB_ATOM_NONE); - xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(conn, false, screen->root, atom, - XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path)); + xcb_get_property_cookie_t cookie + = xcb_get_property_unchecked( + conn, false, screen->root, atom, + XCB_GET_PROPERTY_TYPE_ANY, 0, sizeof(addr->sun_path)); xcb_generic_error_t *err = NULL; - xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, &err); + xcb_get_property_reply_t *reply = + xcb_get_property_reply(conn, cookie, &err); bool ret = false; if (err != NULL) { @@ -99,7 +102,11 @@ bool i3_send_pkg(int sock, int cmd, char *data) { const size_t size = data != NULL ? strlen(data) : 0; - const i3_ipc_header_t hdr = {.magic = I3_IPC_MAGIC, .size = size, .type = cmd}; + const i3_ipc_header_t hdr = { + .magic = I3_IPC_MAGIC, + .size = size, + .type = cmd + }; if (write(sock, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) return false; @@ -113,7 +120,8 @@ i3_send_pkg(int sock, int cmd, char *data) } bool -i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void *data) +i3_receive_loop(int abort_fd, int sock, + const struct i3_ipc_callbacks *cbs, void *data) { /* Initial reply typically requires a couple of KB. But we often * need more later. For example, switching workspaces can result @@ -125,7 +133,10 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void bool err = false; while (!err) { - struct pollfd fds[] = {{.fd = abort_fd, .events = POLLIN}, {.fd = sock, .events = POLLIN}}; + struct pollfd fds[] = { + {.fd = abort_fd, .events = POLLIN}, + {.fd = sock, .events = POLLIN} + }; int res = poll(fds, 2, -1); if (res <= 0) { @@ -148,11 +159,13 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void /* Grow receive buffer, if necessary */ if (buf_idx == reply_buf_size) { - LOG_DBG("growing reply buffer: %zu -> %zu", reply_buf_size, reply_buf_size * 2); + LOG_DBG("growing reply buffer: %zu -> %zu", + reply_buf_size, reply_buf_size * 2); char *new_buf = realloc(buf, reply_buf_size * 2); if (new_buf == NULL) { - LOG_ERR("failed to grow reply buffer from %zu to %zu bytes", reply_buf_size, reply_buf_size * 2); + LOG_ERR("failed to grow reply buffer from %zu to %zu bytes", + reply_buf_size, reply_buf_size * 2); err = true; break; } @@ -175,8 +188,10 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void while (!err && buf_idx >= sizeof(i3_ipc_header_t)) { const i3_ipc_header_t *hdr = (const i3_ipc_header_t *)buf; if (strncmp(hdr->magic, I3_IPC_MAGIC, sizeof(hdr->magic)) != 0) { - LOG_ERR("i3 IPC header magic mismatch: expected \"%.*s\", got \"%.*s\"", (int)sizeof(hdr->magic), - I3_IPC_MAGIC, (int)sizeof(hdr->magic), hdr->magic); + LOG_ERR( + "i3 IPC header magic mismatch: expected \"%.*s\", got \"%.*s\"", + (int)sizeof(hdr->magic), I3_IPC_MAGIC, + (int)sizeof(hdr->magic), hdr->magic); err = true; break; @@ -195,10 +210,10 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void char json_str[hdr->size + 1]; memcpy(json_str, &buf[sizeof(*hdr)], hdr->size); json_str[hdr->size] = '\0'; - // printf("raw: %s\n", json_str); + //printf("raw: %s\n", json_str); LOG_DBG("raw: %s\n", json_str); - // json_tokener *tokener = json_tokener_new(); + //json_tokener *tokener = json_tokener_new(); struct json_object *json = json_tokener_parse(json_str); if (json == NULL) { LOG_ERR("failed to parse json"); @@ -247,13 +262,13 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void break; #endif /* Sway extensions */ - case 100: /* IPC_GET_INPUTS */ + case 100: /* IPC_GET_INPUTS */ pkt_handler = cbs->reply_inputs; break; - /* - * Events - */ + /* + * Events + */ case I3_IPC_EVENT_WORKSPACE: pkt_handler = cbs->event_workspace; @@ -280,7 +295,7 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void pkt_handler = cbs->event_tick; break; - /* Sway extensions */ + /* Sway extensions */ #define SWAY_IPC_EVENT_INPUT ((1u << 31) | 21) case SWAY_IPC_EVENT_INPUT: pkt_handler = cbs->event_input; @@ -294,7 +309,7 @@ i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *cbs, void } if (pkt_handler != NULL) - err = !pkt_handler(sock, hdr->type, json, data); + err = !pkt_handler(hdr->type, json, data); else LOG_DBG("no handler for reply/event %d; ignoring", hdr->type); diff --git a/modules/i3-common.h b/modules/i3-common.h index 6ba6721..e3d94d1 100644 --- a/modules/i3-common.h +++ b/modules/i3-common.h @@ -2,8 +2,8 @@ #include -#include #include +#include #include #include @@ -11,7 +11,7 @@ bool i3_get_socket_address(struct sockaddr_un *addr); bool i3_send_pkg(int sock, int cmd, char *data); -typedef bool (*i3_ipc_callback_t)(int sock, int type, const struct json_object *json, void *data); +typedef bool (*i3_ipc_callback_t)(int type, const struct json_object *json, void *data); struct i3_ipc_callbacks { void (*burst_done)(void *data); @@ -43,4 +43,6 @@ struct i3_ipc_callbacks { i3_ipc_callback_t event_input; }; -bool i3_receive_loop(int abort_fd, int sock, const struct i3_ipc_callbacks *callbacks, void *data); +bool i3_receive_loop( + int abort_fd, int sock, + const struct i3_ipc_callbacks *callbacks, void *data); diff --git a/modules/i3.c b/modules/i3.c index cbdafaf..e2f58de 100644 --- a/modules/i3.c +++ b/modules/i3.c @@ -1,27 +1,27 @@ -#include #include #include -#include #include +#include +#include -#include #include +#include #include #define LOG_MODULE "i3" #define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../particles/dynlist.h" #include "../plugin.h" -#include "i3-common.h" #include "i3-ipc.h" +#include "i3-common.h" -enum sort_mode { SORT_NONE, SORT_NATIVE, SORT_ASCENDING, SORT_DESCENDING }; +enum sort_mode {SORT_NONE, SORT_ASCENDING, SORT_DESCENDING}; struct ws_content { char *name; @@ -29,16 +29,13 @@ struct ws_content { }; struct workspace { - int id; char *name; - int name_as_int; /* -1 if name is not a decimal number */ - bool persistent; + int name_as_int; /* -1 is name is not a decimal number */ char *output; bool visible; bool focused; bool urgent; - bool empty; struct { unsigned id; @@ -48,8 +45,7 @@ struct workspace { } window; }; -struct private -{ +struct private { int left_spacing; int right_spacing; @@ -62,61 +58,22 @@ struct private size_t count; } ws_content; - bool strip_workspace_numbers; enum sort_mode sort_mode; tll(struct workspace) workspaces; - - size_t persistent_count; - char **persistent_workspaces; }; -static int -workspace_name_as_int(const char *name) -{ - int name_as_int = 0; - - /* First check for N:name pattern (set $ws1 “1:foobar”) */ - const char *colon = strchr(name, ':'); - if (colon != NULL) { - for (const char *p = name; p < colon; p++) { - if (!(*p >= '0' && *p < '9')) - return -1; - - name_as_int *= 10; - name_as_int += *p - '0'; - } - - return name_as_int; - } - - /* Then, if the name is a number *only* (set $ws1 1) */ - for (const char *p = name; *p != '\0'; p++) { - if (!(*p >= '0' && *p <= '9')) - return -1; - - name_as_int *= 10; - name_as_int += *p - '0'; - } - - return name_as_int; -} - static bool workspace_from_json(const struct json_object *json, struct workspace *ws) { /* Always present */ - struct json_object *id, *name, *output; - if (!json_object_object_get_ex(json, "id", &id) || !json_object_object_get_ex(json, "name", &name) - || !json_object_object_get_ex(json, "output", &output)) { - LOG_ERR("workspace reply/event without 'name' and/or 'output' " - "properties"); + struct json_object *name, *output; + if (!json_object_object_get_ex(json, "name", &name) || + !json_object_object_get_ex(json, "output", &output)) + { + LOG_ERR("workspace reply/event without 'name' and/or 'output' property"); return false; } - /* Sway only */ - struct json_object *focus = NULL; - json_object_object_get_ex(json, "focus", &focus); - /* Optional */ struct json_object *visible = NULL, *focused = NULL, *urgent = NULL; json_object_object_get_ex(json, "visible", &visible); @@ -125,59 +82,48 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); - const size_t node_count = focus != NULL ? json_object_array_length(focus) : 0; + 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; + } - const bool is_empty = node_count == 0; - int name_as_int = workspace_name_as_int(name_as_string); + name_as_int *= 10; + name_as_int += *p - '0'; + } - *ws = (struct workspace){ - .id = json_object_get_int(id), + *ws = (struct workspace) { .name = strdup(name_as_string), .name_as_int = name_as_int, - .persistent = false, .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), .urgent = json_object_get_boolean(urgent), - .empty = is_empty && json_object_get_boolean(focused), .window = {.title = NULL, .pid = -1}, }; return true; } -static void -workspace_free_persistent(struct workspace *ws) -{ - free(ws->output); - ws->output = NULL; - free(ws->window.title); - ws->window.title = NULL; - free(ws->window.application); - ws->window.application = NULL; - ws->id = -1; -} - static void workspace_free(struct workspace *ws) { - workspace_free_persistent(ws); free(ws->name); - ws->name = NULL; + free(ws->output); + free(ws->window.title); + free(ws->window.application); } static void -workspaces_free(struct private *m, bool free_persistent) +workspaces_free(struct private *m) { tll_foreach(m->workspaces, it) - { - if (free_persistent || !it->item.persistent) { - workspace_free(&it->item); - tll_remove(m->workspaces, it); - } - } + workspace_free(&it->item); + tll_free(m->workspaces); } + static void workspace_add(struct private *m, struct workspace ws) { @@ -186,26 +132,9 @@ workspace_add(struct private *m, struct workspace ws) tll_push_back(m->workspaces, ws); return; - case SORT_NATIVE: - if (ws.name_as_int >= 0) { - tll_foreach(m->workspaces, it) - { - if (it->item.name_as_int < 0) - continue; - if (it->item.name_as_int > ws.name_as_int) { - tll_insert_before(m->workspaces, it, ws); - return; - } - } - }; - - tll_push_back(m->workspaces, ws); - return; - case SORT_ASCENDING: if (ws.name_as_int >= 0) { - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { if (it->item.name_as_int < 0) continue; if (it->item.name_as_int > ws.name_as_int) { @@ -214,9 +143,10 @@ workspace_add(struct private *m, struct workspace ws) } } } else { - tll_foreach(m->workspaces, it) - { - if (strcoll(it->item.name, ws.name) > 0 || it->item.name_as_int >= 0) { + tll_foreach(m->workspaces, it) { + if (strcoll(it->item.name, ws.name) > 0 || + it->item.name_as_int >= 0) + { tll_insert_before(m->workspaces, it, ws); return; } @@ -227,16 +157,14 @@ workspace_add(struct private *m, struct workspace ws) case SORT_DESCENDING: if (ws.name_as_int >= 0) { - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { if (it->item.name_as_int < ws.name_as_int) { tll_insert_before(m->workspaces, it, ws); return; } } } else { - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { if (it->item.name_as_int >= 0) continue; if (strcoll(it->item.name, ws.name) < 0) { @@ -251,13 +179,12 @@ workspace_add(struct private *m, struct workspace ws) } static void -workspace_del(struct private *m, int id) +workspace_del(struct private *m, const char *name) { - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (ws->id != id) + if (strcmp(ws->name, name) != 0) continue; workspace_free(ws); @@ -267,22 +194,9 @@ workspace_del(struct private *m, int id) } static struct workspace * -workspace_lookup(struct private *m, int id) +workspace_lookup(struct private *m, const char *name) { - tll_foreach(m->workspaces, it) - { - struct workspace *ws = &it->item; - if (ws->id == id) - return ws; - } - return NULL; -} - -static struct workspace * -workspace_lookup_by_name(struct private *m, const char *name) -{ - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; if (strcmp(ws->name, name) == 0) return ws; @@ -291,7 +205,7 @@ workspace_lookup_by_name(struct private *m, const char *name) } static bool -handle_get_version_reply(int sock, int type, const struct json_object *json, void *_m) +handle_get_version_reply(int type, const struct json_object *json, void *_m) { struct json_object *version; if (!json_object_object_get_ex(json, "human_readable", &version)) { @@ -304,7 +218,7 @@ handle_get_version_reply(int sock, int type, const struct json_object *json, voi } static bool -handle_subscribe_reply(int sock, int type, const struct json_object *json, void *_m) +handle_subscribe_reply(int type, const struct json_object *json, void *_m) { struct json_object *success; if (!json_object_object_get_ex(json, "success", &success)) { @@ -321,88 +235,36 @@ handle_subscribe_reply(int sock, int type, const struct json_object *json, void } static bool -workspace_update_or_add(struct private *m, const struct json_object *ws_json) -{ - struct json_object *_id; - if (!json_object_object_get_ex(ws_json, "id", &_id)) - return false; - - const int id = json_object_get_int(_id); - struct workspace *already_exists = workspace_lookup(m, id); - - if (already_exists == NULL) { - /* - * No workspace with this ID. - * - * Try looking it up again, but this time using the name. If - * we get a match, check if it’s an empty, persistent - * workspace, and if so, use it. - * - * This is necessary, since empty, persistent workspaces don’t - * exist in the i3/Sway server, and thus we don’t _have_ an - * ID. - */ - struct json_object *_name; - if (json_object_object_get_ex(ws_json, "name", &_name)) { - const char *name = json_object_get_string(_name); - if (name != NULL) { - struct workspace *maybe_persistent = workspace_lookup_by_name(m, name); - - if (maybe_persistent != NULL && maybe_persistent->persistent && maybe_persistent->id < 0) { - already_exists = maybe_persistent; - } - } - } - } - - if (already_exists != NULL) { - bool persistent = already_exists->persistent; - assert(persistent); - - workspace_free(already_exists); - if (!workspace_from_json(ws_json, already_exists)) - return false; - already_exists->persistent = persistent; - } else { - struct workspace ws; - if (!workspace_from_json(ws_json, &ws)) - return false; - - workspace_add(m, ws); - } - - return true; -} - -static bool -handle_get_workspaces_reply(int sock, int type, const struct json_object *json, void *_mod) +handle_get_workspaces_reply(int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; mtx_lock(&mod->lock); - workspaces_free(m, false); + workspaces_free(m); m->dirty = true; size_t count = json_object_array_length(json); for (size_t i = 0; i < count; i++) { - if (!workspace_update_or_add(m, json_object_array_get_idx(json, i))) - goto err; + struct workspace ws = {}; + if (!workspace_from_json(json_object_array_get_idx(json, i), &ws)) { + workspaces_free(m); + mtx_unlock(&mod->lock); + return false; + } + + LOG_DBG("#%zu: %s", i, m->workspaces.v[i].name); + workspace_add(m, ws); } mtx_unlock(&mod->lock); return true; - -err: - workspaces_free(m, false); - mtx_unlock(&mod->lock); - return false; } static bool -handle_workspace_event(int sock, int type, const struct json_object *json, void *_mod) +handle_workspace_event(int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -418,59 +280,67 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void bool is_init = strcmp(change_str, "init") == 0; bool is_empty = strcmp(change_str, "empty") == 0; bool is_focused = strcmp(change_str, "focus") == 0; - bool is_rename = strcmp(change_str, "rename") == 0; - bool is_move = strcmp(change_str, "move") == 0; bool is_urgent = strcmp(change_str, "urgent") == 0; bool is_reload = strcmp(change_str, "reload") == 0; - struct json_object *current, *_current_id; - if ((!json_object_object_get_ex(json, "current", ¤t) - || !json_object_object_get_ex(current, "id", &_current_id)) - && !is_reload) { - LOG_ERR("workspace event without 'current' and/or 'id' properties"); + if (is_reload) { + LOG_WARN("unimplemented: 'reload' event"); + return true; + } + + struct json_object *current, *_current_name; + if (!json_object_object_get_ex(json, "current", ¤t) || + !json_object_object_get_ex(current, "name", &_current_name)) + { + LOG_ERR("workspace event without 'current' and/or 'name' properties"); return false; } - int current_id = json_object_get_int(_current_id); + const char *current_name = json_object_get_string(_current_name); mtx_lock(&mod->lock); if (is_init) { - if (!workspace_update_or_add(m, current)) - goto err; - } + struct workspace *already_exists = workspace_lookup(m, current_name); + if (already_exists != NULL) { + LOG_WARN("workspace 'init' event for already existing workspace: %s", current_name); + workspace_free(already_exists); + if (!workspace_from_json(current, already_exists)) + goto err; + } else { + struct workspace ws; + if (!workspace_from_json(current, &ws)) + goto err; - else if (is_empty) { - struct workspace *ws = workspace_lookup(m, current_id); - assert(ws != NULL); - - if (!ws->persistent) - workspace_del(m, current_id); - else { - workspace_free_persistent(ws); - ws->empty = true; + workspace_add(m, ws); } } + else if (is_empty) { + assert(workspace_lookup(m, current_name) != NULL); + workspace_del(m, current_name); + } + else if (is_focused) { - struct json_object *old, *_old_id, *urgent; - if (!json_object_object_get_ex(json, "old", &old) || !json_object_object_get_ex(old, "id", &_old_id) - || !json_object_object_get_ex(current, "urgent", &urgent)) { + struct json_object *old, *_old_name, *urgent; + if (!json_object_object_get_ex(json, "old", &old) || + !json_object_object_get_ex(old, "name", &_old_name) || + !json_object_object_get_ex(current, "urgent", &urgent)) + { LOG_ERR("workspace 'focused' event without 'old', 'name' and/or 'urgent' property"); mtx_unlock(&mod->lock); return false; } - struct workspace *w = workspace_lookup(m, current_id); + struct workspace *w = workspace_lookup(m, current_name); assert(w != NULL); LOG_DBG("w: %s", w->name); /* Mark all workspaces on current's output invisible */ - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; - if (ws->output != NULL && strcmp(ws->output, w->output) == 0) + if (strcmp(ws->output, w->output) == 0) ws->visible = false; } @@ -479,67 +349,12 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void w->visible = true; /* Old workspace is no longer focused */ - int old_id = json_object_get_int(_old_id); - struct workspace *old_w = workspace_lookup(m, old_id); + const char *old_name = json_object_get_string(_old_name); + struct workspace *old_w = workspace_lookup(m, old_name); if (old_w != NULL) old_w->focused = false; } - else if (is_rename) { - struct workspace *w = workspace_lookup(m, current_id); - assert(w != NULL); - - struct json_object *_current_name; - if (!json_object_object_get_ex(current, "name", &_current_name)) { - LOG_ERR("workspace 'rename' event without 'name' property"); - mtx_unlock(&mod->lock); - return false; - } - - free(w->name); - w->name = strdup(json_object_get_string(_current_name)); - w->name_as_int = workspace_name_as_int(w->name); - - /* Re-add the workspace to ensure correct sorting */ - struct workspace ws = *w; - tll_foreach(m->workspaces, it) - { - if (it->item.id == current_id) { - tll_remove(m->workspaces, it); - break; - } - } - workspace_add(m, ws); - } - - else if (is_move) { - struct workspace *w = workspace_lookup(m, current_id); - - struct json_object *_current_output; - if (!json_object_object_get_ex(current, "output", &_current_output)) { - LOG_ERR("workspace 'move' event without 'output' property"); - mtx_unlock(&mod->lock); - return false; - } - const char *current_output_string = json_object_get_string(_current_output); - - /* Ignore fallback_output ("For when there's no connected outputs") */ - if (strcmp(current_output_string, "FALLBACK") != 0) { - - assert(w != NULL); - free(w->output); - w->output = strdup(current_output_string); - - /* - * If the moved workspace was focused, schedule a full update because - * visibility for other workspaces may have changed. - */ - if (w->focused) { - i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); - } - } - } - else if (is_urgent) { struct json_object *urgent; if (!json_object_object_get_ex(current, "urgent", &urgent)) { @@ -548,20 +363,10 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void return false; } - struct workspace *w = workspace_lookup(m, current_id); + struct workspace *w = workspace_lookup(m, current_name); w->urgent = json_object_get_boolean(urgent); } - else if (is_reload) { - /* Schedule full update to check if anything was changed - * during reload */ - i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); - } - - else { - LOG_WARN("unimplemented workspace event '%s'", change_str); - } - m->dirty = true; mtx_unlock(&mod->lock); return true; @@ -572,7 +377,7 @@ err: } static bool -handle_window_event(int sock, int type, const struct json_object *json, void *_mod) +handle_window_event(int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -594,9 +399,8 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m mtx_lock(&mod->lock); struct workspace *ws = NULL; - __attribute__((unused)) size_t focused = 0; - tll_foreach(m->workspaces, it) - { + size_t focused = 0; + tll_foreach(m->workspaces, it) { if (it->item.focused) { ws = &it->item; focused++; @@ -606,20 +410,6 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m assert(focused == 1); assert(ws != NULL); - struct json_object *container, *id, *name; - if (!json_object_object_get_ex(json, "container", &container) || !json_object_object_get_ex(container, "id", &id) - || !json_object_object_get_ex(container, "name", &name)) { - mtx_unlock(&mod->lock); - LOG_ERR("window event without 'container' with 'id' and 'name'"); - return false; - } - - if ((is_close || is_title) && ws->window.id != json_object_get_int(id)) { - /* Ignore close event and title changed event if it's not current window */ - mtx_unlock(&mod->lock); - return true; - } - if (is_close) { free(ws->window.title); free(ws->window.application); @@ -631,6 +421,23 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m m->dirty = true; mtx_unlock(&mod->lock); return true; + + } + + struct json_object *container, *id, *name; + if (!json_object_object_get_ex(json, "container", &container) || + !json_object_object_get_ex(container, "id", &id) || + !json_object_object_get_ex(container, "name", &name)) + { + mtx_unlock(&mod->lock); + LOG_ERR("window event without 'container' with 'id' and 'name'"); + return false; + } + + if (is_title && ws->window.id != json_object_get_int(id)) { + /* Ignore title changed event if it's not current window */ + mtx_unlock(&mod->lock); + return true; } free(ws->window.title); @@ -651,24 +458,27 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m struct json_object *app_id; struct json_object *pid; - if (json_object_object_get_ex(container, "app_id", &app_id) && json_object_get_string(app_id) != NULL) { + if (json_object_object_get_ex(container, "app_id", &app_id) && + json_object_get_string(app_id) != NULL) + { free(ws->window.application); ws->window.application = strdup(json_object_get_string(app_id)); LOG_DBG("application: \"%s\", via 'app_id'", ws->window.application); } /* If PID has changed, update application name from /proc//comm */ - else if (json_object_object_get_ex(container, "pid", &pid) && ws->window.pid != json_object_get_int(pid)) { + else if (json_object_object_get_ex(container, "pid", &pid) && + ws->window.pid != json_object_get_int(pid)) + { ws->window.pid = json_object_get_int(pid); char path[64]; snprintf(path, sizeof(path), "/proc/%u/comm", ws->window.pid); - int fd = open(path, O_RDONLY | O_CLOEXEC); + int fd = open(path, O_RDONLY); if (fd == -1) { /* Application may simply have terminated */ - free(ws->window.application); - ws->window.application = NULL; + free(ws->window.application); ws->window.application = NULL; ws->window.pid = -1; m->dirty = true; @@ -693,7 +503,7 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m } static bool -handle_mode_event(int sock, int type, const struct json_object *json, void *_mod) +handle_mode_event(int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -735,7 +545,7 @@ run(struct module *mod) if (!i3_get_socket_address(&addr)) return 1; - int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { LOG_ERRNO("failed to create UNIX socket"); return 1; @@ -748,27 +558,6 @@ run(struct module *mod) return 1; } - struct private *m = mod->private; - for (size_t i = 0; i < m->persistent_count; i++) { - const char *name_as_string = m->persistent_workspaces[i]; - - int name_as_int = workspace_name_as_int(name_as_string); - if (m->strip_workspace_numbers) { - const char *colon = strchr(name_as_string, ':'); - if (colon != NULL) - name_as_string = colon++; - } - - struct workspace ws = { - .id = -1, - .name = strdup(name_as_string), - .name_as_int = name_as_int, - .persistent = true, - .empty = true, - }; - workspace_add(m, ws); - } - i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_VERSION, NULL); i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[\"workspace\", \"window\", \"mode\"]"); i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL); @@ -800,11 +589,7 @@ destroy(struct module *mod) } free(m->ws_content.v); - workspaces_free(m, true); - - for (size_t i = 0; i < m->persistent_count; i++) - free(m->persistent_workspaces[i]); - free(m->persistent_workspaces); + workspaces_free(m); free(m->mode); free(m); @@ -823,12 +608,6 @@ ws_content_for_name(struct private *m, const char *name) return NULL; } -static const char * -description(const struct module *mod) -{ - return "i3/sway"; -} - static struct exposable * content(struct module *mod) { @@ -840,47 +619,30 @@ content(struct module *mod) struct exposable *particles[tll_length(m->workspaces) + 1]; struct exposable *current = NULL; - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { struct workspace *ws = &it->item; const struct ws_content *template = NULL; /* Lookup content template for workspace. Fall back to default * template if this workspace doesn't have a specific * template */ - if (ws->name == NULL) { - LOG_ERR("%d %d", ws->name_as_int, ws->id); - } template = ws_content_for_name(m, ws->name); if (template == NULL) { LOG_DBG("no ws template for %s, using default template", ws->name); template = ws_content_for_name(m, ""); } - const char *state = ws->urgent ? "urgent" : ws->visible ? ws->focused ? "focused" : "unfocused" : "invisible"; - - LOG_DBG("name=%s (name-as-int=%d): visible=%s, focused=%s, urgent=%s, empty=%s, state=%s, " - "application=%s, title=%s, mode=%s", - ws->name, ws->name_as_int, ws->visible ? "yes" : "no", ws->focused ? "yes" : "no", - ws->urgent ? "yes" : "no", ws->empty ? "yes" : "no", state, ws->window.application, ws->window.title, - m->mode); - - const char *name = ws->name; - - if (m->strip_workspace_numbers) { - const char *colon = strchr(name, ':'); - if (colon != NULL) - name = colon + 1; - } + const char *state = + ws->urgent ? "urgent" : + ws->visible ? ws->focused ? "focused" : "unfocused" : + "invisible"; struct tag_set tags = { .tags = (struct tag *[]){ - tag_new_string(mod, "name", name), - tag_new_string(mod, "output", ws->output), + tag_new_string(mod, "name", ws->name), tag_new_bool(mod, "visible", ws->visible), tag_new_bool(mod, "focused", ws->focused), tag_new_bool(mod, "urgent", ws->urgent), - tag_new_bool(mod, "empty", ws->empty), tag_new_string(mod, "state", state), tag_new_string(mod, "application", ws->window.application), @@ -888,7 +650,7 @@ content(struct module *mod) tag_new_string(mod, "mode", m->mode), }, - .count = 10, + .count = 8, }; if (ws->focused) { @@ -898,9 +660,12 @@ content(struct module *mod) } if (template == NULL) { - LOG_WARN("no ws template for %s, and no default template available", ws->name); + LOG_WARN( + "no ws template for %s, and no default template available", + ws->name); } else { - particles[particle_count++] = template->content->instantiate(template->content, &tags); + particles[particle_count++] = template->content->instantiate( + template->content, &tags); } tag_set_destroy(&tags); @@ -910,7 +675,8 @@ content(struct module *mod) particles[particle_count++] = current; mtx_unlock(&mod->lock); - return dynlist_exposable_new(particles, particle_count, m->left_spacing, m->right_spacing); + return dynlist_exposable_new( + particles, particle_count, m->left_spacing, m->right_spacing); } /* Maps workspace name to a content particle. */ @@ -920,9 +686,8 @@ struct i3_workspaces { }; static struct module * -i3_new(struct i3_workspaces workspaces[], size_t workspace_count, int left_spacing, int right_spacing, - enum sort_mode sort_mode, size_t persistent_count, const char *persistent_workspaces[static persistent_count], - bool strip_workspace_numbers) +i3_new(struct i3_workspaces workspaces[], size_t workspace_count, + int left_spacing, int right_spacing, enum sort_mode sort_mode) { struct private *m = calloc(1, sizeof(*m)); @@ -938,21 +703,13 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count, int left_spaci m->ws_content.v[i].content = workspaces[i].content; } - m->strip_workspace_numbers = strip_workspace_numbers; m->sort_mode = sort_mode; - m->persistent_count = persistent_count; - m->persistent_workspaces = calloc(persistent_count, sizeof(m->persistent_workspaces[0])); - - for (size_t i = 0; i < persistent_count; i++) - m->persistent_workspaces[i] = strdup(persistent_workspaces[i]); - struct module *mod = module_common_new(); mod->private = m; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -964,55 +721,50 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *sort = yml_get_value(node, "sort"); - const struct yml_node *persistent = yml_get_value(node, "persistent"); - const struct yml_node *strip_workspace_number = yml_get_value(node, "strip-workspace-numbers"); - int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) - : right_spacing != NULL ? yml_value_as_int(right_spacing) - : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : + left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) : + right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; const char *sort_value = sort != NULL ? yml_value_as_string(sort) : NULL; - enum sort_mode sort_mode = sort_value == NULL ? SORT_NONE - : strcmp(sort_value, "none") == 0 ? SORT_NONE - : strcmp(sort_value, "native") == 0 ? SORT_NATIVE - : strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING - : SORT_DESCENDING; - - const size_t persistent_count = persistent != NULL ? yml_list_length(persistent) : 0; - const char *persistent_workspaces[persistent_count]; - - if (persistent != NULL) { - size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(persistent); it.node != NULL; yml_list_next(&it), idx++) { - persistent_workspaces[idx] = yml_value_as_string(it.node); - } - } + enum sort_mode sort_mode = + sort_value == NULL ? SORT_NONE : + strcmp(sort_value, "none") == 0 ? SORT_NONE : + strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING : SORT_DESCENDING; struct i3_workspaces workspaces[yml_dict_length(c)]; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(c); it.key != NULL; yml_dict_next(&it), idx++) { + for (struct yml_dict_iter it = yml_dict_iter(c); + it.key != NULL; + yml_dict_next(&it), idx++) + { workspaces[idx].name = yml_value_as_string(it.key); workspaces[idx].content = conf_to_particle(it.value, inherited); } - return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, persistent_count, persistent_workspaces, - (strip_workspace_number != NULL ? yml_value_as_bool(strip_workspace_number) : false)); + return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode); } static bool verify_content(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node)) { - LOG_ERR("%s: must be a dictionary of workspace-name: particle mappings", conf_err_prefix(chain, node)); + LOG_ERR( + "%s: must be a dictionary of workspace-name: particle mappings", + conf_err_prefix(chain, node)); return false; } - for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) { + for (struct yml_dict_iter it = yml_dict_iter(node); + it.key != NULL; + yml_dict_next(&it)) + { const char *key = yml_value_as_string(it.key); if (key == NULL) { - LOG_ERR("%s: key must be a string (a i3 workspace name)", conf_err_prefix(chain, it.key)); + LOG_ERR("%s: key must be a string (a i3 workspace name)", + conf_err_prefix(chain, it.key)); return false; } @@ -1028,25 +780,18 @@ verify_content(keychain_t *chain, const struct yml_node *node) static bool verify_sort(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum(chain, node, (const char *[]){"none", "native", "ascending", "descending"}, 4); -} - -static bool -verify_persistent(keychain_t *chain, const struct yml_node *node) -{ - return conf_verify_list(chain, node, &conf_verify_string); + return conf_verify_enum( + chain, node, (const char *[]){"none", "ascending", "descending"}, 3); } static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, + {"spacing", false, &conf_verify_int}, + {"left-spacing", false, &conf_verify_int}, + {"right-spacing", false, &conf_verify_int}, {"sort", false, &verify_sort}, - {"persistent", false, &verify_persistent}, - {"strip-workspace-numbers", false, &conf_verify_bool}, {"content", true, &verify_content}, {"anchors", false, NULL}, {NULL, false, NULL}, @@ -1061,5 +806,5 @@ const struct module_iface module_i3_iface = { }; #if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_i3_iface"))); +extern const struct module_iface iface __attribute__((weak, alias("module_i3_iface"))) ; #endif diff --git a/modules/label.c b/modules/label.c index 5f1f158..a29b6bd 100644 --- a/modules/label.c +++ b/modules/label.c @@ -1,14 +1,16 @@ -#include #include +#include #include -#include "../config-verify.h" #include "../config.h" +#include "../config-verify.h" #include "../module.h" #include "../plugin.h" -struct private { struct particle *label; }; +struct private { + struct particle *label; +}; static void destroy(struct module *mod) @@ -19,12 +21,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "label"; -} - static struct exposable * content(struct module *mod) { @@ -49,7 +45,6 @@ label_new(struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } diff --git a/modules/mem.c b/modules/mem.c deleted file mode 100644 index de4e133..0000000 --- a/modules/mem.c +++ /dev/null @@ -1,200 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_MODULE "mem" -#define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" -#include "../log.h" -#include "../plugin.h" - -static const long min_poll_interval = 250; - -struct private -{ - struct particle *label; - uint16_t interval; - uint64_t mem_free; - uint64_t mem_total; -}; - -static void -destroy(struct module *mod) -{ - struct private *m = mod->private; - m->label->destroy(m->label); - free(m); - module_default_destroy(mod); -} - -static const char * -description(const struct module *mod) -{ - return "mem"; -} - -static bool -get_mem_stats(uint64_t *mem_free, uint64_t *mem_total) -{ - bool mem_total_found = false; - bool mem_free_found = false; - - FILE *fp = NULL; - char *line = NULL; - size_t len = 0; - ssize_t read = 0; - - fp = fopen("/proc/meminfo", "re"); - if (NULL == fp) { - LOG_ERRNO("unable to open /proc/meminfo"); - return false; - } - - while ((read = getline(&line, &len, fp)) != -1) { - if (strncmp(line, "MemTotal:", sizeof("MemTotal:") - 1) == 0) { - read = sscanf(line + sizeof("MemTotal:") - 1, "%" SCNu64, mem_total); - mem_total_found = (read == 1); - } - if (strncmp(line, "MemAvailable:", sizeof("MemAvailable:") - 1) == 0) { - read = sscanf(line + sizeof("MemAvailable:"), "%" SCNu64, mem_free); - mem_free_found = (read == 1); - } - } - free(line); - - fclose(fp); - - return mem_free_found && mem_total_found; -} - -static struct exposable * -content(struct module *mod) -{ - const struct private *p = mod->private; - - mtx_lock(&mod->lock); - - const uint64_t mem_free = p->mem_free; - const uint64_t mem_total = p->mem_total; - const uint64_t mem_used = mem_total - mem_free; - - double percent_used = ((double)mem_used * 100) / (mem_total + 1); - double percent_free = ((double)mem_free * 100) / (mem_total + 1); - - struct tag_set tags = { - .tags = (struct tag *[]){tag_new_int(mod, "free", mem_free * 1024), tag_new_int(mod, "used", mem_used * 1024), - tag_new_int(mod, "total", mem_total * 1024), - tag_new_int_range(mod, "percent_free", round(percent_free), 0, 100), - tag_new_int_range(mod, "percent_used", round(percent_used), 0, 100)}, - .count = 5, - }; - - struct exposable *exposable = p->label->instantiate(p->label, &tags); - tag_set_destroy(&tags); - mtx_unlock(&mod->lock); - return exposable; -} - -static int -run(struct module *mod) -{ - const struct bar *bar = mod->bar; - bar->refresh(bar); - struct private *p = mod->private; - while (true) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - - int res = poll(fds, 1, p->interval); - if (res < 0) { - if (EINTR == errno) { - continue; - } - - LOG_ERRNO("unable to poll abort fd"); - return -1; - } - - if (fds[0].revents & POLLIN) - break; - - mtx_lock(&mod->lock); - p->mem_free = 0; - p->mem_total = 0; - if (!get_mem_stats(&p->mem_free, &p->mem_total)) { - LOG_ERR("unable to retrieve the memory stats"); - } - mtx_unlock(&mod->lock); - bar->refresh(bar); - } - - return 0; -} - -static struct module * -mem_new(uint16_t interval, struct particle *label) -{ - struct private *p = calloc(1, sizeof(*p)); - p->label = label; - p->interval = interval; - - struct module *mod = module_common_new(); - mod->private = p; - mod->run = &run; - mod->destroy = &destroy; - mod->content = &content; - mod->description = &description; - return mod; -} - -static struct module * -from_conf(const struct yml_node *node, struct conf_inherit inherited) -{ - const struct yml_node *interval = yml_get_value(node, "poll-interval"); - const struct yml_node *c = yml_get_value(node, "content"); - - return mem_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited)); -} - -static bool -conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_unsigned(chain, node)) - return false; - - if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); - return false; - } - - return true; -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"poll-interval", false, &conf_verify_poll_interval}, - MODULE_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_mem_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_mem_iface"))); -#endif diff --git a/modules/meson.build b/modules/meson.build index f6d53d8..5b480d7 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -2,157 +2,35 @@ module_sdk = declare_dependency(dependencies: [pixman, threads, tllist, fcft]) modules = [] -# Optional deps -alsa = dependency('alsa', required: get_option('plugin-alsa')) -plugin_alsa_enabled = alsa.found() - -udev_backlight = dependency('libudev', required: get_option('plugin-backlight')) -plugin_backlight_enabled = udev_backlight.found() - -udev_battery = dependency('libudev', required: get_option('plugin-battery')) -plugin_battery_enabled = udev_battery.found() - -plugin_clock_enabled = get_option('plugin-clock').allowed() -plugin_cpu_enabled = get_option('plugin-cpu').allowed() -plugin_disk_io_enabled = get_option('plugin-disk-io').allowed() -plugin_dwl_enabled = get_option('plugin-dwl').allowed() -plugin_foreign_toplevel_enabled = backend_wayland and get_option('plugin-foreign-toplevel').allowed() -plugin_mem_enabled = get_option('plugin-mem').allowed() - -mpd = dependency('libmpdclient', required: get_option('plugin-mpd')) -plugin_mpd_enabled = mpd.found() - -# DBus dependency. Used by 'mpris' -sdbus_library = dependency('libsystemd', 'libelogind', 'basu', required: get_option('plugin-mpris')) -plugin_mpris_enabled = sdbus_library.found() - -json_i3 = dependency('json-c', required: get_option('plugin-i3')) -plugin_i3_enabled = json_i3.found() - -plugin_label_enabled = get_option('plugin-label').allowed() -plugin_network_enabled = get_option('plugin-network').allowed() - -pipewire = dependency('libpipewire-0.3', required: get_option('plugin-pipewire')) -json_pipewire = dependency('json-c', required: get_option('plugin-pipewire')) -plugin_pipewire_enabled = pipewire.found() and json_pipewire.found() - -pulse = dependency('libpulse', required: get_option('plugin-pulse')) -plugin_pulse_enabled = pulse.found() - -udev_removables = dependency('libudev', required: get_option('plugin-removables')) -plugin_removables_enabled = udev_removables.found() - -plugin_river_enabled = backend_wayland and get_option('plugin-river').allowed() - -plugin_script_enabled = get_option('plugin-script').allowed() - -json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb')) -plugin_sway_xkb_enabled = json_sway_xkb.found() - -json_niri_language = dependency('json-c', required: get_option('plugin-niri-language')) -plugin_niri_language_enabled = json_niri_language.found() - -json_niri_workspaces = dependency('json-c', required: get_option('plugin-niri-workspaces')) -plugin_niri_workspaces_enabled = json_niri_workspaces.found() - -xcb_xkb = dependency('xcb-xkb', required: get_option('plugin-xkb')) -plugin_xkb_enabled = backend_x11 and xcb_xkb.found() - -plugin_xwindow_enabled = backend_x11 and get_option('plugin-xwindow').allowed() +alsa = dependency('alsa') +udev = dependency('libudev') +json = dependency('json-c') +mpd = dependency('libmpdclient') +xcb_xkb = dependency('xcb-xkb', required: get_option('backend-x11')) # Module name -> (source-list, dep-list) -mod_data = {} +mod_data = { + 'alsa': [[], [m, alsa]], + 'backlight': [[], [m, udev]], + 'battery': [[], [udev]], + 'clock': [[], []], + 'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json]], + 'label': [[], []], + 'mpd': [[], [mpd]], + 'network': [[], []], + 'removables': [[], [dynlist, udev]], + 'script': [[], []], + 'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]], +} -if plugin_alsa_enabled - mod_data += {'alsa': [[], [m, alsa]]} +if backend_x11 + mod_data += { + 'xkb': [[], [xcb_stuff, xcb_xkb]], + 'xwindow': [[], [xcb_stuff]], + } endif -if plugin_backlight_enabled - mod_data += {'backlight': [[], [m, udev_backlight]]} -endif - -if plugin_battery_enabled - mod_data += {'battery': [[], [udev_battery]]} -endif - -if plugin_clock_enabled - mod_data += {'clock': [[], []]} -endif - -if plugin_cpu_enabled - mod_data += {'cpu': [[], [m, dynlist]]} -endif - -if plugin_disk_io_enabled - mod_data += {'disk-io': [[], [dynlist]]} -endif - -if plugin_dwl_enabled - mod_data += {'dwl': [[], [dynlist]]} -endif - -if plugin_mem_enabled - mod_data += {'mem': [[], [m]]} -endif - -if plugin_mpd_enabled - mod_data += {'mpd': [[], [mpd]]} -endif - -if plugin_mpris_enabled - sdbus = declare_dependency(compile_args: ['-DHAVE_' + sdbus_library.name().to_upper()], dependencies:[sdbus_library]) - mod_data += {'mpris': [[], [sdbus]]} -endif - -if plugin_i3_enabled - mod_data += {'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json_i3]]} -endif - -if plugin_label_enabled - mod_data += {'label': [[], []]} -endif - -if plugin_network_enabled - mod_data += {'network': [[], [dynlist]]} -endif - -if plugin_pipewire_enabled - mod_data += {'pipewire': [[], [m, pipewire, dynlist, json_pipewire]]} -endif - -if plugin_pulse_enabled - mod_data += {'pulse': [[], [m, pulse]]} -endif - -if plugin_removables_enabled - mod_data += {'removables': [[], [dynlist, udev_removables]]} -endif - -if plugin_script_enabled - mod_data += {'script': [[], []]} -endif - -if plugin_sway_xkb_enabled - mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]} -endif - -if plugin_niri_language_enabled - mod_data += {'niri-language': [['niri-common.c', 'niri-common.h'], [dynlist, json_niri_language]]} -endif - -if plugin_niri_workspaces_enabled - mod_data += {'niri-workspaces': [['niri-common.c', 'niri-common.h'], [dynlist, json_niri_workspaces]]} -endif - -if plugin_xkb_enabled - mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]} -endif - -if plugin_xwindow_enabled - mod_data += {'xwindow': [[], [xcb_stuff]]} -endif - -if plugin_river_enabled +if backend_wayland river_proto_headers = [] river_proto_src = [] @@ -171,29 +49,9 @@ if plugin_river_enabled command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach - mod_data += {'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist, wayland_client]]} -endif - -if plugin_foreign_toplevel_enabled - ftop_proto_headers = [] - ftop_proto_src = [] - - foreach prot : ['../external/wlr-foreign-toplevel-management-unstable-v1.xml'] - - ftop_proto_headers += custom_target( - prot.underscorify() + '-client-header', - output: '@BASENAME@.h', - input: prot, - command: [wscanner_prog, 'client-header', '@INPUT@', '@OUTPUT@']) - - ftop_proto_src += custom_target( - prot.underscorify() + '-private-code', - output: '@BASENAME@.c', - input: prot, - command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) - endforeach - - mod_data += {'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [m, dynlist, wayland_client]]} + mod_data += { + 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist]], + } endif foreach mod, data : mod_data diff --git a/modules/mpd.c b/modules/mpd.c index e70e41f..a501f76 100644 --- a/modules/mpd.c +++ b/modules/mpd.c @@ -1,34 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include #include -#include -#include -#include #include #define LOG_MODULE "mpd" #define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../plugin.h" -struct private -{ +struct private { char *host; uint16_t port; struct particle *label; @@ -39,12 +38,10 @@ struct private bool repeat; bool random; bool consume; - bool single; - int volume; + int volume; char *album; char *artist; char *title; - char *file; struct { uint64_t value; @@ -62,9 +59,11 @@ destroy(struct module *mod) struct private *m = mod->private; if (m->refresh_thread_id != 0) { assert(m->refresh_abort_fd != -1); - if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { + if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) + != sizeof(uint64_t)) + { LOG_ERRNO("failed to signal abort to refresher thread"); - } else { + } else{ int res; thrd_join(m->refresh_thread_id, &res); } @@ -76,7 +75,6 @@ destroy(struct module *mod) free(m->album); free(m->artist); free(m->title); - free(m->file); assert(m->conn == NULL); m->label->destroy(m->label); @@ -85,12 +83,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "mpd"; -} - static uint64_t timespec_diff_milli_seconds(const struct timespec *a, const struct timespec *b) { @@ -132,11 +124,12 @@ content(struct module *mod) if (m->state == MPD_STATE_PLAY) { elapsed += timespec_diff_milli_seconds(&now, &m->elapsed.when); if (elapsed > m->duration) { - LOG_DBG("dynamic update of elapsed overflowed: " - "elapsed=%" PRIu64 ", duration=%" PRIu64, - elapsed, m->duration); + LOG_DBG( + "dynamic update of elapsed overflowed: " + "elapsed=%"PRIu64", duration=%"PRIu64, elapsed, m->duration); elapsed = m->duration; } + } unsigned elapsed_secs = elapsed / 1000; @@ -153,23 +146,16 @@ content(struct module *mod) state_str = "offline"; else { switch (m->state) { - case MPD_STATE_UNKNOWN: - state_str = "unknown"; - break; - case MPD_STATE_STOP: - state_str = "stopped"; - break; - case MPD_STATE_PAUSE: - state_str = "paused"; - break; - case MPD_STATE_PLAY: - state_str = "playing"; - break; + case MPD_STATE_UNKNOWN: state_str = "unknown"; break; + case MPD_STATE_STOP: state_str = "stopped"; break; + case MPD_STATE_PAUSE: state_str = "paused"; break; + case MPD_STATE_PLAY: state_str = "playing"; break; } } /* Tell particle to real-time track? */ - enum tag_realtime_unit realtime = m->state == MPD_STATE_PLAY ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; + enum tag_realtime_unit realtime = m->state == MPD_STATE_PLAY + ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; struct tag_set tags = { .tags = (struct tag *[]){ @@ -177,19 +163,17 @@ content(struct module *mod) tag_new_bool(mod, "repeat", m->repeat), tag_new_bool(mod, "random", m->random), tag_new_bool(mod, "consume", m->consume), - tag_new_bool(mod, "single", m->single), tag_new_int_range(mod, "volume", m->volume, 0, 100), tag_new_string(mod, "album", m->album), tag_new_string(mod, "artist", m->artist), tag_new_string(mod, "title", m->title), - tag_new_string(mod, "file", m->file), tag_new_string(mod, "pos", pos), tag_new_string(mod, "end", end), tag_new_int(mod, "duration", m->duration), tag_new_int_realtime( mod, "elapsed", elapsed, 0, m->duration, realtime), }, - .count = 14, + .count = 12, }; mtx_unlock(&mod->lock); @@ -233,7 +217,7 @@ wait_for_socket_create(const struct module *mod) struct stat st; if (stat(m->host, &st) == 0 && S_ISSOCK(st.st_mode)) { - int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + int s = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_un addr = {.sun_family = AF_UNIX}; strncpy(addr.sun_path, m->host, sizeof(addr.sun_path) - 1); @@ -244,7 +228,8 @@ wait_for_socket_create(const struct module *mod) LOG_DBG("%s: already exists, and is connectable", m->host); have_mpd_socket = true; } else { - LOG_DBG("%s: already exists, but isn't connectable: %s", m->host, strerror(errno)); + LOG_DBG("%s: already exists, but isn't connectable: %s", + m->host, strerror(errno)); } close(s); @@ -255,15 +240,12 @@ wait_for_socket_create(const struct module *mod) bool ret = false; while (!have_mpd_socket) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = fd, .events = POLLIN}}; + struct pollfd fds[] = { + {.fd = mod->abort_fd, .events = POLLIN}, + {.fd = fd, .events = POLLIN} + }; - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } + poll(fds, 2, -1); if (fds[0].revents & POLLIN) { ret = true; @@ -275,7 +257,7 @@ wait_for_socket_create(const struct module *mod) char buf[1024]; ssize_t len = read(fd, buf, sizeof(buf)); - for (const char *ptr = buf; ptr < buf + len;) { + for (const char *ptr = buf; ptr < buf + len; ) { const struct inotify_event *e = (const struct inotify_event *)ptr; LOG_DBG("inotify: CREATED: %s/%.*s", directory, e->len, e->name); @@ -285,7 +267,7 @@ wait_for_socket_create(const struct module *mod) break; } - ptr += sizeof(*e) + e->len; + ptr += sizeof(*e) + e->len; } } @@ -308,7 +290,8 @@ connect_to_mpd(const struct module *mod) enum mpd_error merr = mpd_connection_get_error(conn); if (merr != MPD_ERROR_SUCCESS) { - LOG_WARN("failed to connect to MPD: %s", mpd_connection_get_error_message(conn)); + LOG_WARN("failed to connect to MPD: %s", + mpd_connection_get_error_message(conn)); mpd_connection_free(conn); return NULL; } @@ -326,7 +309,8 @@ update_status(struct module *mod) struct mpd_status *status = mpd_run_status(m->conn); if (status == NULL) { - LOG_ERR("failed to get status: %s", mpd_connection_get_error_message(m->conn)); + LOG_ERR("failed to get status: %s", + mpd_connection_get_error_message(m->conn)); return false; } @@ -338,7 +322,6 @@ update_status(struct module *mod) m->repeat = mpd_status_get_repeat(status); m->random = mpd_status_get_random(status); m->consume = mpd_status_get_consume(status); - m->single = mpd_status_get_single_state(status) == MPD_SINGLE_ONESHOT; m->volume = mpd_status_get_volume(status); m->duration = mpd_status_get_total_time(status) * 1000; m->elapsed.value = mpd_status_get_elapsed_ms(status); @@ -349,37 +332,30 @@ update_status(struct module *mod) struct mpd_song *song = mpd_run_current_song(m->conn); if (song == NULL && mpd_connection_get_error(m->conn) != MPD_ERROR_SUCCESS) { - LOG_ERR("failed to get current song: %s", mpd_connection_get_error_message(m->conn)); + LOG_ERR("failed to get current song: %s", + mpd_connection_get_error_message(m->conn)); return false; } if (song == NULL) { mtx_lock(&mod->lock); - free(m->album); - m->album = NULL; - free(m->artist); - m->artist = NULL; - free(m->title); - m->title = NULL; - free(m->file); - m->file = NULL; + free(m->album); m->album = NULL; + free(m->artist); m->artist = NULL; + free(m->title); m->title = NULL; mtx_unlock(&mod->lock); } else { const char *album = mpd_song_get_tag(song, MPD_TAG_ALBUM, 0); const char *artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0); const char *title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0); - const char *file = mpd_song_get_uri(song); mtx_lock(&mod->lock); free(m->album); free(m->artist); free(m->title); - free(m->file); m->album = album != NULL ? strdup(album) : NULL; m->artist = artist != NULL ? strdup(artist) : NULL; m->title = title != NULL ? strdup(title) : NULL; - m->file = file != NULL ? strdup(file) : NULL; mtx_unlock(&mod->lock); mpd_song_free(song); @@ -395,9 +371,8 @@ run(struct module *mod) struct private *m = mod->private; bool aborted = false; - int ret = 0; - while (!aborted && ret == 0) { + while (!aborted) { if (m->conn != NULL) { mpd_connection_free(m->conn); @@ -406,21 +381,16 @@ run(struct module *mod) /* Reset state */ mtx_lock(&mod->lock); - free(m->album); - m->album = NULL; - free(m->artist); - m->artist = NULL; - free(m->title); - m->title = NULL; - free(m->file); - m->file = NULL; + free(m->album); m->album = NULL; + free(m->artist); m->artist = NULL; + free(m->title); m->title = NULL; m->state = MPD_STATE_UNKNOWN; m->elapsed.value = m->duration = 0; m->elapsed.when.tv_sec = m->elapsed.when.tv_nsec = 0; mtx_unlock(&mod->lock); /* Keep trying to connect, until we succeed */ - while (!aborted && ret == 0) { + while (!aborted) { if (m->port == 0) { /* Use inotify to watch for socket creation */ aborted = wait_for_socket_create(mod); @@ -438,33 +408,16 @@ run(struct module *mod) * host), wait for a while until we try to re-connect * again. */ - while (!aborted) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - int res = poll(fds, sizeof(fds) / sizeof(fds[0]), 2 * 1000); - - if (res < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - ret = 1; - break; - } - - if (res == 0) { - ret = 0; - break; - } - - else if (res == 1) { - assert(fds[0].revents & POLLIN); - aborted = true; - } + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; + int res = poll(fds, 1, 10 * 1000); + if (res == 1) { + assert(fds[0].revents & POLLIN); + aborted = true; } } - if (aborted || ret != 0) + if (aborted) break; /* Initial state (after establishing a connection) */ @@ -482,18 +435,12 @@ run(struct module *mod) }; if (!mpd_send_idle(m->conn)) { - LOG_ERR("failed to send IDLE command: %s", mpd_connection_get_error_message(m->conn)); + LOG_ERR("failed to send IDLE command: %s", + mpd_connection_get_error_message(m->conn)); break; } - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - ret = 1; - break; - } + poll(fds, 2, -1); if (fds[0].revents & POLLIN) { aborted = true; @@ -506,7 +453,8 @@ run(struct module *mod) } if (fds[1].revents & POLLIN) { - enum mpd_idle idle __attribute__((unused)) = mpd_recv_idle(m->conn, true); + enum mpd_idle idle __attribute__ ((unused)) = + mpd_recv_idle(m->conn, true); LOG_DBG("IDLE mask: %d", idle); @@ -523,7 +471,7 @@ run(struct module *mod) m->conn = NULL; } - return aborted ? 0 : ret; + return 0; } struct refresh_context { @@ -578,7 +526,9 @@ refresh_in(struct module *mod, long milli_seconds) /* Signal abort to thread */ assert(m->refresh_abort_fd != -1); - if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { + if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) + != sizeof(uint64_t)) + { LOG_ERRNO("failed to signal abort to refresher thread"); return false; } @@ -618,7 +568,7 @@ refresh_in(struct module *mod, long milli_seconds) } /* Detach - we don't want to have to thrd_join() it */ - // thrd_detach(tid); + //thrd_detach(tid); return r == 0; } @@ -638,7 +588,6 @@ 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; } @@ -649,8 +598,10 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *port = yml_get_value(node, "port"); const struct yml_node *c = yml_get_value(node, "content"); - return mpd_new(yml_value_as_string(host), port != NULL ? yml_value_as_int(port) : 0, - conf_to_particle(c, inherited)); + return mpd_new( + yml_value_as_string(host), + port != NULL ? yml_value_as_int(port) : 0, + conf_to_particle(c, inherited)); } static bool @@ -658,7 +609,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"host", true, &conf_verify_string}, - {"port", false, &conf_verify_unsigned}, + {"port", false, &conf_verify_int}, MODULE_COMMON_ATTRS, }; diff --git a/modules/mpris.c b/modules/mpris.c deleted file mode 100644 index 5ddf6e0..0000000 --- a/modules/mpris.c +++ /dev/null @@ -1,1100 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define LOG_MODULE "mpris" -#define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" -#include "../log.h" -#include "../plugin.h" - -#include "dbus.h" -#include "yml.h" - -#define is_empty_string(str) ((str) == NULL || (str)[0] == '\0') - -#define DEFAULT_QUERY_TIMEOUT_MS (500 * 1000) - -#define MPRIS_PATH "/org/mpris/MediaPlayer2" -#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2" -#define MPRIS_SERVICE "org.mpris.MediaPlayer2" -#define MPRIS_INTERFACE_PLAYER "org.mpris.MediaPlayer2.Player" - -#define DBUS_PATH "/org/freedesktop/DBus" -#define DBUS_BUS_NAME "org.freedesktop.DBus" -#define DBUS_SERVICE "org.freedesktop.DBus" -#define DBUS_INTERFACE_MONITORING "org.freedesktop.DBus.Monitoring" -#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" - -enum status { - STATUS_OFFLINE, - STATUS_PLAYING, - STATUS_PAUSED, - STATUS_STOPPED, - STATUS_ERROR, -}; - -typedef tll(char *) string_array; - -struct metadata { - uint64_t length_us; - char *trackid; - string_array artists; - char *album; - char *title; -}; - -struct property { - struct metadata metadata; - char *playback_status; - char *loop_status; - uint64_t position_us; - double rate; - double volume; - bool shuffle; -}; - -struct client { - bool has_seeked_support; - enum status status; - const char *bus_name; - const char *bus_unique_name; - - struct property property; - - /* The unix timestamp of the last position change (ie. - * seeking, pausing) */ - struct timespec seeked_when; -}; - -struct context { - const struct private *mpd_config; - - sd_bus *monitor_connection; - sd_bus_message *update_message; - - tll(struct client *) clients; - struct client *current_client; - - bool has_update; -}; - -struct private -{ - thrd_t refresh_thread_id; - int refresh_abort_fd; - - size_t timeout_ms; - string_array identity_list; - struct context context; - struct particle *label; -}; - -#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG -static void __attribute__((unused)) -debug_print_argument_type(sd_bus_message *message) -{ - char type; - const char *content; - sd_bus_message_peek_type(message, &type, &content); - LOG_DBG("argument type: %c -> %s", type, content); -} -#endif - -static void -metadata_clear(struct metadata *metadata) -{ - tll_free_and_free(metadata->artists, free); - - if (metadata->album != NULL) { - free(metadata->album); - metadata->album = NULL; - } - - if (metadata->title != NULL) { - free(metadata->title); - metadata->title = NULL; - } - - if (metadata->trackid != NULL) { - free(metadata->trackid); - metadata->trackid = NULL; - } -} - -static void -client_free(struct client *client) -{ - free((void *)client->bus_name); - free((void *)client->bus_unique_name); - free(client); -} - -static void -client_free_by_unique_name(struct context *context, const char *unique_name) -{ - tll_foreach(context->clients, it) - { - struct client *client = it->item; - if (strcmp(client->bus_unique_name, unique_name) == 0) { - LOG_DBG("client_remove: Removing client %s", client->bus_name); - - client_free(client); - tll_remove(context->clients, it); - } - } -} - -static void -client_add(struct context *context, const char *name, const char *unique_name) -{ - struct client *client = malloc(sizeof(*client)); - (*client) = (struct client){ - .bus_name = strdup(name), - .bus_unique_name = strdup(unique_name), - }; - - tll_push_back(context->clients, client); - LOG_DBG("client_add: name='%s' unique_name='%s'", name, unique_name); -} - -static struct client * -client_lookup_by_unique_name(struct context *context, const char *unique_name) -{ - tll_foreach(context->clients, it) - { - struct client *client = it->item; - if (strcmp(client->bus_unique_name, unique_name) == 0) { - LOG_DBG("client_lookup: name: %s", client->bus_name); - return client; - } - } - - return NULL; -} - -static void -client_change_unique_name(struct client *client, const char *new_name) -{ - if (client->bus_unique_name != NULL) { - free((void *)client->bus_unique_name); - } - - client->bus_unique_name = strdup(new_name); -} - -static bool -verify_bus_name(const string_array *identity_list, const char *name) -{ - tll_foreach(*identity_list, it) - { - const char *ident = it->item; - - if (strlen(name) < strlen(MPRIS_BUS_NAME ".") + strlen(ident)) { - continue; - } - - const char *cmp = name + strlen(MPRIS_BUS_NAME "."); - if (strncmp(cmp, ident, strlen(ident)) != 0) { - continue; - } - - return true; - } - - return false; -} - -static bool -read_string_array(sd_bus_message *message, string_array *list) -{ - int status = 0; - - /* message argument layout: 'vas' */ - /* enter variant */ - status = sd_bus_message_enter_container(message, SD_BUS_TYPE_VARIANT, "as"); - if (status <= 0) { - LOG_DBG("unexpected layout: errno=%d (%s)", status, strerror(-status)); - return false; - } - - /* enter array */ - status = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "s"); - assert(status >= 0); - - const char *string; - while ((status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &string)) > 0) { - if (!is_empty_string(string)) { - tll_push_back(*list, strdup(string)); - } - } - - if (status < 0) { - LOG_ERR("metadata: unexpected layout: errno=%d (%s)", status, strerror(-status)); - return false; - } - - /* close array */ - sd_bus_message_exit_container(message); - /* close variant */ - sd_bus_message_exit_container(message); - - return true; -} - -static bool -metadata_parse_property(const char *property_name, sd_bus_message *message, struct metadata *buffer) -{ - int status = 0; - const char *string = NULL; - - char argument_type = 0; - const char *argument_layout = NULL; - sd_bus_message_peek_type(message, &argument_type, &argument_layout); - assert(argument_type == SD_BUS_TYPE_VARIANT); - assert(!is_empty_string(argument_layout)); - - if (strcmp(property_name, "mpris:trackid") == 0) { - if (argument_layout[0] != SD_BUS_TYPE_STRING && argument_layout[0] != SD_BUS_TYPE_OBJECT_PATH) - goto unexpected_type; - - status = sd_bus_message_read(message, "v", argument_layout, &string); - if (status > 0 && !is_empty_string(string)) - buffer->trackid = strdup(string); - - /* FIXME: "strcmp matches both 'album' as well as 'albumArtist'" */ - } else if (strcmp(property_name, "xesam:album") == 0) { - status = sd_bus_message_read(message, "v", argument_layout, &string); - if (status > 0 && !is_empty_string(string)) - buffer->album = strdup(string); - - } else if (strcmp(property_name, "xesam:artist") == 0) { - status = read_string_array(message, &buffer->artists); - - } else if (strcmp(property_name, "xesam:title") == 0) { - status = sd_bus_message_read(message, "v", "s", &string); - if (status > 0 && !is_empty_string(string)) - buffer->title = strdup(string); - - } else if (strcmp(property_name, "mpris:length") == 0) { - /* MPRIS requires 'mpris:length' to be an i64 (the wording is a bit ambiguous), however some client - * use a u64 instead. */ - if (argument_layout[0] != SD_BUS_TYPE_INT64 && argument_layout[0] != SD_BUS_TYPE_UINT64) - goto unexpected_type; - - status = sd_bus_message_read(message, "v", argument_layout, &buffer->length_us); - - } else { - LOG_DBG("metadata: ignoring property: %s", property_name); - sd_bus_message_skip(message, NULL); - return true; - } - - if (status < 0) { - LOG_ERR("metadata: failed to read property: arg_type='%c' arg_layout='%s' errno=%d (%s)", argument_type, - argument_layout, status, strerror(-status)); - return false; - } - - return true; -unexpected_type: - LOG_ERR("metadata: unexpected type for '%s'", property_name); - return false; -} - -static bool -metadata_parse_array(struct metadata *metadata, sd_bus_message *message) -{ - int status = sd_bus_message_enter_container(message, SD_BUS_TYPE_VARIANT, "a{sv}"); - if (status <= 0) { - LOG_DBG("unexpected layout: errno=%d (%s)", status, strerror(-status)); - return false; - } - status = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "{sv}"); - assert(status >= 0); - - while ((status = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *property_name = NULL; - status = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); - if (status <= 0) { - LOG_DBG("unexpected layout: errno=%d (%s)", status, strerror(-status)); - return false; - } - - status = metadata_parse_property(property_name, message, metadata); - if (status == 0) { - return false; - } - - status = sd_bus_message_exit_container(message); - assert(status >= 0); - } - - /* close array */ - sd_bus_message_exit_container(message); - /* close variant */ - sd_bus_message_exit_container(message); - - return status >= 0; -} - -static bool -property_parse(struct property *prop, const char *property_name, sd_bus_message *message) -{ - /* This function is called in two different ways: - * 1. update_status(): The property is passed directly - * 2. update_status_from_message(): The property is passed wrapped - * inside a variant and has to be unpacked */ - const char *argument_layout = NULL; - char argument_type = 0; - int status = sd_bus_message_peek_type(message, &argument_type, &argument_layout); - - assert(status > 0); - assert(argument_type == SD_BUS_TYPE_VARIANT); - assert(!is_empty_string(argument_layout)); - - const char *string; - if (strcmp(property_name, "PlaybackStatus") == 0) { - status = sd_bus_message_read(message, "v", "s", &string); - if (status && !is_empty_string(string)) - prop->playback_status = strdup(string); - - } else if (strcmp(property_name, "LoopStatus") == 0) { - status = sd_bus_message_read(message, "v", "s", &string); - if (status && !is_empty_string(string)) - prop->loop_status = strdup(string); - - } else if (strcmp(property_name, "Position") == 0) { - /* MPRIS requires 'Position' to be a i64, however some client - * use a u64 instead. */ - if (argument_layout[0] != SD_BUS_TYPE_INT64 && argument_layout[0] != SD_BUS_TYPE_UINT64) { - LOG_ERR("property: unexpected type for '%s'", property_name); - return false; - } - status = sd_bus_message_read(message, "v", argument_layout[0], &prop->position_us); - - } else if (strcmp(property_name, "Shuffle") == 0) { - status = sd_bus_message_read(message, "v", "b", &prop->shuffle); - - } else if (strcmp(property_name, "Metadata") == 0) { - metadata_clear(&prop->metadata); - status = metadata_parse_array(&prop->metadata, message); - - } else { - LOG_DBG("property: ignoring property: %s", property_name); - sd_bus_message_skip(message, NULL); - return true; - } - - return status > 0; -} - -/* ------------- */ - -static void -format_usec_timestamp(unsigned usec, char *s, size_t sz) -{ - uint32_t secs = usec / 1000 / 1000; - uint32_t hours = secs / (60 * 60); - uint32_t minutes = secs % (60 * 60) / 60; - secs %= 60; - - if (hours > 0) - snprintf(s, sz, "%02u:%02u:%02u", hours, minutes, secs); - else - snprintf(s, sz, "%02u:%02u", minutes, secs); -} - -static void -destroy(struct module *mod) -{ - struct private *m = mod->private; - - tll_free_and_free(m->context.clients, client_free); - sd_bus_close(m->context.monitor_connection); - - tll_free_and_free(m->identity_list, free); - m->label->destroy(m->label); - free(m); - - module_default_destroy(mod); -} - -static void -context_event_handle_name_owner_changed(sd_bus_message *message, struct context *context) -{ - /* NameOwnerChanged (STRING name, STRING old_owner, STRING new_owner) */ - /* This signal indicates that the owner of a name has changed, ie. - * it was acquired, lost or changed */ - - const char *bus_name = NULL, *old_owner = NULL, *new_owner = NULL; - int status __attribute__((unused)) = sd_bus_message_read(message, "sss", &bus_name, &old_owner, &new_owner); - assert(status > 0); - - LOG_DBG("event_handler: 'NameOwnerChanged': bus_name: '%s' old_owner: '%s' new_ower: '%s'", bus_name, old_owner, - new_owner); - - if (is_empty_string(new_owner) && !is_empty_string(old_owner)) { - /* Target bus has been lost */ - struct client *client = client_lookup_by_unique_name(context, old_owner); - - if (client == NULL) - return; - - LOG_DBG("event_handler: 'NameOwnerChanged': Target bus disappeared: %s", client->bus_name); - client_free_by_unique_name(context, client->bus_unique_name); - - if (context->current_client == client) - context->current_client = NULL; - - return; - } else if (is_empty_string(old_owner) && !is_empty_string(new_owner)) { - /* New unique name registered. Not used */ - return; - } - - /* Name changed */ - assert(!is_empty_string(new_owner)); - assert(!is_empty_string(old_owner)); - - struct client *client = client_lookup_by_unique_name(context, old_owner); - LOG_DBG("'NameOwnerChanged': Name changed from '%s' to '%s' for client '%s'", old_owner, new_owner, - client->bus_name); - client_change_unique_name(client, new_owner); -} - -static void -context_event_handle_name_acquired(sd_bus_message *message, struct context *context) -{ - /* Spy on applications that requested an "MPRIS style" bus name */ - - /* NameAcquired (STRING name) */ - /* " This signal is sent to a specific application when it gains ownership of a name. " */ - const char *name = NULL; - int status __attribute__((unused)) = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &name); - assert(status > 0); - - LOG_DBG("event_handler: 'NameAcquired': name: '%s'", name); - - if (strncmp(name, MPRIS_BUS_NAME, strlen(MPRIS_BUS_NAME)) != 0) { - return; - } - - if (verify_bus_name(&context->mpd_config->identity_list, name)) { - const char *unique_name = sd_bus_message_get_destination(message); - LOG_DBG("'NameAcquired': Acquired new client: %s unique: %s", name, unique_name); - client_add(context, name, unique_name); - } -} - -static int -context_event_handler(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) -{ - struct context *context = userdata; - - const char *member = sd_bus_message_get_member(message); - const char *sender = sd_bus_message_get_sender(message); - const char *path_name = sd_bus_message_get_path(message); - -#if 0 - const char *destination = sd_bus_message_get_destination(message); - const char *self = sd_bus_message_get_sender(message); - LOG_DBG("member: '%s' self: '%s' dest: '%s' sender: '%s'", member, self, - destination, sender); -#endif - - if (tll_length(context->clients) == 0 && strcmp(member, "NameAcquired") != 0) { - return 1; - } - - /* TODO: Allow multiple clients to connect */ - if (strcmp(path_name, DBUS_PATH) == 0 && strcmp(member, "NameAcquired") == 0) { - context_event_handle_name_acquired(message, context); - } - - if (strcmp(path_name, DBUS_PATH) == 0 && strcmp(member, "NameOwnerChanged") == 0) { - context_event_handle_name_owner_changed(message, context); - return 1; - } - - /* Copy the 'PropertiesChanged/Seeked' message, so it can be parsed - * later on */ - if (strcmp(path_name, MPRIS_PATH) == 0 - && (strcmp(member, "PropertiesChanged") == 0 || strcmp(member, "Seeked") == 0)) { - struct client *client = client_lookup_by_unique_name(context, sender); - if (client == NULL) - return 1; - - LOG_DBG("event_handler: '%s': name: '%s' unique_name: '%s'", member, client->bus_name, client->bus_unique_name); - - context->has_update = true; - context->current_client = client; - context->update_message = sd_bus_message_ref(message); - - assert(context->update_message != NULL); - } - - return 1; -} - -static bool -context_process_events(struct context *context, uint32_t timeout_ms) -{ - int status = -1; - - status = sd_bus_wait(context->monitor_connection, timeout_ms); - if (status < 0) { - if (status == -ENOTCONN) - LOG_DBG("Disconnect signal has been processed"); - else - LOG_ERR("Failed to query monitor connection: errno=%d", status); - - return false; - } - - /* 'sd_bus_process' processes one 'action' per call. - * This includes: connection, authentication, message processing */ - status = sd_bus_process(context->monitor_connection, NULL); - - if (status < 0) { - if (status == -ENOTCONN) - LOG_DBG("Disconnect signal has been processed"); - else - LOG_ERR("Failed to query monitor connection: errno=%d", status); - - return false; - } - - return true; -} - -static bool -context_setup(struct context *context) -{ - int status = true; - sd_bus *connection; - if ((status = sd_bus_default_user(&connection)) < 0) { - LOG_ERR("Failed to connect to the desktop bus. errno: %d", status); - return false; - } - - context->monitor_connection = connection; - - /* Turn this connection into a monitor */ - sd_bus_message *message; - status = sd_bus_message_new_method_call(connection, &message, DBUS_SERVICE, DBUS_PATH, DBUS_INTERFACE_MONITORING, - "BecomeMonitor"); - - const char *matching_rules[] = { - /* Listen for... */ - /* ... new MPRIS clients */ - "type='signal',interface='org.freedesktop.DBus',member='NameAcquired',path='/org/freedesktop/" - "DBus',arg0namespace='org.mpris.MediaPlayer2'", - /* ... name changes */ - "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'," - "path='/org/freedesktop/DBus'", - /* ... property changes */ - "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged', " - "path='/org/mpris/MediaPlayer2'", - /* ... changes in playback position */ - "type='signal',interface='org.mpris.MediaPlayer2.Player',member='Seeked', " - "path='/org/mpris/MediaPlayer2'", - }; - - /* TODO: Error handling */ - /* "BecomeMonitor" ('asu'): (Rules: String[], Flags: UINT32) */ - /* https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor */ - status = sd_bus_message_open_container(message, SD_BUS_TYPE_ARRAY, "s"); - for (uint32_t i = 0; i < sizeof(matching_rules) / sizeof(matching_rules[0]); i++) { - status = sd_bus_message_append(message, "s", matching_rules[i]); - } - status = sd_bus_message_close_container(message); - status = sd_bus_message_append_basic(message, SD_BUS_TYPE_UINT32, &(uint32_t){0}); - - sd_bus_message *reply = NULL; - sd_bus_error error = {}; - status = sd_bus_call(NULL, message, context->mpd_config->timeout_ms, &error, &reply); - - if (status < 0 && sd_bus_error_is_set(&error)) { - LOG_ERR("context_setup: got error: %s: %s (%d)", error.name, error.message, sd_bus_error_get_errno(&error)); - return false; - } - - sd_bus_message_unref(message); - sd_bus_message_unref(reply); - - sd_bus_add_filter(connection, NULL, context_event_handler, context); - - return status >= 0; -} - -static uint64_t -timespec_diff_us(const struct timespec *a, const struct timespec *b) -{ - uint64_t nsecs_a = a->tv_sec * 1000000000 + a->tv_nsec; - uint64_t nsecs_b = b->tv_sec * 1000000000 + b->tv_nsec; - - assert(nsecs_a >= nsecs_b); - uint64_t nsec_diff = nsecs_a - nsecs_b; - return nsec_diff / 1000; -} - -static bool -update_status_from_message(struct module *mod, sd_bus_message *message) -{ - struct private *m = mod->private; - mtx_lock(&mod->lock); - - struct client *client = m->context.current_client; - int status = 1; - - /* Player.Seeked (UINT64 position)*/ - if (strcmp(sd_bus_message_get_member(message), "Seeked") == 0) { - client->has_seeked_support = true; - - status = sd_bus_message_read_basic(message, SD_BUS_TYPE_INT64, &client->property.position_us); - if (status <= 0) - return status; - - clock_gettime(CLOCK_MONOTONIC, &client->seeked_when); - return true; - } - - /* Properties.PropertiesChanged (STRING interface_name, - * ARRAY of DICT_ENTRY changed_properties, - * ARRAY invalidated_properties); */ - assert(strcmp(sd_bus_message_get_member(message), "PropertiesChanged") == 0); - assert(strcmp(sd_bus_message_get_signature(message, 1), "sa{sv}as") == 0); - - /* argument: 'interface_name' layout: 's' */ - const char *interface_name = NULL; - sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &interface_name); - - if (strcmp(interface_name, MPRIS_INTERFACE_PLAYER) != 0) { - LOG_DBG("Ignoring interface: %s", interface_name); - mtx_unlock(&mod->lock); - return true; - } - - /* argument: 'changed_properties' layout: 'a{sv}' */ - - /* Make sure we reset the position on metadata change unless the - * update contains its own position value */ - bool should_reset_position = true; - bool has_entries = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "{sv}"); - - while ((has_entries = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *property_name = NULL; - int status __attribute__((unused)) = sd_bus_message_read_basic(message, SD_BUS_TYPE_STRING, &property_name); - assert(status > 0); - - if (!property_parse(&client->property, property_name, message)) { - return false; - } - - status = sd_bus_message_exit_container(message); - assert(status >= 0); - - if (strcmp(property_name, "PlaybackStatus") == 0) { - if (strcmp(client->property.playback_status, "Stopped") == 0) { - client->status = STATUS_STOPPED; - - } else if (strcmp(client->property.playback_status, "Playing") == 0) { - clock_gettime(CLOCK_MONOTONIC, &client->seeked_when); - client->status = STATUS_PLAYING; - - } else if (strcmp(client->property.playback_status, "Paused") == 0) { - /* Update our position to include the elapsed time */ - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - client->status = STATUS_PAUSED; - client->property.position_us += timespec_diff_us(&now, &client->seeked_when); - } - } - - /* Make sure to reset the position upon metadata/song changes */ - if (should_reset_position && strcmp(property_name, "Metadata") == 0) { - client->property.position_us = 0; - - if (client->property.playback_status == NULL) { - client->property.playback_status = "Paused"; - client->status = STATUS_PAUSED; - } - } - - if (strcmp(property_name, "Position") == 0) { - should_reset_position = false; - } - } - - status = sd_bus_message_exit_container(message); - assert(status > 0); - - mtx_unlock(&mod->lock); - return true; -} - -static struct exposable * -content_empty(struct module *mod) -{ - struct private *m = mod->private; - mtx_lock(&mod->lock); - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_bool(mod, "has-seeked-support", "false"), - tag_new_string(mod, "state", "offline"), - tag_new_bool(mod, "shuffle", "false"), - tag_new_string(mod, "loop", "None"), - tag_new_int_range(mod, "volume", 0, 0, 100), - tag_new_string(mod, "album", ""), - tag_new_string(mod, "artist", ""), - tag_new_string(mod, "title", ""), - tag_new_string(mod, "pos", ""), - tag_new_string(mod, "end", ""), - tag_new_int_realtime( - mod, "elapsed", 0, 0, 0, TAG_REALTIME_NONE), - }, - .count = 10, - }; - - struct exposable *exposable = m->label->instantiate(m->label, &tags); - tag_set_destroy(&tags); - mtx_unlock(&mod->lock); - - return exposable; -} - -static struct exposable * -content(struct module *mod) -{ - const struct private *m = mod->private; - const struct client *client = m->context.current_client; - - if (client == NULL) { - return content_empty(mod); - } - - const struct metadata *metadata = &client->property.metadata; - const struct property *property = &client->property; - - /* Calculate the current playback position */ - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - uint64_t elapsed_us = client->property.position_us; - uint64_t length_us = metadata->length_us; - - if (client->has_seeked_support && client->status == STATUS_PLAYING) { - elapsed_us += timespec_diff_us(&now, &client->seeked_when); - if (elapsed_us > length_us) { - LOG_DBG("dynamic update of elapsed overflowed: " - "elapsed=%" PRIu64 ", duration=%" PRIu64, - elapsed_us, length_us); - elapsed_us = length_us; - } - } - - /* Some clients can report misleading or incomplete updates to the - * playback position, potentially causing the position to exceed - * the length */ - if (elapsed_us > length_us) - elapsed_us = length_us = 0; - - char tag_pos_value[16] = {0}, tag_end_value[16] = {0}; - if (length_us > 0) { - format_usec_timestamp(elapsed_us, tag_pos_value, sizeof(tag_pos_value)); - format_usec_timestamp(length_us, tag_end_value, sizeof(tag_end_value)); - } - - char *tag_state_value = NULL; - switch (client->status) { - case STATUS_ERROR: - tag_state_value = "error"; - break; - case STATUS_OFFLINE: - tag_state_value = "offline"; - break; - case STATUS_PLAYING: - tag_state_value = "playing"; - break; - case STATUS_PAUSED: - tag_state_value = "paused"; - break; - case STATUS_STOPPED: - tag_state_value = "stopped"; - break; - } - - const char *tag_loop_value = (property->loop_status == NULL) ? "" : property->loop_status; - const char *tag_album_value = (metadata->album == NULL) ? "" : metadata->album; - const char *tag_artists_value = (tll_length(metadata->artists) <= 0) ? "" : tll_front(metadata->artists); - const char *tag_title_value = (metadata->title == NULL) ? "" : metadata->title; - const uint32_t tag_volume_value = (property->volume >= 0.995) ? 100 : 100 * property->volume; - const bool tag_shuffle_value = property->shuffle; - const enum tag_realtime_unit realtime_unit - = (client->has_seeked_support && client->status == STATUS_PLAYING) ? TAG_REALTIME_MSECS : TAG_REALTIME_NONE; - - mtx_lock(&mod->lock); - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_bool(mod, "has_seeked_support", client->has_seeked_support), - tag_new_bool(mod, "shuffle", tag_shuffle_value), - tag_new_int_range(mod, "volume", tag_volume_value, 0, 100), - tag_new_string(mod, "album", tag_album_value), - tag_new_string(mod, "artist", tag_artists_value), - tag_new_string(mod, "end", tag_end_value), - tag_new_string(mod, "loop", tag_loop_value), - tag_new_string(mod, "pos", tag_pos_value), - tag_new_string(mod, "state", tag_state_value), - tag_new_string(mod, "title", tag_title_value), - tag_new_int_realtime( - mod, "elapsed", elapsed_us, 0, length_us, realtime_unit), - }, - .count = 11, - }; - - struct exposable *exposable = m->label->instantiate(m->label, &tags); - tag_set_destroy(&tags); - mtx_unlock(&mod->lock); - - return exposable; -} - -struct refresh_context { - struct module *mod; - int abort_fd; - long milli_seconds; -}; - -static int -refresh_in_thread(void *arg) -{ - struct refresh_context *ctx = arg; - struct module *mod = ctx->mod; - - /* Extract data from context so that we can free it */ - int abort_fd = ctx->abort_fd; - long milli_seconds = ctx->milli_seconds; - free(ctx); - - /*LOG_DBG("going to sleep for %ldms", milli_seconds);*/ - - /* Wait for timeout, or abort signal */ - struct pollfd fds[] = {{.fd = abort_fd, .events = POLLIN}}; - int r = poll(fds, 1, milli_seconds); - - if (r < 0) { - LOG_ERRNO("failed to poll() in refresh thread"); - return 1; - } - - /* Aborted? */ - if (r == 1) { - assert(fds[0].revents & POLLIN); - /*LOG_DBG("refresh thread aborted");*/ - return 0; - } - - LOG_DBG("timed refresh"); - mod->bar->refresh(mod->bar); - - return 0; -} - -static bool -refresh_in(struct module *mod, long milli_seconds) -{ - struct private *m = mod->private; - - /* Abort currently running refresh thread */ - if (m->refresh_thread_id != 0) { - /*LOG_DBG("aborting current refresh thread");*/ - - /* Signal abort to thread */ - assert(m->refresh_abort_fd != -1); - if (write(m->refresh_abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) { - LOG_ERRNO("failed to signal abort to refresher thread"); - return false; - } - - /* Wait for it to finish */ - int res; - thrd_join(m->refresh_thread_id, &res); - - /* Close and cleanup */ - close(m->refresh_abort_fd); - m->refresh_abort_fd = -1; - m->refresh_thread_id = 0; - } - - /* Create a new eventfd, to be able to signal abort to the thread */ - int abort_fd = eventfd(0, EFD_CLOEXEC); - if (abort_fd == -1) { - LOG_ERRNO("failed to create eventfd"); - return false; - } - - /* Thread context */ - struct refresh_context *ctx = malloc(sizeof(*ctx)); - ctx->mod = mod; - ctx->abort_fd = m->refresh_abort_fd = abort_fd; - ctx->milli_seconds = milli_seconds; - - /* Create thread */ - int r = thrd_create(&m->refresh_thread_id, &refresh_in_thread, ctx); - - if (r != thrd_success) { - LOG_ERR("failed to create refresh thread"); - close(m->refresh_abort_fd); - m->refresh_abort_fd = -1; - m->refresh_thread_id = 0; - free(ctx); - } - - /* Detach - we don't want to have to thrd_join() it */ - // thrd_detach(tid); - return r == 0; -} - -static int -run(struct module *mod) -{ - const struct bar *bar = mod->bar; - struct private *m = mod->private; - - if (!context_setup(&m->context)) { - LOG_ERR("Failed to setup context"); - return -1; - } - - struct context *context = &m->context; - - int ret = 0; - bool aborted = false; - while (ret == 0 && !aborted) { - const uint32_t timeout_ms = 50; - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - - /* Check for abort event */ - if (poll(fds, 1, timeout_ms) < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } - - if (fds[0].revents & POLLIN) { - aborted = true; - break; - } - - if (!context_process_events(context, m->timeout_ms)) { - aborted = true; - break; - } - - /* Process dynamic updates, received through the contexts - * monitor connection. The 'upate_message' attribute is set - * inside the contexts event callback, if there are any - * updates to be processed. */ - if (context->has_update) { - assert(context->current_client != NULL); - assert(context->update_message != NULL); - - context->has_update = false; - aborted = !update_status_from_message(mod, context->update_message); - context->update_message = sd_bus_message_unref(context->update_message); - } - - bar->refresh(bar); - } - - LOG_DBG("exiting"); - return ret; -} - -static const char * -description(const struct module *mod) -{ - return "mpris"; -} - -static struct module * -mpris_new(const struct yml_node *ident_list, size_t timeout_ms, struct particle *label) -{ - struct private *priv = calloc(1, sizeof(*priv)); - priv->label = label; - priv->timeout_ms = timeout_ms; - priv->context.mpd_config = priv; - - size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(ident_list); iter.node != NULL; yml_list_next(&iter), i++) { - char *string = strdup(yml_value_as_string(iter.node)); - tll_push_back(priv->identity_list, string); - } - - struct module *mod = module_common_new(); - mod->private = priv; - mod->run = &run; - mod->destroy = &destroy; - mod->content = &content; - mod->description = &description; - mod->refresh_in = &refresh_in; - return mod; -} - -static struct module * -from_conf(const struct yml_node *node, struct conf_inherit inherited) -{ - const struct yml_node *ident_list = yml_get_value(node, "identities"); - const struct yml_node *query_timeout = yml_get_value(node, "query_timeout"); - const struct yml_node *c = yml_get_value(node, "content"); - - size_t timeout_ms = DEFAULT_QUERY_TIMEOUT_MS; - if (query_timeout != NULL) - timeout_ms = yml_value_as_int(query_timeout) * 1000; - - return mpris_new(ident_list, timeout_ms, conf_to_particle(c, inherited)); -} - -static bool -conf_verify_indentities(keychain_t *chain, const struct yml_node *node) -{ - return conf_verify_list(chain, node, &conf_verify_string); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"identities", true, &conf_verify_indentities}, - {"query_timeout", false, &conf_verify_unsigned}, - MODULE_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_mpris_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_mpris_iface"))); -#endif diff --git a/modules/network.c b/modules/network.c index 46a3148..51839bf 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1,48 +1,29 @@ -#include -#include -#include #include #include +#include #include -#include +#include #include -#include -#include #include - -#include -#include +#include #include -#include #include -#include #include -#include #include #include #define LOG_MODULE "network" -#define LOG_ENABLE_DBG 1 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" +#define LOG_ENABLE_DBG 0 #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../module.h" -#include "../particles/dynlist.h" #include "../plugin.h" -#define max(x, y) ((x) > (y) ? (x) : (y)) - -static const long min_poll_interval = 250; - -struct rt_stats_msg { - struct rtmsg rth; - struct rtnl_link_stats64 stats; -}; - struct af_addr { int family; union { @@ -51,92 +32,38 @@ struct af_addr { } addr; }; -struct iface { - char *name; - char *type; /* ARPHRD_NNN */ - char *kind; /* IFLA_LINKINFO::IFLA_INFO_KIND */ - - uint32_t get_stats_seq_nr; - - int index; - uint8_t mac[6]; - bool carrier; - uint8_t state; /* IFLA_OPERSTATE */ - - /* IPv4 and IPv6 addresses */ - tll(struct af_addr) addrs; - - /* WiFi extensions */ - char *ssid; - int signal_strength_dbm; - uint32_t rx_bitrate; - uint32_t tx_bitrate; - - double ul_speed; - uint64_t ul_bits; - - double dl_speed; - uint64_t dl_bits; -}; - -struct private -{ +struct private { + char *iface; struct particle *label; - int poll_interval; - int left_spacing; - int right_spacing; + int nl_sock; bool get_addresses; - int genl_sock; - int rt_sock; - int urandom_fd; + int ifindex; + uint8_t mac[6]; + bool carrier; + uint8_t state; /* IFLA_OPERSTATE */ - struct { - uint16_t family_id; - uint32_t get_interface_seq_nr; - uint32_t get_scan_seq_nr; - } nl80211; - - tll(struct iface) ifaces; + /* IPv4 and IPv6 addresses */ + tll(struct af_addr) addrs; }; -static void -free_iface(struct iface iface) -{ - tll_free(iface.addrs); - free(iface.ssid); - free(iface.kind); - free(iface.type); - free(iface.name); -} - static void destroy(struct module *mod) { struct private *m = mod->private; - assert(m->rt_sock == -1); + assert(m->nl_sock == -1); m->label->destroy(m->label); - if (m->urandom_fd >= 0) - close(m->urandom_fd); - - tll_foreach(m->ifaces, it) { - free_iface(it->item); - tll_remove(m->ifaces, it); - } + tll_free(m->addrs); + free(m->iface); free(m); - module_default_destroy(mod); -} -static const char * -description(const struct module *mod) -{ - return "network"; + module_default_destroy(mod); } static struct exposable * @@ -146,97 +73,52 @@ content(struct module *mod) mtx_lock(&mod->lock); - struct exposable *exposables[max(tll_length(m->ifaces), 1)]; - size_t idx = 0; - - tll_foreach(m->ifaces, it) - { - struct iface *iface = &it->item; - - const char *state = NULL; - switch (iface->state) { - case IF_OPER_UNKNOWN: - state = "unknown"; - break; - case IF_OPER_NOTPRESENT: - state = "not present"; - break; - case IF_OPER_DOWN: - state = "down"; - break; - case IF_OPER_LOWERLAYERDOWN: - state = "lower layers down"; - break; - case IF_OPER_TESTING: - state = "testing"; - break; - case IF_OPER_DORMANT: - state = "dormant"; - break; - case IF_OPER_UP: - state = "up"; - break; - default: - state = "unknown"; - break; - } - - char mac_str[6 * 2 + 5 + 1]; - char ipv4_str[INET_ADDRSTRLEN] = {0}; - char ipv6_str[INET6_ADDRSTRLEN] = {0}; - - snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", iface->mac[0], iface->mac[1], iface->mac[2], - iface->mac[3], iface->mac[4], iface->mac[5]); - - /* TODO: this exposes the *last* added address of each kind. Can - * we expose all in some way? */ - tll_foreach(iface->addrs, it) - { - if (it->item.family == AF_INET) - inet_ntop(AF_INET, &it->item.addr.ipv4, ipv4_str, sizeof(ipv4_str)); - else if (it->item.family == AF_INET6) - if (!IN6_IS_ADDR_LINKLOCAL(&it->item.addr.ipv6)) - inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); - } - - int quality = 0; - if (iface->signal_strength_dbm != 0) { - if (iface->signal_strength_dbm <= -100) - quality = 0; - else if (iface->signal_strength_dbm >= -50) - quality = 100; - else - quality = 2 * (iface->signal_strength_dbm + 100); - } - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_string(mod, "name", iface->name), - tag_new_string(mod, "type", iface->type), - tag_new_string(mod, "kind", iface->kind), - tag_new_int(mod, "index", iface->index), - tag_new_bool(mod, "carrier", iface->carrier), - tag_new_string(mod, "state", state), - tag_new_string(mod, "mac", mac_str), - tag_new_string(mod, "ipv4", ipv4_str), - tag_new_string(mod, "ipv6", ipv6_str), - tag_new_string(mod, "ssid", iface->ssid), - tag_new_int(mod, "signal", iface->signal_strength_dbm), - tag_new_int_range(mod, "quality", quality, 0, 100), - tag_new_int(mod, "rx-bitrate", iface->rx_bitrate), - tag_new_int(mod, "tx-bitrate", iface->tx_bitrate), - tag_new_float(mod, "dl-speed", iface->dl_speed), - tag_new_float(mod, "ul-speed", iface->ul_speed), - }, - .count = 16, - }; - exposables[idx++] = m->label->instantiate(m->label, &tags); - tag_set_destroy(&tags); + const char *state = NULL; + switch (m->state) { + case IF_OPER_UNKNOWN: state = "unknown"; break; + case IF_OPER_NOTPRESENT: state = "not present"; break; + case IF_OPER_DOWN: state = "down"; break; + case IF_OPER_LOWERLAYERDOWN: state = "lower layers down"; break; + case IF_OPER_TESTING: state = "testing"; break; + case IF_OPER_DORMANT: state = "dormant"; break; + case IF_OPER_UP: state = "up"; break; + default: state = "unknown"; break; } + char mac_str[6 * 2 + 5 + 1]; + char ipv4_str[INET_ADDRSTRLEN] = {0}; + char ipv6_str[INET6_ADDRSTRLEN] = {0}; + + snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", + m->mac[0], m->mac[1], m->mac[2], m->mac[3], m->mac[4], m->mac[5]); + + /* TODO: this exposes the *last* added address of each kind. Can + * we expose all in some way? */ + tll_foreach(m->addrs, it) { + if (it->item.family == AF_INET) + inet_ntop(AF_INET, &it->item.addr.ipv4, ipv4_str, sizeof(ipv4_str)); + else if (it->item.family == AF_INET6) + inet_ntop(AF_INET6, &it->item.addr.ipv6, ipv6_str, sizeof(ipv6_str)); + } + + struct tag_set tags = { + .tags = (struct tag *[]){ + tag_new_string(mod, "name", m->iface), + tag_new_int(mod, "index", m->ifindex), + tag_new_bool(mod, "carrier", m->carrier), + tag_new_string(mod, "state", state), + tag_new_string(mod, "mac", mac_str), + tag_new_string(mod, "ipv4", ipv4_str), + tag_new_string(mod, "ipv6", ipv6_str), + }, + .count = 7, + }; + mtx_unlock(&mod->lock); - return dynlist_exposable_new(exposables, idx, m->left_spacing, m->right_spacing); + struct exposable *exposable = m->label->instantiate(m->label, &tags); + tag_set_destroy(&tags); + return exposable; } /* Returns a value suitable for nl_pid/nlmsg_pid */ @@ -248,9 +130,9 @@ nl_pid_value(void) /* Connect and bind to netlink socket. Returns socket fd, or -1 on error */ static int -netlink_connect_rt(void) +netlink_connect(void) { - int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock == -1) { LOG_ERRNO("failed to create netlink socket"); return -1; @@ -262,30 +144,7 @@ netlink_connect_rt(void) .nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, }; - if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { - LOG_ERRNO("failed to bind netlink RT socket"); - close(sock); - return -1; - } - - return sock; -} - -static int -netlink_connect_genl(void) -{ - int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_GENERIC); - if (sock == -1) { - LOG_ERRNO("failed to create netlink socket"); - return -1; - } - - const struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, .nl_pid = nl_pid_value(), - /* no multicast notifications by default, will be added later */ - }; - - if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { LOG_ERRNO("failed to bind netlink socket"); close(sock); return -1; @@ -295,20 +154,11 @@ netlink_connect_genl(void) } static bool -send_nlmsg(int sock, const void *nlmsg, size_t len) -{ - int r = sendto(sock, nlmsg, len, 0, (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, - sizeof(struct sockaddr_nl)); - - return r == len; -} - -static bool -send_rt_request(struct private *m, int request) +send_rt_request(int nl_sock, int request) { struct { struct nlmsghdr hdr; - struct rtgenmsg rt __attribute__((aligned(NLMSG_ALIGNTO))); + struct rtgenmsg rt; } req = { .hdr = { .nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)), @@ -323,408 +173,101 @@ send_rt_request(struct private *m, int request) }, }; - if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink RT request (%d)", request); + int r = sendto( + nl_sock, &req, req.hdr.nlmsg_len, 0, + (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, + sizeof(struct sockaddr_nl)); + + if (r == -1) { + LOG_ERRNO("failed to send netlink request"); return false; } return true; } - static bool -send_rt_getstats_request(struct private *m, struct iface *iface) +find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) { - if (iface->get_stats_seq_nr > 0) { - LOG_DBG("%s: RT get-stats request already in progress", iface->name); - return true; - } + struct private *m = mod->private; - LOG_DBG("%s: sending RT get-stats request", iface->name); + for (const struct rtattr *attr = IFLA_RTA(msg); + RTA_OK(attr, len); + attr = RTA_NEXT(attr, len)) + { + switch (attr->rta_type) { + case IFLA_IFNAME: + if (strcmp((const char *)RTA_DATA(attr), m->iface) == 0) { + LOG_INFO("%s: ifindex=%d", m->iface, msg->ifi_index); - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); - return false; - } + mtx_lock(&mod->lock); + m->ifindex = msg->ifi_index; + mtx_unlock(&mod->lock); + return true; + } - struct { - struct nlmsghdr hdr; - struct if_stats_msg rt; - } req = { - .hdr = { - .nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)), - .nlmsg_type = RTM_GETSTATS, - .nlmsg_flags = NLM_F_REQUEST, - .nlmsg_seq = seq, - .nlmsg_pid = nl_pid_value(), - }, - - .rt = { - .ifindex = iface->index, - .filter_mask = IFLA_STATS_LINK_64, - .family = AF_UNSPEC, - }, - }; - - if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("%s: failed to send netlink RT getstats request (%d)", iface->name, RTM_GETSTATS); - return false; - } - iface->get_stats_seq_nr = seq; - return true; -} - -static bool -send_ctrl_get_family_request(struct private *m) -{ - const struct { - struct nlmsghdr hdr; - struct { - struct genlmsghdr genl; - struct { - struct nlattr hdr; - char data[8] __attribute__((aligned(NLA_ALIGNTO))); - } family_name_attr __attribute__((aligned(NLA_ALIGNTO))); - } msg __attribute__((aligned(NLMSG_ALIGNTO))); - } req = { - .hdr = { - .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), - .nlmsg_type = GENL_ID_CTRL, - .nlmsg_flags = NLM_F_REQUEST, - .nlmsg_seq = 1, - .nlmsg_pid = nl_pid_value(), - }, - - .msg = { - .genl = { - .cmd = CTRL_CMD_GETFAMILY, - .version = 1, - }, - - .family_name_attr = { - .hdr = { - .nla_type = CTRL_ATTR_FAMILY_NAME, - .nla_len = sizeof(req.msg.family_name_attr), - }, - - .data = NL80211_GENL_NAME, - }, - }, - }; - - _Static_assert(sizeof(req.msg.family_name_attr) == NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)), - ""); - - if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink ctrl-get-family request"); - return false; - } - - return true; -} - -static bool -send_nl80211_request(struct private *m, uint8_t cmd, uint32_t seq) -{ - if (m->nl80211.family_id == (uint16_t)-1) - return false; - - const struct { - struct nlmsghdr hdr; - struct { - struct genlmsghdr genl; - } msg __attribute__((aligned(NLMSG_ALIGNTO))); - } req = { - .hdr = { - .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), - .nlmsg_type = m->nl80211.family_id, - .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, - .nlmsg_seq = seq, - .nlmsg_pid = nl_pid_value(), - }, - - .msg = { - .genl = { - .cmd = cmd, - .version = 1, - }, - }, - }; - - if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink nl80211 get-inteface request"); - return false; - } - - return true; -} - -static bool -send_nl80211_get_interface(struct private *m) -{ - if (m->nl80211.get_interface_seq_nr > 0) { - LOG_DBG("nl80211 get-interface request already in progress"); - return true; - } - - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); - return false; - } - - LOG_DBG("sending nl80211 get-interface request %d", seq); - - if (!send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, seq)) - return false; - - m->nl80211.get_interface_seq_nr = seq; - return true; -} - -static bool -send_nl80211_get_station(struct private *m, struct iface *iface) -{ - LOG_DBG("sending nl80211 get-station request"); - - if (m->nl80211.family_id == (uint16_t)-1) - return false; - - const struct { - struct nlmsghdr hdr; - struct { - struct genlmsghdr genl; - struct { - struct nlattr attr; - int index __attribute__((aligned(NLA_ALIGNTO))); - } ifindex __attribute__((aligned(NLA_ALIGNTO))); - } msg __attribute__((aligned(NLMSG_ALIGNTO))); - } req = { - .hdr = { - .nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)), - .nlmsg_type = m->nl80211.family_id, - .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, - .nlmsg_seq = 1, - .nlmsg_pid = nl_pid_value(), - }, - - .msg = { - .genl = { - .cmd = NL80211_CMD_GET_STATION, - .version = 1, - }, - - .ifindex = { - .attr = { - .nla_type = NL80211_ATTR_IFINDEX, - .nla_len = sizeof(req.msg.ifindex), - }, - - .index = iface->index, - }, - }, - }; - - if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink nl80211 get-inteface request"); - return false; - } - - return true; -} - -static bool -send_nl80211_get_scan(struct private *m) -{ - if (m->nl80211.get_scan_seq_nr > 0) { - LOG_ERR("nl80211 get-scan request already in progress"); - return true; - } - - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); - return false; - } - - LOG_DBG("sending nl80211 get-scan request %d", seq); - - if (!send_nl80211_request(m, NL80211_CMD_GET_SCAN, seq)) - return false; - - m->nl80211.get_scan_seq_nr = seq; - return true; -} - -static bool -foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len, - bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *ctx), - void *ctx) -{ - const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; - const uint8_t *end = (const uint8_t *)genl + len; - - for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; - raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { - uint16_t type = attr->nla_type & NLA_TYPE_MASK; - bool nested = (attr->nla_type & NLA_F_NESTED) != 0; - ; - const void *payload = raw + NLA_HDRLEN; - - if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) return false; + } } - return true; -} - -static bool -foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len, - bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, - const void *payload, size_t len, void *ctx), - void *ctx) -{ - const uint8_t *raw = parent_payload; - const uint8_t *end = parent_payload + len; - - for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end; - raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) { - uint16_t type = attr->nla_type & NLA_TYPE_MASK; - bool nested = (attr->nla_type & NLA_F_NESTED) != 0; - const void *payload = raw + NLA_HDRLEN; - - if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) - return false; - } - - return true; -} - -static bool -parse_linkinfo(struct module *mod, struct iface *iface, uint16_t type, - bool nested, const void *payload, size_t len, void *_void) -{ - switch (type) { - case IFLA_INFO_KIND: { - const char *kind = payload; - free(iface->kind); - iface->kind = strndup(kind, len); - - LOG_DBG("%s: IFLA_INFO_KIND: %s", iface->name, iface->kind); - break; - } - - case IFLA_INFO_DATA: - //LOG_DBG("%s: IFLA_INFO_DATA", iface->name); - break; - - default: - LOG_WARN("unrecognized IFLA_LINKINFO attribute: " - "type=%hu, nested=%d, len=%zu", - type, nested, len); - break; - } - - return true; + return false; } static void -handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size_t len) +handle_link(struct module *mod, uint16_t type, + const struct ifinfomsg *msg, size_t len) { assert(type == RTM_NEWLINK || type == RTM_DELLINK); struct private *m = mod->private; - if (type == RTM_DELLINK) { - tll_foreach(m->ifaces, it) - { - if (msg->ifi_index != it->item.index) - continue; - mtx_lock(&mod->lock); - tll_remove_and_free(m->ifaces, it, free_iface); - mtx_unlock(&mod->lock); - break; + if (m->ifindex == -1) { + /* We don't know our own ifindex yet. Let's see if we can find + * it in the message */ + if (!find_my_ifindex(mod, msg, len)) { + /* Nope, message wasn't for us (IFLA_IFNAME mismatch) */ + return; } + } - mod->bar->refresh(mod->bar); + assert(m->ifindex >= 0); + + if (msg->ifi_index != m->ifindex) { + /* Not for us */ return; } - struct iface *iface = NULL; - tll_foreach(m->ifaces, it) + bool update_bar = false; + + for (const struct rtattr *attr = IFLA_RTA(msg); + RTA_OK(attr, len); + attr = RTA_NEXT(attr, len)) { - if (msg->ifi_index != it->item.index) - continue; - iface = &it->item; - break; - } - - if (iface == NULL) { - char *type = NULL; - - switch (msg->ifi_type) { - case ARPHRD_ETHER: - type = strdup("ether"); - break; - - case ARPHRD_LOOPBACK: - type = strdup("loopback"); - break; - - case ARPHRD_IEEE80211: - type = strdup("wlan"); - break; - - default: - if (asprintf(&type, "ARPHRD_%hu", msg->ifi_type) < 0) - type = strdup("unknown"); - break; - } - - mtx_lock(&mod->lock); - tll_push_back(m->ifaces, ((struct iface){ - .index = msg->ifi_index, - .type = type, - .state = IF_OPER_DOWN, - .addrs = tll_init(), - })); - mtx_unlock(&mod->lock); - iface = &tll_back(m->ifaces); - } - - for (const struct rtattr *attr = IFLA_RTA(msg); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { switch (attr->rta_type) { - case IFLA_IFNAME: - mtx_lock(&mod->lock); - iface->name = strdup((const char *)RTA_DATA(attr)); - LOG_DBG("%s: index=%d, type=%s", iface->name, iface->index, iface->type); - mtx_unlock(&mod->lock); - break; - case IFLA_OPERSTATE: { uint8_t operstate = *(const uint8_t *)RTA_DATA(attr); - if (iface->state == operstate) + if (m->state == operstate) break; - LOG_DBG("%s: IFLA_OPERSTATE: %hhu -> %hhu", iface->name, iface->state, operstate); + LOG_DBG("%s: IFLA_OPERSTATE: %hhu -> %hhu", m->iface, m->state, operstate); mtx_lock(&mod->lock); - iface->state = operstate; + m->state = operstate; mtx_unlock(&mod->lock); + update_bar = true; break; } case IFLA_CARRIER: { uint8_t carrier = *(const uint8_t *)RTA_DATA(attr); - if (iface->carrier == carrier) + if (m->carrier == carrier) break; - LOG_DBG("%s: IFLA_CARRIER: %hhu -> %hhu", iface->name, iface->carrier, carrier); + LOG_DBG("%s: IFLA_CARRIER: %hhu -> %hhu", m->iface, m->carrier, carrier); mtx_lock(&mod->lock); - iface->carrier = carrier; + m->carrier = carrier; mtx_unlock(&mod->lock); + update_bar = true; break; } @@ -733,62 +276,47 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size break; const uint8_t *mac = RTA_DATA(attr); - if (memcmp(iface->mac, mac, sizeof(iface->mac)) == 0) + if (memcmp(m->mac, mac, sizeof(m->mac)) == 0) break; LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", - iface->name, mac[0], mac[1], mac[2], mac[3], - mac[4], mac[5]); + m->iface, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mtx_lock(&mod->lock); - memcpy(iface->mac, mac, sizeof(iface->mac)); + memcpy(m->mac, mac, sizeof(m->mac)); mtx_unlock(&mod->lock); - break; - } - - case IFLA_LINKINFO: { - foreach_nlattr_nested( - mod, iface, RTA_DATA(attr), RTA_PAYLOAD(attr), - &parse_linkinfo, NULL); + update_bar = true; break; } } } - assert(iface->name != NULL); - - /* Reset address initialization */ - m->get_addresses = true; - - send_nl80211_get_interface(m); - mod->bar->refresh(mod->bar); + if (update_bar) + mod->bar->refresh(mod->bar); } static void -handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, size_t len) +handle_address(struct module *mod, uint16_t type, + const struct ifaddrmsg *msg, size_t len) { assert(type == RTM_NEWADDR || type == RTM_DELADDR); struct private *m = mod->private; - bool update_bar = false; + assert(m->ifindex >= 0); - struct iface *iface = NULL; - - tll_foreach(m->ifaces, it) - { - if (msg->ifa_index != it->item.index) - continue; - iface = &it->item; - break; - } - - if (iface == NULL) { - LOG_ERR("failed to find network interface with index %d. Probably a yambar bug", msg->ifa_index); + if (msg->ifa_index != m->ifindex) { + /* Not for us */ return; } - for (const struct rtattr *attr = IFA_RTA(msg); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { + bool update_bar = false; + + for (const struct rtattr *attr = IFA_RTA(msg); + RTA_OK(attr, len); + attr = RTA_NEXT(attr, len)) + { switch (attr->rta_type) { case IFA_ADDRESS: { const void *raw_addr = RTA_DATA(attr); @@ -798,21 +326,21 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s char s[INET6_ADDRSTRLEN]; inet_ntop(msg->ifa_family, raw_addr, s, sizeof(s)); #endif - LOG_DBG("%s: IFA_ADDRESS (%s): %s", iface->name, type == RTM_NEWADDR ? "add" : "del", s); + LOG_DBG("%s: IFA_ADDRESS (%s): %s", m->iface, + type == RTM_NEWADDR ? "add" : "del", s); mtx_lock(&mod->lock); if (type == RTM_DELADDR) { /* Find address in our list and remove it */ - tll_foreach(iface->addrs, it) - { + tll_foreach(m->addrs, it) { if (it->item.family != msg->ifa_family) continue; if (memcmp(&it->item.addr, raw_addr, addr_len) != 0) continue; - tll_remove(iface->addrs, it); + tll_remove(m->addrs, it); update_bar = true; break; } @@ -820,7 +348,7 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s /* Append address to our list */ struct af_addr a = {.family = msg->ifa_family}; memcpy(&a.addr, raw_addr, addr_len); - tll_push_back(iface->addrs, a); + tll_push_back(m->addrs, a); update_bar = true; } @@ -834,368 +362,6 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s mod->bar->refresh(mod->bar); } -struct mcast_group { - uint32_t id; - const char *name; -}; - -static bool -parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, - void *_ctx) -{ - struct mcast_group *ctx = _ctx; - - switch (type) { - case CTRL_ATTR_MCAST_GRP_ID: { - ctx->id = *(uint32_t *)payload; - break; - } - - case CTRL_ATTR_MCAST_GRP_NAME: { - ctx->name = (const char *)payload; - break; - } - - default: - LOG_WARN("unrecognized GENL MCAST GRP attribute: " - "type=%hu, nested=%d, len=%zu", - type, nested, len); - break; - } - - return true; -} - -static bool -parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, - void *_ctx) -{ - struct private *m = mod->private; - - struct mcast_group group = {0}; - foreach_nlattr_nested(mod, NULL, payload, len, &parse_mcast_group, &group); - - LOG_DBG("MCAST: %s -> %u", group.name, group.id); - - if (strcmp(group.name, NL80211_MULTICAST_GROUP_MLME) == 0) { - /* - * Join the nl80211 MLME multicast group - for - * CONNECT/DISCONNECT events. - */ - - int r = setsockopt(m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group.id, sizeof(int)); - - if (r < 0) - LOG_ERRNO("failed to joint the nl80211 MLME mcast group"); - } - - return true; -} - -static bool -handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, - void *_ctx) -{ - struct private *m = mod->private; - - switch (type) { - case CTRL_ATTR_FAMILY_ID: { - m->nl80211.family_id = *(const uint16_t *)payload; - send_nl80211_get_interface(m); - break; - } - - case CTRL_ATTR_FAMILY_NAME: - // LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); - break; - - case CTRL_ATTR_MCAST_GROUPS: - foreach_nlattr_nested(mod, NULL, payload, len, &parse_mcast_groups, NULL); - break; - - default: - LOG_DBG("unrecognized GENL CTRL attribute: " - "type=%hu, nested=%d, len=%zu", - type, nested, len); - break; - } - - return true; -} - -static bool -find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool nested, const void *payload, - size_t len, void *ctx) -{ - struct private *m = mod->private; - struct iface **iface = ctx; - - switch (type) { - case NL80211_ATTR_IFINDEX: - if (*iface != NULL) - if (*(uint32_t *)payload == (*iface)->index) - return false; - tll_foreach(m->ifaces, it) - { - if (*(uint32_t *)payload != it->item.index) - continue; - *iface = &it->item; - return false; - } - LOG_ERR("could not find interface with index %d", *(uint32_t *)payload); - break; - } - - return true; -} - -static bool -handle_nl80211_new_interface(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) -{ - switch (type) { - case NL80211_ATTR_IFINDEX: - assert(*(uint32_t *)payload == iface->index); - break; - - case NL80211_ATTR_SSID: { - const char *ssid = payload; - - if (iface->ssid == NULL || strncmp(iface->ssid, ssid, len) != 0) - LOG_INFO("%s: SSID: %.*s", iface->name, (int)len, ssid); - - mtx_lock(&mod->lock); - free(iface->ssid); - iface->ssid = strndup(ssid, len); - mtx_unlock(&mod->lock); - - mod->bar->refresh(mod->bar); - break; - } - - default: - LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); - break; - } - - return true; -} - -struct rate_info_ctx { - unsigned bitrate; -}; - -static bool -handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) -{ - struct rate_info_ctx *ctx = _ctx; - - switch (type) { - case NL80211_RATE_INFO_BITRATE32: { - uint32_t bitrate_100kbit = *(uint32_t *)payload; - ctx->bitrate = bitrate_100kbit * 100 * 1000; - break; - } - - case NL80211_RATE_INFO_BITRATE: - if (ctx->bitrate == 0) { - uint16_t bitrate_100kbit = *(uint16_t *)payload; - ctx->bitrate = bitrate_100kbit * 100 * 1000; - } else { - /* Prefer the BITRATE32 attribute */ - } - break; - - default: - LOG_DBG("%s: unrecognized nl80211 rate info attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); - break; - } - - return true; -} - -struct station_info_ctx { - bool update_bar; -}; - -static bool -handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) -{ - struct station_info_ctx *ctx = _ctx; - - switch (type) { - case NL80211_STA_INFO_SIGNAL: - LOG_DBG("signal strength (last): %hhd dBm", *(uint8_t *)payload); - break; - - case NL80211_STA_INFO_SIGNAL_AVG: - LOG_DBG("signal strength (average): %hhd dBm", *(uint8_t *)payload); - mtx_lock(&mod->lock); - iface->signal_strength_dbm = *(int8_t *)payload; - mtx_unlock(&mod->lock); - ctx->update_bar = true; - break; - - case NL80211_STA_INFO_TX_BITRATE: { - struct rate_info_ctx rctx = {0}; - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); - - LOG_DBG("TX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); - mtx_lock(&mod->lock); - iface->tx_bitrate = rctx.bitrate; - mtx_unlock(&mod->lock); - ctx->update_bar = true; - break; - } - - case NL80211_STA_INFO_RX_BITRATE: { - struct rate_info_ctx rctx = {0}; - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); - - LOG_DBG("RX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); - mtx_lock(&mod->lock); - iface->rx_bitrate = rctx.bitrate; - mtx_unlock(&mod->lock); - ctx->update_bar = true; - break; - } - - default: - LOG_DBG("%s: unrecognized nl80211 station info attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); - break; - } - - return true; -} - -static bool -handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) -{ - switch (type) { - case NL80211_ATTR_IFINDEX: - break; - - case NL80211_ATTR_STA_INFO: { - struct station_info_ctx ctx = {0}; - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_station_info, &ctx); - - if (ctx.update_bar) - mod->bar->refresh(mod->bar); - break; - } - - default: - LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); - break; - } - - return true; -} - -static bool -handle_ies(struct module *mod, struct iface *iface, const void *_ies, size_t len) -{ - const uint8_t *ies = _ies; - - while (len >= 2 && len - 2 >= ies[1]) { - switch (ies[0]) { - case 0: { /* SSID */ - const char *ssid = (const char *)&ies[2]; - const size_t ssid_len = ies[1]; - - if (iface->ssid == NULL || strncmp(iface->ssid, ssid, ssid_len) != 0) - LOG_INFO("%s: SSID: %.*s", iface->name, (int)ssid_len, ssid); - - mtx_lock(&mod->lock); - free(iface->ssid); - iface->ssid = strndup(ssid, ssid_len); - mtx_unlock(&mod->lock); - - mod->bar->refresh(mod->bar); - break; - } - } - len -= ies[1] + 2; - ies += ies[1] + 2; - } - - return true; -} - -struct scan_results_context { - bool associated; - - const void *ies; - size_t ies_size; -}; - -static bool -handle_nl80211_bss(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, - void *_ctx) -{ - struct scan_results_context *ctx = _ctx; - - switch (type) { - case NL80211_BSS_STATUS: { - const uint32_t status = *(uint32_t *)payload; - - if (status == NL80211_BSS_STATUS_ASSOCIATED) { - ctx->associated = true; - - if (ctx->ies != NULL) { - /* Deferred handling of BSS_INFORMATION_ELEMENTS */ - return handle_ies(mod, iface, ctx->ies, ctx->ies_size); - } - } - break; - } - - case NL80211_BSS_INFORMATION_ELEMENTS: - if (ctx->associated) - return handle_ies(mod, iface, payload, len); - else { - /* - * We’re either not associated, or, we haven’t seen the - * BSS_STATUS attribute yet. - * - * Save a pointer to the IES payload, so that we can - * process it later, if we see a - * BSS_STATUS == BSS_STATUS_ASSOCIATED. - */ - ctx->ies = payload; - ctx->ies_size = len; - } - } - - return true; -} - -static bool -handle_nl80211_scan_results(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) -{ - struct scan_results_context ctx = {0}; - - switch (type) { - case NL80211_ATTR_BSS: - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_bss, &ctx); - break; - } - - return true; -} - /* * Reads at least one (possibly more) message. * @@ -1234,57 +400,24 @@ netlink_receive_messages(int sock, void **reply, size_t *len) return true; } -static void -handle_stats(struct module *mod, uint32_t seq, struct rt_stats_msg *msg) -{ - struct private *m = mod->private; - - struct iface *iface = NULL; - - tll_foreach(m->ifaces, it) - { - if (seq != it->item.get_stats_seq_nr) - continue; - iface = &it->item; - /* Current request is now considered complete */ - iface->get_stats_seq_nr = 0; - break; - } - - if (iface == NULL) { - LOG_ERR("Couldn't find iface"); - return; - } - - uint64_t ul_bits = msg->stats.tx_bytes * 8; - uint64_t dl_bits = msg->stats.rx_bytes * 8; - - const double poll_interval_secs = (double)m->poll_interval / 1000.; - - if (iface->ul_bits != 0) - iface->ul_speed = (double)(ul_bits - iface->ul_bits) / poll_interval_secs; - if (iface->dl_bits != 0) - iface->dl_speed = (double)(dl_bits - iface->dl_bits) / poll_interval_secs; - - iface->ul_bits = ul_bits; - iface->dl_bits = dl_bits; -} - static bool -parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) +parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) { struct private *m = mod->private; /* Process response */ for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { - switch (hdr->nlmsg_type) { case NLMSG_DONE: + if (m->ifindex == -1) { + LOG_ERR("%s: failed to find interface", m->iface); + return false; + } /* Request initial list of IPv4/6 addresses */ - if (m->get_addresses) { + if (m->get_addresses && m->ifindex != -1) { m->get_addresses = false; - send_rt_request(m, RTM_GETADDR); + send_rt_request(m->nl_sock, RTM_GETADDR); } break; @@ -1305,146 +438,17 @@ parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) handle_address(mod, hdr->nlmsg_type, msg, msg_len); break; } - case RTM_NEWSTATS: { - struct rt_stats_msg *msg = NLMSG_DATA(hdr); - handle_stats(mod, hdr->nlmsg_seq, msg); - break; - } - case NLMSG_ERROR: { + case NLMSG_ERROR:{ const struct nlmsgerr *err = NLMSG_DATA(hdr); - LOG_ERRNO_P(-err->error, "netlink RT reply"); + LOG_ERRNO_P("netlink", err->error); return false; } default: - LOG_WARN("unrecognized netlink message type: 0x%x", hdr->nlmsg_type); - return false; - } - } - - return true; -} - -static bool -parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) -{ - struct private *m = mod->private; - struct iface *iface = NULL; - - for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { - if (hdr->nlmsg_type == GENL_ID_CTRL) { - assert(hdr->nlmsg_seq == 1); - const struct genlmsghdr *genl = NLMSG_DATA(hdr); - const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); - foreach_nlattr(mod, NULL, genl, msg_size, &handle_genl_ctrl, NULL); - continue; - } - - if (hdr->nlmsg_seq == m->nl80211.get_interface_seq_nr) { - /* Current request is now considered complete */ - m->nl80211.get_interface_seq_nr = 0; - - /* Can’t issue both get-station and get-scan at the - * same time. So, always run a get-scan when a - * get-station is complete */ - send_nl80211_get_scan(m); - } - - if (hdr->nlmsg_type == NLMSG_DONE) { - if (hdr->nlmsg_seq == m->nl80211.get_scan_seq_nr) { - /* Current request is now considered complete */ - m->nl80211.get_scan_seq_nr = 0; - - tll_foreach(m->ifaces, it) send_nl80211_get_station(m, &it->item); - } - } - - else if (hdr->nlmsg_type == m->nl80211.family_id) { - const struct genlmsghdr *genl = NLMSG_DATA(hdr); - const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); - - switch (genl->cmd) { - case NL80211_CMD_NEW_INTERFACE: - if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; - - LOG_DBG("%s: got interface information", iface->name); - free(iface->type); - iface->type = strdup("wlan"); - foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_interface, NULL); - break; - - case NL80211_CMD_CONNECT: - /* - * Update SSID - * - * Unfortunately, the SSID doesn’t appear to be - * included in *any* of the notifications sent when - * associating, authenticating and connecting to a - * station. - * - * Thus, we need to explicitly request an update. - */ - LOG_DBG("connected, requesting interface information"); - send_nl80211_get_interface(m); - break; - - case NL80211_CMD_DISCONNECT: - if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; - - LOG_DBG("%s: disconnected, resetting SSID etc", iface->name); - - mtx_lock(&mod->lock); - free(iface->ssid); - iface->ssid = NULL; - iface->signal_strength_dbm = 0; - iface->rx_bitrate = iface->tx_bitrate = 0; - mtx_unlock(&mod->lock); - break; - - case NL80211_CMD_NEW_STATION: - if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; - - LOG_DBG("%s: got station information", iface->name); - foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_station, NULL); - - LOG_DBG("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", iface->name, iface->signal_strength_dbm, - iface->rx_bitrate / 1000 / 1000, iface->tx_bitrate / 1000 / 1000); - break; - - case NL80211_CMD_NEW_SCAN_RESULTS: - if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; - - LOG_DBG("%s: got scan results", iface->name); - foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_scan_results, NULL); - break; - - default: - LOG_DBG("unrecognized nl80211 command: %hhu", genl->cmd); - break; - } - } - - else if (hdr->nlmsg_type == NLMSG_ERROR) { - const struct nlmsgerr *err = NLMSG_DATA(hdr); - int nl_errno = -err->error; - - if (nl_errno == ENODEV) - ; /* iface is not an nl80211 device */ - else if (nl_errno == ENOENT) - ; /* iface down? */ - else { - LOG_ERRNO_P(nl_errno, "nl80211 reply (seq-nr: %u)", hdr->nlmsg_seq); - } - } - - else { - LOG_WARN("unrecognized netlink message type: 0x%x", hdr->nlmsg_type); - return false; + LOG_WARN( + "unrecognized netlink message type: 0x%x", hdr->nlmsg_type); + break; } } @@ -1454,195 +458,92 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) static int run(struct module *mod) { - int ret = 1; struct private *m = mod->private; - int timer_fd = -1; - if (m->poll_interval > 0) { - timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - if (timer_fd < 0) { - LOG_ERRNO("failed to create poll timer FD"); - goto out; - } + m->nl_sock = netlink_connect(); + if (m->nl_sock == -1) + return 1; - const long secs = m->poll_interval / 1000; - const long msecs = m->poll_interval % 1000; - - struct itimerspec poll_time = { - .it_value = {.tv_sec = secs, .tv_nsec = msecs * 1000000}, - .it_interval = {.tv_sec = secs, .tv_nsec = msecs * 1000000}, - }; - - if (timerfd_settime(timer_fd, 0, &poll_time, NULL) < 0) { - LOG_ERRNO("failed to arm poll timer"); - goto out; - } - } - - m->rt_sock = netlink_connect_rt(); - m->genl_sock = netlink_connect_genl(); - - if (m->rt_sock < 0 || m->genl_sock < 0) - goto out; - - if (!send_rt_request(m, RTM_GETLINK) || !send_ctrl_get_family_request(m)) { - goto out; + if (!send_rt_request(m->nl_sock, RTM_GETLINK)) { + close(m->nl_sock); + m->nl_sock = -1; + return 1; } /* Main loop */ while (true) { struct pollfd fds[] = { {.fd = mod->abort_fd, .events = POLLIN}, - {.fd = m->rt_sock, .events = POLLIN}, - {.fd = m->genl_sock, .events = POLLIN}, - {.fd = timer_fd, .events = POLLIN}, + {.fd = m->nl_sock, .events = POLLIN} }; - poll(fds, 3 + (timer_fd >= 0 ? 1 : 0), -1); + poll(fds, 2, -1); - if (fds[0].revents & (POLLIN | POLLHUP)) + if (fds[0].revents & POLLIN) break; - if ((fds[1].revents & POLLHUP) || (fds[2].revents & POLLHUP)) { + if (fds[1].revents & POLLHUP) { LOG_ERR("disconnected from netlink socket"); break; } - if (fds[3].revents & POLLHUP) { - LOG_ERR("disconnected from timer FD"); + assert(fds[1].revents & POLLIN); + + /* Read one (or more) messages */ + void *reply; + size_t len; + if (!netlink_receive_messages(m->nl_sock, &reply, &len)) + break; + + /* Parse (and act upon) the received message(s) */ + if (!parse_reply(mod, (const struct nlmsghdr *)reply, len)) { + free(reply); break; } - if (fds[1].revents & POLLIN) { - /* Read one (or more) messages */ - void *reply; - size_t len; - if (!netlink_receive_messages(m->rt_sock, &reply, &len)) - break; - - /* Parse (and act upon) the received message(s) */ - if (!parse_rt_reply(mod, (const struct nlmsghdr *)reply, len)) { - free(reply); - break; - } - - free(reply); - } - - if (fds[2].revents & POLLIN) { - /* Read one (or more) messages */ - void *reply; - size_t len; - if (!netlink_receive_messages(m->genl_sock, &reply, &len)) - break; - - if (!parse_genl_reply(mod, (const struct nlmsghdr *)reply, len)) { - free(reply); - break; - } - - free(reply); - } - - if (fds[3].revents & POLLIN) { - uint64_t count; - ssize_t amount = read(timer_fd, &count, sizeof(count)); - if (amount < 0) { - LOG_ERRNO("failed to read from timer FD"); - break; - } - - tll_foreach(m->ifaces, it) - { - send_nl80211_get_station(m, &it->item); - send_rt_getstats_request(m, &it->item); - }; - } + free(reply); } - ret = 0; - -out: - if (m->rt_sock >= 0) - close(m->rt_sock); - if (m->genl_sock >= 0) - close(m->genl_sock); - if (timer_fd >= 0) - close(timer_fd); - m->rt_sock = m->genl_sock = -1; - return ret; + close(m->nl_sock); + m->nl_sock = -1; + return 0; } static struct module * -network_new(struct particle *label, int poll_interval, int left_spacing, int right_spacing) +network_new(const char *iface, struct particle *label) { - int urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); - if (urandom_fd < 0) { - LOG_ERRNO("failed to open /dev/urandom"); - return NULL; - } - struct private *priv = calloc(1, sizeof(*priv)); + priv->iface = strdup(iface); priv->label = label; - priv->poll_interval = poll_interval; - priv->left_spacing = left_spacing; - priv->right_spacing = right_spacing; - priv->genl_sock = -1; - priv->rt_sock = -1; - priv->urandom_fd = urandom_fd; - priv->get_addresses = false; - priv->nl80211.family_id = -1; + priv->nl_sock = -1; + priv->get_addresses = true; + priv->ifindex = -1; + priv->state = IF_OPER_DOWN; struct module *mod = module_common_new(); mod->private = priv; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { + const struct yml_node *name = yml_get_value(node, "name"); const struct yml_node *content = yml_get_value(node, "content"); - const struct yml_node *poll = yml_get_value(node, "poll-interval"); - const struct yml_node *spacing = yml_get_value(node, "spacing"); - const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); - const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); - int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) - : right_spacing != NULL ? yml_value_as_int(right_spacing) - : 0; - - return network_new(conf_to_particle(content, inherited), poll != NULL ? yml_value_as_int(poll) : 0, left, right); -} - -static bool -conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_unsigned(chain, node)) - return false; - - int interval = yml_value_as_int(node); - if (interval > 0 && interval < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); - return false; - } - - return true; + return network_new( + yml_value_as_string(name), conf_to_particle(content, inherited)); } static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, - {"poll-interval", false, &conf_verify_poll_interval}, + {"name", true, &conf_verify_string}, MODULE_COMMON_ATTRS, }; diff --git a/modules/niri-common.c b/modules/niri-common.c deleted file mode 100644 index ac53921..0000000 --- a/modules/niri-common.c +++ /dev/null @@ -1,377 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../log.h" -#include "niri-common.h" - -#define LOG_MODULE "niri:common" -#define LOG_ENABLE_DBG 0 - -static struct niri_socket instance = { - .fd = -1, - .abort_fd = -1, -}; - -static void -workspace_free(struct niri_workspace *workspace) -{ - free(workspace->name); - free(workspace); -} - -static void -parser(char *response) -{ - enum json_tokener_error error = json_tokener_success; - struct json_object *json = json_tokener_parse_verbose(response, &error); - if (error != json_tokener_success) { - LOG_WARN("failed to parse niri socket's response"); - return; - } - - enum niri_event events = 0; - struct json_object_iterator it = json_object_iter_begin(json); - struct json_object_iterator end = json_object_iter_end(json); - while (!json_object_iter_equal(&it, &end)) { - char const *key = json_object_iter_peek_name(&it); - - // "WorkspacesChanged": { - // "workspaces": [ - // { - // "id": 3, - // "idx": 1, - // "name": null, - // "output": "DP-4", - // "is_active": true, - // "is_focused": true, - // "active_window_id": 24 - // }, - // ... - // ] - // } - if (strcmp(key, "WorkspacesChanged") == 0) { - mtx_lock(&instance.mtx); - tll_foreach(instance.workspaces, it) { tll_remove_and_free(instance.workspaces, it, workspace_free); } - mtx_unlock(&instance.mtx); - - json_object *obj = json_object_iter_peek_value(&it); - json_object *workspaces = json_object_object_get(obj, "workspaces"); - - size_t length = json_object_array_length(workspaces); - for (size_t i = 0; i < length; ++i) { - json_object *ws_obj = json_object_array_get_idx(workspaces, i); - - // only add workspaces on the current yambar's monitor - struct json_object *output = json_object_object_get(ws_obj, "output"); - if (strcmp(instance.monitor, json_object_get_string(output)) != 0) - continue; - - struct niri_workspace *ws = calloc(1, sizeof(*ws)); - ws->idx = json_object_get_int(json_object_object_get(ws_obj, "idx")); - ws->id = json_object_get_int(json_object_object_get(ws_obj, "id")); - ws->active = json_object_get_boolean(json_object_object_get(ws_obj, "is_active")); - ws->focused = json_object_get_boolean(json_object_object_get(ws_obj, "is_focused")); - ws->empty = json_object_get_int(json_object_object_get(ws_obj, "active_window_id")) == 0; - - char const *name = json_object_get_string(json_object_object_get(ws_obj, "name")); - if (name) - ws->name = strdup(name); - - mtx_lock(&instance.mtx); - bool inserted = false; - tll_foreach(instance.workspaces, it) - { - if (it->item->idx > ws->idx) { - tll_insert_before(instance.workspaces, it, ws); - inserted = true; - break; - } - } - if (!inserted) - tll_push_back(instance.workspaces, ws); - mtx_unlock(&instance.mtx); - - events |= workspaces_changed; - } - } - - // "WorkspaceActivated": { - // "id": 7, - // "focused":true - // } - else if (strcmp(key, "WorkspaceActivated") == 0) { - json_object *obj = json_object_iter_peek_value(&it); - int id = json_object_get_int(json_object_object_get(obj, "id")); - - mtx_lock(&instance.mtx); - tll_foreach(instance.workspaces, it) - { - bool b = it->item->id == id; - it->item->focused = b; - it->item->active = b; - } - mtx_unlock(&instance.mtx); - - events |= workspace_activated; - } - - // "WorkspaceActiveWindowChanged": { - // "workspace_id": 3, - // "active_window_id": 8 - // } - else if (strcmp(key, "WorkspaceActiveWindowChanged") == 0) { - json_object *obj = json_object_iter_peek_value(&it); - int id = json_object_get_int(json_object_object_get(obj, "id")); - bool empty = json_object_get_int(json_object_object_get(obj, "active_window_id")) == 0; - - mtx_lock(&instance.mtx); - tll_foreach(instance.workspaces, it) - { - if (it->item->id == id) { - it->item->empty = empty; - break; - } - } - mtx_unlock(&instance.mtx); - - events |= workspace_active_window_changed; - } - - // - // "KeyboardLayoutsChanged": { - // "keyboard_layouts": { - // "names": [ - // "English (US)", - // "Russian" - // ], - // "current_idx": 0 - // } - // } - else if (strcmp(key, "KeyboardLayoutsChanged") == 0) { - tll_foreach(instance.keyboard_layouts, it) { tll_remove_and_free(instance.keyboard_layouts, it, free); } - - json_object *obj = json_object_iter_peek_value(&it); - json_object *kb_layouts = json_object_object_get(obj, "keyboard_layouts"); - - instance.keyboard_layout_index = json_object_get_int(json_object_object_get(kb_layouts, "current_idx")); - - json_object *names = json_object_object_get(kb_layouts, "names"); - size_t names_length = json_object_array_length(names); - for (size_t i = 0; i < names_length; ++i) { - char const *name = json_object_get_string(json_object_array_get_idx(names, i)); - tll_push_back(instance.keyboard_layouts, strdup(name)); - } - - events |= keyboard_layouts_changed; - } - - // "KeyboardLayoutSwitched": { - // "idx": 1 - // } - else if (strcmp(key, "KeyboardLayoutSwitched") == 0) { - json_object *obj = json_object_iter_peek_value(&it); - instance.keyboard_layout_index = json_object_get_int(json_object_object_get(obj, "idx")); - - events |= keyboard_layouts_switched; - } - - json_object_iter_next(&it); - } - - json_object_put(json); - - mtx_lock(&instance.mtx); - tll_foreach(instance.subscribers, it) - { - if (it->item->events & events) - if (write(it->item->fd, &(uint64_t){1}, sizeof(uint64_t)) == -1) - LOG_ERRNO("failed to write"); - } - mtx_unlock(&instance.mtx); -} - -static int -run(void *userdata) -{ - static char msg[] = "\"EventStream\"\n"; - static char expected[] = "{\"Ok\":\"Handled\"}"; - - if (write(instance.fd, msg, sizeof(msg) / sizeof(msg[0])) == -1) { - LOG_ERRNO("failed to sent message to niri socket"); - return thrd_error; - } - - static char buffer[8192]; - if (read(instance.fd, buffer, sizeof(buffer) / sizeof(buffer[0]) - 1) == -1) { - LOG_ERRNO("failed to read response of niri socket"); - return thrd_error; - } - - char *saveptr; - char *response = strtok_r(buffer, "\n", &saveptr); - if (response == NULL || strcmp(expected, response) != 0) { - // unexpected first response, something went wrong - LOG_ERR("unexpected response of niri socket"); - return thrd_error; - } - - while ((response = strtok_r(NULL, "\n", &saveptr)) != NULL) - parser(response); - - while (true) { - struct pollfd fds[] = { - (struct pollfd){.fd = instance.abort_fd, .events = POLLIN}, - (struct pollfd){.fd = instance.fd, .events = POLLIN}, - }; - - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } - - if (fds[0].revents & POLLIN) - break; - - static char buffer[8192]; - ssize_t length = read(fds[1].fd, buffer, sizeof(buffer) / sizeof(buffer[0])); - - if (length == 0) - break; - - if (length == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - - LOG_ERRNO("unable to read niri socket"); - break; - } - - buffer[length] = '\0'; - saveptr = NULL; - response = strtok_r(buffer, "\n", &saveptr); - do { - parser(response); - } while ((response = strtok_r(NULL, "\n", &saveptr)) != NULL); - } - - return thrd_success; -} - -struct niri_socket * -niri_socket_open(char const *monitor) -{ - if (instance.fd >= 0) - return &instance; - - char const *path = getenv("NIRI_SOCKET"); - if (path == NULL) { - LOG_ERR("NIRI_SOCKET is empty. Is niri running?"); - return NULL; - } - - if ((instance.fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) { - LOG_ERRNO("failed to create socket"); - goto error; - } - - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); - - if (connect(instance.fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - LOG_ERRNO("failed to connect to niri socket"); - goto error; - } - - if ((instance.abort_fd = eventfd(0, EFD_CLOEXEC)) == -1) { - LOG_ERRNO("failed to create abort_fd"); - goto error; - } - - if (mtx_init(&instance.mtx, mtx_plain) != thrd_success) { - LOG_ERR("failed to initialize mutex"); - goto error; - } - - if (thrd_create(&instance.thrd, run, NULL) != thrd_success) { - LOG_ERR("failed to create thread"); - mtx_destroy(&instance.mtx); - goto error; - } - - instance.monitor = monitor; - - return &instance; - -error: - if (instance.fd >= 0) - close(instance.fd); - if (instance.abort_fd >= 0) - close(instance.abort_fd); - instance.fd = -1; - instance.abort_fd = -1; - instance.monitor = NULL; - - return NULL; -} - -static void -socket_close(void) -{ - if (write(instance.abort_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) - LOG_ERRNO("failed to write to abort_fd"); - - thrd_join(instance.thrd, NULL); - - close(instance.abort_fd); - close(instance.fd); - instance.abort_fd = -1; - instance.fd = -1; - - mtx_destroy(&instance.mtx); - - tll_free_and_free(instance.subscribers, free); - tll_free_and_free(instance.workspaces, workspace_free); - tll_free_and_free(instance.keyboard_layouts, free); -} - -void -niri_socket_close(void) -{ - static once_flag flag = ONCE_FLAG_INIT; - call_once(&flag, socket_close); -} - -int -niri_socket_subscribe(enum niri_event events) -{ - int fd = eventfd(0, EFD_CLOEXEC); - if (fd == -1) { - LOG_ERRNO("failed to create eventfd"); - return -1; - } - - struct niri_subscriber *subscriber = calloc(1, sizeof(*subscriber)); - subscriber->events = events; - subscriber->fd = fd; - - mtx_lock(&instance.mtx); - tll_push_back(instance.subscribers, subscriber); - mtx_unlock(&instance.mtx); - - return subscriber->fd; -} diff --git a/modules/niri-common.h b/modules/niri-common.h deleted file mode 100644 index 18afe38..0000000 --- a/modules/niri-common.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include - -enum niri_event { - workspaces_changed = (1 << 0), - workspace_activated = (1 << 1), - workspace_active_window_changed = (1 << 2), - keyboard_layouts_changed = (1 << 3), - keyboard_layouts_switched = (1 << 4), -}; - -struct niri_subscriber { - int events; - int fd; -}; - -struct niri_workspace { - int id; - int idx; - char *name; - bool active; - bool focused; - bool empty; -}; - -struct niri_socket { - char const *monitor; - int abort_fd; - int fd; - - tll(struct niri_subscriber *) subscribers; - tll(struct niri_workspace *) workspaces; - tll(char *) keyboard_layouts; - size_t keyboard_layout_index; - - thrd_t thrd; - mtx_t mtx; -}; - -struct niri_socket *niri_socket_open(char const *monitor); -void niri_socket_close(void); -int niri_socket_subscribe(enum niri_event events); diff --git a/modules/niri-language.c b/modules/niri-language.c deleted file mode 100644 index f8138ee..0000000 --- a/modules/niri-language.c +++ /dev/null @@ -1,160 +0,0 @@ -#include -#include -#include -#include -#include - -#define LOG_MODULE "niri-language" -#define LOG_ENABLE_DBG 0 -#include "niri-common.h" - -#include "../log.h" -#include "../particles/dynlist.h" -#include "../plugin.h" - -struct private -{ - struct particle *label; - struct niri_socket *niri; -}; - -static void -destroy(struct module *module) -{ - struct private *private = module->private; - private->label->destroy(private->label); - - free(private); - - module_default_destroy(module); -} - -static const char * -description(const struct module *module) -{ - return "niri-lang"; -} - -static struct exposable * -content(struct module *module) -{ - const struct private *private = module->private; - - if (private->niri == NULL) - return dynlist_exposable_new(&((struct exposable *){0}), 0, 0, 0); - - mtx_lock(&module->lock); - mtx_lock(&private->niri->mtx); - - char *name = "???"; - size_t i = 0; - tll_foreach(private->niri->keyboard_layouts, it) - { - if (i++ == private->niri->keyboard_layout_index) - name = it->item; - } - - struct tag_set tags = { - .tags = (struct tag *[]){tag_new_string(module, "language", name)}, - .count = 1, - }; - - struct exposable *exposable = private->label->instantiate(private->label, &tags); - tag_set_destroy(&tags); - mtx_unlock(&private->niri->mtx); - mtx_unlock(&module->lock); - return exposable; -} - -static int -run(struct module *module) -{ - struct private *private = module->private; - - /* Ugly, but I didn't find better way for waiting - * the monitor's name to be set */ - char const *monitor; - do { - monitor = module->bar->output_name(module->bar); - usleep(50); - } while (monitor == NULL); - - private->niri = niri_socket_open(monitor); - if (private->niri == NULL) - return 1; - - int fd = niri_socket_subscribe(keyboard_layouts_changed | keyboard_layouts_switched); - if (fd == -1) { - niri_socket_close(); - return 1; - } - - module->bar->refresh(module->bar); - - while (true) { - struct pollfd fds[] = { - (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, - (struct pollfd){.fd = fd, .events = POLLIN}, - }; - - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } - - if (fds[0].revents & POLLIN) - break; - - if (read(fds[1].fd, &(uint64_t){0}, sizeof(uint64_t)) == -1) - LOG_ERRNO("failed to read from eventfd"); - - module->bar->refresh(module->bar); - } - - niri_socket_close(); - return 0; -} - -static struct module * -niri_language_new(struct particle *label) -{ - struct private *private = calloc(1, sizeof(struct private)); - private->label = label; - - struct module *module = module_common_new(); - module->private = private; - module->run = &run; - module->destroy = &destroy; - module->content = &content; - module->description = &description; - - return module; -} - -static struct module * -from_conf(struct yml_node const *node, struct conf_inherit inherited) -{ - struct yml_node const *content = yml_get_value(node, "content"); - return niri_language_new(conf_to_particle(content, inherited)); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static struct attr_info const attrs[] = { - MODULE_COMMON_ATTRS, - }; - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_niri_language_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_niri_language_iface"))); -#endif diff --git a/modules/niri-workspaces.c b/modules/niri-workspaces.c deleted file mode 100644 index bca0150..0000000 --- a/modules/niri-workspaces.c +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include -#include -#include - -#define LOG_MODULE "niri-workspaces" -#define LOG_ENABLE_DBG 0 -#include "niri-common.h" - -#include "../log.h" -#include "../particles/dynlist.h" -#include "../plugin.h" - -struct private -{ - struct particle *label; - struct niri_socket *niri; -}; - -static void -destroy(struct module *module) -{ - struct private *private = module->private; - private->label->destroy(private->label); - - free(private); - - module_default_destroy(module); -} - -static const char * -description(const struct module *module) -{ - return "niri-ws"; -} - -static struct exposable * -content(struct module *module) -{ - struct private const *private = module->private; - - if (private->niri == NULL) - return dynlist_exposable_new(&((struct exposable *){0}), 0, 0, 0); - - mtx_lock(&module->lock); - mtx_lock(&private->niri->mtx); - - size_t i = 0; - struct exposable *exposable[tll_length(private->niri->workspaces)]; - tll_foreach(private->niri->workspaces, it) - { - struct tag_set tags = { - .tags = (struct tag*[]){ - tag_new_int(module, "id", it->item->idx), - tag_new_string(module, "name", it->item->name), - tag_new_bool(module, "active", it->item->active), - tag_new_bool(module, "focused", it->item->focused), - tag_new_bool(module, "empty", it->item->empty), - }, - .count = 5, - }; - - exposable[i++] = private->label->instantiate(private->label, &tags); - tag_set_destroy(&tags); - } - - mtx_unlock(&private->niri->mtx); - mtx_unlock(&module->lock); - return dynlist_exposable_new(exposable, i, 0, 0); -} - -static int -run(struct module *module) -{ - struct private *private = module->private; - - /* Ugly, but I didn't find better way for waiting - * the monitor's name to be set */ - char const *monitor; - do { - monitor = module->bar->output_name(module->bar); - usleep(50); - } while (monitor == NULL); - - private->niri = niri_socket_open(monitor); - if (private->niri == NULL) - return 1; - - int fd = niri_socket_subscribe(workspaces_changed | workspace_activated | workspace_active_window_changed); - if (fd == -1) { - niri_socket_close(); - return 1; - } - - module->bar->refresh(module->bar); - - while (true) { - struct pollfd fds[] = { - (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, - (struct pollfd){.fd = fd, .events = POLLIN}, - }; - - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } - - if (fds[0].revents & POLLIN) - break; - - if (read(fds[1].fd, &(uint64_t){0}, sizeof(uint64_t)) == -1) - LOG_ERRNO("failed to read from eventfd"); - - module->bar->refresh(module->bar); - } - - niri_socket_close(); - return 0; -} - -static struct module * -niri_workspaces_new(struct particle *label) -{ - struct private *private = calloc(1, sizeof(struct private)); - private->label = label; - - struct module *module = module_common_new(); - module->private = private; - module->run = &run; - module->destroy = &destroy; - module->content = &content; - module->description = &description; - - return module; -} - -static struct module * -from_conf(struct yml_node const *node, struct conf_inherit inherited) -{ - struct yml_node const *content = yml_get_value(node, "content"); - return niri_workspaces_new(conf_to_particle(content, inherited)); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static struct attr_info const attrs[] = { - MODULE_COMMON_ATTRS, - }; - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_niri_workspaces_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_niri_workspaces_iface"))); -#endif diff --git a/modules/pipewire.c b/modules/pipewire.c deleted file mode 100644 index 1ff3642..0000000 --- a/modules/pipewire.c +++ /dev/null @@ -1,1025 +0,0 @@ -#include "spa/utils/list.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_MODULE "pipewire" -#define LOG_ENABLE_DBG 0 -#include "../config-verify.h" -#include "../config.h" -#include "../log.h" -#include "../module.h" -#include "../particle.h" -#include "../particles/dynlist.h" -#include "../plugin.h" -#include "../yml.h" - -#define ARRAY_LENGTH(x) (sizeof((x)) / sizeof((x)[0])) -/* clang-format off */ -#define X_FREE_SET(t, v) do { free((t)); (t) = (v); } while (0) -/* clang-format on */ -#define X_STRDUP(s) ((s) != NULL ? strdup((s)) : NULL) - -struct output_informations { - /* internal */ - uint32_t device_id; - uint32_t card_profile_device_id; - - /* information */ - bool muted; - uint16_t linear_volume; /* classic volume */ - uint16_t cubic_volume; /* volume a la pulseaudio */ - char *name; - char *description; - char *form_factor; /* headset, headphone, speaker, ..., can be null */ - char *bus; /* alsa, bluetooth, etc */ - char *icon; -}; -static struct output_informations const output_informations_null; - -static void -output_informations_destroy(struct output_informations *output_informations) -{ - free(output_informations->name); - free(output_informations->description); - free(output_informations->icon); - free(output_informations->form_factor); - free(output_informations->bus); -} - -struct data; -struct private -{ - struct particle *label; - struct data *data; - int left_spacing; - int right_spacing; - - /* pipewire related */ - struct output_informations sink_informations; - struct output_informations source_informations; -}; - -/* This struct is needed because when param event occur, the function - * `node_events_param` will receive the corresponding event about the node - * but there's no simple way of knowing from which node the event come from */ -struct node_data { - struct data *data; - /* otherwise is_source */ - bool is_sink; -}; - -/* struct data */ -struct node; -struct data { - /* yambar module */ - struct module *module; - - char *target_sink; - char *target_source; - - struct node *binded_sink; - struct node *binded_source; - - struct node_data node_data_sink; - struct node_data node_data_source; - - /* proxies */ - void *metadata; - void *node_sink; - void *node_source; - - /* main struct */ - struct pw_main_loop *loop; - struct pw_context *context; - struct pw_core *core; - struct pw_registry *registry; - - /* listener */ - struct spa_hook registry_listener; - struct spa_hook core_listener; - struct spa_hook metadata_listener; - struct spa_hook node_sink_listener; - struct spa_hook node_source_listener; - - /* list */ - struct spa_list node_list; - struct spa_list device_list; - - int sync; -}; - -/* struct Route */ -struct route { - struct device *device; - - struct spa_list link; - - enum spa_direction direction; /* direction */ - int profile_device_id; /* device */ - char *form_factor; /* info.type */ - char *icon_name; /* info.icon-name */ -}; - -static void -route_free(struct route *route) -{ - free(route->form_factor); - free(route->icon_name); - spa_list_remove(&route->link); - free(route); -} - -/* struct Device */ -struct device { - struct data *data; - - struct spa_list link; - uint32_t id; - struct spa_list routes; - - void *proxy; - struct spa_hook listener; -}; - -static void -device_free(struct device *device, struct data *data) -{ - struct route *route = NULL; - spa_list_consume(route, &device->routes, link) route_free(route); - - spa_hook_remove(&device->listener); - pw_proxy_destroy((struct pw_proxy *)device->proxy); - - spa_list_remove(&device->link); - free(device); -} - -static struct route * -route_find_or_create(struct device *device, uint32_t profile_device_id) -{ - struct route *route = NULL; - spa_list_for_each(route, &device->routes, link) - { - if (route->profile_device_id == profile_device_id) - return route; - } - - /* route not found, let's create it */ - route = calloc(1, sizeof(struct route)); - assert(route != NULL); - route->device = device; - route->profile_device_id = profile_device_id; - spa_list_append(&device->routes, &route->link); - return route; -} - -struct node { - struct spa_list link; - uint32_t id; - char *name; -}; - -/* struct node */ -static struct route * -node_find_route(struct data *data, bool is_sink) -{ - struct private *private = data->module->private; - struct output_informations *output_informations = NULL; - - if (is_sink) { - if (data->node_sink == NULL) - return NULL; - output_informations = &private->sink_informations; - } else { - if (data->node_source == NULL) - return NULL; - output_informations = &private->source_informations; - } - - struct device *device = NULL; - spa_list_for_each(device, &data->device_list, link) - { - if (device->id != output_informations->device_id) - continue; - - struct route *route = NULL; - spa_list_for_each(route, &device->routes, link) - { - if (route->profile_device_id == output_informations->card_profile_device_id) - return route; - } - } - - return NULL; -} - -static void -node_unhook_binded_node(struct data *data, bool is_sink) -{ - struct private *private = data->module->private; - - struct node **target_node = NULL; - struct spa_hook *target_listener = NULL; - void **target_proxy = NULL; - struct output_informations *output_informations = NULL; - - if (is_sink) { - target_node = &data->binded_sink; - target_listener = &data->node_sink_listener; - target_proxy = &data->node_sink; - output_informations = &private->sink_informations; - } else { - target_node = &data->binded_source; - target_listener = &data->node_source_listener; - target_proxy = &data->node_source; - output_informations = &private->source_informations; - } - - if (*target_node == NULL) - return; - - spa_hook_remove(target_listener); - pw_proxy_destroy(*target_proxy); - - *target_node = NULL; - *target_proxy = NULL; - - output_informations_destroy(output_informations); - *output_informations = output_informations_null; -} - -static void -node_free(struct node *node, struct data *data) -{ - if (data->binded_sink == node) - node_unhook_binded_node(data, true); - else if (data->binded_source == node) - node_unhook_binded_node(data, false); - - spa_list_remove(&node->link); - free(node->name); - free(node); -} - -/* Device events */ -static void -device_events_info(void *userdata, const struct pw_device_info *info) -{ - struct device *device = userdata; - - /* We only want the "Route" param, which is in Params */ - if (!(info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS)) - return; - - for (size_t i = 0; i < info->n_params; ++i) { - if (info->params[i].id == SPA_PARAM_Route) { - pw_device_enum_params(device->proxy, 0, info->params[i].id, 0, -1, NULL); - break; - } - } -} - -static void -device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) -{ - /* We should only receive ParamRoute */ - assert(spa_pod_is_object_type(param, SPA_TYPE_OBJECT_ParamRoute)); - - struct route data = {0}; - struct spa_pod_prop const *prop = NULL; - - /* device must be present otherwise I can't do anything with the data */ - prop = spa_pod_find_prop(param, NULL, SPA_PARAM_ROUTE_device); - if (prop == NULL) - return; - spa_pod_get_int(&prop->value, &data.profile_device_id); - - /* same for direction, required too */ - prop = spa_pod_find_prop(param, NULL, SPA_PARAM_ROUTE_direction); - if (prop == NULL) - return; - char const *direction = NULL; - spa_pod_get_string(&prop->value, &direction); - if (spa_streq(direction, "Output")) - data.direction = SPA_DIRECTION_OUTPUT; - else - data.direction = SPA_DIRECTION_INPUT; - - /* same for info, it's required */ - prop = spa_pod_find_prop(param, NULL, SPA_PARAM_ROUTE_info); - if (prop == NULL) - return; - - struct spa_pod *iter = NULL; - char const *header = NULL; - SPA_POD_STRUCT_FOREACH(&prop->value, iter) - { - /* no previous header */ - if (header == NULL) { - /* headers are always string */ - if (spa_pod_is_string(iter)) - spa_pod_get_string(iter, &header); - /* otherwise it's the first iteration (number of elements in the struct) */ - continue; - } - - /* Values needed: - * - (string) device.icon_name [icon_name] - * - (string) port.type [form_factor] */ - if (spa_pod_is_string(iter)) { - if (spa_streq(header, "device.icon_name")) - spa_pod_get_string(iter, (char const **)&data.icon_name); - else if (spa_streq(header, "port.type")) { - spa_pod_get_string(iter, (char const **)&data.form_factor); - } - } - - header = NULL; - } - - struct device *device = userdata; - - struct route *route = route_find_or_create(device, data.profile_device_id); - X_FREE_SET(route->form_factor, X_STRDUP(data.form_factor)); - X_FREE_SET(route->icon_name, X_STRDUP(data.icon_name)); - route->direction = data.direction; - - /* set missing information if possible */ - struct private *private = device->data->module->private; - struct node *binded_node = NULL; - struct output_informations *output_informations = NULL; - - if (route->direction == SPA_DIRECTION_INPUT) { - binded_node = private->data->binded_source; - output_informations = &private->source_informations; - } else { - binded_node = private->data->binded_sink; - output_informations = &private->sink_informations; - } - - /* Node not binded */ - if (binded_node == NULL) - return; - - /* Node's device is the same as route's device */ - if (output_informations->device_id != route->device->id) - return; - - /* Route is not the Node's device route */ - if (output_informations->card_profile_device_id != route->profile_device_id) - return; - - /* Update missing information */ - X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor)); - X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name)); - - device->data->module->bar->refresh(device->data->module->bar); -} - -static struct pw_device_events const device_events = { - PW_VERSION_DEVICE_EVENTS, - .info = device_events_info, - .param = device_events_param, -}; - -/* Node events */ -static void -node_events_info(void *userdata, struct pw_node_info const *info) -{ - struct node_data *node_data = userdata; - struct data *data = node_data->data; - struct private *private = data->module->private; - - if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { - /* We only need the Props param, so let's try to find it */ - for (size_t i = 0; i < info->n_params; ++i) { - if (info->params[i].id == SPA_PARAM_Props) { - void *target_node = (node_data->is_sink ? data->node_sink : data->node_source); - /* Found it, will emit a param event, the param will then be handled - * in node_events_param */ - pw_node_enum_params(target_node, 0, info->params[i].id, 0, -1, NULL); - break; - } - } - } - - if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) { - struct output_informations *output_informations - = (node_data->is_sink ? &private->sink_informations : &private->source_informations); - struct spa_dict_item const *item = NULL; - - item = spa_dict_lookup_item(info->props, "node.name"); - X_FREE_SET(output_informations->name, item != NULL ? X_STRDUP(item->value) : NULL); - - item = spa_dict_lookup_item(info->props, "node.description"); - X_FREE_SET(output_informations->description, item != NULL ? X_STRDUP(item->value) : NULL); - - item = spa_dict_lookup_item(info->props, "device.id"); - if (item != NULL) { - uint32_t value = 0; - spa_atou32(item->value, &value, 10); - output_informations->device_id = value; - } else { - output_informations->device_id = 0; - } - - item = spa_dict_lookup_item(info->props, "card.profile.device"); - if (item != NULL) { - uint32_t value = 0; - spa_atou32(item->value, &value, 10); - output_informations->card_profile_device_id = value; - } else { - output_informations->card_profile_device_id = 0; - } - - /* Device's information has an more important priority than node's information */ - /* icon_name */ - struct route *route = node_find_route(data, node_data->is_sink); - if (route != NULL && route->icon_name != NULL) - X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name)); - else { - item = spa_dict_lookup_item(info->props, "device.icon-name"); - X_FREE_SET(output_informations->icon, item != NULL ? X_STRDUP(item->value) : NULL); - } - /* form_factor */ - if (route != NULL && route->form_factor != NULL) - X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor)); - else { - item = spa_dict_lookup_item(info->props, "device.form-factor"); - X_FREE_SET(output_informations->form_factor, item != NULL ? X_STRDUP(item->value) : NULL); - } - - item = spa_dict_lookup_item(info->props, "device.bus"); - X_FREE_SET(output_informations->bus, item != NULL ? X_STRDUP(item->value) : NULL); - - data->module->bar->refresh(data->module->bar); - } -} - -static void -node_events_param(void *userdata, __attribute__((unused)) int seq, __attribute__((unused)) uint32_t id, - __attribute__((unused)) uint32_t index, __attribute__((unused)) uint32_t next, - const struct spa_pod *param) -{ - struct node_data *node_data = userdata; - struct data *data = node_data->data; - struct private *private = data->module->private; - - struct output_informations *output_informations - = (node_data->is_sink ? &private->sink_informations : &private->source_informations); - struct spa_pod_prop const *prop = NULL; - - prop = spa_pod_find_prop(param, NULL, SPA_PROP_mute); - if (prop != NULL) { - bool value = false; - spa_pod_get_bool(&prop->value, &value); - output_informations->muted = value; - } - - prop = spa_pod_find_prop(param, NULL, SPA_PROP_channelVolumes); - if (prop != NULL) { - uint32_t n_values = 0; - float *values = spa_pod_get_array(&prop->value, &n_values); - float total = 0.0f; - - /* Array can be empty some times, assume that values have not changed */ - if (n_values != 0) { - for (uint32_t i = 0; i < n_values; ++i) - total += values[i]; - - float base_volume = total / n_values; - output_informations->linear_volume = roundf(base_volume * 100); - output_informations->cubic_volume = roundf(cbrtf(base_volume) * 100); - } - } - - data->module->bar->refresh(data->module->bar); -} - -static struct pw_node_events const node_events = { - PW_VERSION_NODE_EVENTS, - .info = node_events_info, - .param = node_events_param, -}; - -/* Metadata events */ -static int -metadata_property(void *userdata, __attribute__((unused)) uint32_t id, char const *key, - __attribute__((unused)) char const *type, char const *value) -{ - struct data *data = userdata; - bool is_sink = false; // true for source mode - char **target_name = NULL; - - /* We only want default.audio.sink or default.audio.source */ - if (spa_streq(key, "default.audio.sink")) { - is_sink = true; - target_name = &data->target_sink; - } else if (spa_streq(key, "default.audio.source")) { - is_sink = false; /* just to be explicit */ - target_name = &data->target_source; - } else - return 0; - - /* Value is NULL when the profile is set to `off`. */ - if (value == NULL) { - node_unhook_binded_node(data, is_sink); - free(*target_name); - *target_name = NULL; - data->module->bar->refresh(data->module->bar); - return 0; - } - - struct json_object *json = json_tokener_parse(value); - struct json_object_iterator json_it = json_object_iter_begin(json); - struct json_object_iterator json_it_end = json_object_iter_end(json); - - while (!json_object_iter_equal(&json_it, &json_it_end)) { - char const *key = json_object_iter_peek_name(&json_it); - if (!spa_streq(key, "name")) { - json_object_iter_next(&json_it); - continue; - } - - /* Found name */ - struct json_object *value = json_object_iter_peek_value(&json_it); - assert(json_object_is_type(value, json_type_string)); - - char const *name = json_object_get_string(value); - /* `auto_null` is the same as `value == NULL` see lines above. */ - if (spa_streq(name, "auto_null")) { - node_unhook_binded_node(data, is_sink); - free(*target_name); - *target_name = NULL; - data->module->bar->refresh(data->module->bar); - break; - } - - /* target_name is the same */ - if (spa_streq(name, *target_name)) - break; - - /* Unhook the binded_node */ - node_unhook_binded_node(data, is_sink); - - /* Update the target */ - free(*target_name); - *target_name = strdup(name); - - /* Sync the core, core_events_done will then try to bind the good node */ - data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); - break; - } - - json_object_put(json); - - return 0; -} - -static struct pw_metadata_events const metadata_events = { - PW_VERSION_METADATA_EVENTS, - .property = metadata_property, -}; - -/* Registry events */ -static void -registry_event_global(void *userdata, uint32_t id, __attribute__((unused)) uint32_t permissions, char const *type, - __attribute__((unused)) uint32_t version, struct spa_dict const *props) -{ - struct data *data = userdata; - - /* New device */ - if (spa_streq(type, PW_TYPE_INTERFACE_Device)) { - struct device *device = calloc(1, sizeof(struct device)); - assert(device != NULL); - device->data = data; - device->id = id; - spa_list_init(&device->routes); - device->proxy = pw_registry_bind(data->registry, id, type, PW_VERSION_DEVICE, 0); - assert(device->proxy != NULL); - pw_device_add_listener(device->proxy, &device->listener, &device_events, device); - - spa_list_append(&data->device_list, &device->link); - } - /* New node */ - else if (spa_streq(type, PW_TYPE_INTERFACE_Node)) { - /* Fill a new node */ - struct node *node = calloc(1, sizeof(struct node)); - assert(node != NULL); - node->id = id; - node->name = strdup(spa_dict_lookup(props, PW_KEY_NODE_NAME)); - - /* Store it */ - spa_list_append(&data->node_list, &node->link); - } - /* New metadata */ - else if (spa_streq(type, PW_TYPE_INTERFACE_Metadata)) { - /* A metadata has already been bind */ - if (data->metadata != NULL) - return; - - /* Target only metadata which has "default" key */ - char const *name = spa_dict_lookup(props, PW_KEY_METADATA_NAME); - if (name == NULL || !spa_streq(name, "default")) - return; - - /* Bind metadata */ - data->metadata = pw_registry_bind(data->registry, id, type, PW_VERSION_METADATA, 0); - assert(data->metadata != NULL); - pw_metadata_add_listener(data->metadata, &data->metadata_listener, &metadata_events, data); - } - - /* `core_events_done` will then try to bind the good node */ - data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); -} - -static void -registry_event_global_remove(void *userdata, uint32_t id) -{ - struct data *data = userdata; - - /* Try to find a node with the same `id` */ - struct node *node = NULL, *node_temp = NULL; - spa_list_for_each_safe(node, node_temp, &data->node_list, link) - { - if (node->id == id) { - node_free(node, data); - return; - } - } - - /* No node with this `id` maybe it's a device */ - struct device *device = NULL, *device_temp = NULL; - spa_list_for_each_safe(device, device_temp, &data->device_list, link) - { - if (device->id == id) { - device_free(device, data); - return; - } - } -} - -static struct pw_registry_events const registry_events = { - PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, - .global_remove = registry_event_global_remove, -}; - -static void -try_to_bind_node(struct node_data *node_data, char const *target_name, struct node **target_node, void **target_proxy, - struct spa_hook *target_listener) -{ - /* profile deactivated */ - if (target_name == NULL) - return; - - struct data *data = node_data->data; - - struct node *node = NULL; - spa_list_for_each(node, &data->node_list, link) - { - if (!spa_streq(target_name, node->name)) - continue; - - /* Found good node */ - - *target_node = node; - *target_proxy = pw_registry_bind(data->registry, node->id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0); - assert(*target_proxy != NULL); - pw_node_add_listener(*target_proxy, target_listener, &node_events, node_data); - break; - } -} - -/* Core events */ -static void -core_events_done(void *userdata, uint32_t id, int seq) -{ - struct data *data = userdata; - - if (id != PW_ID_CORE) - return; - - /* Not our seq */ - if (data->sync != seq) - return; - - /* Sync ended, try to bind the node which has the targeted sink or the targeted source */ - - /* Node sink not binded and target_sink is set */ - if (data->binded_sink == NULL && data->target_sink != NULL) - try_to_bind_node(&data->node_data_sink, data->target_sink, &data->binded_sink, &data->node_sink, - &data->node_sink_listener); - - /* Node source not binded and target_source is set */ - if (data->binded_source == NULL && data->target_source != NULL) - try_to_bind_node(&data->node_data_source, data->target_source, &data->binded_source, &data->node_source, - &data->node_source_listener); -} - -static void -core_events_error(void *userdata, uint32_t id, int seq, int res, char const *message) -{ - pw_log_error("error id:%u seq:%d res:%d (%s): %s", id, seq, res, spa_strerror(res), message); - - if (id == PW_ID_CORE && res == -EPIPE) { - struct data *data = userdata; - pw_main_loop_quit(data->loop); - } -} - -static struct pw_core_events const core_events = { - PW_VERSION_CORE_EVENTS, - .done = core_events_done, - .error = core_events_error, -}; - -/* init, deinit */ -static struct data * -pipewire_init(struct module *module) -{ - pw_init(NULL, NULL); - - /* Data */ - struct data *data = calloc(1, sizeof(struct data)); - assert(data != NULL); - - spa_list_init(&data->node_list); - spa_list_init(&data->device_list); - - /* Main loop */ - data->loop = pw_main_loop_new(NULL); - if (data->loop == NULL) { - LOG_ERR("failed to instantiate main loop"); - goto err; - } - - /* Context */ - data->context = pw_context_new(pw_main_loop_get_loop(data->loop), NULL, 0); - if (data->context == NULL) { - LOG_ERR("failed to instantiate pipewire context"); - goto err; - } - - /* Core */ - data->core = pw_context_connect(data->context, NULL, 0); - if (data->core == NULL) { - LOG_ERR("failed to connect to pipewire"); - goto err; - } - pw_core_add_listener(data->core, &data->core_listener, &core_events, data); - - /* Registry */ - data->registry = pw_core_get_registry(data->core, PW_VERSION_REGISTRY, 0); - if (data->registry == NULL) { - LOG_ERR("failed to get core registry"); - goto err; - } - pw_registry_add_listener(data->registry, &data->registry_listener, ®istry_events, data); - - /* Sync */ - data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); - - data->module = module; - - /* node_events_param_data */ - data->node_data_sink.data = data; - data->node_data_sink.is_sink = true; - data->node_data_source.data = data; - data->node_data_source.is_sink = false; - - return data; - -err: - if (data->registry != NULL) - pw_proxy_destroy((struct pw_proxy *)data->registry); - if (data->core != NULL) - pw_core_disconnect(data->core); - if (data->context != NULL) - pw_context_destroy(data->context); - if (data->loop != NULL) - pw_main_loop_destroy(data->loop); - free(data); - return NULL; -} - -static void -pipewire_deinit(struct data *data) -{ - if (data == NULL) - return; - - struct node *node = NULL; - spa_list_consume(node, &data->node_list, link) node_free(node, data); - - struct device *device = NULL; - spa_list_consume(device, &data->device_list, link) device_free(device, data); - - if (data->metadata) - pw_proxy_destroy((struct pw_proxy *)data->metadata); - spa_hook_remove(&data->registry_listener); - pw_proxy_destroy((struct pw_proxy *)data->registry); - spa_hook_remove(&data->core_listener); - spa_hook_remove(&data->metadata_listener); - pw_core_disconnect(data->core); - pw_context_destroy(data->context); - pw_main_loop_destroy(data->loop); - free(data->target_sink); - free(data->target_source); - pw_deinit(); -} - -static void -destroy(struct module *module) -{ - struct private *private = module->private; - - pipewire_deinit(private->data); - private->label->destroy(private->label); - - output_informations_destroy(&private->sink_informations); - output_informations_destroy(&private->source_informations); - - free(private); - module_default_destroy(module); -} - -static char const * -description(const struct module *module) -{ - return "pipewire"; -} - -static struct exposable * -content(struct module *module) -{ - struct private *private = module->private; - - if (private->data == NULL) - return dynlist_exposable_new(NULL, 0, 0, 0); - - mtx_lock(&module->lock); - - struct exposable *exposables[2]; - size_t exposables_length = ARRAY_LENGTH(exposables); - - struct output_informations const *output_informations = NULL; - - /* sink */ - output_informations - = (private->data->target_sink == NULL ? &output_informations_null : &private->sink_informations); - - struct tag_set sink_tag_set = (struct tag_set){ - .tags = (struct tag *[]){ - tag_new_string(module, "type", "sink"), - tag_new_string(module, "name", output_informations->name), - tag_new_string(module, "description", output_informations->description), - tag_new_string(module, "icon", output_informations->icon), - tag_new_string(module, "form_factor", output_informations->form_factor), - tag_new_string(module, "bus", output_informations->bus), - tag_new_bool(module, "muted", output_informations->muted), - tag_new_int_range(module, "linear_volume", output_informations->linear_volume, 0, 100), - tag_new_int_range(module, "cubic_volume", output_informations->cubic_volume, 0, 100), - }, - .count = 9, - }; - - /* source */ - output_informations - = (private->data->target_source == NULL ? &output_informations_null : &private->source_informations); - - struct tag_set source_tag_set = (struct tag_set){ - .tags = (struct tag *[]){ - tag_new_string(module, "type", "source"), - tag_new_string(module, "name", output_informations->name), - tag_new_string(module, "description", output_informations->description), - tag_new_string(module, "icon", output_informations->icon), - tag_new_string(module, "form_factor", output_informations->form_factor), - tag_new_string(module, "bus", output_informations->bus), - tag_new_bool(module, "muted", output_informations->muted), - tag_new_int_range(module, "linear_volume", output_informations->linear_volume, 0, 100), - tag_new_int_range(module, "cubic_volume", output_informations->cubic_volume, 0, 100), - }, - .count = 9, - }; - - exposables[0] = private->label->instantiate(private->label, &sink_tag_set); - exposables[1] = private->label->instantiate(private->label, &source_tag_set); - - tag_set_destroy(&sink_tag_set); - tag_set_destroy(&source_tag_set); - - mtx_unlock(&module->lock); - - return dynlist_exposable_new(exposables, exposables_length, private->left_spacing, private->right_spacing); -} - -static int -run(struct module *module) -{ - struct private *private = module->private; - if (private->data == NULL) - return 1; - - struct pw_loop *pw_loop = pw_main_loop_get_loop(private->data->loop); - struct pollfd pollfds[] = { - /* abort_fd */ - (struct pollfd){.fd = module->abort_fd, .events = POLLIN}, - /* pipewire */ - (struct pollfd){.fd = pw_loop_get_fd(pw_loop), .events = POLLIN}, - }; - - while (true) { - if (poll(pollfds, ARRAY_LENGTH(pollfds), -1) == -1) { - if (errno == EINTR) - continue; - - LOG_ERRNO("Unable to poll: %s", strerror(errno)); - break; - } - - /* abort_fd */ - if (pollfds[0].revents & POLLIN) - break; - - /* pipewire */ - if (!(pollfds[1].revents & POLLIN)) - /* issue happened */ - break; - - int result = pw_loop_iterate(pw_loop, 0); - if (result < 0) { - LOG_ERRNO("Unable to iterate pipewire loop: %s", spa_strerror(result)); - break; - } - } - - return 0; -} - -static struct module * -pipewire_new(struct particle *label, int left_spacing, int right_spacing) -{ - struct private *private = calloc(1, sizeof(struct private)); - assert(private != NULL); - private->label = label; - private->left_spacing = left_spacing; - private->right_spacing = right_spacing; - - struct module *module = module_common_new(); - module->private = private; - module->run = &run; - module->destroy = &destroy; - module->content = &content; - module->description = &description; - - private->data = pipewire_init(module); - - return module; -} - -static struct module * -from_conf(struct yml_node const *node, struct conf_inherit inherited) -{ - struct yml_node const *content = yml_get_value(node, "content"); - struct yml_node const *spacing = yml_get_value(node, "spacing"); - struct yml_node const *left_spacing = yml_get_value(node, "left-spacing"); - struct yml_node const *right_spacing = yml_get_value(node, "right-spacing"); - - int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) - : right_spacing != NULL ? yml_value_as_int(right_spacing) - : 0; - - return pipewire_new(conf_to_particle(content, inherited), left, right); -} - -static bool -verify_conf(keychain_t *keychain, struct yml_node const *node) -{ - static struct attr_info const attrs[] = { - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, - MODULE_COMMON_ATTRS, - }; - return conf_verify_dict(keychain, node, attrs); -} - -struct module_iface const module_pipewire_iface = { - .from_conf = &from_conf, - .verify_conf = &verify_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern struct module_iface const iface __attribute__((weak, alias("module_pipewire_iface"))); -#endif diff --git a/modules/pulse.c b/modules/pulse.c deleted file mode 100644 index f6c7f69..0000000 --- a/modules/pulse.c +++ /dev/null @@ -1,523 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define LOG_MODULE "pulse" -#define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" -#include "../log.h" -#include "../plugin.h" - -struct private -{ - char *sink_name; - char *source_name; - struct particle *label; - - bool online; - - bool sink_online; - pa_cvolume sink_volume; - bool sink_muted; - char *sink_port; - uint32_t sink_index; - - bool source_online; - pa_cvolume source_volume; - bool source_muted; - char *source_port; - uint32_t source_index; - - int refresh_timer_fd; - bool refresh_scheduled; - - pa_mainloop *mainloop; - pa_context *context; -}; - -static void -destroy(struct module *mod) -{ - struct private *priv = mod->private; - priv->label->destroy(priv->label); - free(priv->sink_name); - free(priv->source_name); - free(priv->sink_port); - free(priv->source_port); - free(priv); - module_default_destroy(mod); -} - -static const char * -description(const struct module *mod) -{ - return "pulse"; -} - -static struct exposable * -content(struct module *mod) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - - pa_volume_t sink_volume_max = pa_cvolume_max(&priv->sink_volume); - pa_volume_t source_volume_max = pa_cvolume_max(&priv->source_volume); - int sink_percent = round(100.0 * sink_volume_max / PA_VOLUME_NORM); - int source_percent = round(100.0 * source_volume_max / PA_VOLUME_NORM); - - struct tag_set tags = { - .tags = (struct tag *[]){ - tag_new_bool(mod, "online", priv->online), - - tag_new_bool(mod, "sink_online", priv->sink_online), - tag_new_int_range(mod, "sink_percent", sink_percent, 0, 100), - tag_new_bool(mod, "sink_muted", priv->sink_muted), - tag_new_string(mod, "sink_port", priv->sink_port), - - tag_new_bool(mod, "source_online", priv->source_online), - tag_new_int_range(mod, "source_percent", source_percent, 0, 100), - tag_new_bool(mod, "source_muted", priv->source_muted), - tag_new_string(mod, "source_port", priv->source_port), - }, - .count = 9, - }; - - mtx_unlock(&mod->lock); - - struct exposable *exposable = priv->label->instantiate(priv->label, &tags); - - tag_set_destroy(&tags); - return exposable; -} - -static const char * -context_error(pa_context *c) -{ - return pa_strerror(pa_context_errno(c)); -} - -static void -abort_event_cb(pa_mainloop_api *api, pa_io_event *event, int fd, pa_io_event_flags_t flags, void *userdata) -{ - struct module *mod = userdata; - struct private *priv = mod->private; - - pa_context_disconnect(priv->context); -} - -static void -refresh_timer_cb(pa_mainloop_api *api, pa_io_event *event, int fd, pa_io_event_flags_t flags, void *userdata) -{ - struct module *mod = userdata; - struct private *priv = mod->private; - - // Drain the refresh timer. - uint64_t n; - if (read(priv->refresh_timer_fd, &n, sizeof n) < 0) - LOG_ERRNO("failed to read from timerfd"); - - // Clear the refresh flag. - priv->refresh_scheduled = false; - - // Refresh the bar. - mod->bar->refresh(mod->bar); -} - -// Refresh the bar after a small delay. Without the delay, the bar -// would be refreshed multiple times per event (e.g., a volume change), -// and sometimes the active port would be reported incorrectly for a -// brief moment. (This behavior was observed with PipeWire 0.3.61.) -static void -schedule_refresh(struct module *mod) -{ - struct private *priv = mod->private; - - // Do nothing if a refresh has already been scheduled. - if (priv->refresh_scheduled) - return; - - // Start the refresh timer. - struct itimerspec t = { - .it_interval = {.tv_sec = 0, .tv_nsec = 0}, - .it_value = {.tv_sec = 0, .tv_nsec = 50000000}, - }; - timerfd_settime(priv->refresh_timer_fd, 0, &t, NULL); - - // Set the refresh flag. - priv->refresh_scheduled = true; -} - -static void -set_server_online(struct module *mod) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - priv->online = true; - mtx_unlock(&mod->lock); - - schedule_refresh(mod); -} - -static void -set_server_offline(struct module *mod) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - priv->online = false; - priv->sink_online = false; - priv->source_online = false; - mtx_unlock(&mod->lock); - - schedule_refresh(mod); -} - -static void -set_sink_info(struct module *mod, const pa_sink_info *sink_info) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - - free(priv->sink_port); - - priv->sink_online = true; - priv->sink_index = sink_info->index; - priv->sink_volume = sink_info->volume; - priv->sink_muted = sink_info->mute; - priv->sink_port = sink_info->active_port != NULL ? strdup(sink_info->active_port->description) : NULL; - - mtx_unlock(&mod->lock); - - schedule_refresh(mod); -} - -static void -set_sink_offline(struct module *mod) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - priv->sink_online = false; - mtx_unlock(&mod->lock); - - schedule_refresh(mod); -} - -static void -set_source_info(struct module *mod, const pa_source_info *source_info) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - - free(priv->source_port); - - priv->source_online = true; - priv->source_index = source_info->index; - priv->source_volume = source_info->volume; - priv->source_muted = source_info->mute; - priv->source_port = source_info->active_port != NULL ? strdup(source_info->active_port->description) : NULL; - - mtx_unlock(&mod->lock); - - schedule_refresh(mod); -} - -static void -set_source_offline(struct module *mod) -{ - struct private *priv = mod->private; - - mtx_lock(&mod->lock); - priv->source_online = false; - mtx_unlock(&mod->lock); - - schedule_refresh(mod); -} - -static void -sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) -{ - struct module *mod = userdata; - - if (eol < 0) { - LOG_ERR("failed to get sink info: %s", context_error(c)); - set_sink_offline(mod); - } else if (eol == 0) { - set_sink_info(mod, i); - } -} - -static void -source_info_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) -{ - struct module *mod = userdata; - - if (eol < 0) { - LOG_ERR("failed to get source info: %s", context_error(c)); - set_source_offline(mod); - } else if (eol == 0) { - set_source_info(mod, i); - } -} - -static void -server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) -{ - LOG_INFO("%s, version %s", i->server_name, i->server_version); -} - -static void -get_sink_info_by_name(pa_context *c, const char *name, void *userdata) -{ - pa_operation *o = pa_context_get_sink_info_by_name(c, name, sink_info_cb, userdata); - pa_operation_unref(o); -} - -static void -get_source_info_by_name(pa_context *c, const char *name, void *userdata) -{ - pa_operation *o = pa_context_get_source_info_by_name(c, name, source_info_cb, userdata); - pa_operation_unref(o); -} - -static void -get_sink_info_by_index(pa_context *c, uint32_t index, void *userdata) -{ - pa_operation *o = pa_context_get_sink_info_by_index(c, index, sink_info_cb, userdata); - pa_operation_unref(o); -} - -static void -get_source_info_by_index(pa_context *c, uint32_t index, void *userdata) -{ - pa_operation *o = pa_context_get_source_info_by_index(c, index, source_info_cb, userdata); - pa_operation_unref(o); -} - -static void -get_server_info(pa_context *c, void *userdata) -{ - pa_operation *o = pa_context_get_server_info(c, server_info_cb, userdata); - pa_operation_unref(o); -} - -static void -subscribe(pa_context *c, void *userdata) -{ - pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SERVER | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; - pa_operation *o = pa_context_subscribe(c, mask, NULL, userdata); - pa_operation_unref(o); -} - -static pa_context *connect_to_server(struct module *mod); - -static void -context_state_change_cb(pa_context *c, void *userdata) -{ - struct module *mod = userdata; - struct private *priv = mod->private; - - pa_context_state_t state = pa_context_get_state(c); - switch (state) { - case PA_CONTEXT_UNCONNECTED: - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - - case PA_CONTEXT_READY: - set_server_online(mod); - subscribe(c, mod); - get_server_info(c, mod); - get_sink_info_by_name(c, priv->sink_name, mod); - get_source_info_by_name(c, priv->source_name, mod); - break; - - case PA_CONTEXT_FAILED: - LOG_WARN("connection lost"); - set_server_offline(mod); - pa_context_unref(priv->context); - priv->context = connect_to_server(mod); - break; - - case PA_CONTEXT_TERMINATED: - LOG_DBG("connection terminated"); - set_server_offline(mod); - pa_mainloop_quit(priv->mainloop, 0); - break; - } -} - -static void -subscription_event_cb(pa_context *c, pa_subscription_event_type_t event_type, uint32_t index, void *userdata) -{ - struct module *mod = userdata; - struct private *priv = mod->private; - - int facility = event_type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - int type = event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; - - switch (facility) { - case PA_SUBSCRIPTION_EVENT_SERVER: - get_sink_info_by_name(c, priv->sink_name, mod); - get_source_info_by_name(c, priv->source_name, mod); - break; - - case PA_SUBSCRIPTION_EVENT_SINK: - if (index == priv->sink_index) { - if (type == PA_SUBSCRIPTION_EVENT_CHANGE) - get_sink_info_by_index(c, index, mod); - else if (type == PA_SUBSCRIPTION_EVENT_REMOVE) - set_sink_offline(mod); - } - break; - - case PA_SUBSCRIPTION_EVENT_SOURCE: - if (index == priv->source_index) { - if (type == PA_SUBSCRIPTION_EVENT_CHANGE) - get_source_info_by_index(c, index, mod); - else if (type == PA_SUBSCRIPTION_EVENT_REMOVE) - set_source_offline(mod); - } - break; - } -} - -static pa_context * -connect_to_server(struct module *mod) -{ - struct private *priv = mod->private; - - // Create connection context. - pa_mainloop_api *api = pa_mainloop_get_api(priv->mainloop); - pa_context *c = pa_context_new(api, "yambar"); - if (c == NULL) { - LOG_ERR("failed to create PulseAudio connection context"); - return NULL; - } - - // Register callback functions. - pa_context_set_state_callback(c, context_state_change_cb, mod); - pa_context_set_subscribe_callback(c, subscription_event_cb, mod); - - // Connect to server. - pa_context_flags_t flags = PA_CONTEXT_NOFAIL | PA_CONTEXT_NOAUTOSPAWN; - if (pa_context_connect(c, NULL, flags, NULL) < 0) { - LOG_ERR("failed to connect to PulseAudio server: %s", context_error(c)); - pa_context_unref(c); - return NULL; - } - - return c; -} - -static int -run(struct module *mod) -{ - struct private *priv = mod->private; - int ret = -1; - - // Create main loop. - priv->mainloop = pa_mainloop_new(); - if (priv->mainloop == NULL) { - LOG_ERR("failed to create PulseAudio main loop"); - return -1; - } - - // Create refresh timer. - priv->refresh_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); - if (priv->refresh_timer_fd < 0) { - LOG_ERRNO("failed to create timerfd"); - pa_mainloop_free(priv->mainloop); - return -1; - } - - // Connect to server. - priv->context = connect_to_server(mod); - if (priv->context == NULL) { - pa_mainloop_free(priv->mainloop); - close(priv->refresh_timer_fd); - return -1; - } - - // Poll refresh timer and abort event. - pa_mainloop_api *api = pa_mainloop_get_api(priv->mainloop); - api->io_new(api, priv->refresh_timer_fd, PA_IO_EVENT_INPUT, refresh_timer_cb, mod); - api->io_new(api, mod->abort_fd, PA_IO_EVENT_INPUT | PA_IO_EVENT_HANGUP, abort_event_cb, mod); - - // Run main loop. - if (pa_mainloop_run(priv->mainloop, &ret) < 0) { - LOG_ERR("PulseAudio main loop error"); - ret = -1; - } - - // Clean up. - pa_context_unref(priv->context); - pa_mainloop_free(priv->mainloop); - close(priv->refresh_timer_fd); - - return ret; -} - -static struct module * -pulse_new(const char *sink_name, const char *source_name, struct particle *label) -{ - struct private *priv = calloc(1, sizeof *priv); - priv->label = label; - priv->sink_name = strdup(sink_name); - priv->source_name = strdup(source_name); - - struct module *mod = module_common_new(); - mod->private = priv; - mod->run = &run; - mod->destroy = &destroy; - mod->content = &content; - mod->description = &description; - return mod; -} - -static struct module * -from_conf(const struct yml_node *node, struct conf_inherit inherited) -{ - const struct yml_node *sink = yml_get_value(node, "sink"); - const struct yml_node *source = yml_get_value(node, "source"); - const struct yml_node *content = yml_get_value(node, "content"); - - return pulse_new(sink != NULL ? yml_value_as_string(sink) : "@DEFAULT_SINK@", - source != NULL ? yml_value_as_string(source) : "@DEFAULT_SOURCE@", - conf_to_particle(content, inherited)); -} - -static bool -verify_conf(keychain_t *chain, const struct yml_node *node) -{ - static const struct attr_info attrs[] = { - {"sink", false, &conf_verify_string}, - {"source", false, &conf_verify_string}, - MODULE_COMMON_ATTRS, - }; - - return conf_verify_dict(chain, node, attrs); -} - -const struct module_iface module_pulse_iface = { - .verify_conf = &verify_conf, - .from_conf = &from_conf, -}; - -#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_pulse_iface"))); -#endif diff --git a/modules/removables.c b/modules/removables.c index df4ade4..498824b 100644 --- a/modules/removables.c +++ b/modules/removables.c @@ -1,15 +1,14 @@ -#include -#include -#include -#include -#include #include +#include +#include +#include #include #include +#include -#include #include #include +#include #include @@ -17,15 +16,13 @@ #define LOG_MODULE "removables" #define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../particles/dynlist.h" #include "../plugin.h" -#define max(x, y) ((x) > (y) ? (x) : (y)) - typedef tll(char *) mount_point_list_t; struct partition { @@ -36,8 +33,8 @@ struct partition { char *label; uint64_t size; - bool audio_cd; + /*tll(char *) mount_points;*/ mount_point_list_t mount_points; }; @@ -54,8 +51,7 @@ struct block_device { tll(struct partition) partitions; }; -struct private -{ +struct private { struct particle *label; int left_spacing; int right_spacing; @@ -76,7 +72,8 @@ free_partition(struct partition *p) static void free_device(struct block_device *b) { - tll_foreach(b->partitions, it) free_partition(&it->item); + tll_foreach(b->partitions, it) + free_partition(&it->item); tll_free(b->partitions); free(b->sys_path); @@ -91,7 +88,8 @@ destroy(struct module *mod) struct private *m = mod->private; m->label->destroy(m->label); - tll_foreach(m->devices, it) free_device(&it->item); + tll_foreach(m->devices, it) + free_device(&it->item); tll_free(m->devices); tll_free_and_free(m->ignore, free); @@ -99,12 +97,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "removables"; -} - static struct exposable * content(struct module *mod) { @@ -112,23 +104,24 @@ content(struct module *mod) tll(const struct partition *) partitions = tll_init(); - tll_foreach(m->devices, dev) - { - tll_foreach(dev->item.partitions, part) { tll_push_back(partitions, &part->item); } + tll_foreach(m->devices, dev) { + tll_foreach(dev->item.partitions, part) { + tll_push_back(partitions, &part->item); + } } - struct exposable *exposables[max(tll_length(partitions), 1)]; + struct exposable *exposables[tll_length(partitions)]; size_t idx = 0; - tll_foreach(partitions, it) - { + tll_foreach(partitions, it) { const struct partition *p = it->item; char dummy_label[16]; const char *label = p->label; if (label == NULL) { - snprintf(dummy_label, sizeof(dummy_label), "%.1f GB", (double)p->size / 1024 / 1024 / 1024 * 512); + snprintf(dummy_label, sizeof(dummy_label), + "%.1f GB", (double)p->size / 1024 / 1024 / 1024 * 512); label = dummy_label; } @@ -140,14 +133,13 @@ content(struct module *mod) tag_new_string(mod, "vendor", p->block->vendor), tag_new_string(mod, "model", p->block->model), tag_new_bool(mod, "optical", p->block->optical), - tag_new_bool(mod, "audio", p->audio_cd), tag_new_string(mod, "device", p->dev_path), tag_new_int_range(mod, "size", p->size, 0, p->block->size), tag_new_string(mod, "label", label), tag_new_bool(mod, "mounted", is_mounted), tag_new_string(mod, "mount_point", mount_point), }, - .count = 9, + .count = 8, }; exposables[idx++] = m->label->instantiate(m->label, &tags); @@ -155,24 +147,24 @@ content(struct module *mod) } tll_free(partitions); - return dynlist_exposable_new(exposables, idx, m->left_spacing, m->right_spacing); + return dynlist_exposable_new( + exposables, idx, m->left_spacing, m->right_spacing); } static void find_mount_points(const char *dev_path, mount_point_list_t *mount_points) { - FILE *f = fopen("/proc/self/mountinfo", "re"); - - if (f == NULL) { - LOG_ERRNO("failed to open /proc/self/mountinfo"); - return; - } + FILE *f = fopen("/proc/self/mountinfo", "r"); + assert(f != NULL); char line[4096]; + while (fgets(line, sizeof(line), f) != NULL) { char *dev = NULL, *path = NULL; - if (sscanf(line, "%*u %*u %*u:%*u %*s %ms %*[^-] - %*s %ms %*s", &path, &dev) != 2) { + if (sscanf(line, "%*u %*u %*u:%*u %*s %ms %*[^-] - %*s %ms %*s", + &path, &dev) != 2) + { LOG_ERR("failed to parse /proc/self/mountinfo: %s", line); free(dev); free(path); @@ -199,11 +191,9 @@ update_mount_points(struct partition *partition) /* Remove mount points that no longer exists (i.e. old mount * points that aren't in the new list) */ - tll_foreach(partition->mount_points, old) - { + tll_foreach(partition->mount_points, old) { bool gone = true; - tll_foreach(new_mounts, new) - { + tll_foreach(new_mounts, new) { if (strcmp(new->item, old->item) == 0) { /* Remove from new list, as it's already in the * partitions list */ @@ -222,8 +212,7 @@ update_mount_points(struct partition *partition) /* Add new mount points (i.e. mount points in the new list, that * aren't in the old list) */ - tll_foreach(new_mounts, new) - { + tll_foreach(new_mounts, new) { LOG_DBG("%s: mounted on %s", partition->dev_path, new->item); tll_push_back(partition->mount_points, new->item); @@ -237,13 +226,14 @@ update_mount_points(struct partition *partition) } static struct partition * -add_partition(struct module *mod, struct block_device *block, struct udev_device *dev) +add_partition(struct module *mod, struct block_device *block, + struct udev_device *dev) { struct private *m = mod->private; const char *_size = udev_device_get_sysattr_value(dev, "size"); uint64_t size = 0; if (_size != NULL) - sscanf(_size, "%" SCNu64, &size); + sscanf(_size, "%"SCNu64, &size); #if 0 struct udev_list_entry *e = NULL; @@ -254,8 +244,7 @@ add_partition(struct module *mod, struct block_device *block, struct udev_device const char *devname = udev_device_get_property_value(dev, "DEVNAME"); if (devname != NULL) { - tll_foreach(m->ignore, it) - { + tll_foreach(m->ignore, it) { if (strcmp(it->item, devname) == 0) { LOG_DBG("ignoring %s because it is on the ignore list", devname); return NULL; @@ -267,70 +256,20 @@ add_partition(struct module *mod, struct block_device *block, struct udev_device if (label == NULL) label = udev_device_get_property_value(dev, "ID_LABEL"); - LOG_INFO("partition: add: %s: label=%s, size=%" PRIu64, udev_device_get_devnode(dev), label, size); + LOG_INFO("partition: add: %s: label=%s, size=%"PRIu64, + udev_device_get_devnode(dev), label, size); mtx_lock(&mod->lock); - tll_push_back(block->partitions, ((struct partition){.block = block, - .sys_path = strdup(udev_device_get_devpath(dev)), - .dev_path = strdup(udev_device_get_devnode(dev)), - .label = label != NULL ? strdup(label) : NULL, - .size = size, - .audio_cd = false, - .mount_points = tll_init()})); - - struct partition *p = &tll_back(block->partitions); - update_mount_points(p); - mtx_unlock(&mod->lock); - - return p; -} - -static struct partition * -add_audio_cd(struct module *mod, struct block_device *block, struct udev_device *dev) -{ - struct private *m = mod->private; - const char *_size = udev_device_get_sysattr_value(dev, "size"); - uint64_t size = 0; - if (_size != NULL) - sscanf(_size, "%" SCNu64, &size); - -#if 0 - struct udev_list_entry *e = NULL; - udev_list_entry_foreach(e, udev_device_get_properties_list_entry(dev)) { - LOG_DBG("%s -> %s", udev_list_entry_get_name(e), udev_list_entry_get_value(e)); - } -#endif - - const char *devname = udev_device_get_property_value(dev, "DEVNAME"); - if (devname != NULL) { - tll_foreach(m->ignore, it) - { - if (strcmp(it->item, devname) == 0) { - LOG_DBG("ignoring %s because it is on the ignore list", devname); - return NULL; - } - } - } - - const char *_track_count = udev_device_get_property_value(dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); - unsigned long track_count = strtoul(_track_count, NULL, 10); - - char label[64]; - snprintf(label, sizeof(label), "Audio CD - %lu tracks", track_count); - - LOG_INFO("audio CD: add: %s: tracks=%lu, label=%s, size=%" PRIu64, udev_device_get_devnode(dev), track_count, label, - size); - - mtx_lock(&mod->lock); - - tll_push_back(block->partitions, ((struct partition){.block = block, - .sys_path = strdup(udev_device_get_devpath(dev)), - .dev_path = strdup(udev_device_get_devnode(dev)), - .label = label != NULL ? strdup(label) : NULL, - .size = size, - .audio_cd = true, - .mount_points = tll_init()})); + tll_push_back( + block->partitions, + ((struct partition){ + .block = block, + .sys_path = strdup(udev_device_get_devpath(dev)), + .dev_path = strdup(udev_device_get_devnode(dev)), + .label = label != NULL ? strdup(label) : NULL, + .size = size, + .mount_points = tll_init()})); struct partition *p = &tll_back(block->partitions); update_mount_points(p); @@ -340,15 +279,15 @@ add_audio_cd(struct module *mod, struct block_device *block, struct udev_device } static bool -del_partition(struct module *mod, struct block_device *block, struct udev_device *dev) +del_partition(struct module *mod, struct block_device *block, + struct udev_device *dev) { const char *sys_path = udev_device_get_devpath(dev); mtx_lock(&mod->lock); - tll_foreach(block->partitions, it) - { + tll_foreach(block->partitions, it) { if (strcmp(it->item.sys_path, sys_path) == 0) { - LOG_INFO("%s: del: %s", it->item.audio_cd ? "audio CD" : "partition", it->item.dev_path); + LOG_INFO("partition: del: %s", it->item.dev_path); free_partition(&it->item); tll_remove(block->partitions, it); @@ -377,8 +316,7 @@ add_device(struct module *mod, struct udev_device *dev) const char *devname = udev_device_get_property_value(dev, "DEVNAME"); if (devname != NULL) { - tll_foreach(m->ignore, it) - { + tll_foreach(m->ignore, it) { if (strcmp(it->item, devname) == 0) { LOG_DBG("ignoring %s because it is on the ignore list", devname); return NULL; @@ -389,12 +327,11 @@ add_device(struct module *mod, struct udev_device *dev) const char *_size = udev_device_get_sysattr_value(dev, "size"); uint64_t size = 0; if (_size != NULL) - sscanf(_size, "%" SCNu64, &size); + sscanf(_size, "%"SCNu64, &size); #if 1 struct udev_list_entry *e = NULL; - udev_list_entry_foreach(e, udev_device_get_properties_list_entry(dev)) - { + udev_list_entry_foreach(e, udev_device_get_properties_list_entry(dev)) { LOG_DBG("%s -> %s", udev_list_entry_get_name(e), udev_list_entry_get_value(e)); } #endif @@ -405,38 +342,31 @@ add_device(struct module *mod, struct udev_device *dev) const char *_optical = udev_device_get_property_value(dev, "ID_CDROM"); bool optical = _optical != NULL && strcmp(_optical, "1") == 0; - const char *_media = udev_device_get_property_value(dev, "ID_CDROM_MEDIA"); - bool media = _media != NULL && strcmp(_media, "1") == 0; - const char *_fs_usage = udev_device_get_property_value(dev, "ID_FS_USAGE"); - bool have_fs = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; + bool media = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; - const char *_audio_track_count = udev_device_get_property_value(dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); - unsigned long audio_track_count = _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; - - LOG_DBG("device: add: %s: vendor=%s, model=%s, optical=%d, size=%" PRIu64, udev_device_get_devnode(dev), vendor, - model, optical, size); + LOG_DBG("device: add: %s: vendor=%s, model=%s, optical=%d, size=%"PRIu64, + udev_device_get_devnode(dev), vendor, model, optical, size); mtx_lock(&mod->lock); - tll_push_back(m->devices, ((struct block_device){.sys_path = strdup(udev_device_get_devpath(dev)), - .dev_path = strdup(udev_device_get_devnode(dev)), - .size = size, - .vendor = vendor != NULL ? strdup(vendor) : NULL, - .model = model != NULL ? strdup(model) : NULL, - .optical = optical, - .media = media, - .partitions = tll_init()})); + tll_push_back( + m->devices, + ((struct block_device){ + .sys_path = strdup(udev_device_get_devpath(dev)), + .dev_path = strdup(udev_device_get_devnode(dev)), + .size = size, + .vendor = vendor != NULL ? strdup(vendor) : NULL, + .model = model != NULL ? strdup(model) : NULL, + .optical = optical, + .media = media, + .partitions = tll_init()})); mtx_unlock(&mod->lock); struct block_device *block = &tll_back(m->devices); - if (optical) { - if (have_fs) - add_partition(mod, block, dev); - else if (audio_track_count > 0) - add_audio_cd(mod, block, dev); - } + if (optical && media) + add_partition(mod, block, dev); return &tll_back(m->devices); } @@ -448,8 +378,7 @@ del_device(struct module *mod, struct udev_device *dev) const char *sys_path = udev_device_get_devpath(dev); mtx_lock(&mod->lock); - tll_foreach(m->devices, it) - { + tll_foreach(m->devices, it) { if (strcmp(it->item.sys_path, sys_path) == 0) { LOG_DBG("device: del: %s", it->item.dev_path); @@ -471,51 +400,31 @@ change_device(struct module *mod, struct udev_device *dev) const char *sys_path = udev_device_get_devpath(dev); mtx_lock(&mod->lock); - struct block_device *block = NULL; - - tll_foreach(m->devices, it) - { + tll_foreach(m->devices, it) { if (strcmp(it->item.sys_path, sys_path) == 0) { - block = &it->item; - break; + LOG_DBG("device: change: %s", it->item.dev_path); + + if (it->item.optical) { + const char *_media = udev_device_get_property_value(dev, "ID_FS_USAGE"); + bool media = _media != NULL && strcmp(_media, "filesystem") == 0; + bool media_change = media != it->item.media; + + it->item.media = media; + mtx_unlock(&mod->lock); + + if (media_change) { + LOG_INFO("device: change: %s: media %s", + it->item.dev_path, media ? "inserted" : "removed"); + + if (media) + return add_partition(mod, &it->item, dev) != NULL; + else + return del_partition(mod, &it->item, dev); + } + } } } - if (block == NULL) - goto out; - - LOG_DBG("device: change: %s", block->dev_path); - - if (!block->optical) - goto out; - - const char *_media = udev_device_get_property_value(dev, "ID_CDROM_MEDIA"); - bool media = _media != NULL && strcmp(_media, "1") == 0; - - const char *_fs_usage = udev_device_get_property_value(dev, "ID_FS_USAGE"); - bool have_fs = _fs_usage != NULL && strcmp(_fs_usage, "filesystem") == 0; - - const char *_audio_track_count = udev_device_get_property_value(dev, "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO"); - unsigned long audio_track_count = _audio_track_count != NULL ? strtoul(_audio_track_count, NULL, 10) : 0; - - bool media_change = media != block->media; - - block->media = media; - mtx_unlock(&mod->lock); - - if (media_change) { - LOG_INFO("device: change: %s: media %s", block->dev_path, media ? "inserted" : "removed"); - - if (media) { - if (have_fs) - return add_partition(mod, block, dev) != NULL; - else if (audio_track_count > 0) - return add_audio_cd(mod, block, dev) != NULL; - } else - return del_partition(mod, block, dev); - } - -out: mtx_unlock(&mod->lock); return false; } @@ -550,8 +459,7 @@ handle_udev_event(struct module *mod, struct udev_device *dev) struct udev_device *parent = udev_device_get_parent(dev); const char *parent_sys_path = udev_device_get_devpath(parent); - tll_foreach(m->devices, it) - { + tll_foreach(m->devices, it) { if (strcmp(it->item.sys_path, parent_sys_path) != 0) continue; @@ -560,7 +468,8 @@ handle_udev_event(struct module *mod, struct udev_device *dev) else if (del) return del_partition(mod, &it->item, dev); else { - LOG_ERR("unimplemented: 'change' event on partition: %s", udev_device_get_devpath(dev)); + LOG_ERR("unimplemented: 'change' event on partition: %s", + udev_device_get_devpath(dev)); return false; } break; @@ -587,15 +496,15 @@ run(struct module *mod) udev_enumerate_add_match_subsystem(dev_enum, "block"); /* TODO: verify how an optical presents itself */ - // udev_enumerate_add_match_sysattr(dev_enum, "removable", "1"); + //udev_enumerate_add_match_sysattr(dev_enum, "removable", "1"); udev_enumerate_add_match_property(dev_enum, "DEVTYPE", "disk"); udev_enumerate_scan_devices(dev_enum); /* Loop list, and for each device, enumerate its partitions */ struct udev_list_entry *entry = NULL; - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(dev_enum)) - { - struct udev_device *dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(entry)); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(dev_enum)) { + struct udev_device *dev = udev_device_new_from_syspath( + udev, udev_list_entry_get_name(entry)); struct block_device *block = add_device(mod, dev); if (block == NULL) { @@ -612,9 +521,9 @@ run(struct module *mod) udev_enumerate_scan_devices(part_enum); struct udev_list_entry *sub_entry = NULL; - udev_list_entry_foreach(sub_entry, udev_enumerate_get_list_entry(part_enum)) - { - struct udev_device *partition = udev_device_new_from_syspath(udev, udev_list_entry_get_name(sub_entry)); + udev_list_entry_foreach(sub_entry, udev_enumerate_get_list_entry(part_enum)) { + struct udev_device *partition = udev_device_new_from_syspath( + udev, udev_list_entry_get_name(sub_entry)); add_partition(mod, block, partition); udev_device_unref(partition); } @@ -628,9 +537,7 @@ run(struct module *mod) /* To be able to poll() mountinfo for changes, to detect * mount/unmount operations */ - int mount_info_fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC); - - int ret = 1; + int mount_info_fd = open("/proc/self/mountinfo", O_RDONLY); while (true) { struct pollfd fds[] = { @@ -638,26 +545,16 @@ run(struct module *mod) {.fd = udev_monitor_get_fd(dev_mon), .events = POLLIN}, {.fd = mount_info_fd, .events = POLLPRI}, }; - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { - if (errno == EINTR) - continue; + poll(fds, 3, -1); - LOG_ERRNO("failed to poll"); + if (fds[0].revents & POLLIN) break; - } - - if (fds[0].revents & POLLIN) { - ret = 0; - break; - } bool update = false; if (fds[2].revents & POLLPRI) { - tll_foreach(m->devices, dev) - { - tll_foreach(dev->item.partitions, part) - { + tll_foreach(m->devices, dev) { + tll_foreach(dev->item.partitions, part) { if (update_mount_points(&part->item)) update = true; } @@ -666,9 +563,6 @@ run(struct module *mod) if (fds[1].revents & POLLIN) { struct udev_device *dev = udev_monitor_receive_device(dev_mon); - if (dev == NULL) - continue; - if (handle_udev_event(mod, dev)) update = true; udev_device_unref(dev); @@ -682,12 +576,12 @@ run(struct module *mod) udev_monitor_unref(dev_mon); udev_unref(udev); - return ret; + return 0; } static struct module * -removables_new(struct particle *label, int left_spacing, int right_spacing, size_t ignore_count, - const char *ignore[static ignore_count]) +removables_new(struct particle *label, int left_spacing, int right_spacing, + size_t ignore_count, const char *ignore[static ignore_count]) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; @@ -702,7 +596,6 @@ removables_new(struct particle *label, int left_spacing, int right_spacing, size mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -715,22 +608,26 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *ignore_list = yml_get_value(node, "ignore"); - int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) - : right_spacing != NULL ? yml_value_as_int(right_spacing) - : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : + left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) : + right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; size_t ignore_count = ignore_list != NULL ? yml_list_length(ignore_list) : 0; - const char *ignore[max(ignore_count, 1)]; + const char *ignore[ignore_count]; if (ignore_list != NULL) { size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(ignore_list); iter.node != NULL; yml_list_next(&iter), i++) { + for (struct yml_list_iter iter = yml_list_iter(ignore_list); + iter.node != NULL; + yml_list_next(&iter), i++) + { ignore[i] = yml_value_as_string(iter.node); } } - return removables_new(conf_to_particle(content, inherited), left, right, ignore_count, ignore); + return removables_new( + conf_to_particle(content, inherited), left, right, ignore_count, ignore); } static bool @@ -743,9 +640,9 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, + {"spacing", false, &conf_verify_int}, + {"left-spacing", false, &conf_verify_int}, + {"right-spacing", false, &conf_verify_int}, {"ignore", false, &verify_ignore}, MODULE_COMMON_ATTRS, }; diff --git a/modules/river.c b/modules/river.c index ec25f9f..b6e3db1 100644 --- a/modules/river.c +++ b/modules/river.c @@ -1,23 +1,21 @@ -#include -#include -#include #include #include +#include +#include +#include -#include #include +#include #define LOG_MODULE "river" -#define LOG_ENABLE_DBG 0 +#define LOG_ENABLE_DBG 1 #include "../log.h" -#include "../particles/dynlist.h" #include "../plugin.h" +#include "../particles/dynlist.h" #include "river-status-unstable-v1.h" #include "xdg-output-unstable-v1.h" -#define min(x, y) ((x) < (y) ? (x) : (y)) - struct private; struct output { @@ -31,10 +29,6 @@ struct output { /* Tags */ uint32_t occupied; uint32_t focused; - uint32_t urgent; - - /* Layout */ - char *layout; }; struct seat { @@ -44,21 +38,18 @@ struct seat { uint32_t wl_name; char *name; - char *mode; char *title; struct output *output; }; -struct private -{ +struct private { struct module *mod; - bool is_running; struct zxdg_output_manager_v1 *xdg_output_manager; struct zriver_status_manager_v1 *status_manager; struct particle *template; struct particle *title; - bool all_monitors; + bool is_starting_up; tll(struct output) outputs; tll(struct seat) seats; }; @@ -74,46 +65,24 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "river"; -} - static struct exposable * content(struct module *mod) { const struct private *m = mod->private; - const char *output_bar_is_on = mod->bar->output_name(mod->bar); - mtx_lock(&m->mod->lock); - if (!m->is_running) { - mtx_unlock(&m->mod->lock); - return dynlist_exposable_new(NULL, 0, 0, 0); - } - - uint32_t urgent = 0; - uint32_t occupied = 0; uint32_t output_focused = 0; uint32_t seat_focused = 0; + uint32_t occupied = 0; - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { const struct output *output = &it->item; - if (!m->all_monitors && output_bar_is_on != NULL && output->name != NULL - && strcmp(output->name, output_bar_is_on) != 0) { - continue; - } - output_focused |= output->focused; - urgent |= output->urgent; occupied |= output->occupied; - tll_foreach(m->seats, it2) - { + tll_foreach(m->seats, it2) { const struct seat *seat = &it2->item; if (seat->output == output) { seat_focused |= output->focused; @@ -121,64 +90,57 @@ content(struct module *mod) } } - const size_t seat_count = m->title != NULL ? tll_length(m->seats) : 0; + const size_t seat_count = m->title != NULL && !m->is_starting_up + ? tll_length(m->seats) : 0; struct exposable *tag_parts[32 + seat_count]; for (unsigned i = 0; i < 32; i++) { /* It's visible if any output has it focused */ - bool is_visible = output_focused & (1u << i); + bool visible = output_focused & (1u << i); /* It's focused if any output that has seat focus has it focused */ - bool is_focused = seat_focused & (1u << i); + bool focused = seat_focused & (1u << i); - bool is_urgent = urgent & (1u << i); - bool is_occupied = occupied & (1u << i); - - const char *state = is_urgent ? "urgent" : is_visible ? is_focused ? "focused" : "unfocused" : "invisible"; + const char *state = visible ? focused ? "focused" : "unfocused" : "invisible"; #if 0 LOG_DBG("tag: #%u, visible=%d, focused=%d, occupied=%d, state=%s", - i, is_visible, is_focused, is_occupied & (1u << i), state); + i, visible, focused, occupied & (1u << i), state); #endif struct tag_set tags = { .tags = (struct tag *[]){ tag_new_int(mod, "id", i + 1), - tag_new_bool(mod, "urgent", is_urgent), - tag_new_bool(mod, "visible", is_visible), - tag_new_bool(mod, "focused", is_focused), - tag_new_bool(mod, "occupied", is_occupied), + tag_new_bool(mod, "visible", visible), + tag_new_bool(mod, "focused", focused), + tag_new_bool(mod, "occupied", occupied & (1u << i)), tag_new_string(mod, "state", state), }, - .count = 6, + .count = 5, }; tag_parts[i] = m->template->instantiate(m->template, &tags); tag_set_destroy(&tags); } - if (m->title != NULL) { + if (m->title != NULL && !m->is_starting_up) { size_t i = 32; - tll_foreach(m->seats, it) - { + tll_foreach(m->seats, it) { const struct seat *seat = &it->item; - const char *layout = seat->output != NULL && seat->output->layout != NULL ? seat->output->layout : ""; struct tag_set tags = { .tags = (struct tag *[]){ tag_new_string(mod, "seat", seat->name), tag_new_string(mod, "title", seat->title), - tag_new_string(mod, "mode", seat->mode), - tag_new_string(mod, "layout", layout), }, - .count = 4, + .count = 2, }; tag_parts[i++] = m->title->instantiate(m->title, &tags); tag_set_destroy(&tags); } } - + mtx_unlock(&m->mod->lock); return dynlist_exposable_new(tag_parts, 32 + seat_count, 0, 0); } @@ -189,27 +151,21 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", + iface, wanted, version); return false; } static void output_destroy(struct output *output) { - tll_foreach(output->m->seats, it) - { - struct seat *seat = &it->item; - if (seat->output == output) - seat->output = NULL; - } free(output->name); - free(output->layout); if (output->status != NULL) zriver_output_status_v1_destroy(output->status); if (output->xdg_output != NULL) zxdg_output_v1_destroy(output->xdg_output); if (output->wl_output != NULL) - wl_output_release(output->wl_output); + wl_output_destroy(output->wl_output); } static void @@ -217,7 +173,6 @@ seat_destroy(struct seat *seat) { free(seat->title); free(seat->name); - free(seat->mode); if (seat->status != NULL) zriver_seat_status_v1_destroy(seat->status); if (seat->wl_seat != NULL) @@ -225,24 +180,25 @@ seat_destroy(struct seat *seat) } static void -focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, uint32_t tags) +focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) { struct output *output = data; - - if (output->focused == tags) - return; + struct module *mod = output->m->mod; 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); } static void -view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, struct wl_array *tags) +view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + struct wl_array *tags) { struct output *output = data; struct module *mod = output->m->mod; @@ -254,81 +210,31 @@ view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, s /* Each entry in the list is a view, and the value is the tags * associated with that view */ uint32_t *set; - wl_array_for_each(set, tags) { output->occupied |= *set; } - + wl_array_for_each(set, tags) { + output->occupied |= *set; + } + LOG_DBG("output: %s: occupied tags: 0x%0x", output->name, output->occupied); } mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); } -static void -urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, uint32_t tags) -{ - struct output *output = data; - struct module *mod = output->m->mod; - - mtx_lock(&mod->lock); - { - output->urgent = tags; - } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); -} - -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) -static void -layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, const char *name) -{ - struct output *output = data; - struct module *mod = output->m->mod; - - mtx_lock(&mod->lock); - { - free(output->layout); - output->layout = name != NULL ? strdup(name) : NULL; - } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); -} -#endif - -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) -static void -layout_name_clear(void *data, struct zriver_output_status_v1 *zriver_output_status_v1) -{ - struct output *output = data; - struct module *mod = output->m->mod; - - mtx_lock(&mod->lock); - { - free(output->layout); - output->layout = NULL; - } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); -} -#endif - static const struct zriver_output_status_v1_listener river_status_output_listener = { .focused_tags = &focused_tags, .view_tags = &view_tags, - .urgent_tags = &urgent_tags, -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) - .layout_name = &layout_name, -#endif -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) - .layout_name_clear = &layout_name_clear, -#endif }; static void -xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) +xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, + int32_t x, int32_t y) { } static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, + int32_t width, int32_t height) { } @@ -338,7 +244,8 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) } static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, + const char *name) { struct output *output = data; struct module *mod = output->m->mod; @@ -353,7 +260,8 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char } static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, + const char *description) { } @@ -366,67 +274,37 @@ static struct zxdg_output_v1_listener xdg_output_listener = { }; static void -update_output(struct output *output) +instantiate_output(struct output *output) { + if (output->m->is_starting_up) + return; + assert(output->wl_output != NULL); - if (output->m->status_manager != NULL) { - /* - * Bind river output status, if we have already bound the status manager - */ + if (output->m->status_manager != NULL && output->status == NULL) { + output->status = zriver_status_manager_v1_get_river_output_status( + output->m->status_manager, output->wl_output); if (output->status != NULL) { - zriver_output_status_v1_destroy(output->status); - output->status = NULL; - } - - output->status = zriver_status_manager_v1_get_river_output_status(output->m->status_manager, output->wl_output); - - if (output->status != NULL) { - zriver_output_status_v1_add_listener(output->status, &river_status_output_listener, output); + zriver_output_status_v1_add_listener( + output->status, &river_status_output_listener, output); } } if (output->m->xdg_output_manager != NULL && output->xdg_output == NULL) { - output->xdg_output = zxdg_output_manager_v1_get_xdg_output(output->m->xdg_output_manager, output->wl_output); + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + output->m->xdg_output_manager, output->wl_output); if (output->xdg_output != NULL) { - zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + zxdg_output_v1_add_listener( + output->xdg_output, &xdg_output_listener, output); } } } static void -focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *wl_output) -{ - struct seat *seat = data; - struct private *m = seat->m; - struct module *mod = m->mod; - - 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); - - if (seat->output != output) { - mtx_lock(&mod->lock); - seat->output = output; - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); - } -} - -static void -unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *wl_output) +focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + struct wl_output *wl_output) { struct seat *seat = data; struct private *m = seat->m; @@ -435,8 +313,37 @@ unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1 mtx_lock(&mod->lock); { struct output *output = NULL; - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { + if (it->item.wl_output == wl_output) { + output = &it->item; + break; + } + } + + 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); +} + +static void +unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + struct wl_output *wl_output) +{ + struct seat *seat = data; + struct private *m = seat->m; + 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; @@ -454,65 +361,32 @@ unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1 } static void -focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *title) +focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + const char *title) { struct seat *seat = data; struct module *mod = seat->m->mod; - 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); - const char *output_bar_is_on = mod->bar->output_name(mod->bar); - - if (seat->m->all_monitors - || (output_bar_is_on != NULL && seat->output != NULL && seat->output->name != NULL - && strcmp(output_bar_is_on, seat->output->name) == 0)) { - mtx_lock(&mod->lock); - { - free(seat->title); - seat->title = title != NULL ? strdup(title) : NULL; - } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); - } -} - -#if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) -static void -mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *name) -{ - struct seat *seat = data; - struct module *mod = seat->m->mod; - mtx_lock(&mod->lock); { - free(seat->mode); - seat->mode = strdup(name); - mtx_unlock(&mod->lock); + free(seat->title); + seat->title = title != NULL ? strdup(title) : NULL; } + mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); - - LOG_DBG("seat: %s, current mode: %s", seat->name, seat->mode); } -#endif - static const struct zriver_seat_status_v1_listener river_seat_status_listener = { .focused_output = &focused_output, .unfocused_output = &unfocused_output, .focused_view = &focused_view, -#if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) - .mode = &mode, -#endif }; static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { } @@ -537,51 +411,46 @@ static const struct wl_seat_listener seat_listener = { }; static void -update_seat(struct seat *seat) +instantiate_seat(struct seat *seat) { assert(seat->wl_seat != NULL); + if (seat->m->is_starting_up) + return; + if (seat->m->status_manager == NULL) return; - if (seat->status != NULL) { - zriver_seat_status_v1_destroy(seat->status); - seat->status = NULL; - } - - seat->status = zriver_status_manager_v1_get_river_seat_status(seat->m->status_manager, seat->wl_seat); + seat->status = zriver_status_manager_v1_get_river_seat_status( + seat->m->status_manager, seat->wl_seat); if (seat->status == NULL) return; - zriver_seat_status_v1_add_listener(seat->status, &river_seat_status_listener, seat); + zriver_seat_status_v1_add_listener( + seat->status, &river_seat_status_listener, seat); } static void -handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { struct private *m = data; if (strcmp(interface, wl_output_interface.name) == 0) { - const uint32_t required = 3; + const uint32_t required = 1; if (!verify_iface_version(interface, version, required)) return; - struct wl_output *wl_output = wl_registry_bind(registry, name, &wl_output_interface, required); + struct wl_output *wl_output = wl_registry_bind( + registry, name, &wl_output_interface, required); if (wl_output == NULL) return; - struct output output = { - .m = m, - .wl_output = wl_output, - .wl_name = name, - }; - mtx_lock(&m->mod->lock); - tll_push_back(m->outputs, output); - update_output(&tll_back(m->outputs)); - tll_foreach(m->seats, it) update_seat(&it->item); + tll_push_back(m->outputs, ((struct output){.m = m, .wl_output = wl_output, .wl_name = name})); + instantiate_output(&tll_back(m->outputs)); mtx_unlock(&m->mod->lock); } @@ -590,10 +459,12 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - m->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, required); + m->xdg_output_manager = wl_registry_bind( + registry, name, &zxdg_output_manager_v1_interface, required); mtx_lock(&m->mod->lock); - tll_foreach(m->outputs, it) update_output(&it->item); + tll_foreach(m->outputs, it) + instantiate_output(&it->item); mtx_unlock(&m->mod->lock); } @@ -602,7 +473,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha if (!verify_iface_version(interface, version, required)) return; - struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, required); + struct wl_seat *wl_seat = wl_registry_bind( + registry, name, &wl_seat_interface, required); if (wl_seat == NULL) return; @@ -612,20 +484,23 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha struct seat *seat = &tll_back(m->seats); wl_seat_add_listener(wl_seat, &seat_listener, seat); - update_seat(seat); + instantiate_seat(seat); mtx_unlock(&m->mod->lock); } else if (strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { - const uint32_t required = 2; + const uint32_t required = 1; if (!verify_iface_version(interface, version, required)) return; - m->status_manager = wl_registry_bind(registry, name, &zriver_status_manager_v1_interface, min(version, 4)); + m->status_manager = wl_registry_bind( + registry, name, &zriver_status_manager_v1_interface, required); mtx_lock(&m->mod->lock); - tll_foreach(m->outputs, it) update_output(&it->item); - tll_foreach(m->seats, it) update_seat(&it->item); + tll_foreach(m->outputs, it) + instantiate_output(&it->item); + tll_foreach(m->seats, it) + instantiate_seat(&it->item); mtx_unlock(&m->mod->lock); } } @@ -636,8 +511,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) struct private *m = data; mtx_lock(&m->mod->lock); - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { if (it->item.wl_name == name) { output_destroy(&it->item); tll_remove(m->outputs, it); @@ -646,8 +520,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) } } - tll_foreach(m->seats, it) - { + tll_foreach(m->seats, it) { if (it->item.wl_name == name) { seat_destroy(&it->item); tll_remove(m->seats, it); @@ -678,8 +551,9 @@ run(struct module *mod) goto out; } - if ((registry = wl_display_get_registry(display)) == NULL - || wl_registry_add_listener(registry, ®istry_listener, m) != 0) { + if ((registry = wl_display_get_registry(display)) == NULL || + wl_registry_add_listener(registry, ®istry_listener, m) != 0) + { LOG_ERR("failed to get Wayland registry"); goto out; } @@ -691,10 +565,21 @@ run(struct module *mod) goto out; } - m->is_running = true; - wl_display_roundtrip(display); + bool unlock_at_exit = true; + mtx_lock(&mod->lock); + + m->is_starting_up = false; + + tll_foreach(m->outputs, it) + instantiate_output(&it->item); + tll_foreach(m->seats, it) + instantiate_seat(&it->item); + + unlock_at_exit = false; + mtx_unlock(&mod->lock); + while (true) { wl_display_flush(display); @@ -726,9 +611,11 @@ run(struct module *mod) ret = 0; out: - tll_foreach(m->seats, it) seat_destroy(&it->item); + tll_foreach(m->seats, it) + seat_destroy(&it->item); tll_free(m->seats); - tll_foreach(m->outputs, it) output_destroy(&it->item); + tll_foreach(m->outputs, it) + output_destroy(&it->item); tll_free(m->outputs); if (m->xdg_output_manager != NULL) @@ -739,23 +626,25 @@ out: wl_registry_destroy(registry); if (display != NULL) wl_display_disconnect(display); + + if (unlock_at_exit) + mtx_unlock(&mod->lock); return ret; } static struct module * -river_new(struct particle *template, struct particle *title, bool all_monitors) +river_new(struct particle *template, struct particle *title) { struct private *m = calloc(1, sizeof(*m)); m->template = template; m->title = title; - m->all_monitors = all_monitors; + m->is_starting_up = true; struct module *mod = module_common_new(); mod->private = m; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; m->mod = mod; return mod; } @@ -765,10 +654,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) { const struct yml_node *c = yml_get_value(node, "content"); const struct yml_node *title = yml_get_value(node, "title"); - const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); - - return river_new(conf_to_particle(c, inherited), title != NULL ? conf_to_particle(title, inherited) : NULL, - all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); + return river_new( + conf_to_particle(c, inherited), + title != NULL ? conf_to_particle(title, inherited) : NULL); } static bool @@ -776,7 +664,6 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"title", false, &conf_verify_particle}, - {"all-monitors", false, &conf_verify_bool}, MODULE_COMMON_ATTRS, }; diff --git a/modules/script.c b/modules/script.c index 9f9b40a..766ea96 100644 --- a/modules/script.c +++ b/modules/script.c @@ -1,36 +1,30 @@ -#include -#include -#include -#include -#include #include +#include #include +#include +#include #include -#include #include +#include #include #include #include +#include #define LOG_MODULE "script" #define LOG_ENABLE_DBG 0 -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../config.h" +#include "../config-verify.h" #include "../module.h" #include "../plugin.h" -static const long min_poll_interval = 250; - -struct private -{ +struct private { char *path; size_t argc; char **argv; - int poll_interval; - bool aborted; struct particle *content; @@ -62,19 +56,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - static char desc[32]; - const 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) { @@ -111,8 +92,9 @@ process_line(struct module *mod, const char *line, size_t len) size_t value_len = line + len - _value; - LOG_DBG("%.*s: name=\"%.*s\", type=\"%.*s\", value=\"%.*s\"", (int)len, line, (int)name_len, _name, (int)type_len, - type, (int)value_len, _value); + LOG_DBG("%.*s: name=\"%.*s\", type=\"%.*s\", value=\"%.*s\"", + (int)len, line, + (int)name_len, _name, (int)type_len, type, (int)value_len, _value); name = malloc(name_len + 1); memcpy(name, _name, name_len); @@ -165,12 +147,16 @@ process_line(struct module *mod, const char *line, size_t len) tag = tag_new_float(mod, name, v); } - else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || (type_len > 9 && memcmp(type, "realtime:", 9) == 0)) { + else if ((type_len > 6 && memcmp(type, "range:", 6) == 0) || + (type_len > 9 && memcmp(type, "realtime:", 9 == 0))) + { const char *_start = type + 6; const char *split = memchr(_start, '-', type_len - 6); if (split == NULL || split == _start || (split + 1) - type >= type_len) { - LOG_ERR("tag range delimiter ('-') not found in type: %.*s", (int)type_len, type); + LOG_ERR( + "tag range delimiter ('-') not found in type: %.*s", + (int)type_len, type); goto bad_tag; } @@ -182,7 +168,9 @@ process_line(struct module *mod, const char *line, size_t len) long start = 0; for (size_t i = 0; i < start_len; i++) { if (!(_start[i] >= '0' && _start[i] <= '9')) { - LOG_ERR("tag range start is not an integer: %.*s", (int)start_len, _start); + LOG_ERR( + "tag range start is not an integer: %.*s", + (int)start_len, _start); goto bad_tag; } @@ -192,8 +180,10 @@ process_line(struct module *mod, const char *line, size_t len) long end = 0; for (size_t i = 0; i < end_len; i++) { - if (!(_end[i] >= '0' && _end[i] <= '9')) { - LOG_ERR("tag range end is not an integer: %.*s", (int)end_len, _end); + if (!(_end[i] >= '0' && _end[i] < '9')) { + LOG_ERR( + "tag range end is not an integer: %.*s", + (int)end_len, _end); goto bad_tag; } @@ -215,7 +205,8 @@ process_line(struct module *mod, const char *line, size_t len) } if (v < start || v > end) { - LOG_ERR("tag value is outside range: %ld <= %ld <= %ld", start, v, end); + LOG_ERR("tag value is outside range: %ld <= %ld <= %ld", + start, v, end); goto bad_tag; } @@ -289,7 +280,7 @@ data_received(struct module *mod, const char *data, size_t len) { struct private *m = mod->private; - while (len > m->recv_buf.sz - m->recv_buf.idx) { + if (len > m->recv_buf.sz - m->recv_buf.idx) { size_t new_sz = m->recv_buf.sz == 0 ? 1024 : m->recv_buf.sz * 2; char *new_buf = realloc(m->recv_buf.data, new_sz); @@ -306,28 +297,28 @@ data_received(struct module *mod, const char *data, size_t len) memcpy(&m->recv_buf.data[m->recv_buf.idx], data, len); m->recv_buf.idx += len; - while (true) { - const char *eot = memmem(m->recv_buf.data, m->recv_buf.idx, "\n\n", 2); - if (eot == NULL) { - /* End of transaction not yet available */ - return true; - } - - const size_t transaction_size = eot - m->recv_buf.data + 1; - process_transaction(mod, transaction_size); - - assert(m->recv_buf.idx >= transaction_size + 1); - memmove(m->recv_buf.data, &m->recv_buf.data[transaction_size + 1], m->recv_buf.idx - (transaction_size + 1)); - m->recv_buf.idx -= transaction_size + 1; + const char *eot = memmem(m->recv_buf.data, m->recv_buf.idx, "\n\n", 2); + if (eot == NULL) { + /* End of transaction not yet available */ + return true; } + const size_t transaction_size = eot - m->recv_buf.data + 1; + process_transaction(mod, transaction_size); + + assert(m->recv_buf.idx >= transaction_size + 1); + memmove(m->recv_buf.data, + &m->recv_buf.data[transaction_size + 1], + m->recv_buf.idx - (transaction_size + 1)); + m->recv_buf.idx -= transaction_size + 1; + return true; } static int run_loop(struct module *mod, pid_t pid, int comm_fd) { - int ret = 1; + int ret = 0; while (true) { struct pollfd fds[] = { @@ -356,18 +347,19 @@ run_loop(struct module *mod, pid_t pid, int comm_fd) data_received(mod, data, amount); } - if (fds[0].revents & (POLLHUP | POLLIN)) { + if (fds[0].revents & POLLHUP) { /* 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?)"); - ret = 0; + break; + } + + if (fds[0].revents & POLLIN) { + /* Aborted */ break; } } @@ -376,7 +368,7 @@ run_loop(struct module *mod, pid_t pid, int comm_fd) } static int -execute_script(struct module *mod) +run(struct module *mod) { struct private *m = mod->private; @@ -389,7 +381,7 @@ execute_script(struct module *mod) /* Stdout redirection pipe */ int comm_pipe[2]; - if (pipe2(comm_pipe, O_CLOEXEC) < 0) { + if (pipe(comm_pipe) < 0) { LOG_ERRNO("failed to create stdin/stdout redirection pipe"); close(exec_pipe[0]); close(exec_pipe[1]); @@ -421,8 +413,11 @@ execute_script(struct module *mod) sigemptyset(&mask); const struct sigaction sa = {.sa_handler = SIG_DFL}; - if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0 || sigaction(SIGCHLD, &sa, NULL) < 0 - || sigprocmask(SIG_SETMASK, &mask, NULL) < 0) { + if (sigaction(SIGINT, &sa, NULL) < 0 || + sigaction(SIGTERM, &sa, NULL) < 0 || + sigaction(SIGCHLD, &sa, NULL) < 0 || + sigprocmask(SIG_SETMASK, &mask, NULL) < 0) + { goto fail; } @@ -434,11 +429,13 @@ execute_script(struct module *mod) close(comm_pipe[0]); /* Re-direct stdin/stdout */ - int dev_null = open("/dev/null", O_RDONLY | O_CLOEXEC); + int dev_null = open("/dev/null", O_RDONLY); if (dev_null < 0) goto fail; - if (dup2(dev_null, STDIN_FILENO) < 0 || dup2(comm_pipe[1], STDOUT_FILENO) < 0) { + if (dup2(dev_null, STDIN_FILENO) < 0 || + dup2(comm_pipe[1], STDOUT_FILENO) < 0) + { goto fail; } @@ -446,6 +443,16 @@ execute_script(struct module *mod) close(comm_pipe[1]); comm_pipe[1] = -1; + /* Close *all* other FDs */ + for (int i = STDERR_FILENO + 1; i < 65536; i++) { + if (i == exec_pipe[1]) { + /* Needed for error reporting. Automatically closed + * when execvp() succeeds */ + continue; + } + close(i); + } + execvp(m->path, argv); fail: @@ -474,7 +481,7 @@ execute_script(struct module *mod) } if (r > 0) { - LOG_ERRNO_P(_errno, "%s: failed to start", m->path); + LOG_ERRNO_P("%s: failed to start", _errno, m->path); close(comm_pipe[0]); waitpid(pid, NULL, 0); return -1; @@ -517,7 +524,9 @@ execute_script(struct module *mod) usleep(10000); pid_t waited_pid; - while ((waited_pid = waitpid(pid, NULL, timeout > 0 ? WNOHANG : 0)) == 0) { + while ((waited_pid = waitpid( + pid, NULL, timeout > 0 ? WNOHANG : 0)) == 0) + { struct timeval now; gettimeofday(&now, NULL); @@ -529,7 +538,7 @@ execute_script(struct module *mod) /* Don't spinning */ thrd_yield(); - usleep(100000); /* 100ms */ + usleep(100000); /* 100ms */ } if (waited_pid == pid) { @@ -543,134 +552,48 @@ execute_script(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 / 1000, - .tv_usec = (m->poll_interval % 1000) * 1000, - }; - - 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(char *path, size_t argc, const char *const argv[static argc], int poll_interval, struct particle *_content) +script_new(const char *path, size_t argc, const char *const argv[static argc], + struct particle *_content) { struct private *m = calloc(1, sizeof(*m)); - m->path = path; + m->path = strdup(path); m->content = _content; m->argc = argc; m->argv = malloc(argc * sizeof(m->argv[0])); for (size_t i = 0; i < argc; i++) m->argv[i] = strdup(argv[i]); - m->poll_interval = poll_interval; struct module *mod = module_common_new(); mod->private = m; mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } static struct module * from_conf(const struct yml_node *node, struct conf_inherit inherited) { - const struct yml_node *path_node = yml_get_value(node, "path"); + const struct yml_node *path = yml_get_value(node, "path"); const struct yml_node *args = yml_get_value(node, "args"); const struct yml_node *c = yml_get_value(node, "content"); - 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]; if (args != NULL) { size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(args); iter.node != NULL; yml_list_next(&iter), i++) { + for (struct yml_list_iter iter = yml_list_iter(args); + iter.node != NULL; + yml_list_next(&iter), i++) + { argv[i] = yml_value_as_string(iter.node); } } - const char *yml_path = yml_value_as_string(path_node); - char *path = NULL; - - if (yml_path[0] == '~' && yml_path[1] == '/') { - const char *home_dir = getenv("HOME"); - - if (home_dir == NULL) { - LOG_ERRNO("failed to expand '~"); - return NULL; - } - - if (asprintf(&path, "%s/%s", home_dir, yml_path + 2) < 0) { - LOG_ERRNO("failed to expand '~"); - return NULL; - } - } else - path = strdup(yml_path); - - return script_new(path, argc, argv, poll_interval != NULL ? yml_value_as_int(poll_interval) : 0, - conf_to_particle(c, inherited)); + return script_new( + yml_value_as_string(path), argc, argv, conf_to_particle(c, inherited)); } static bool @@ -680,12 +603,8 @@ conf_verify_path(keychain_t *chain, const struct yml_node *node) return false; const char *path = yml_value_as_string(node); - - const bool is_tilde = path[0] == '~' && path[1] == '/'; - const bool is_absolute = path[0] == '/'; - - if (!is_tilde && !is_absolute) { - LOG_ERR("%s: path must either be absolute, or begin with '~/'", conf_err_prefix(chain, node)); + if (strlen(path) < 1 || path[0] != '/') { + LOG_ERR("%s: path must be absolute", conf_err_prefix(chain, node)); return false; } @@ -698,27 +617,12 @@ conf_verify_args(keychain_t *chain, const struct yml_node *node) return conf_verify_list(chain, node, &conf_verify_string); } -static bool -conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node) -{ - if (!conf_verify_unsigned(chain, node)) - return false; - - if (yml_value_as_int(node) < min_poll_interval) { - LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval); - return false; - } - - return true; -} - static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"path", true, &conf_verify_path}, {"args", false, &conf_verify_args}, - {"poll-interval", false, &conf_verify_poll_interval}, MODULE_COMMON_ATTRS, }; diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 1507241..114a361 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -3,17 +3,15 @@ #define LOG_MODULE "sway-xkb" #define LOG_ENABLE_DBG 0 +#include "../log.h" #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" -#include "../log.h" #include "../particles/dynlist.h" #include "../plugin.h" -#include "i3-common.h" #include "i3-ipc.h" - -#define max(x, y) ((x) > (y) ? (x) : (y)) +#include "i3-common.h" struct input { bool exists; @@ -21,8 +19,7 @@ struct input { char *layout; }; -struct private -{ +struct private { struct particle *template; int left_spacing; int right_spacing; @@ -55,12 +52,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "sway-xkb"; -} - static struct exposable * content(struct module *mod) { @@ -68,9 +59,7 @@ content(struct module *mod) mtx_lock(&mod->lock); - assert(m->num_existing_inputs <= m->num_inputs); - struct exposable *particles[max(m->num_existing_inputs, 1)]; - + struct exposable *particles[m->num_existing_inputs]; for (size_t i = 0, j = 0; i < m->num_inputs; i++) { const struct input *input = &m->inputs[i]; @@ -90,11 +79,12 @@ content(struct module *mod) } mtx_unlock(&mod->lock); - return dynlist_exposable_new(particles, m->num_existing_inputs, m->left_spacing, m->right_spacing); + return dynlist_exposable_new( + particles, m->num_existing_inputs, m->left_spacing, m->right_spacing); } static bool -handle_input_reply(int sock, int type, const struct json_object *json, void *_mod) +handle_input_reply(int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -109,23 +99,12 @@ handle_input_reply(int sock, int type, const struct json_object *json, void *_mo 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]; - if (strcmp(maybe_input->identifier, id) == 0 && !maybe_input->exists) { + if (strcmp(maybe_input->identifier, id) == 0) { input = maybe_input; - LOG_DBG("adding: %s", id); - mtx_lock(&mod->lock); input->exists = true; m->num_existing_inputs++; @@ -141,7 +120,8 @@ handle_input_reply(int sock, int type, const struct json_object *json, void *_mo /* Get current/active layout */ struct json_object *layout; - if (!json_object_object_get_ex(obj, "xkb_active_layout_name", &layout)) + if (!json_object_object_get_ex( + obj, "xkb_active_layout_name", &layout)) return false; const char *new_layout_str = json_object_get_string(layout); @@ -160,7 +140,7 @@ handle_input_reply(int sock, int type, const struct json_object *json, void *_mo } static bool -handle_input_event(int sock, int type, const struct json_object *json, void *_mod) +handle_input_event(int type, const struct json_object *json, void *_mod) { struct module *mod = _mod; struct private *m = mod->private; @@ -186,15 +166,6 @@ handle_input_event(int sock, int type, const struct json_object *json, void *_mo 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]; @@ -209,36 +180,27 @@ handle_input_event(int sock, int type, const struct json_object *json, void *_mo return true; } - if (is_removed) { - if (input->exists) { - LOG_DBG("removing: %s", id); + if (is_removed || is_added) { + mtx_lock(&mod->lock); + assert((is_removed && input->exists) || + (is_added && !input->exists)); - mtx_lock(&mod->lock); - input->exists = false; - m->num_existing_inputs--; - m->dirty = true; - mtx_unlock(&mod->lock); - } - return true; - } + input->exists = !input->exists; + m->num_existing_inputs += is_added ? 1 : -1; + m->dirty = true; - if (is_added) { - if (!input->exists) { - LOG_DBG("adding: %s", id); + mtx_unlock(&mod->lock); - mtx_lock(&mod->lock); - input->exists = true; - m->num_existing_inputs++; - m->dirty = true; - mtx_unlock(&mod->lock); - } + if (is_removed) + return true; - /* “fallthrough”, to query current/active layout */ + /* let is_added fall through, to update layout */ } /* Get current/active layout */ struct json_object *layout; - if (!json_object_object_get_ex(obj, "xkb_active_layout_name", &layout)) + if (!json_object_object_get_ex( + obj, "xkb_active_layout_name", &layout)) return false; const char *new_layout_str = json_object_get_string(layout); @@ -278,7 +240,7 @@ run(struct module *mod) if (!i3_get_socket_address(&addr)) return 1; - int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + int sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { LOG_ERRNO("failed to create UNIX socket"); return 1; @@ -306,8 +268,8 @@ run(struct module *mod) } static struct module * -sway_xkb_new(struct particle *template, const char *identifiers[], size_t num_identifiers, int left_spacing, - int right_spacing) +sway_xkb_new(struct particle *template, const char *identifiers[], + size_t num_identifiers, int left_spacing, int right_spacing) { struct private *m = calloc(1, sizeof(*m)); m->template = template; @@ -327,7 +289,6 @@ sway_xkb_new(struct particle *template, const char *identifiers[], size_t num_id mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } @@ -340,32 +301,40 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); - int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) - : right_spacing != NULL ? yml_value_as_int(right_spacing) - : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : + left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) : + right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; const struct yml_node *ids = yml_get_value(node, "identifiers"); const size_t num_ids = yml_list_length(ids); const char *identifiers[num_ids]; size_t i = 0; - for (struct yml_list_iter it = yml_list_iter(ids); it.node != NULL; yml_list_next(&it), i++) { + for (struct yml_list_iter it = yml_list_iter(ids); + it.node != NULL; + yml_list_next(&it), i++) + { identifiers[i] = yml_value_as_string(it.node); } - return sway_xkb_new(conf_to_particle(c, inherited), identifiers, num_ids, left, right); + return sway_xkb_new( + conf_to_particle(c, inherited), identifiers, num_ids, left, right); } static bool verify_identifiers(keychain_t *chain, const struct yml_node *node) { if (!yml_is_list(node)) { - LOG_ERR("%s: identifiers must be a list of strings", conf_err_prefix(chain, node)); + LOG_ERR("%s: identifiers must be a list of strings", + conf_err_prefix(chain, node)); return false; } - for (struct yml_list_iter it = yml_list_iter(node); it.node != NULL; yml_list_next(&it)) { + for (struct yml_list_iter it = yml_list_iter(node); + it.node != NULL; + yml_list_next(&it)) + { if (!conf_verify_string(chain, it.node)) return false; } @@ -377,9 +346,9 @@ static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, + {"spacing", false, &conf_verify_int}, + {"left-spacing", false, &conf_verify_int}, + {"right-spacing", false, &conf_verify_int}, {"identifiers", true, &verify_identifiers}, MODULE_COMMON_ATTRS, }; @@ -393,5 +362,5 @@ const struct module_iface module_sway_xkb_iface = { }; #if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -extern const struct module_iface iface __attribute__((weak, alias("module_sway_xkb_iface"))); +extern const struct module_iface iface __attribute__((weak, alias("module_sway_xkb_iface"))) ; #endif diff --git a/modules/xkb.c b/modules/xkb.c index e8e3c91..16cc864 100644 --- a/modules/xkb.c +++ b/modules/xkb.c @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #include @@ -10,10 +10,10 @@ #define LOG_MODULE "xkb" #define LOG_ENABLE_DBG 0 -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../plugin.h" #include "../xcb.h" @@ -32,8 +32,7 @@ struct indicators { char **names; }; -struct private -{ +struct private { struct particle *label; struct indicators indicators; struct layouts layouts; @@ -73,12 +72,6 @@ destroy(struct module *mod) module_default_destroy(mod); } -static const char * -description(const struct module *mod) -{ - return "xkb"; -} - static struct exposable * content(struct module *mod) { @@ -118,8 +111,10 @@ xkb_enable(xcb_connection_t *conn) { xcb_generic_error_t *err; - xcb_xkb_use_extension_cookie_t cookie = xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); - xcb_xkb_use_extension_reply_t *reply = xcb_xkb_use_extension_reply(conn, cookie, &err); + xcb_xkb_use_extension_cookie_t cookie = xcb_xkb_use_extension( + conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xcb_xkb_use_extension_reply_t *reply = xcb_xkb_use_extension_reply( + conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to query for XKB extension: %s", xcb_error(err)); @@ -141,7 +136,8 @@ xkb_enable(xcb_connection_t *conn) static int get_xkb_event_base(xcb_connection_t *conn) { - const struct xcb_query_extension_reply_t *reply = xcb_get_extension_data(conn, &xcb_xkb_id); + const struct xcb_query_extension_reply_t *reply = xcb_get_extension_data( + conn, &xcb_xkb_id); if (reply == NULL) { LOG_ERR("failed to get XKB extension data"); @@ -157,14 +153,19 @@ get_xkb_event_base(xcb_connection_t *conn) } static bool -get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, struct indicators *indicators) +get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, + struct indicators *indicators) { xcb_generic_error_t *err; - xcb_xkb_get_names_cookie_t cookie = xcb_xkb_get_names(conn, XCB_XKB_ID_USE_CORE_KBD, - XCB_XKB_NAME_DETAIL_GROUP_NAMES | XCB_XKB_NAME_DETAIL_SYMBOLS - | XCB_XKB_NAME_DETAIL_INDICATOR_NAMES); + xcb_xkb_get_names_cookie_t cookie = xcb_xkb_get_names( + conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_GROUP_NAMES | + XCB_XKB_NAME_DETAIL_SYMBOLS | + XCB_XKB_NAME_DETAIL_INDICATOR_NAMES); - xcb_xkb_get_names_reply_t *reply = xcb_xkb_get_names_reply(conn, cookie, &err); + xcb_xkb_get_names_reply_t *reply = xcb_xkb_get_names_reply( + conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to get layouts and indicators: %s", xcb_error(err)); @@ -174,18 +175,22 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru xcb_xkb_get_names_value_list_t vlist; void *buf = xcb_xkb_get_names_value_list(reply); - xcb_xkb_get_names_value_list_unpack(buf, reply->nTypes, reply->indicators, reply->virtualMods, reply->groupNames, - reply->nKeys, reply->nKeyAliases, reply->nRadioGroups, reply->which, &vlist); + xcb_xkb_get_names_value_list_unpack( + buf, reply->nTypes, reply->indicators, reply->virtualMods, + reply->groupNames, reply->nKeys, reply->nKeyAliases, + reply->nRadioGroups, reply->which, &vlist); /* Number of groups (aka layouts) */ layouts->count = xcb_xkb_get_names_value_list_groups_length(reply, &vlist); layouts->layouts = calloc(layouts->count, sizeof(layouts->layouts[0])); /* Number of indicators */ - indicators->count = xcb_xkb_get_names_value_list_indicator_names_length(reply, &vlist); + indicators->count = xcb_xkb_get_names_value_list_indicator_names_length( + reply, &vlist); indicators->names = calloc(indicators->count, sizeof(indicators->names[0])); - xcb_get_atom_name_cookie_t symbols_name_cookie = xcb_get_atom_name(conn, vlist.symbolsName); + xcb_get_atom_name_cookie_t symbols_name_cookie = xcb_get_atom_name( + conn, vlist.symbolsName); xcb_get_atom_name_cookie_t group_name_cookies[layouts->count]; for (size_t i = 0; i < layouts->count; i++) @@ -198,14 +203,17 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru char *symbols = NULL; /* Get layout short names (e.g. "us") */ - xcb_get_atom_name_reply_t *atom_name = xcb_get_atom_name_reply(conn, symbols_name_cookie, &err); + xcb_get_atom_name_reply_t *atom_name = xcb_get_atom_name_reply( + conn, symbols_name_cookie, &err); if (err != NULL) { LOG_ERR("failed to get 'symbols' atom name: %s", xcb_error(err)); free(err); goto err; } - symbols = strndup(xcb_get_atom_name_name(atom_name), xcb_get_atom_name_name_length(atom_name)); + symbols = strndup( + xcb_get_atom_name_name(atom_name), + xcb_get_atom_name_name_length(atom_name)); LOG_DBG("symbols: %s", symbols); free(atom_name); @@ -218,7 +226,9 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru goto err; } - layouts->layouts[i].name = strndup(xcb_get_atom_name_name(atom_name), xcb_get_atom_name_name_length(atom_name)); + layouts->layouts[i].name = strndup( + xcb_get_atom_name_name(atom_name), + xcb_get_atom_name_name_length(atom_name)); LOG_DBG("layout #%zd: long name: %s", i, layouts->layouts[i].name); free(atom_name); @@ -233,7 +243,9 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru goto err; } - indicators->names[i] = strndup(xcb_get_atom_name_name(atom_name), xcb_get_atom_name_name_length(atom_name)); + indicators->names[i] = strndup( + xcb_get_atom_name_name(atom_name), + xcb_get_atom_name_name_length(atom_name)); LOG_DBG("indicator #%zd: %s", i, indicators->names[i]); free(atom_name); @@ -241,7 +253,8 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru /* e.g. pc+us+inet(evdev)+group(..) */ size_t layout_idx = 0; - for (char *tok_ctx = NULL, *tok = strtok_r(symbols, "+", &tok_ctx); tok != NULL; + for (char *tok_ctx = NULL, *tok = strtok_r(symbols, "+", &tok_ctx); + tok != NULL; tok = strtok_r(NULL, "+", &tok_ctx)) { char *fname = strtok(tok, "()"); @@ -260,7 +273,8 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru continue; if (layout_idx >= layouts->count) { - LOG_ERR("layout vs group name count mismatch: %zd > %zd", layout_idx + 1, layouts->count); + LOG_ERR("layout vs group name count mismatch: %zd > %zd", + layout_idx + 1, layouts->count); goto err; } @@ -270,7 +284,8 @@ get_layouts_and_indicators(xcb_connection_t *conn, struct layouts *layouts, stru } if (layout_idx != layouts->count) { - LOG_ERR("layout vs group name count mismatch: %zd != %zd", layout_idx, layouts->count); + LOG_ERR("layout vs group name count mismatch: %zd != %zd", + layout_idx, layouts->count); goto err; } @@ -291,8 +306,10 @@ get_current_layout(xcb_connection_t *conn) { xcb_generic_error_t *err; - xcb_xkb_get_state_cookie_t cookie = xcb_xkb_get_state(conn, XCB_XKB_ID_USE_CORE_KBD); - xcb_xkb_get_state_reply_t *reply = xcb_xkb_get_state_reply(conn, cookie, &err); + xcb_xkb_get_state_cookie_t cookie = xcb_xkb_get_state( + conn, XCB_XKB_ID_USE_CORE_KBD); + xcb_xkb_get_state_reply_t *reply = xcb_xkb_get_state_reply( + conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to get XKB state: %s", xcb_error(err)); @@ -309,8 +326,10 @@ static uint32_t get_indicator_state(xcb_connection_t *conn) { xcb_generic_error_t *err; - xcb_xkb_get_indicator_state_cookie_t cookie = xcb_xkb_get_indicator_state(conn, XCB_XKB_ID_USE_CORE_KBD); - xcb_xkb_get_indicator_state_reply_t *reply = xcb_xkb_get_indicator_state_reply(conn, cookie, &err); + xcb_xkb_get_indicator_state_cookie_t cookie = xcb_xkb_get_indicator_state( + conn, XCB_XKB_ID_USE_CORE_KBD); + xcb_xkb_get_indicator_state_reply_t *reply = xcb_xkb_get_indicator_state_reply( + conn, cookie, &err); if (err != NULL) { LOG_ERR("failed to get indicator state: %s", xcb_error(err)); @@ -328,14 +347,23 @@ get_indicator_state(xcb_connection_t *conn) static bool register_for_events(xcb_connection_t *conn) { - xcb_void_cookie_t cookie - = xcb_xkb_select_events_checked(conn, XCB_XKB_ID_USE_CORE_KBD, - (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY - | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY), - 0, - (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY - | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY), - 0, 0, NULL); + xcb_void_cookie_t cookie = xcb_xkb_select_events_checked( + conn, + XCB_XKB_ID_USE_CORE_KBD, + ( + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY + ), + 0, + ( + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY + ), + 0, 0, NULL); xcb_generic_error_t *err = xcb_request_check(conn, cookie); if (err != NULL) { @@ -359,17 +387,13 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) assert(xcb_fd >= 0); while (!has_error) { - struct pollfd pfds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = xcb_fd, .events = POLLIN | POLLHUP}}; + struct pollfd pfds[] = { + {.fd = mod->abort_fd, .events = POLLIN }, + {.fd = xcb_fd, .events = POLLIN | POLLHUP } + }; /* Use poll() since xcb_wait_for_events() doesn't return on signals */ - if (poll(pfds, sizeof(pfds) / sizeof(pfds[0]), -1) < 0) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll"); - break; - } - + poll(pfds, sizeof(pfds) / sizeof(pfds[0]), -1); if (pfds[0].revents & POLLIN) { ret = true; break; @@ -388,7 +412,9 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) * not for long though... */ - for (xcb_generic_event_t *_evt = xcb_wait_for_event(conn); _evt != NULL; _evt = xcb_poll_for_event(conn)) { + for (xcb_generic_event_t *_evt = xcb_wait_for_event(conn); + _evt != NULL; + _evt = xcb_poll_for_event(conn)) { if (_evt->response_type != xkb_event_base) { LOG_WARN("non-XKB event ignored: %d", _evt->response_type); @@ -396,7 +422,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) continue; } - switch (_evt->pad0) { + switch(_evt->pad0) { default: LOG_WARN("unimplemented XKB event: %d", _evt->pad0); break; @@ -424,7 +450,7 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) mtx_unlock(&mod->lock); bar->refresh(bar); } else { - /* Can happen while transitioning to a new map */ + /* Can happen while transitioning to a new map */ free_layouts(layouts); free_indicators(indicators); } @@ -433,7 +459,8 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) } case XCB_XKB_STATE_NOTIFY: { - const xcb_xkb_state_notify_event_t *evt = (const xcb_xkb_state_notify_event_t *)_evt; + const xcb_xkb_state_notify_event_t *evt = + (const xcb_xkb_state_notify_event_t *)_evt; if (evt->changed & XCB_XKB_STATE_PART_GROUP_STATE) { mtx_lock(&mod->lock); @@ -450,8 +477,8 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) break; case XCB_XKB_INDICATOR_STATE_NOTIFY: { - const xcb_xkb_indicator_state_notify_event_t *evt - = (const xcb_xkb_indicator_state_notify_event_t *)_evt; + const xcb_xkb_indicator_state_notify_event_t *evt = + (const xcb_xkb_indicator_state_notify_event_t *)_evt; #if 0 size_t idx = __builtin_ctz(evt->stateChanged); @@ -468,7 +495,8 @@ event_loop(struct module *mod, xcb_connection_t *conn, int xkb_event_base) continue; bool enabled = (evt->state >> i) & 1; - LOG_DBG("%s: %s", m->indicators.names[i], enabled ? "enabled" : "disabled"); + LOG_DBG("%s: %s", m->indicators.names[i], + enabled ? "enabled" : "disabled"); const char *name = m->indicators.names[i]; bool is_caps = strcasecmp(name, "caps lock") == 0; @@ -568,12 +596,18 @@ talk_to_xkb(struct module *mod, xcb_connection_t *conn) size_t idx = 0; for (size_t i = 0; i < layouts.count; i++) { - idx += snprintf(&buf[idx], sizeof(buf) - idx, "%s%s (%s)%s", i == m->current ? "*" : "", - layouts.layouts[i].name, layouts.layouts[i].symbol, i + 1 < layouts.count ? ", " : ""); + idx += snprintf(&buf[idx], sizeof(buf) - idx, "%s%s (%s)%s", + i == m->current ? "*" : "", + layouts.layouts[i].name, + layouts.layouts[i].symbol, + i + 1 < layouts.count ? ", " : ""); } - LOG_INFO("layouts: %s, caps-lock:%s, num-lock:%s, scroll-lock:%s", buf, caps_lock ? "on" : "off", - num_lock ? "on" : "off", scroll_lock ? "on" : "off"); + LOG_INFO("layouts: %s, caps-lock:%s, num-lock:%s, scroll-lock:%s", + buf, + caps_lock ? "on" : "off", + num_lock ? "on" : "off", + scroll_lock ? "on" : "off"); } mtx_lock(&mod->lock); @@ -616,7 +650,6 @@ 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 c730128..ad856a4 100644 --- a/modules/xwindow.c +++ b/modules/xwindow.c @@ -1,30 +1,27 @@ -#include -#include -#include #include #include #include -#include +#include #include +#include +#include #include #include -#include #include #include #include #define LOG_MODULE "xwindow" -#include "../bar/bar.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../bar/bar.h" +#include "../config.h" +#include "../config-verify.h" #include "../plugin.h" #include "../xcb.h" -struct private -{ +struct private { /* Accessed from bar thread only */ struct particle *label; @@ -39,29 +36,27 @@ struct private xcb_window_t active_win; }; -static const char * -description(const struct module *mod) -{ - return "xwindow"; -} - static void update_active_window(struct private *m) { if (m->active_win != 0) { - xcb_void_cookie_t c = xcb_change_window_attributes_checked(m->conn, m->active_win, XCB_CW_EVENT_MASK, - (const uint32_t[]){XCB_EVENT_MASK_NO_EVENT}); + xcb_void_cookie_t c = xcb_change_window_attributes_checked( + m->conn, m->active_win, XCB_CW_EVENT_MASK, + (const uint32_t []){XCB_EVENT_MASK_NO_EVENT}); xcb_generic_error_t *e = xcb_request_check(m->conn, c); if (e != NULL) { - LOG_DBG("failed to de-register events on previous active window: %s", xcb_error(e)); + LOG_DBG( + "failed to de-register events on previous active window: %s", + xcb_error(e)); free(e); } m->active_win = 0; } - xcb_get_property_cookie_t c = xcb_get_property(m->conn, 0, m->root_win, _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 0, 32); + xcb_get_property_cookie_t c = xcb_get_property( + m->conn, 0, m->root_win, _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 0, 32); xcb_generic_error_t *e; xcb_get_property_reply_t *r = xcb_get_property_reply(m->conn, c, &e); @@ -83,8 +78,9 @@ update_active_window(struct private *m) free(r); if (m->active_win != 0) { - xcb_change_window_attributes(m->conn, m->active_win, XCB_CW_EVENT_MASK, - (const uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE}); + xcb_change_window_attributes( + m->conn, m->active_win, XCB_CW_EVENT_MASK, + (const uint32_t []){XCB_EVENT_MASK_PROPERTY_CHANGE}); } } @@ -101,7 +97,8 @@ update_application(struct module *mod) if (m->active_win == 0) return; - xcb_get_property_cookie_t c = xcb_get_property(m->conn, 0, m->active_win, _NET_WM_PID, XCB_ATOM_CARDINAL, 0, 32); + xcb_get_property_cookie_t c = xcb_get_property( + m->conn, 0, m->active_win, _NET_WM_PID, XCB_ATOM_CARDINAL, 0, 32); xcb_generic_error_t *e; xcb_get_property_reply_t *r = xcb_get_property_reply(m->conn, c, &e); @@ -130,7 +127,7 @@ update_application(struct module *mod) char path[1024]; snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - int fd = open(path, O_RDONLY | O_CLOEXEC); + int fd = open(path, O_RDONLY); if (fd == -1) return; @@ -159,11 +156,12 @@ update_title(struct module *mod) if (m->active_win == 0) return; - xcb_get_property_cookie_t c1 - = xcb_get_property(m->conn, 0, m->active_win, _NET_WM_VISIBLE_NAME, UTF8_STRING, 0, 1000); - xcb_get_property_cookie_t c2 = xcb_get_property(m->conn, 0, m->active_win, _NET_WM_NAME, UTF8_STRING, 0, 1000); - xcb_get_property_cookie_t c3 - = xcb_get_property(m->conn, 0, m->active_win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1000); + xcb_get_property_cookie_t c1 = xcb_get_property( + m->conn, 0, m->active_win, _NET_WM_VISIBLE_NAME, UTF8_STRING, 0, 1000); + xcb_get_property_cookie_t c2 = xcb_get_property( + m->conn, 0, m->active_win, _NET_WM_NAME, UTF8_STRING, 0, 1000); + xcb_get_property_cookie_t c3 = xcb_get_property( + m->conn, 0, m->active_win, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1000); xcb_generic_error_t *e1, *e2, *e3; xcb_get_property_reply_t *r1 = xcb_get_property_reply(m->conn, c1, &e1); @@ -201,7 +199,7 @@ update_title(struct module *mod) free(r1); free(r2); free(r3); -} + } static int run(struct module *mod) @@ -221,16 +219,19 @@ run(struct module *mod) /* Need a window(?) to be able to process events */ m->monitor_win = xcb_generate_id(m->conn); - xcb_create_window(m->conn, screen->root_depth, m->monitor_win, screen->root, -1, -1, 1, 1, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_OVERRIDE_REDIRECT, - (const uint32_t[]){1}); + xcb_create_window(m->conn, screen->root_depth, m->monitor_win, screen->root, + -1, -1, 1, 1, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, + XCB_CW_OVERRIDE_REDIRECT, (const uint32_t []){1}); xcb_map_window(m->conn, m->monitor_win); /* Register for property changes on root window. This allows us to * catch e.g. window switches etc */ - xcb_change_window_attributes(m->conn, screen->root, XCB_CW_EVENT_MASK, - (const uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE}); + xcb_change_window_attributes( + m->conn, screen->root, XCB_CW_EVENT_MASK, + (const uint32_t []){XCB_EVENT_MASK_PROPERTY_CHANGE}); xcb_flush(m->conn); @@ -239,25 +240,19 @@ run(struct module *mod) update_title(mod); mod->bar->refresh(mod->bar); - int ret = 1; - int xcb_fd = xcb_get_file_descriptor(m->conn); while (true) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = xcb_fd, .events = POLLIN}}; - if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) < 0) { - if (errno == EINTR) - continue; + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, + {.fd = xcb_fd, .events = POLLIN}}; + poll(fds, 2, -1); - LOG_ERRNO("failed to poll"); + if (fds[0].revents & POLLIN) break; - } - if (fds[0].revents & POLLIN) { - ret = 0; - break; - } - - for (xcb_generic_event_t *_e = xcb_wait_for_event(m->conn); _e != NULL; _e = xcb_poll_for_event(m->conn)) { + for (xcb_generic_event_t *_e = xcb_wait_for_event(m->conn); + _e != NULL; + _e = xcb_poll_for_event(m->conn)) + { switch (XCB_EVENT_RESPONSE_TYPE(_e)) { case 0: LOG_ERR("XCB: %s", xcb_error((const xcb_generic_error_t *)_e)); @@ -265,13 +260,18 @@ run(struct module *mod) case XCB_PROPERTY_NOTIFY: { xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)_e; - if (e->atom == _NET_ACTIVE_WINDOW || e->atom == _NET_CURRENT_DESKTOP) { + if (e->atom == _NET_ACTIVE_WINDOW || + e->atom == _NET_CURRENT_DESKTOP) + { /* Active desktop and/or window changed */ update_active_window(m); update_application(mod); update_title(mod); mod->bar->refresh(mod->bar); - } else if (e->atom == _NET_WM_VISIBLE_NAME || e->atom == _NET_WM_NAME || e->atom == XCB_ATOM_WM_NAME) { + } else if (e->atom == _NET_WM_VISIBLE_NAME || + e->atom == _NET_WM_NAME || + e->atom == XCB_ATOM_WM_NAME) + { assert(e->window == m->active_win); update_title(mod); mod->bar->refresh(mod->bar); @@ -286,7 +286,7 @@ run(struct module *mod) xcb_destroy_window(m->conn, m->monitor_win); xcb_disconnect(m->conn); - return ret; + return 0; } static struct exposable * @@ -332,7 +332,6 @@ xwindow_new(struct particle *label) mod->run = &run; mod->destroy = &destroy; mod->content = &content; - mod->description = &description; return mod; } diff --git a/particle.c b/particle.c index f35b5d1..ffe330f 100644 --- a/particle.c +++ b/particle.c @@ -1,21 +1,20 @@ #include "particle.h" -#include -#include -#include #include #include #include +#include +#include -#include -#include #include +#include #include +#include #define LOG_MODULE "particle" #define LOG_ENABLE_DBG 0 -#include "bar/bar.h" #include "log.h" +#include "bar/bar.h" void particle_default_destroy(struct particle *particle) @@ -23,49 +22,42 @@ particle_default_destroy(struct particle *particle) if (particle->deco != NULL) particle->deco->destroy(particle->deco); fcft_destroy(particle->font); - for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) - free(particle->on_click_templates[i]); + free(particle->on_click_template); free(particle); } struct particle * -particle_common_new(int left_margin, int right_margin, char **on_click_templates, struct fcft_font *font, - enum font_shaping font_shaping, pixman_color_t foreground, struct deco *deco) +particle_common_new(int left_margin, int right_margin, + const char *on_click_template, + 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->font_shaping = font_shaping; 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] = on_click_templates[i]; - } - } - } - return p; } void exposable_default_destroy(struct exposable *exposable) { - for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) - free(exposable->on_click[i]); + free(exposable->on_click); free(exposable); } void -exposable_render_deco(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height) +exposable_render_deco(const struct exposable *exposable, + pixman_image_t *pix, int x, int y, int height) { const struct deco *deco = exposable->particle->deco; if (deco != NULL) deco->expose(deco, pix, x, y, exposable->width, height); + } static bool @@ -110,7 +102,9 @@ tokenize_cmdline(char *cmdline, char ***argv) return false; } - if (!push_argv(argv, &argv_size, p, &idx) || !push_argv(argv, &argv_size, NULL, &idx)) { + if (!push_argv(argv, &argv_size, p, &idx) || + !push_argv(argv, &argv_size, NULL, &idx)) + { goto err; } else return true; @@ -146,35 +140,21 @@ err: } void -exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, - int x, int y) +exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, + enum mouse_event event, 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", - [MOUSE_BTN_WHEEL_UP] = "wheel-up", - [MOUSE_BTN_WHEEL_DOWN] = "wheel-down", - [MOUSE_BTN_PREVIOUS] = "previous", - [MOUSE_BTN_NEXT] = "next", - }; - LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", exposable, - event == ON_MOUSE_MOTION ? "motion" : "click", button_name[btn], x, y, exposable->on_click[btn]); -#endif + LOG_DBG("on_mouse: exposable=%p, event=%s, x=%d, y=%d (on-click=%s)", + exposable, event == ON_MOUSE_MOTION ? "motion" : "click", x, y, + exposable->on_click); /* If we have a handler, change cursor to a hand */ - const char *cursor - = (exposable->particle != NULL && exposable->particle->have_on_click_template) ? "hand2" : "left_ptr"; - bar->set_cursor(bar, cursor); + bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2"); /* If this is a mouse click, and we have a handler, execute it */ - if (exposable->on_click[btn] != NULL && event == ON_MOUSE_CLICK) { + if (exposable->on_click != NULL && event == ON_MOUSE_CLICK) { /* Need a writeable copy, whose scope *we* control */ - char *cmd = strdup(exposable->on_click[btn]); - LOG_DBG("cmd = \"%s\"", exposable->on_click[btn]); + char *cmd = strdup(exposable->on_click); + LOG_DBG("cmd = \"%s\"", exposable->on_click); char **argv; if (!tokenize_cmdline(cmd, &argv)) { @@ -192,15 +172,15 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo int wstatus; if (waitpid(pid, &wstatus, 0) == -1) - LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click[btn]); + LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click); if (WIFEXITED(wstatus)) { if (WEXITSTATUS(wstatus) != 0) - LOG_ERRNO_P(WEXITSTATUS(wstatus), "%s: failed to execute", exposable->on_click[btn]); + LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click); } else - LOG_ERR("%s: did not exit normally", exposable->on_click[btn]); + LOG_ERR("%s: did not exit normally", exposable->on_click); - LOG_DBG("%s: launched", exposable->on_click[btn]); + LOG_DBG("%s: launched", exposable->on_click); } else { /* * Use a pipe with O_CLOEXEC to communicate exec() failure @@ -235,7 +215,7 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo case 0: /* Child */ - close(pipe_fds[0]); /* Close read end */ + close(pipe_fds[0]); /* Close read end */ LOG_DBG("executing on-click handler: %s", cmd); @@ -243,8 +223,11 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo sigemptyset(&mask); const struct sigaction sa = {.sa_handler = SIG_DFL}; - if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0 - || sigaction(SIGCHLD, &sa, NULL) < 0 || sigprocmask(SIG_SETMASK, &mask, NULL) < 0) { + if (sigaction(SIGINT, &sa, NULL) < 0 || + sigaction(SIGTERM, &sa, NULL) < 0 || + sigaction(SIGCHLD, &sa, NULL) < 0 || + sigprocmask(SIG_SETMASK, &mask, NULL) < 0) + { goto fail; } @@ -257,12 +240,18 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo goto fail; } - if (dup2(dev_null_r, STDIN_FILENO) == -1 || dup2(dev_null_w, STDOUT_FILENO) == -1 - || dup2(dev_null_w, STDERR_FILENO) == -1) { + if (dup2(dev_null_r, STDIN_FILENO) == -1 || + dup2(dev_null_w, STDOUT_FILENO) == -1 || + dup2(dev_null_w, STDERR_FILENO) == -1) + { LOG_ERRNO("failed to redirect stdin/stdout/stderr"); goto fail; } + /* Close *all* other FDs (e.g. script modules' FDs) */ + for (int i = STDERR_FILENO + 1; i < 65536; i++) + close(i); + execvp(argv[0], argv); fail: @@ -278,8 +267,6 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo 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); @@ -294,14 +281,11 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo } struct exposable * -exposable_common_new(const struct particle *particle, const struct tag_set *tags) +exposable_common_new(const struct particle *particle, const char *on_click) { struct exposable *exposable = calloc(1, sizeof(*exposable)); exposable->particle = particle; - - if (particle != NULL && particle->have_on_click_template) { - tags_expand_templates(exposable->on_click, (const char **)particle->on_click_templates, MOUSE_BTN_COUNT, tags); - } + exposable->on_click = on_click != NULL ? strdup(on_click) : NULL; exposable->destroy = &exposable_default_destroy; exposable->on_mouse = &exposable_default_on_mouse; return exposable; diff --git a/particle.h b/particle.h index bc8648d..e89b9c4 100644 --- a/particle.h +++ b/particle.h @@ -5,44 +5,28 @@ #include "color.h" #include "decoration.h" -#include "font-shaping.h" #include "tag.h" -enum mouse_event { - ON_MOUSE_MOTION, - ON_MOUSE_CLICK, -}; - -enum mouse_button { - MOUSE_BTN_NONE, - MOUSE_BTN_LEFT, - MOUSE_BTN_MIDDLE, - MOUSE_BTN_RIGHT, - MOUSE_BTN_WHEEL_UP, - MOUSE_BTN_WHEEL_DOWN, - MOUSE_BTN_PREVIOUS, - MOUSE_BTN_NEXT, - - 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]; + char *on_click_template; pixman_color_t foreground; struct fcft_font *font; - enum font_shaping font_shaping; struct deco *deco; void (*destroy)(struct particle *particle); - struct exposable *(*instantiate)(const struct particle *particle, const struct tag_set *tags); + struct exposable *(*instantiate)(const struct particle *particle, + const struct tag_set *tags); +}; + +enum mouse_event { + ON_MOUSE_MOTION, + ON_MOUSE_CLICK, }; struct exposable { @@ -50,35 +34,40 @@ struct exposable { void *private; int width; /* Should be set by begin_expose(), at latest */ - char *on_click[MOUSE_BTN_COUNT]; + char *on_click; void (*destroy)(struct exposable *exposable); int (*begin_expose)(struct exposable *exposable); - void (*expose)(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); + void (*expose)(const struct exposable *exposable, pixman_image_t *pix, + int x, int y, int height); - void (*on_mouse)(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, - int y); + void (*on_mouse)(struct exposable *exposable, struct bar *bar, + enum mouse_event event, int x, int y); }; -struct particle *particle_common_new(int left_margin, int right_margin, char *on_click_templates[], - struct fcft_font *font, enum font_shaping font_shaping, pixman_color_t foreground, - struct deco *deco); +struct particle *particle_common_new( + int left_margin, int right_margin, const char *on_click_template, + 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 struct tag_set *tags); +struct exposable *exposable_common_new( + const struct particle *particle, const char *on_click); void exposable_default_destroy(struct exposable *exposable); -void exposable_render_deco(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); +void exposable_render_deco( + const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); -void exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y); +void exposable_default_on_mouse( + struct exposable *exposable, struct bar *bar, + enum mouse_event event, int x, int y); /* List of attributes *all* particles implement */ -#define PARTICLE_COMMON_ATTRS \ - {"margin", false, &conf_verify_unsigned}, {"left-margin", false, &conf_verify_unsigned}, \ - {"right-margin", false, &conf_verify_unsigned}, {"on-click", false, &conf_verify_on_click}, \ - {"font", false, &conf_verify_font}, {"font-shaping", false, &conf_verify_font_shaping}, \ - {"foreground", false, &conf_verify_color}, {"deco", false, &conf_verify_decoration}, \ - { \ - NULL, false, NULL \ - } +#define PARTICLE_COMMON_ATTRS \ + {"margin", false, &conf_verify_int}, \ + {"left-margin", false, &conf_verify_int}, \ + {"right-margin", false, &conf_verify_int}, \ + {"on-click", false, &conf_verify_string}, \ + {"font", false, &conf_verify_font}, \ + {"foreground", false, &conf_verify_color}, \ + {"deco", false, &conf_verify_decoration}, \ + {NULL, false, NULL} diff --git a/particles/dynlist.c b/particles/dynlist.c index fcd0066..c04d610 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -1,14 +1,12 @@ #include "dynlist.h" -#include #include #define LOG_MODULE "dynlist" #include "../log.h" #include "../particle.h" -struct private -{ +struct private { int left_spacing; int right_spacing; @@ -38,24 +36,15 @@ dynlist_begin_expose(struct exposable *exposable) const struct private *e = exposable->private; exposable->width = 0; - bool have_at_least_one = false; for (size_t i = 0; i < e->count; i++) { struct exposable *ee = e->exposables[i]; e->widths[i] = ee->begin_expose(ee); - assert(e->widths[i] >= 0); - - if (e->widths[i] > 0) { - exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; - have_at_least_one = true; - } + exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; } - if (have_at_least_one) - exposable->width -= e->left_spacing + e->right_spacing; - else - assert(exposable->width == 0); + exposable->width -= e->left_spacing + e->right_spacing; return exposable->width; } @@ -72,39 +61,42 @@ dynlist_expose(const struct exposable *exposable, pixman_image_t *pix, int x, in for (size_t i = 0; i < e->count; i++) { const struct exposable *ee = e->exposables[i]; ee->expose(ee, pix, x + left_spacing, y, height); - if (e->widths[i] > 0) - x += left_spacing + e->widths[i] + right_spacing; + x += left_spacing + e->widths[i] + right_spacing; } } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, + enum mouse_event event, int x, int y) { + //const struct particle *p = exposable->particle; const struct private *e = exposable->private; - if (exposable->on_click[btn] != NULL) { - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + if (exposable->on_click != NULL) { + exposable_default_on_mouse(exposable, bar, event, x, y); return; } - int px = /*p->left_margin;*/ 0; + int px = /*p->left_margin;*/0; for (size_t i = 0; i < e->count; i++) { if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { - e->exposables[i]->on_mouse(e->exposables[i], bar, event, btn, x - px, y); + e->exposables[i]->on_mouse( + e->exposables[i], bar, event, x - px, y); } return; } - if (e->exposables[i]->width > 0) - px += e->left_spacing + e->exposables[i]->width + e->right_spacing; + + px += e->left_spacing + e->exposables[i]->width + e->right_spacing; } LOG_DBG("on_mouse missed all sub-particles"); - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, x, y); } struct exposable * -dynlist_exposable_new(struct exposable **exposables, size_t count, int left_spacing, int right_spacing) +dynlist_exposable_new(struct exposable **exposables, size_t count, + int left_spacing, int right_spacing) { struct private *e = calloc(1, sizeof(*e)); e->count = count; diff --git a/particles/dynlist.h b/particles/dynlist.h index 810df45..4867997 100644 --- a/particles/dynlist.h +++ b/particles/dynlist.h @@ -3,5 +3,5 @@ #include struct particle; -struct exposable *dynlist_exposable_new(struct exposable **exposables, size_t count, int left_spacing, - int right_spacing); +struct exposable *dynlist_exposable_new( + struct exposable **exposables, size_t count, int left_spacing, int right_spacing); diff --git a/particles/empty.c b/particles/empty.c index 052eacd..e97f929 100644 --- a/particles/empty.c +++ b/particles/empty.c @@ -1,14 +1,15 @@ #include -#include "../config-verify.h" #include "../config.h" +#include "../config-verify.h" #include "../particle.h" #include "../plugin.h" static int begin_expose(struct exposable *exposable) { - exposable->width = exposable->particle->left_margin + exposable->particle->right_margin; + exposable->width = exposable->particle->left_margin + + exposable->particle->right_margin; return exposable->width; } @@ -21,9 +22,13 @@ 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) { - struct exposable *exposable = exposable_common_new(particle, tags); + char *on_click = tags_expand_template(particle->on_click_template, tags); + + struct exposable *exposable = exposable_common_new(particle, on_click); exposable->begin_expose = &begin_expose; exposable->expose = &expose; + + free(on_click); return exposable; } diff --git a/particles/list.c b/particles/list.c index 83b5d0c..6f51152 100644 --- a/particles/list.c +++ b/particles/list.c @@ -2,14 +2,13 @@ #define LOG_MODULE "list" #define LOG_ENABLE_DBG 0 -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../config.h" +#include "../config-verify.h" #include "../particle.h" #include "../plugin.h" -struct private -{ +struct private { struct particle **particles; size_t count; int left_spacing, right_spacing; @@ -22,6 +21,7 @@ struct eprivate { int left_spacing, right_spacing; }; + static void exposable_destroy(struct exposable *exposable) { @@ -39,29 +39,18 @@ static int begin_expose(struct exposable *exposable) { const struct eprivate *e = exposable->private; - bool have_at_least_one = false; - exposable->width = 0; + exposable->width = exposable->particle->left_margin; for (size_t i = 0; i < e->count; i++) { struct exposable *ee = e->exposables[i]; e->widths[i] = ee->begin_expose(ee); - assert(e->widths[i] >= 0); - - if (e->widths[i] > 0) { - exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; - have_at_least_one = true; - } + exposable->width += e->left_spacing + e->widths[i] + e->right_spacing; } - if (have_at_least_one) { - exposable->width -= e->left_spacing + e->right_spacing; - exposable->width += exposable->particle->left_margin; - exposable->width += exposable->particle->right_margin; - } else - assert(exposable->width == 0); - + exposable->width -= e->left_spacing + e->right_spacing; + exposable->width += exposable->particle->right_margin; return exposable->width; } @@ -80,20 +69,20 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int for (size_t i = 0; i < e->count; i++) { const struct exposable *ee = e->exposables[i]; ee->expose(ee, pix, x + left_spacing, y, height); - if (e->widths[i] > 0) - x += left_spacing + e->widths[i] + right_spacing; + x += left_spacing + e->widths[i] + right_spacing; } } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, + enum mouse_event event, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { + if (exposable->on_click != NULL) { /* We have our own handler */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, x, y); return; } @@ -101,16 +90,17 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e for (size_t i = 0; i < e->count; i++) { if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { - e->exposables[i]->on_mouse(e->exposables[i], bar, event, btn, x - px, y); + e->exposables[i]->on_mouse( + e->exposables[i], bar, event, x - px, y); } return; } - if (e->exposables[i]->width > 0) - px += e->left_spacing + e->exposables[i]->width + e->right_spacing; + + px += e->left_spacing + e->exposables[i]->width + e->right_spacing; } /* We're between sub-particles (or in the left/right margin) */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, x, y); } static struct exposable * @@ -131,12 +121,16 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(e->exposables[i] != NULL); } - struct exposable *exposable = exposable_common_new(particle, tags); + char *on_click = tags_expand_template(particle->on_click_template, tags); + + struct exposable *exposable = exposable_common_new(particle, on_click); 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; } @@ -152,8 +146,9 @@ particle_destroy(struct particle *particle) } struct particle * -particle_list_new(struct particle *common, struct particle *particles[], size_t count, int left_spacing, - int right_spacing) +particle_list_new(struct particle *common, + struct particle *particles[], size_t count, + int left_spacing, int right_spacing) { struct private *p = calloc(1, sizeof(*p)); p->particles = malloc(count * sizeof(p->particles[0])); @@ -178,20 +173,21 @@ from_conf(const struct yml_node *node, struct particle *common) const struct yml_node *_left_spacing = yml_get_value(node, "left-spacing"); const struct yml_node *_right_spacing = yml_get_value(node, "right-spacing"); - int left_spacing = spacing != NULL ? yml_value_as_int(spacing) - : _left_spacing != NULL ? yml_value_as_int(_left_spacing) - : 0; - int right_spacing = spacing != NULL ? yml_value_as_int(spacing) - : _right_spacing != NULL ? yml_value_as_int(_right_spacing) - : 2; + int left_spacing = spacing != NULL ? yml_value_as_int(spacing) : + _left_spacing != NULL ? yml_value_as_int(_left_spacing) : 0; + int right_spacing = spacing != NULL ? yml_value_as_int(spacing) : + _right_spacing != NULL ? yml_value_as_int(_right_spacing) : 2; size_t count = yml_list_length(items); struct particle *parts[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) { - parts[idx] - = conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); + for (struct yml_list_iter it = yml_list_iter(items); + it.node != NULL; + yml_list_next(&it), idx++) + { + parts[idx] = conf_to_particle( + it.node, (struct conf_inherit){common->font, common->foreground}); } return particle_list_new(common, parts, count, left_spacing, right_spacing); @@ -202,9 +198,9 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"items", true, &conf_verify_particle_list_items}, - {"spacing", false, &conf_verify_unsigned}, - {"left-spacing", false, &conf_verify_unsigned}, - {"right-spacing", false, &conf_verify_unsigned}, + {"spacing", false, &conf_verify_int}, + {"left-spacing", false, &conf_verify_int}, + {"right-spacing", false, &conf_verify_int}, PARTICLE_COMMON_ATTRS, }; diff --git a/particles/map.c b/particles/map.c index c5510ff..57bb0fb 100644 --- a/particles/map.c +++ b/particles/map.c @@ -1,253 +1,22 @@ -#include -#include #include #include +#include #define LOG_MODULE "map" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../config.h" +#include "../config-verify.h" #include "../particle.h" #include "../plugin.h" #include "dynlist.h" -#include "map.h" - -// String globbing match. -// Note: Uses "non-greedy" implementation for "*" wildcard matching -static bool -string_like(const char* name, const char* pattern) -{ - LOG_DBG("pattern:%s name:%s", pattern, name); - int px = 0, nx = 0; - int nextpx = 0, nextnx = 0; - - while (px < strlen(pattern) || nx < strlen(name)) { - if (px < strlen(pattern)) { - char c = pattern[px]; - switch (c) { - case '?': { - // single character - px++; - nx++; - continue; - } - case '*': { - // zero or more glob - nextpx=px; - nextnx=nx+1; - px++; - continue; - } - default: { - // normal character - if (nx < strlen(name) && name[nx] == c) - { - px++; - nx++; - continue; - } - } - } - - } - - // mismatch - if (0 < nextnx && nextnx <= strlen(name)) { - px = nextpx; - nx = nextnx; - continue; - } - - return false; - - } - - LOG_DBG("map: name %s matched all the pattern %s", name, pattern); - // Matched all of pattern to all of name. Success. - return true; -} - -static bool -int_condition(const long tag_value, const long cond_value, enum map_op op) -{ - switch (op) { - case MAP_OP_EQ: - return tag_value == cond_value; - case MAP_OP_NE: - return tag_value != cond_value; - case MAP_OP_LE: - return tag_value <= cond_value; - case MAP_OP_LT: - return tag_value < cond_value; - case MAP_OP_GE: - return tag_value >= cond_value; - case MAP_OP_GT: - return tag_value > cond_value; - case MAP_OP_SELF: - LOG_WARN("using int tag as bool"); - default: - return false; - } -} - -static bool -float_condition(const double tag_value, const double cond_value, enum map_op op) -{ - switch (op) { - case MAP_OP_EQ: - return tag_value == cond_value; - case MAP_OP_NE: - return tag_value != cond_value; - case MAP_OP_LE: - return tag_value <= cond_value; - case MAP_OP_LT: - return tag_value < cond_value; - case MAP_OP_GE: - return tag_value >= cond_value; - case MAP_OP_GT: - return tag_value > cond_value; - case MAP_OP_SELF: - LOG_WARN("using float tag as bool"); - default: - return false; - } -} - -static bool -str_condition(const char *tag_value, const char *cond_value, enum map_op op) -{ - switch (op) { - case MAP_OP_EQ: - return strcmp(tag_value, cond_value) == 0; - case MAP_OP_NE: - return strcmp(tag_value, cond_value) != 0; - case MAP_OP_LE: - return strcmp(tag_value, cond_value) <= 0; - case MAP_OP_LT: - return strcmp(tag_value, cond_value) < 0; - case MAP_OP_GE: - return strcmp(tag_value, cond_value) >= 0; - case MAP_OP_GT: - return strcmp(tag_value, cond_value) > 0; - case MAP_OP_LIKE: - return string_like(tag_value, cond_value) != 0; - case MAP_OP_SELF: - LOG_WARN("using String tag as bool"); - default: - return false; - } -} - -static bool -eval_comparison(const struct map_condition *map_cond, const struct tag_set *tags) -{ - const struct tag *tag = tag_for_name(tags, map_cond->tag); - if (tag == NULL) { - LOG_WARN("tag %s not found", map_cond->tag); - return false; - } - - switch (tag->type(tag)) { - case TAG_TYPE_INT: { - errno = 0; - char *end; - const long cond_value = strtol(map_cond->value, &end, 0); - - if (errno == ERANGE) { - LOG_WARN("value %s is too large", map_cond->value); - return false; - } else if (*end != '\0') { - LOG_WARN("failed to parse %s into int", map_cond->value); - return false; - } - - const long tag_value = tag->as_int(tag); - return int_condition(tag_value, cond_value, map_cond->op); - } - case TAG_TYPE_FLOAT: { - errno = 0; - char *end; - const double cond_value = strtod(map_cond->value, &end); - - if (errno == ERANGE) { - LOG_WARN("value %s is too large", map_cond->value); - return false; - } else if (*end != '\0') { - LOG_WARN("failed to parse %s into float", map_cond->value); - return false; - } - - const double tag_value = tag->as_float(tag); - return float_condition(tag_value, cond_value, map_cond->op); - } - case TAG_TYPE_BOOL: - if (map_cond->op == MAP_OP_SELF) - return tag->as_bool(tag); - else { - LOG_WARN("boolean tag '%s' should be used directly", map_cond->tag); - return false; - } - case TAG_TYPE_STRING: { - const char *tag_value = tag->as_string(tag); - return str_condition(tag_value, map_cond->value, map_cond->op); - } - } - return false; -} - -static bool -eval_map_condition(const struct map_condition *map_cond, const struct tag_set *tags) -{ - switch (map_cond->op) { - case MAP_OP_NOT: - return !eval_map_condition(map_cond->cond1, tags); - - case MAP_OP_AND: - return eval_map_condition(map_cond->cond1, tags) && eval_map_condition(map_cond->cond2, tags); - - case MAP_OP_OR: - return eval_map_condition(map_cond->cond1, tags) || eval_map_condition(map_cond->cond2, tags); - - default: - return eval_comparison(map_cond, tags); - } -} - -void -free_map_condition(struct map_condition *c) -{ - switch (c->op) { - case MAP_OP_EQ: - case MAP_OP_NE: - case MAP_OP_LE: - case MAP_OP_LT: - case MAP_OP_GE: - case MAP_OP_LIKE: - case MAP_OP_GT: - free(c->value); - /* FALLTHROUGH */ - case MAP_OP_SELF: - free(c->tag); - break; - case MAP_OP_AND: - case MAP_OP_OR: - free_map_condition(c->cond2); - /* FALLTHROUGH */ - case MAP_OP_NOT: - free_map_condition(c->cond1); - break; - } - free(c); -} - struct particle_map { - struct map_condition *condition; + const char *tag_value; struct particle *particle; }; -struct private -{ +struct private { + char *tag; struct particle *default_particle; struct particle_map *map; size_t count; @@ -272,13 +41,11 @@ begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; - int width = e->exposable->begin_expose(e->exposable); - assert(width >= 0); + exposable->width = ( + exposable->particle->left_margin + + e->exposable->begin_expose(e->exposable) + + exposable->particle->right_margin); - if (width > 0) - width += exposable->particle->left_margin + exposable->particle->right_margin; - - exposable->width = width; return exposable->width; } @@ -288,42 +55,53 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int struct eprivate *e = exposable->private; exposable_render_deco(exposable, pix, x, y, height); - e->exposable->expose(e->exposable, pix, x + exposable->particle->left_margin, y, height); + e->exposable->expose( + e->exposable, pix, x + exposable->particle->left_margin, y, height); } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { + if (exposable->on_click != NULL) { /* We have our own handler */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, 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, btn, x - px, y); + e->exposable->on_mouse(e->exposable, bar, event, x - px, y); return; } /* In the left- or right margin */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, x, y); } static struct exposable * instantiate(const struct particle *particle, const struct tag_set *tags) { const struct private *p = particle->private; + const struct tag *tag = tag_for_name(tags, p->tag); + + if (tag == NULL) { + return p->default_particle != NULL + ? p->default_particle->instantiate(p->default_particle, tags) + : dynlist_exposable_new(NULL, 0, 0, 0); + } + + const char *tag_value = tag->as_string(tag); struct particle *pp = NULL; for (size_t i = 0; i < p->count; i++) { const struct particle_map *e = &p->map[i]; - if (!eval_map_condition(e->condition, tags)) + if (strcmp(e->tag_value, tag_value) != 0) continue; pp = e->particle; @@ -341,12 +119,15 @@ instantiate(const struct particle *particle, const struct tag_set *tags) assert(e->exposable != NULL); - struct exposable *exposable = exposable_common_new(particle, tags); + char *on_click = tags_expand_template(particle->on_click_template, tags); + struct exposable *exposable = exposable_common_new(particle, on_click); 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; } @@ -361,25 +142,28 @@ particle_destroy(struct particle *particle) for (size_t i = 0; i < p->count; i++) { struct particle *pp = p->map[i].particle; pp->destroy(pp); - free_map_condition(p->map[i].condition); + free((char *)p->map[i].tag_value); } free(p->map); + free(p->tag); free(p); particle_default_destroy(particle); } static struct particle * -map_new(struct particle *common, const struct particle_map particle_map[], size_t count, +map_new(struct particle *common, const char *tag, + const struct particle_map particle_map[], size_t count, struct particle *default_particle) { struct private *priv = calloc(1, sizeof(*priv)); + priv->tag = strdup(tag); priv->default_particle = default_particle; priv->count = count; priv->map = malloc(count * sizeof(priv->map[0])); for (size_t i = 0; i < count; i++) { - priv->map[i].condition = particle_map[i].condition; + priv->map[i].tag_value = strdup(particle_map[i].tag_value); priv->map[i].particle = particle_map[i].particle; } @@ -390,30 +174,24 @@ map_new(struct particle *common, const struct particle_map particle_map[], size_ } static bool -verify_map_conditions(keychain_t *chain, const struct yml_node *node) +verify_map_values(keychain_t *chain, const struct yml_node *node) { if (!yml_is_dict(node)) { - LOG_ERR("%s: must be a dictionary of workspace-name: particle mappings", conf_err_prefix(chain, node)); + LOG_ERR( + "%s: must be a dictionary of workspace-name: particle mappings", + conf_err_prefix(chain, node)); return false; } - bool result = true; - for (struct yml_dict_iter it = yml_dict_iter(node); it.key != NULL; yml_dict_next(&it)) { + for (struct yml_dict_iter it = yml_dict_iter(node); + it.key != NULL; + yml_dict_next(&it)) + { const char *key = yml_value_as_string(it.key); if (key == NULL) { LOG_ERR("%s: key must be a string", conf_err_prefix(chain, it.key)); return false; } - char *key_clone = strdup(key); - YY_BUFFER_STATE buffer = yy_scan_string(key_clone); - if (yyparse() != 0) { - LOG_ERR("%s: %s", conf_err_prefix(chain, it.key), MAP_PARSER_ERROR_MSG); - free(MAP_PARSER_ERROR_MSG); - result = false; - } else - free_map_condition(MAP_CONDITION_PARSE_RESULT); - yy_delete_buffer(buffer); - free(key_clone); if (!conf_verify_particle(chain_push(chain, key), it.value)) return false; @@ -421,42 +199,46 @@ verify_map_conditions(keychain_t *chain, const struct yml_node *node) chain_pop(chain); } - return result; + return true; } static struct particle * from_conf(const struct yml_node *node, struct particle *common) { - const struct yml_node *conditions = yml_get_value(node, "conditions"); + const struct yml_node *tag = yml_get_value(node, "tag"); + const struct yml_node *values = yml_get_value(node, "values"); const struct yml_node *def = yml_get_value(node, "default"); - struct particle_map particle_map[yml_dict_length(conditions)]; + struct particle_map particle_map[yml_dict_length(values)]; - struct conf_inherit inherited - = {.font = common->font, .font_shaping = common->font_shaping, .foreground = common->foreground}; + struct conf_inherit inherited = { + .font = common->font, + .foreground = common->foreground + }; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(conditions); it.key != NULL; yml_dict_next(&it), idx++) { - /* Note we can skip the error checking here */ - char *key_clone = strdup(yml_value_as_string(it.key)); - YY_BUFFER_STATE buffer = yy_scan_string(key_clone); - yyparse(); - particle_map[idx].condition = MAP_CONDITION_PARSE_RESULT; - yy_delete_buffer(buffer); - free(key_clone); + for (struct yml_dict_iter it = yml_dict_iter(values); + it.key != NULL; + yml_dict_next(&it), idx++) + { + particle_map[idx].tag_value = yml_value_as_string(it.key); particle_map[idx].particle = conf_to_particle(it.value, inherited); } - struct particle *default_particle = def != NULL ? conf_to_particle(def, inherited) : NULL; + struct particle *default_particle = def != NULL + ? conf_to_particle(def, inherited) : NULL; - return map_new(common, particle_map, yml_dict_length(conditions), default_particle); + return map_new( + common, yml_value_as_string(tag), particle_map, yml_dict_length(values), + default_particle); } static bool verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { - {"conditions", true, &verify_map_conditions}, + {"tag", true, &conf_verify_string}, + {"values", true, &verify_map_values}, {"default", false, &conf_verify_particle}, PARTICLE_COMMON_ATTRS, }; diff --git a/particles/map.h b/particles/map.h deleted file mode 100644 index 1256744..0000000 --- a/particles/map.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -enum map_op { - MAP_OP_EQ, - MAP_OP_NE, - MAP_OP_LE, - MAP_OP_LT, - MAP_OP_GE, - MAP_OP_GT, - MAP_OP_SELF, - MAP_OP_NOT, - MAP_OP_LIKE, - - MAP_OP_AND, - MAP_OP_OR, -}; - -struct map_condition { - union { - char *tag; - struct map_condition *cond1; - }; - enum map_op op; - union { - char *value; - struct map_condition *cond2; - }; -}; - -void free_map_condition(struct map_condition *c); - -typedef struct yy_buffer_state *YY_BUFFER_STATE; -YY_BUFFER_STATE yy_scan_string(const char *str); -int yyparse(); -void yy_delete_buffer(YY_BUFFER_STATE buffer); - -extern struct map_condition *MAP_CONDITION_PARSE_RESULT; -extern char *MAP_PARSER_ERROR_MSG; diff --git a/particles/map.l b/particles/map.l deleted file mode 100644 index 034353c..0000000 --- a/particles/map.l +++ /dev/null @@ -1,80 +0,0 @@ -%{ -#include -#include "map.h" -#include "map.tab.h" -void yyerror(const char *s); -%} - -%option warn nodefault nounput noinput noyywrap - - char *quoted = NULL; - size_t quote_len = 0; - -%x QUOTE - -%% - -[[:alnum:]_-]+ yylval.str = strdup(yytext); return WORD; - -\" { - BEGIN(QUOTE); - quoted = calloc(1, sizeof(quoted[0])); -} - -[^\\\"]* { - /* printf("CAT: %s\n", yytext); */ - const size_t yy_length = strlen(yytext); - quoted = realloc(quoted, quote_len + yy_length + 1); - strcat(quoted, yytext); - quote_len += yy_length; -} - -\\\" { - /* printf("escaped quote\n"); */ - quoted = realloc(quoted, quote_len + 1 + 1); - strcat(quoted, "\""); - quote_len++; -} - -\\. { - /* printf("CAT: %s\n", yytext); */ - const size_t yy_length = strlen(yytext); - quoted = realloc(quoted, quote_len + yy_length + 1); - strcat(quoted, yytext); - quote_len += yy_length; -} - -\\ { - /* quoted string that ends with a backslash: "string\ */ - quoted = realloc(quoted, quote_len + 1 + 1); - strcat(quoted, "\\"); - quote_len++; -} - -\" { - /* printf("QUOTED=%s\n", quoted); */ - yylval.str = strdup(quoted); - - free(quoted); - quoted = NULL; - quote_len = 0; - - BEGIN(INITIAL); - return STRING; -} - -== yylval.op = MAP_OP_EQ; return CMP_OP; -!= yylval.op = MAP_OP_NE; return CMP_OP; -\<= yylval.op = MAP_OP_LE; return CMP_OP; -\< yylval.op = MAP_OP_LT; return CMP_OP; ->= yylval.op = MAP_OP_GE; return CMP_OP; -> yylval.op = MAP_OP_GT; return CMP_OP; -~~ yylval.op = MAP_OP_LIKE; return CMP_OP; -&& yylval.op = MAP_OP_AND; return BOOL_OP; -\|\| yylval.op = MAP_OP_OR; return BOOL_OP; -~ return NOT; -\( return L_PAR; -\) return R_PAR; -[ \t\n] ; -. yylval.str = strdup(yytext); return STRING; -%% diff --git a/particles/map.y b/particles/map.y deleted file mode 100644 index 8f3f46b..0000000 --- a/particles/map.y +++ /dev/null @@ -1,125 +0,0 @@ -%{ -#include -#include - -#include "map.h" - -struct map_condition *MAP_CONDITION_PARSE_RESULT; -char *MAP_PARSER_ERROR_MSG; - -static const int NUM_TOKENS = 7; -int yylex(); -void yyerror(const char *str); -%} - -%define parse.lac full -%define parse.error custom - -%union { - char *str; - struct map_condition *condition; - enum map_op op; -} - -%token WORD STRING CMP_OP L_PAR R_PAR -%left BOOL_OP -%precedence NOT - -%destructor { free_map_condition($$); } condition -%destructor { free($$); } WORD -%destructor { free($$); } STRING - -%% -result: condition { MAP_CONDITION_PARSE_RESULT = $1; }; - -condition: - WORD { - $$ = malloc(sizeof(struct map_condition)); - $$->tag = $1; - $$->op = MAP_OP_SELF; - } - | - WORD CMP_OP WORD { - $$ = malloc(sizeof(struct map_condition)); - $$->tag = $1; - $$->op = $2; - $$->value = $3; - } - | - WORD CMP_OP STRING { - $$ = malloc(sizeof(struct map_condition)); - $$->tag = $1; - $$->op = $2; - $$->value = $3; - } - | - L_PAR condition R_PAR { $$ = $2; } - | - NOT condition { - $$ = malloc(sizeof(struct map_condition)); - $$->cond1 = $2; - $$->op = MAP_OP_NOT; - } - | - condition BOOL_OP condition { - $$ = malloc(sizeof(struct map_condition)); - $$->cond1 = $1; - $$->op = $2; - $$->cond2 = $3; - } - ; -%% - -void yyerror(const char *str) -{ - fprintf(stderr, "error: %s\n", str); -} - -static char const* -token_to_str(yysymbol_kind_t tkn) -{ - switch (tkn) { - case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >, ~~"; - case YYSYMBOL_BOOL_OP: return "||, &&"; - case YYSYMBOL_L_PAR: return "("; - case YYSYMBOL_R_PAR: return ")"; - case YYSYMBOL_NOT: return "~"; - default: return yysymbol_name(tkn); - } -} - -static int -yyreport_syntax_error (const yypcontext_t *ctx) -{ - int res = 0; - char *errmsg = malloc(1024); - errmsg[0] = '\0'; - - // Report the tokens expected at this point. - yysymbol_kind_t expected[NUM_TOKENS]; - int n = yypcontext_expected_tokens(ctx, expected, NUM_TOKENS); - if (n < 0) - res = n; // Forward errors to yyparse. - else { - for (int i = 0; i < n; ++i) { - strcat(errmsg, i == 0 ? "expected [" : ", "); - strcat(errmsg, token_to_str(expected[i])); - } - strcat(errmsg, "]"); - } - - // Report the unexpected token. - yysymbol_kind_t lookahead = yypcontext_token(ctx); - if (lookahead != YYSYMBOL_YYEMPTY) { - strcat(errmsg, ", found "); - if (!(lookahead == YYSYMBOL_STRING || lookahead == YYSYMBOL_WORD)) - strcat(errmsg, yysymbol_name(lookahead)); - else if (yylval.str != NULL) - strcat(errmsg, yylval.str); - else - strcat(errmsg, "nothing"); - } - - MAP_PARSER_ERROR_MSG = errmsg; - return res; -} diff --git a/particles/meson.build b/particles/meson.build index 091f551..6b31081 100644 --- a/particles/meson.build +++ b/particles/meson.build @@ -1,25 +1,3 @@ -flex = find_program('flex', required: true) -bison = find_program('bison', required: true) - -lgen = generator( - flex, - output : '@BASENAME@.yy.c', - arguments : ['-o', '@OUTPUT@', '@INPUT@'] -) -lfiles = lgen.process('map.l') - -pgen = generator( - bison, - output : ['@BASENAME@.tab.c', '@BASENAME@.tab.h'], - arguments : ['-Wall', - '-Wcounterexamples', - '--defines=@OUTPUT1@', - '--output=@OUTPUT0@', '@INPUT@'] -) -pfiles = pgen.process('map.y') - -map_parser = declare_dependency(sources: [pfiles, lfiles], include_directories: '.') - particle_sdk = declare_dependency(dependencies: [pixman, tllist, fcft]) dynlist_lib = build_target( @@ -36,7 +14,7 @@ dynlist = declare_dependency(link_with: dynlist_lib) deps = { 'empty': [], 'list': [], - 'map': [dynlist, map_parser], + 'map': [dynlist], 'progress-bar': [], 'ramp': [], 'string': [], diff --git a/particles/progress-bar.c b/particles/progress-bar.c index f0bacbf..ad5c4cd 100644 --- a/particles/progress-bar.c +++ b/particles/progress-bar.c @@ -1,17 +1,16 @@ -#include #include #include +#include #define LOG_MODULE "progress_bar" #define LOG_ENABLE_DBG 0 -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../config.h" +#include "../config-verify.h" #include "../particle.h" #include "../plugin.h" -struct private -{ +struct private { char *tag; int width; @@ -58,26 +57,14 @@ static int begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; - bool have_at_least_one = false; - - exposable->width = 0; - - /* Sub-exposables */ - for (size_t i = 0; i < e->count; i++) { - int width = e->exposables[i]->begin_expose(e->exposables[i]); - - assert(width >= 0); - if (width >= 0) { - exposable->width += width; - have_at_least_one = true; - } - } /* Margins */ - if (have_at_least_one) { - exposable->width += exposable->particle->left_margin + exposable->particle->right_margin; - } else - assert(exposable->width == 0); + exposable->width = exposable->particle->left_margin + + exposable->particle->right_margin; + + /* Sub-exposables */ + for (size_t i = 0; i < e->count; i++) + exposable->width += e->exposables[i]->begin_expose(e->exposables[i]); return exposable->width; } @@ -97,8 +84,30 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + int x, int y) { + if (exposable->on_click == NULL) { + exposable_default_on_mouse(exposable, bar, event, x, y); + return; + } + + /* + * Hack-warning! + * + * In order to pass the *clicked* position to the on_click + * handler, we expand the handler *again* (first time would be + * when the particle instantiated us). + * + * We pass a single tag, "where", which is a percentage value. + * + * Keep a reference to the un-expanded string, to be able to reset + * it after executing the handler. + * + * Note that we only consider the actual progress bar to be + * clickable. This means we ignore the start and end markers. + */ + const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; @@ -111,7 +120,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e /* Mouse is over the start-marker */ struct exposable *start = e->exposables[0]; if (start->on_mouse != NULL) - start->on_mouse(start, bar, event, btn, x - p->left_margin, y); + start->on_mouse(start, bar, event, x - p->left_margin, y); } else { /* Mouse if over left margin */ bar->set_cursor(bar, "left_ptr"); @@ -130,7 +139,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e /* 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, btn, x - x_offset - clickable_width, y); + end->on_mouse(end, bar, event, x - x_offset - clickable_width, y); } else { /* Mouse is over the right margin */ bar->set_cursor(bar, "left_ptr"); @@ -138,48 +147,30 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e return; } - /* - * Hack-warning! - * - * In order to pass the *clicked* position to the on_click - * handler, we expand the handler *again* (first time would be - * when the particle instantiated us). - * - * We pass a single tag, "where", which is a percentage value. - * - * Keep a reference to the un-expanded string, to be able to - * reset it after executing the handler. - * - * Note that we only consider the actual progress bar to be - * clickable. This means we ignore the start and end markers. - */ - /* Remember the original handler, so that we can restore it */ - char *original[MOUSE_BTN_COUNT]; - for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) - original[i] = exposable->on_click[i]; + char *original = exposable->on_click; if (event == ON_MOUSE_CLICK) { - long where = clickable_width > 0 ? 100 * (x - x_offset) / clickable_width : 0; + long where = clickable_width > 0 + ? 100 * (x - x_offset) / clickable_width + : 0; struct tag_set tags = { .tags = (struct tag *[]){tag_new_int(NULL, "where", where)}, .count = 1, }; - tags_expand_templates(exposable->on_click, (const char **)exposable->on_click, MOUSE_BTN_COUNT, &tags); + exposable->on_click = tags_expand_template(exposable->on_click, &tags); tag_set_destroy(&tags); } /* Call default implementation, which will execute our handler */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, x, y); if (event == ON_MOUSE_CLICK) { /* Reset handler string */ - for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) { - free(exposable->on_click[i]); - exposable->on_click[i] = original[i]; - } + free(exposable->on_click); + exposable->on_click = original; } } @@ -193,17 +184,19 @@ instantiate(const struct particle *particle, const struct tag_set *tags) long min = tag != NULL ? tag->min(tag) : 0; long max = tag != NULL ? tag->max(tag) : 0; - LOG_DBG("%s: value=%ld, min=%ld, max=%ld", tag != NULL ? tag->name(tag) : "", value, min, max); + LOG_DBG("%s: value=%ld, min=%ld, max=%ld", + tag != NULL ? tag->name(tag) : "", value, min, max); long fill_count = max == min ? 0 : p->width * value / (max - min); long empty_count = p->width - fill_count; struct eprivate *epriv = calloc(1, sizeof(*epriv)); - epriv->count = (1 + /* Start marker */ - fill_count + /* Before current position */ - 1 + /* Current position indicator */ - empty_count + /* After current position */ - 1); /* End marker */ + epriv->count = ( + 1 + /* Start marker */ + fill_count + /* Before current position */ + 1 + /* Current position indicator */ + empty_count + /* After current position */ + 1); /* End marker */ epriv->exposables = malloc(epriv->count * sizeof(epriv->exposables[0])); @@ -220,7 +213,10 @@ instantiate(const struct particle *particle, const struct tag_set *tags) for (size_t i = 0; i < epriv->count; i++) assert(epriv->exposables[i] != NULL); - struct exposable *exposable = exposable_common_new(particle, tags); + char *on_click = tags_expand_template(particle->on_click_template, tags); + + struct exposable *exposable = exposable_common_new(particle, on_click); + free(on_click); exposable->private = epriv; exposable->destroy = &exposable_destroy; @@ -252,7 +248,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) LOG_DBG("tag: %s, value: %ld, " "units-per-segment: %f, units-filled: %f, units-til-next: %f", - tag->name(tag), value, units_per_segment, units_filled, units_til_next_segment); + tag->name(tag), value, + units_per_segment, units_filled, units_til_next_segment); #endif @@ -263,8 +260,10 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } static struct particle * -progress_bar_new(struct particle *common, const char *tag, int width, struct particle *start_marker, - struct particle *end_marker, struct particle *fill, struct particle *empty, struct particle *indicator) +progress_bar_new(struct particle *common, const char *tag, int width, + struct particle *start_marker, struct particle *end_marker, + struct particle *fill, struct particle *empty, + struct particle *indicator) { struct private *priv = calloc(1, sizeof(*priv)); priv->tag = strdup(tag); @@ -294,14 +293,18 @@ from_conf(const struct yml_node *node, struct particle *common) struct conf_inherit inherited = { .font = common->font, - .font_shaping = common->font_shaping, .foreground = common->foreground, }; - return progress_bar_new(common, yml_value_as_string(tag), yml_value_as_int(length), - conf_to_particle(start, inherited), conf_to_particle(end, inherited), - conf_to_particle(fill, inherited), conf_to_particle(empty, inherited), - conf_to_particle(indicator, inherited)); + return progress_bar_new( + common, + yml_value_as_string(tag), + yml_value_as_int(length), + conf_to_particle(start, inherited), + conf_to_particle(end, inherited), + conf_to_particle(fill, inherited), + conf_to_particle(empty, inherited), + conf_to_particle(indicator, inherited)); } static bool @@ -309,7 +312,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"tag", true, &conf_verify_string}, - {"length", true, &conf_verify_unsigned}, + {"length", true, &conf_verify_int}, /* TODO: make these optional? Default to empty */ {"start", true, &conf_verify_particle}, {"end", true, &conf_verify_particle}, diff --git a/particles/ramp.c b/particles/ramp.c index befe1d9..45cc277 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -1,24 +1,16 @@ -#include #include #include +#include #include -#define LOG_MODULE "ramp" -#define LOG_ENABLE_DBG 0 -#include "../config-verify.h" #include "../config.h" -#include "../log.h" +#include "../config-verify.h" #include "../particle.h" #include "../plugin.h" -struct private -{ +struct private { char *tag; - bool use_custom_min; - long min; - bool use_custom_max; - long max; struct particle **particles; size_t count; }; @@ -42,13 +34,11 @@ begin_expose(struct exposable *exposable) { struct eprivate *e = exposable->private; - int width = e->exposable->begin_expose(e->exposable); - assert(width >= 0); + exposable->width = ( + exposable->particle->left_margin + + e->exposable->begin_expose(e->exposable) + + exposable->particle->right_margin); - if (width > 0) - width += exposable->particle->left_margin + exposable->particle->right_margin; - - exposable->width = width; return exposable->width; } @@ -58,30 +48,32 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int struct eprivate *e = exposable->private; exposable_render_deco(exposable, pix, x, y, height); - e->exposable->expose(e->exposable, pix, x + exposable->particle->left_margin, y, height); + e->exposable->expose( + e->exposable, pix, x + exposable->particle->left_margin, y, height); } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { + if (exposable->on_click != NULL) { /* We have our own handler */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, 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, btn, x - px, y); + e->exposable->on_mouse(e->exposable, bar, event, x - px, y); return; } /* In the left- or right margin */ - exposable_default_on_mouse(exposable, bar, event, btn, x, y); + exposable_default_on_mouse(exposable, bar, event, x, y); } static void @@ -110,29 +102,6 @@ instantiate(const struct particle *particle, const struct tag_set *tags) long min = tag != NULL ? tag->min(tag) : 0; long max = tag != NULL ? tag->max(tag) : 0; - min = p->use_custom_min ? p->min : min; - max = p->use_custom_max ? p->max : max; - - if (min > max) { - LOG_WARN("tag's minimum value is greater than its maximum: " - "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); @@ -154,28 +123,27 @@ instantiate(const struct particle *particle, const struct tag_set *tags) e->exposable = pp->instantiate(pp, tags); assert(e->exposable != NULL); - struct exposable *exposable = exposable_common_new(particle, tags); + char *on_click = tags_expand_template(particle->on_click_template, tags); + struct exposable *exposable = exposable_common_new(particle, on_click); 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; } static struct particle * -ramp_new(struct particle *common, const char *tag, struct particle *particles[], size_t count, bool use_custom_min, - long min, bool use_custom_max, long max) +ramp_new(struct particle *common, const char *tag, + struct particle *particles[], size_t count) { struct private *priv = calloc(1, sizeof(*priv)); priv->tag = strdup(tag); priv->particles = malloc(count * sizeof(priv->particles[0])); priv->count = count; - priv->use_custom_max = use_custom_max; - priv->max = max; - priv->use_custom_min = use_custom_min; - priv->min = min; for (size_t i = 0; i < count; i++) priv->particles[i] = particles[i]; @@ -191,22 +159,20 @@ from_conf(const struct yml_node *node, struct particle *common) { const struct yml_node *tag = yml_get_value(node, "tag"); const struct yml_node *items = yml_get_value(node, "items"); - const struct yml_node *min = yml_get_value(node, "min"); - const struct yml_node *max = yml_get_value(node, "max"); size_t count = yml_list_length(items); struct particle *parts[count]; size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(items); it.node != NULL; yml_list_next(&it), idx++) { - parts[idx] - = conf_to_particle(it.node, (struct conf_inherit){common->font, common->font_shaping, common->foreground}); + for (struct yml_list_iter it = yml_list_iter(items); + it.node != NULL; + yml_list_next(&it), idx++) + { + parts[idx] = conf_to_particle( + it.node, (struct conf_inherit){common->font, common->foreground}); } - long min_v = min != NULL ? yml_value_as_int(min) : 0; - long max_v = max != NULL ? yml_value_as_int(max) : 0; - - return ramp_new(common, yml_value_as_string(tag), parts, count, min != NULL, min_v, max != NULL, max_v); + return ramp_new(common, yml_value_as_string(tag), parts, count); } static bool @@ -215,8 +181,6 @@ verify_conf(keychain_t *chain, const struct yml_node *node) static const struct attr_info attrs[] = { {"tag", true, &conf_verify_string}, {"items", true, &conf_verify_particle_list_items}, - {"min", false, &conf_verify_int}, - {"max", false, &conf_verify_int}, PARTICLE_COMMON_ATTRS, }; diff --git a/particles/string.c b/particles/string.c index 4922d7d..5e98132 100644 --- a/particles/string.c +++ b/particles/string.c @@ -1,36 +1,25 @@ -#include #include #include +#include #define LOG_MODULE "string" #define LOG_ENABLE_DBG 0 -#include "../char32.h" -#include "../config-verify.h" -#include "../config.h" #include "../log.h" +#include "../config.h" +#include "../config-verify.h" #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 -{ +struct private { char *text; size_t max_len; - - size_t cache_size; - struct text_run_cache *cache; }; struct eprivate { - ssize_t cache_idx; + /* Set when instantiating */ + char *text; + const struct fcft_glyph **glyphs; - const struct fcft_glyph **allocated_glyphs; long *kern_x; int num_glyphs; }; @@ -40,7 +29,8 @@ exposable_destroy(struct exposable *exposable) { struct eprivate *e = exposable->private; - free(e->allocated_glyphs); + free(e->text); + free(e->glyphs); free(e->kern_x); free(e); exposable_default_destroy(exposable); @@ -50,18 +40,43 @@ 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; - exposable->width = exposable->particle->left_margin + exposable->particle->right_margin; + e->glyphs = NULL; + e->num_glyphs = 0; - 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; + 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 (glyph == NULL) + continue; + + e->glyphs[e->num_glyphs++] = glyph; + + if (i == 0) + continue; + + fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL); + } } + 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; + return exposable->width; } @@ -73,11 +88,6 @@ 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; @@ -87,17 +97,18 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int * its descent. This way, the part of the font *above* the * baseline is centered. * - * "EEEE" will typically be dead center, with the middle of each character being in the bar's center. + * "EEEE" will typically be dead center, with the middle of each character being in the bar's center. * "eee" will be slightly below the center. * "jjj" will be even further below the center. * * Finally, if the font's descent is negative, ignore it (except * for the height calculation). This is unfortunately not based on - * any real facts, but works very well with e.g. the "Awesome 6" + * any real facts, but works very well with e.g. the "Awesome 5" * font family. */ - const double baseline - = (double)y + (double)(height + font->ascent + font->descent) / 2.0 - (font->descent > 0 ? font->descent : 0); + const double baseline = (double)y + + (double)(height + font->ascent + font->descent) / 2.0 - + (font->descent > 0 ? font->descent : 0); x += exposable->particle->left_margin; @@ -110,13 +121,17 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int if (pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) { /* Glyph surface is a pre-rendered image (typically a color emoji...) */ - pixman_image_composite32(PIXMAN_OP_OVER, glyph->pix, NULL, pix, 0, 0, 0, 0, x + glyph->x, - baseline - glyph->y, glyph->width, glyph->height); + pixman_image_composite32( + PIXMAN_OP_OVER, glyph->pix, NULL, pix, 0, 0, 0, 0, + x + glyph->x, baseline - glyph->y, + glyph->width, glyph->height); } else { /* Glyph surface is an alpha mask */ pixman_image_t *src = pixman_image_create_solid_fill(&exposable->particle->foreground); - pixman_image_composite32(PIXMAN_OP_OVER, src, glyph->pix, pix, 0, 0, 0, 0, x + glyph->x, - baseline - glyph->y, glyph->width, glyph->height); + pixman_image_composite32( + PIXMAN_OP_OVER, src, glyph->pix, pix, 0, 0, 0, 0, + x + glyph->x, baseline - glyph->y, + glyph->width, glyph->height); pixman_image_unref(src); } @@ -124,132 +139,48 @@ 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) { - struct private *p = (struct private *)particle->private; + const struct private *p = particle->private; struct eprivate *e = calloc(1, sizeof(*e)); - struct fcft_font *font = particle->font; - char32_t *wtext = NULL; - char *text = tags_expand_template(p->text, tags); - - e->glyphs = e->allocated_glyphs = NULL; + e->text = tags_expand_template(p->text, tags); + e->glyphs = NULL; e->num_glyphs = 0; - e->kern_x = NULL; - e->cache_idx = -1; - uint64_t hash = sdbm_hash(text); + if (p->max_len > 0) { + const size_t len = strlen(e->text); + if (len > p->max_len) { - /* 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 char32_t */ - wtext = ambstoc32(text); - size_t chars = wtext != NULL ? c32len(wtext) : 0; - - /* Truncate, if necessary */ - if (p->max_len > 0 && chars > p->max_len) { - chars = p->max_len; - if (p->max_len > 3) - wtext[p->max_len - 1] = U'…'; - wtext[p->max_len] = U'\0'; - } - - e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - - if (particle->font_shaping == FONT_SHAPE_FULL && fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { - struct fcft_text_run *run = fcft_rasterize_text_run_utf32(font, chars, wtext, FCFT_SUBPIXEL_NONE); - - if (run != NULL) { - int w = 0; - 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; - } + size_t end = p->max_len; + if (end >= 3) { + /* "allocate" room for three dots at the end */ + end -= 3; } - 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])); + /* Mucho importante - don't cut in the middle of a utf8 multibyte */ + while (end > 0 && e->text[end - 1] >> 7) + end--; - 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 (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 (e->glyphs == NULL) { - e->allocated_glyphs = malloc(chars * sizeof(e->glyphs[0])); + char *on_click = tags_expand_template(particle->on_click_template, tags); - /* Convert text to glyph masks/images. */ - for (size_t i = 0; i < chars; i++) { - const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(font, wtext[i], FCFT_SUBPIXEL_NONE); - - 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); + struct exposable *exposable = exposable_common_new(particle, on_click); exposable->private = e; exposable->destroy = &exposable_destroy; exposable->begin_expose = &begin_expose; exposable->expose = &expose; + + free(on_click); return exposable; } @@ -257,9 +188,6 @@ static void particle_destroy(struct particle *particle) { struct private *p = particle->private; - 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); @@ -271,8 +199,6 @@ 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->cache_size = 0; - p->cache = NULL; common->private = p; common->destroy = &particle_destroy; @@ -286,7 +212,10 @@ from_conf(const struct yml_node *node, struct particle *common) const struct yml_node *text = yml_get_value(node, "text"); const struct yml_node *max = yml_get_value(node, "max"); - return string_new(common, yml_value_as_string(text), max != NULL ? yml_value_as_int(max) : 0); + return string_new( + common, + yml_value_as_string(text), + max != NULL ? yml_value_as_int(max) : 0); } static bool @@ -294,7 +223,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"text", true, &conf_verify_string}, - {"max", false, &conf_verify_unsigned}, + {"max", false, &conf_verify_int}, PARTICLE_COMMON_ATTRS, }; diff --git a/plugin.c b/plugin.c index 2ed0a4f..05a2c1a 100644 --- a/plugin.c +++ b/plugin.c @@ -1,104 +1,51 @@ #include "plugin.h" -#include #include +#include #include #define LOG_MODULE "plugin" #define LOG_ENABLE_DBG 0 -#include "config.h" #include "log.h" +#include "config.h" #if !defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -#define EXTERN_MODULE(plug_name) \ - extern const struct module_iface module_##plug_name##_iface; \ - extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \ - extern struct module *plug_name##_from_conf(const struct yml_node *node, struct conf_inherit inherited); +#define EXTERN_MODULE(plug_name) \ + extern const struct module_iface module_##plug_name##_iface; \ + extern bool plug_name##_verify_conf( \ + keychain_t *chain, const struct yml_node *node); \ + extern struct module *plug_name##_from_conf( \ + const struct yml_node *node, struct conf_inherit inherited); -#define EXTERN_PARTICLE(plug_name) \ - extern const struct particle_iface particle_##plug_name##_iface; \ - extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \ - extern struct particle *plug_name##_from_conf(const struct yml_node *node, struct particle *common); +#define EXTERN_PARTICLE(plug_name) \ + extern const struct particle_iface particle_##plug_name##_iface; \ + extern bool plug_name##_verify_conf( \ + keychain_t *chain, const struct yml_node *node); \ + extern struct particle *plug_name##_from_conf( \ + const struct yml_node *node, struct particle *common); -#define EXTERN_DECORATION(plug_name) \ - extern const struct deco_iface deco_##plug_name##_iface; \ - extern bool plug_name##_verify_conf(keychain_t *chain, const struct yml_node *node); \ +#define EXTERN_DECORATION(plug_name) \ + extern const struct deco_iface deco_##plug_name##_iface; \ + extern bool plug_name##_verify_conf( \ + keychain_t *chain, const struct yml_node *node); \ extern struct deco *plug_name##_from_conf(const struct yml_node *node); -#if defined(HAVE_PLUGIN_alsa) EXTERN_MODULE(alsa); -#endif -#if defined(HAVE_PLUGIN_backlight) EXTERN_MODULE(backlight); -#endif -#if defined(HAVE_PLUGIN_battery) EXTERN_MODULE(battery); -#endif -#if defined(HAVE_PLUGIN_clock) EXTERN_MODULE(clock); -#endif -#if defined(HAVE_PLUGIN_cpu) -EXTERN_MODULE(cpu); -#endif -#if defined(HAVE_PLUGIN_disk_io) -EXTERN_MODULE(disk_io); -#endif -#if defined(HAVE_PLUGIN_dwl) -EXTERN_MODULE(dwl); -#endif -#if defined(HAVE_PLUGIN_foreign_toplevel) -EXTERN_MODULE(foreign_toplevel); -#endif -#if defined(HAVE_PLUGIN_mem) -EXTERN_MODULE(mem); -#endif -#if defined(HAVE_PLUGIN_mpd) -EXTERN_MODULE(mpd); -#endif -#if defined(HAVE_PLUGIN_mpris) -EXTERN_MODULE(mpris); -#endif -#if defined(HAVE_PLUGIN_i3) EXTERN_MODULE(i3); -#endif -#if defined(HAVE_PLUGIN_label) EXTERN_MODULE(label); -#endif -#if defined(HAVE_PLUGIN_network) +EXTERN_MODULE(mpd); EXTERN_MODULE(network); -#endif -#if defined(HAVE_PLUGIN_pipewire) -EXTERN_MODULE(pipewire); -#endif -#if defined(HAVE_PLUGIN_pulse) -EXTERN_MODULE(pulse); -#endif -#if defined(HAVE_PLUGIN_removables) EXTERN_MODULE(removables); -#endif -#if defined(HAVE_PLUGIN_river) EXTERN_MODULE(river); -#endif -#if defined(HAVE_PLUGIN_script) -EXTERN_MODULE(script); -#endif -#if defined(HAVE_PLUGIN_sway_xkb) EXTERN_MODULE(sway_xkb); -#endif -#if defined(HAVE_PLUGIN_niri_language) -EXTERN_MODULE(niri_language); -#endif -#if defined(HAVE_PLUGIN_niri_workspaces) -EXTERN_MODULE(niri_workspaces); -#endif -#if defined(HAVE_PLUGIN_xkb) +EXTERN_MODULE(script); EXTERN_MODULE(xkb); -#endif -#if defined(HAVE_PLUGIN_xwindow) EXTERN_MODULE(xwindow); -#endif EXTERN_PARTICLE(empty); EXTERN_PARTICLE(list); @@ -108,10 +55,8 @@ EXTERN_PARTICLE(ramp); EXTERN_PARTICLE(string); EXTERN_DECORATION(background); -EXTERN_DECORATION(border); EXTERN_DECORATION(stack); EXTERN_DECORATION(underline); -EXTERN_DECORATION(overline); #undef EXTERN_DECORATION #undef EXTERN_PARTICLE @@ -125,113 +70,57 @@ static const char * type2str(enum plugin_type type) { switch (type) { - case PLUGIN_MODULE: - return "module"; - case PLUGIN_PARTICLE: - return "particle"; - case PLUGIN_DECORATION: - return "decoration"; + case PLUGIN_MODULE: return "module"; + case PLUGIN_PARTICLE: return "particle"; + case PLUGIN_DECORATION: return "decoration"; } - assert(false && "invalid type"); - return ""; + return NULL; } -static void __attribute__((constructor)) init(void) +static void __attribute__((constructor)) +init(void) { #if !defined(CORE_PLUGINS_AS_SHARED_LIBRARIES) -#define REGISTER_CORE_PLUGIN(plug_name, func_prefix, plug_type) \ - do { \ - tll_push_back(plugins, ((struct plugin){ \ - .name = strdup(#plug_name), \ - .type = (plug_type), \ - .lib = RTLD_DEFAULT, \ - })); \ +#define REGISTER_CORE_PLUGIN(plug_name, func_prefix, plug_type) \ + do { \ + tll_push_back( \ + plugins, \ + ((struct plugin){ \ + .name = strdup(#plug_name), \ + .type = (plug_type), \ + .lib = RTLD_DEFAULT, \ + })); \ } while (0) -#define REGISTER_CORE_MODULE(plug_name, func_prefix) \ - do { \ - REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_MODULE); \ - tll_back(plugins).module = &module_##func_prefix##_iface; \ +#define REGISTER_CORE_MODULE(plug_name, func_prefix) do { \ + REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_MODULE); \ + tll_back(plugins).module = &module_##func_prefix##_iface; \ } while (0) -#define REGISTER_CORE_PARTICLE(plug_name, func_prefix) \ - do { \ - REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_PARTICLE); \ - tll_back(plugins).particle = &particle_##func_prefix##_iface; \ +#define REGISTER_CORE_PARTICLE(plug_name, func_prefix) do { \ + REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_PARTICLE); \ + tll_back(plugins).particle = &particle_##func_prefix##_iface; \ } while (0) -#define REGISTER_CORE_DECORATION(plug_name, func_prefix) \ - do { \ - REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_DECORATION); \ - tll_back(plugins).decoration = &deco_##func_prefix##_iface; \ +#define REGISTER_CORE_DECORATION(plug_name, func_prefix) do { \ + REGISTER_CORE_PLUGIN(plug_name, func_prefix, PLUGIN_DECORATION); \ + tll_back(plugins).decoration = &deco_##func_prefix##_iface; \ } while (0) -#if defined(HAVE_PLUGIN_alsa) REGISTER_CORE_MODULE(alsa, alsa); -#endif -#if defined(HAVE_PLUGIN_backlight) REGISTER_CORE_MODULE(backlight, backlight); -#endif -#if defined(HAVE_PLUGIN_battery) REGISTER_CORE_MODULE(battery, battery); -#endif -#if defined(HAVE_PLUGIN_clock) REGISTER_CORE_MODULE(clock, clock); -#endif -#if defined(HAVE_PLUGIN_cpu) - REGISTER_CORE_MODULE(cpu, cpu); -#endif -#if defined(HAVE_PLUGIN_disk_io) - REGISTER_CORE_MODULE(disk-io, disk_io); -#endif -#if defined(HAVE_PLUGIN_dwl) - REGISTER_CORE_MODULE(dwl, dwl); -#endif -#if defined(HAVE_PLUGIN_foreign_toplevel) - REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel); -#endif -#if defined(HAVE_PLUGIN_mem) - REGISTER_CORE_MODULE(mem, mem); -#endif -#if defined(HAVE_PLUGIN_mpd) - REGISTER_CORE_MODULE(mpd, mpd); -#endif -#if defined(HAVE_PLUGIN_mpris) - REGISTER_CORE_MODULE(mpris, mpris); -#endif -#if defined(HAVE_PLUGIN_i3) REGISTER_CORE_MODULE(i3, i3); -#endif -#if defined(HAVE_PLUGIN_label) REGISTER_CORE_MODULE(label, label); -#endif -#if defined(HAVE_PLUGIN_network) + REGISTER_CORE_MODULE(mpd, mpd); REGISTER_CORE_MODULE(network, network); -#endif -#if defined(HAVE_PLUGIN_pipewire) - REGISTER_CORE_MODULE(pipewire, pipewire); -#endif -#if defined(HAVE_PLUGIN_pulse) - REGISTER_CORE_MODULE(pulse, pulse); -#endif -#if defined(HAVE_PLUGIN_removables) REGISTER_CORE_MODULE(removables, removables); -#endif #if defined(HAVE_PLUGIN_river) REGISTER_CORE_MODULE(river, river); #endif -#if defined(HAVE_PLUGIN_script) - REGISTER_CORE_MODULE(script, script); -#endif -#if defined(HAVE_PLUGIN_sway_xkb) REGISTER_CORE_MODULE(sway-xkb, sway_xkb); -#endif -#if defined(HAVE_PLUGIN_niri_language) - REGISTER_CORE_MODULE(niri-language, niri_language); -#endif -#if defined(HAVE_PLUGIN_niri_workspaces) - REGISTER_CORE_MODULE(niri-workspaces, niri_workspaces); -#endif + REGISTER_CORE_MODULE(script, script); #if defined(HAVE_PLUGIN_xkb) REGISTER_CORE_MODULE(xkb, xkb); #endif @@ -247,10 +136,8 @@ static void __attribute__((constructor)) init(void) REGISTER_CORE_PARTICLE(string, string); REGISTER_CORE_DECORATION(background, background); - REGISTER_CORE_DECORATION(border, border); REGISTER_CORE_DECORATION(stack, stack); REGISTER_CORE_DECORATION(underline, underline); - REGISTER_CORE_DECORATION(overline, overline); #undef REGISTER_CORE_DECORATION #undef REGISTER_CORE_PARTICLE @@ -274,13 +161,16 @@ free_plugin(struct plugin plug) free(plug.name); } -static void __attribute__((destructor)) fini(void) { tll_free_and_free(plugins, free_plugin); } +static void __attribute__((destructor)) +fini(void) +{ + tll_free_and_free(plugins, free_plugin); +} const struct plugin * plugin_load(const char *name, enum plugin_type type) { - tll_foreach(plugins, plug) - { + tll_foreach(plugins, plug) { if (plug->item.type == type && strcmp(plug->item.name, name) == 0) { LOG_DBG("%s: %s already loaded: %p", type2str(type), name, plug->item.lib); assert(plug->item.dummy != NULL); @@ -288,6 +178,7 @@ plugin_load(const char *name, enum plugin_type type) } } + char path[128]; snprintf(path, sizeof(path), "%s_%s.so", type2str(type), name); diff --git a/plugin.h b/plugin.h index 4c49caa..1b2da24 100644 --- a/plugin.h +++ b/plugin.h @@ -1,7 +1,7 @@ #pragma once -#include "config-verify.h" #include "config.h" +#include "config-verify.h" #include "module.h" #include "particle.h" @@ -9,12 +9,14 @@ typedef bool (*verify_func_t)(keychain_t *chain, const struct yml_node *node); struct module_iface { verify_func_t verify_conf; - struct module *(*from_conf)(const struct yml_node *node, struct conf_inherit inherited); + struct module *(*from_conf)( + const struct yml_node *node, struct conf_inherit inherited); }; struct particle_iface { verify_func_t verify_conf; - struct particle *(*from_conf)(const struct yml_node *node, struct particle *common); + struct particle *(*from_conf)( + const struct yml_node *node, struct particle *common); }; struct deco_iface { diff --git a/subprojects/fcft.wrap b/subprojects/fcft.wrap deleted file mode 100644 index d2709d4..0000000 --- a/subprojects/fcft.wrap +++ /dev/null @@ -1,3 +0,0 @@ -[wrap-git] -url = https://codeberg.org/dnkl/fcft.git -revision = master diff --git a/subprojects/tllist.wrap b/subprojects/tllist.wrap deleted file mode 100644 index 75f395a..0000000 --- a/subprojects/tllist.wrap +++ /dev/null @@ -1,3 +0,0 @@ -[wrap-git] -url = https://codeberg.org/dnkl/tllist.git -revision = master diff --git a/tag.c b/tag.c index d6609af..02e0794 100644 --- a/tag.c +++ b/tag.c @@ -1,20 +1,16 @@ #include "tag.h" -#include -#include -#include -#include -#include -#include #include +#include #include +#include +#include #define LOG_MODULE "tag" #define LOG_ENABLE_DBG 1 #include "log.h" #include "module.h" -struct private -{ +struct private { char *name; union { struct { @@ -36,30 +32,6 @@ tag_name(const struct tag *tag) return priv->name; } -static enum tag_type -bool_type(const struct tag *tag) -{ - return TAG_TYPE_BOOL; -} - -static enum tag_type -int_type(const struct tag *tag) -{ - return TAG_TYPE_INT; -} - -static enum tag_type -float_type(const struct tag *tag) -{ - return TAG_TYPE_FLOAT; -} - -static enum tag_type -string_type(const struct tag *tag) -{ - return TAG_TYPE_STRING; -} - static long unimpl_min_max(const struct tag *tag) { @@ -157,8 +129,8 @@ int_refresh_in(const struct tag *tag, long units) if (tag->owner == NULL || tag->owner->refresh_in == NULL) return false; - assert(priv->value_as_int.realtime_unit == TAG_REALTIME_SECS - || priv->value_as_int.realtime_unit == TAG_REALTIME_MSECS); + assert(priv->value_as_int.realtime_unit == TAG_REALTIME_SECS || + priv->value_as_int.realtime_unit == TAG_REALTIME_MSECS); long milli_seconds = units; if (priv->value_as_int.realtime_unit == TAG_REALTIME_SECS) @@ -270,14 +242,15 @@ tag_new_int(struct module *owner, const char *name, long value) } struct tag * -tag_new_int_range(struct module *owner, const char *name, long value, long min, long max) +tag_new_int_range(struct module *owner, const char *name, long value, + long min, long max) { return tag_new_int_realtime(owner, name, value, min, max, TAG_REALTIME_NONE); } struct tag * -tag_new_int_realtime(struct module *owner, const char *name, long value, long min, long max, - enum tag_realtime_unit unit) +tag_new_int_realtime(struct module *owner, const char *name, long value, + long min, long max, enum tag_realtime_unit unit) { struct private *priv = malloc(sizeof(*priv)); priv->name = strdup(name); @@ -291,7 +264,6 @@ tag_new_int_realtime(struct module *owner, const char *name, long value, long mi tag->owner = owner; tag->destroy = &destroy_int_and_float; tag->name = &tag_name; - tag->type = &int_type; tag->min = &int_min; tag->max = &int_max; tag->realtime = &int_realtime; @@ -315,7 +287,6 @@ tag_new_bool(struct module *owner, const char *name, bool value) tag->owner = owner; tag->destroy = &destroy_int_and_float; tag->name = &tag_name; - tag->type = &bool_type; tag->min = &unimpl_min_max; tag->max = &unimpl_min_max; tag->realtime = &no_realtime; @@ -339,7 +310,6 @@ tag_new_float(struct module *owner, const char *name, double value) tag->owner = owner; tag->destroy = &destroy_int_and_float; tag->name = &tag_name; - tag->type = &float_type; tag->min = &unimpl_min_max; tag->max = &unimpl_min_max; tag->realtime = &no_realtime; @@ -363,7 +333,6 @@ tag_new_string(struct module *owner, const char *name, const char *value) tag->owner = owner; tag->destroy = &destroy_string; tag->name = &tag_name; - tag->type = &string_type; tag->min = &unimpl_min_max; tag->max = &unimpl_min_max; tag->realtime = &no_realtime; @@ -414,7 +383,7 @@ sbuf_append_at_most(struct sbuf *s1, const char *s2, size_t n) s1->size = 2 * required_size; s1->s = realloc(s1->s, s1->size); - // s1->s[s1->len] = '\0'; + //s1->s[s1->len] = '\0'; } memcpy(&s1->s[s1->len], s2, n); @@ -428,21 +397,6 @@ sbuf_append(struct sbuf *s1, const char *s2) sbuf_append_at_most(s1, s2, strlen(s2)); } -// stores the number in "*value" on success -static bool -is_number(const char *str, long *value) -{ - errno = 0; - - char *end; - long v = strtol(str, &end, 10); - if (errno != 0 || *end != '\0') - return false; - - *value = v; - return true; -} - char * tags_expand_template(const char *template, const struct tag_set *tags) { @@ -474,7 +428,7 @@ tags_expand_template(const char *template, const struct tag_set *tags) strncpy(tag_name_and_arg, begin + 1, end - begin - 1); tag_name_and_arg[end - begin - 1] = '\0'; - static const size_t MAX_TAG_ARGS = 4; + static const size_t MAX_TAG_ARGS = 3; const char *tag_name = NULL; const char *tag_args[MAX_TAG_ARGS]; memset(tag_args, 0, sizeof(tag_args)); @@ -492,9 +446,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) } /* Lookup tag */ - const struct tag *tag = NULL; - - if (tag_name == NULL || (tag = tag_for_name(tags, tag_name)) == NULL) { + const struct tag *tag = tag_for_name(tags, tag_name); + if (tag == NULL) { /* No such tag, copy as-is instead */ sbuf_append_at_most(&formatted, template, begin - template + 1); template = begin + 1; @@ -505,28 +458,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) sbuf_append_at_most(&formatted, template, begin - template); /* Parse arguments */ - enum { - FMT_DEFAULT, - FMT_HEX, - FMT_OCT, - FMT_PERCENT, - FMT_DIVIDE, - } format - = FMT_DEFAULT; - - enum { - VALUE_VALUE, - VALUE_MIN, - VALUE_MAX, - VALUE_UNIT, - } kind - = VALUE_VALUE; - - long digits = 0; - long decimals = 2; - long divider = 1; - bool zero_pad = false; - char *point = NULL; + enum { FMT_DEFAULT, FMT_HEX, FMT_OCT } format = FMT_DEFAULT; + enum { VALUE_VALUE, VALUE_MIN, VALUE_MAX, VALUE_UNIT } kind = VALUE_VALUE; for (size_t i = 0; i < MAX_TAG_ARGS; i++) { if (tag_args[i] == NULL) @@ -535,130 +468,27 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_HEX; else if (strcmp(tag_args[i], "oct") == 0) format = FMT_OCT; - else if (strcmp(tag_args[i], "%") == 0) - format = FMT_PERCENT; - else if (*tag_args[i] == '/') { - format = FMT_DIVIDE; - const char *divider_str = tag_args[i] + 1; - if (!is_number(divider_str, ÷r) || divider == 0) { - divider = 1; - LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str); - } - } - else if (strcmp(tag_args[i], "kb") == 0) { - format = FMT_DIVIDE; - divider = 1000; - } - else if (strcmp(tag_args[i], "mb") == 0) { - format = FMT_DIVIDE; - divider = 1000 * 1000; - } - else if (strcmp(tag_args[i], "gb") == 0) { - format = FMT_DIVIDE; - divider = 1000 * 1000 * 1000; - } - else if (strcmp(tag_args[i], "kib") == 0) { - format = FMT_DIVIDE; - divider = 1024; - } - else if (strcmp(tag_args[i], "mib") == 0) { - format = FMT_DIVIDE; - divider = 1024 * 1024; - } - else if (strcmp(tag_args[i], "gib") == 0) { - format = FMT_DIVIDE; - divider = 1024 * 1024 * 1024; - } else if (strcmp(tag_args[i], "min") == 0) kind = VALUE_MIN; else if (strcmp(tag_args[i], "max") == 0) kind = VALUE_MAX; else if (strcmp(tag_args[i], "unit") == 0) kind = VALUE_UNIT; - else if (is_number(tag_args[i], &digits)) // i.e.: "{tag:3}" - zero_pad = tag_args[i][0] == '0'; - else if ((point = strchr(tag_args[i], '.')) != NULL) { - *point = '\0'; - - const char *digits_str = tag_args[i]; - const char *decimals_str = point + 1; - - if (digits_str[0] != '\0') { // guards against i.e. "{tag:.3}" - if (!is_number(digits_str, &digits)) { - LOG_WARN("tag `%s`: invalid field width formatter. Ignoring...", tag_name); - } - } - - if (decimals_str[0] != '\0') { // guards against i.e. "{tag:3.}" - if (!is_number(decimals_str, &decimals)) { - LOG_WARN("tag `%s`: invalid decimals formatter. Ignoring...", tag_name); - } - } - zero_pad = digits_str[0] == '0'; - } else - LOG_WARN("invalid tag formatter: %s", tag_args[i]); } /* Copy tag value */ switch (kind) { case VALUE_VALUE: switch (format) { - case FMT_DEFAULT: { - switch (tag->type(tag)) { - case TAG_TYPE_FLOAT: { - const char *fmt = zero_pad ? "%0*.*f" : "%*.*f"; - char str[24]; - snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag)); - sbuf_append(&formatted, str); - break; - } - - case TAG_TYPE_INT: { - const char *fmt = zero_pad ? "%0*ld" : "%*ld"; - char str[24]; - snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag)); - sbuf_append(&formatted, str); - break; - } - - default: - sbuf_append(&formatted, tag->as_string(tag)); - break; - } - + case FMT_DEFAULT: + sbuf_append(&formatted, tag->as_string(tag)); break; - } case FMT_HEX: case FMT_OCT: { - const char *fmt = format == FMT_HEX ? zero_pad ? "%0*lx" : "%*lx" : zero_pad ? "%0*lo" : "%*lo"; char str[24]; - snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag)); - sbuf_append(&formatted, str); - break; - } - - case FMT_PERCENT: { - const long min = tag->min(tag); - const long max = tag->max(tag); - const long cur = tag->as_int(tag); - - const char *fmt = zero_pad ? "%0*lu" : "%*lu"; - char str[4]; - snprintf(str, sizeof(str), fmt, digits, (cur - min) * 100 / (max - min)); - sbuf_append(&formatted, str); - break; - } - - case FMT_DIVIDE: { - char str[24]; - if (tag->type(tag) == TAG_TYPE_FLOAT) { - const char *fmt = zero_pad ? "%0*.*f" : "%*.*f"; - snprintf(str, sizeof(str), fmt, digits, decimals, tag->as_float(tag) / (double)divider); - } else { - const char *fmt = zero_pad ? "%0*lu" : "%*lu"; - snprintf(str, sizeof(str), fmt, digits, tag->as_int(tag) / divider); - } + snprintf(str, sizeof(str), format == FMT_HEX ? "%lx" : "%lo", + tag->as_int(tag)); sbuf_append(&formatted, str); break; } @@ -667,37 +497,17 @@ tags_expand_template(const char *template, const struct tag_set *tags) case VALUE_MIN: case VALUE_MAX: { - const long min = tag->min(tag); - const long max = tag->max(tag); - long value = kind == VALUE_MIN ? min : max; + const long value = kind == VALUE_MIN ? tag->min(tag) : tag->max(tag); - const char *fmt = NULL; + const char *fmt; switch (format) { - case FMT_DEFAULT: - fmt = zero_pad ? "%0*ld" : "%*ld"; - break; - case FMT_HEX: - fmt = zero_pad ? "%0*lx" : "%*lx"; - break; - case FMT_OCT: - fmt = zero_pad ? "%0*lo" : "%*lo"; - break; - case FMT_PERCENT: - value = (value - min) * 100 / (max - min); - fmt = zero_pad ? "%0*lu" : "%*lu"; - break; - - case FMT_DIVIDE: { - value /= divider; - fmt = zero_pad ? "%0*lu" : "%*lu"; - break; + case FMT_DEFAULT: fmt = "%ld"; break; + case FMT_HEX: fmt = "%lx"; break; + case FMT_OCT: fmt = "%lo"; break; } - } - - assert(fmt != NULL); char str[24]; - snprintf(str, sizeof(str), fmt, digits, value); + snprintf(str, sizeof(str), fmt, value); sbuf_append(&formatted, str); break; } @@ -706,15 +516,9 @@ tags_expand_template(const char *template, const struct tag_set *tags) const char *value = NULL; switch (tag->realtime(tag)) { - case TAG_REALTIME_NONE: - value = ""; - break; - case TAG_REALTIME_SECS: - value = "s"; - break; - case TAG_REALTIME_MSECS: - value = "ms"; - break; + case TAG_REALTIME_NONE: value = ""; break; + case TAG_REALTIME_SECS: value = "s"; break; + case TAG_REALTIME_MSECS: value = "ms"; break; } sbuf_append(&formatted, value); @@ -728,10 +532,3 @@ 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 6149b1e..2c629c5 100644 --- a/tag.h +++ b/tag.h @@ -1,14 +1,7 @@ #pragma once -#include #include - -enum tag_type { - TAG_TYPE_BOOL, - TAG_TYPE_INT, - TAG_TYPE_FLOAT, - TAG_TYPE_STRING, -}; +#include enum tag_realtime_unit { TAG_REALTIME_NONE, @@ -24,7 +17,6 @@ struct tag { void (*destroy)(struct tag *tag); const char *(*name)(const struct tag *tag); - enum tag_type (*type)(const struct tag *tag); const char *(*as_string)(const struct tag *tag); long (*as_int)(const struct tag *tag); bool (*as_bool)(const struct tag *tag); @@ -43,16 +35,18 @@ struct tag_set { }; struct tag *tag_new_int(struct module *owner, const char *name, long value); -struct tag *tag_new_int_range(struct module *owner, const char *name, long value, long min, long max); -struct tag *tag_new_int_realtime(struct module *owner, const char *name, long value, long min, long max, - enum tag_realtime_unit unit); +struct tag *tag_new_int_range( + struct module *owner, const char *name, long value, long min, long max); +struct tag *tag_new_int_realtime( + struct module *owner, const char *name, long value, long min, + long max, enum tag_realtime_unit unit); struct tag *tag_new_bool(struct module *owner, const char *name, bool value); struct tag *tag_new_float(struct module *owner, const char *name, double value); -struct tag *tag_new_string(struct module *owner, const char *name, const char *value); +struct tag *tag_new_string( + struct module *owner, const char *name, const char *value); const struct tag *tag_for_name(const struct tag_set *set, const char *name); void tag_set_destroy(struct tag_set *set); /* Utility functions */ char *tags_expand_template(const char *template, const struct tag_set *tags); -void tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, const struct tag_set *tags); diff --git a/test/full-conf-good.yml b/test/full-conf-good.yml index a6f6e99..6270487 100644 --- a/test/full-conf-good.yml +++ b/test/full-conf-good.yml @@ -39,13 +39,8 @@ bar: host: 127.0.0.1 content: {string: {text: "{state}"}} - network: - content: - map: - default: - string: {text: "{name}: {state} ({ipv4})"} - conditions: - ipv4 == "": - string: {text: "{name}: {state}"} + name: ldsjfdf + content: {string: {text: "{name}"}} - removables: content: {string: {text: "{label}"}} # - xkb: @@ -67,9 +62,10 @@ bar: - clock: content: map: + tag: date default: {string: {text: default value}} - conditions: - date == 1234: {string: {text: specific value}} + values: + 1234: {string: {text: specific value}} - clock: content: progress-bar: diff --git a/test/meson.build b/test/meson.build index a3635a2..e8c7138 100644 --- a/test/meson.build +++ b/test/meson.build @@ -7,9 +7,4 @@ test('no-config', yambar, args: ['-C', '-c', 'xyz'], should_fail: true) test('config-isnt-file', yambar, args: ['-C', '-c', '.'], should_fail: true) test('config-no-bar', yambar, args: ['-C', '-c', join_paths(pwd, 'no-bar.yml')], should_fail: true) -if plugin_alsa_enabled and plugin_backlight_enabled and \ - plugin_battery_enabled and plugin_clock_enabled and \ - plugin_i3_enabled and plugin_mpd_enabled and plugin_network_enabled \ - and plugin_removables_enabled - test('full-conf-good', yambar, args: ['-C', '-c', join_paths(pwd, 'full-conf-good.yml')]) -endif +test('full-conf-good', yambar, args: ['-C', '-c', join_paths(pwd, 'full-conf-good.yml')]) diff --git a/xcb.c b/xcb.c index d3d2e30..a157c1a 100644 --- a/xcb.c +++ b/xcb.c @@ -1,16 +1,16 @@ #include "xcb.h" -#include #include #include #include +#include +#include #include #include -#include #if defined(HAVE_XCB_ERRORS) -#include + #include #endif #define LOG_MODULE "xcb" @@ -36,7 +36,8 @@ xcb_atom_t _NET_WM_NAME; static xcb_errors_context_t *err_context; #endif -static void __attribute__((destructor)) fini(void) +static void __attribute__((destructor)) +fini(void) { #if defined(HAVE_XCB_ERRORS) xcb_errors_context_free(err_context); @@ -62,17 +63,19 @@ xcb_init(void) /* Vendor release number */ unsigned release = setup->release_number; - unsigned major = release / 10000000; - release %= 10000000; - unsigned minor = release / 100000; - release %= 100000; + unsigned major = release / 10000000; release %= 10000000; + unsigned minor = release / 100000; release %= 100000; unsigned patch = release / 1000; #endif - LOG_DBG("%.*s %u.%u.%u (protocol: %u.%u)", xcb_setup_vendor_length(setup), xcb_setup_vendor(setup), major, minor, - patch, setup->protocol_major_version, setup->protocol_minor_version); + LOG_DBG("%.*s %u.%u.%u (protocol: %u.%u)", + xcb_setup_vendor_length(setup), xcb_setup_vendor(setup), + major, minor, patch, + setup->protocol_major_version, + setup->protocol_minor_version); - const xcb_query_extension_reply_t *randr = xcb_get_extension_data(conn, &xcb_randr_id); + const xcb_query_extension_reply_t *randr = + xcb_get_extension_data(conn, &xcb_randr_id); if (randr == NULL || !randr->present) { LOG_ERR("RANDR extension not present"); @@ -80,7 +83,8 @@ xcb_init(void) return false; } - const xcb_query_extension_reply_t *render = xcb_get_extension_data(conn, &xcb_render_id); + const xcb_query_extension_reply_t *render = + xcb_get_extension_data(conn, &xcb_render_id); if (render == NULL || !render->present) { LOG_ERR("RENDER extension not present"); @@ -88,15 +92,18 @@ xcb_init(void) return false; } - xcb_randr_query_version_cookie_t randr_cookie - = xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); - xcb_render_query_version_cookie_t render_cookie - = xcb_render_query_version(conn, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION); + xcb_randr_query_version_cookie_t randr_cookie = + xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, + XCB_RANDR_MINOR_VERSION); + xcb_render_query_version_cookie_t render_cookie = + xcb_render_query_version(conn, XCB_RENDER_MAJOR_VERSION, + XCB_RENDER_MINOR_VERSION); xcb_flush(conn); xcb_generic_error_t *e; - xcb_randr_query_version_reply_t *randr_version = xcb_randr_query_version_reply(conn, randr_cookie, &e); + xcb_randr_query_version_reply_t *randr_version = + xcb_randr_query_version_reply(conn, randr_cookie, &e); if (e != NULL) { LOG_ERR("failed to query RANDR version: %s", xcb_error(e)); free(e); @@ -104,7 +111,8 @@ xcb_init(void) return false; } - xcb_render_query_version_reply_t *render_version = xcb_render_query_version_reply(conn, render_cookie, &e); + xcb_render_query_version_reply_t *render_version = + xcb_render_query_version_reply(conn, render_cookie, &e); if (e != NULL) { LOG_ERR("failed to query RENDER version: %s", xcb_error(e)); free(e); @@ -112,8 +120,10 @@ xcb_init(void) return false; } - LOG_DBG("RANDR: %u.%u", randr_version->major_version, randr_version->minor_version); - LOG_DBG("RENDER: %u.%u", render_version->major_version, render_version->minor_version); + LOG_DBG("RANDR: %u.%u", + randr_version->major_version, randr_version->minor_version); + LOG_DBG("RENDER: %u.%u", + render_version->major_version, render_version->minor_version); free(randr_version); free(render_version); @@ -121,7 +131,7 @@ xcb_init(void) /* Cache atoms */ UTF8_STRING = get_atom(conn, "UTF8_STRING"); _NET_WM_PID = get_atom(conn, "_NET_WM_PID"); - _NET_WM_WINDOW_TYPE = get_atom(conn, "_NET_WM_WINDOW_TYPE"); + _NET_WM_WINDOW_TYPE = get_atom(conn, "_NET_WM_WINDOW_TYPE"); _NET_WM_WINDOW_TYPE_DOCK = get_atom(conn, "_NET_WM_WINDOW_TYPE_DOCK"); _NET_WM_STATE = get_atom(conn, "_NET_WM_STATE"); _NET_WM_STATE_ABOVE = get_atom(conn, "_NET_WM_STATE_ABOVE"); @@ -143,7 +153,10 @@ xcb_atom_t get_atom(xcb_connection_t *conn, const char *name) { xcb_generic_error_t *e; - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, xcb_intern_atom(conn, 0, strlen(name), name), &e); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply( + conn, + xcb_intern_atom(conn, 0, strlen(name), name), + &e); if (e != NULL) { LOG_ERR("%s: failed to get atom for %s", name, xcb_error(e)); @@ -169,7 +182,8 @@ char * get_atom_name(xcb_connection_t *conn, xcb_atom_t atom) { xcb_generic_error_t *e; - xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), &e); + xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply( + conn, xcb_get_atom_name(conn, atom), &e); if (e != NULL) { LOG_ERR("failed to get atom name: %s", xcb_error(e)); @@ -178,7 +192,8 @@ get_atom_name(xcb_connection_t *conn, xcb_atom_t atom) return NULL; } - char *name = strndup(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); + char *name = strndup( + xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); LOG_DBG("atom name: %s", name); @@ -192,17 +207,22 @@ xcb_error(const xcb_generic_error_t *error) static char msg[1024]; #if defined(HAVE_XCB_ERRORS) - const char *major = xcb_errors_get_name_for_major_code(err_context, error->major_code); - const char *minor = xcb_errors_get_name_for_minor_code(err_context, error->major_code, error->minor_code); + const char *major = xcb_errors_get_name_for_major_code( + err_context, error->major_code); + const char *minor = xcb_errors_get_name_for_minor_code( + err_context, error->major_code, error->minor_code); const char *extension; - const char *name = xcb_errors_get_name_for_error(err_context, error->error_code, &extension); + const char *name = xcb_errors_get_name_for_error( + err_context, error->error_code, &extension); - snprintf(msg, sizeof(msg), "major=%s, minor=%s), code=%s, extension=%s, sequence=%u", major, minor, name, extension, - error->sequence); + snprintf(msg, sizeof(msg), + "major=%s, minor=%s), code=%s, extension=%s, sequence=%u", + major, minor, name, extension, error->sequence); #else - snprintf(msg, sizeof(msg), "op %hhu:%hu, code %hhu, sequence %hu", error->major_code, error->minor_code, - error->error_code, error->sequence); + snprintf(msg, sizeof(msg), "op %hhu:%hu, code %hhu, sequence %hu", + error->major_code, error->minor_code, error->error_code, + error->sequence); #endif return msg; diff --git a/yml.c b/yml.c index 4769d38..bdf474a 100644 --- a/yml.c +++ b/yml.c @@ -1,15 +1,13 @@ #include "yml.h" -#include -#include -#include -#include #include +#include +#include +#include +#include -#include #include - -#define UNUSED __attribute__((unused)) +#include enum yml_error { YML_ERR_NONE, @@ -78,8 +76,7 @@ clone_node(struct yml_node *parent, const struct yml_node *node) break; case DICT: - tll_foreach(node->dict.pairs, it) - { + tll_foreach(node->dict.pairs, it) { struct dict_pair p = { .key = clone_node(clone, it->item.key), .value = clone_node(clone, it->item.value), @@ -89,7 +86,8 @@ clone_node(struct yml_node *parent, const struct yml_node *node) break; case LIST: - tll_foreach(node->list.values, it) tll_push_back(clone->list.values, clone_node(clone, it->item)); + tll_foreach(node->list.values, it) + tll_push_back(clone->list.values, clone_node(clone, it->item)); break; case ROOT: @@ -119,8 +117,7 @@ dict_has_key(const struct yml_node *node, const struct yml_node *key) { assert(node->type == DICT); - tll_foreach(node->dict.pairs, pair) - { + tll_foreach(node->dict.pairs, pair) { if (node_equal(pair->item.key, key)) return true; } @@ -131,7 +128,7 @@ dict_has_key(const struct yml_node *node, const struct yml_node *key) static enum yml_error add_node(struct yml_node *parent, struct yml_node *new_node, yaml_mark_t loc) { - new_node->line = loc.line + 1; /* yaml uses 0-based line numbers */ + new_node->line = loc.line + 1; /* yaml uses 0-based line numbers */ new_node->column = loc.column; switch (parent->type) { @@ -169,7 +166,8 @@ add_node(struct yml_node *parent, struct yml_node *new_node, yaml_mark_t loc) } static void -add_anchor(struct yml_node *root, const char *anchor, const struct yml_node *node) +add_anchor(struct yml_node *root, const char *anchor, + const struct yml_node *node) { assert(root->type == ROOT); @@ -179,34 +177,31 @@ add_anchor(struct yml_node *root, const char *anchor, const struct yml_node *nod root->root.anchor_count++; } -static bool -post_process(struct yml_node *node, char **error) +static void +post_process(struct yml_node *node) { switch (node->type) { case ROOT: if (node->root.root != NULL) - if (!post_process(node->root.root, error)) - return false; + post_process(node->root.root); break; case SCALAR: - // assert(strcmp(node->scalar.value, "<<") != 0); + //assert(strcmp(node->scalar.value, "<<") != 0); break; case LIST: - tll_foreach(node->list.values, it) if (!post_process(it->item, error)) return false; + tll_foreach(node->list.values, it) + post_process(it->item); break; case DICT: - tll_foreach(node->dict.pairs, it) - { - if (!post_process(it->item.key, error) || !post_process(it->item.value, error)) { - return false; - } + tll_foreach(node->dict.pairs, it) { + post_process(it->item.key); + post_process(it->item.value); } - tll_foreach(node->dict.pairs, it) - { + tll_foreach(node->dict.pairs, it) { if (it->item.key->type != SCALAR) continue; @@ -218,32 +213,19 @@ post_process(struct yml_node *node, char **error) * Merge value is a list (of dictionaries) * e.g. <<: [*foo, *bar] */ - tll_foreach(it->item.value->list.values, v_it) - { - 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) - { + tll_foreach(it->item.value->list.values, v_it) { + assert(v_it->item->type == DICT); + tll_foreach(v_it->item->dict.pairs, vv_it) { struct dict_pair p = { .key = vv_it->item.key, .value = vv_it->item.value, }; + /* TODO: handle this. Is it an error? Or + * should we replace the existing key/value + * pair */ + assert(!dict_has_key(node, vv_it->item.key)); - if (dict_has_key(node, vv_it->item.key)) { - /* Prefer value in target dictionary, over the - * value from the anchor */ - yml_destroy(vv_it->item.key); - yml_destroy(vv_it->item.value); - } else { - tll_push_back(node->dict.pairs, p); - } + tll_push_back(node->dict.pairs, p); } /* Destroy list, but don't free (since its nodes @@ -258,30 +240,18 @@ post_process(struct yml_node *node, char **error) * Merge value is a dictionary only * e.g. <<: *foo */ - if (it->item.value->type != DICT) { - int cnt = snprintf(NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", it->item.value->line, - it->item.value->column); - *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) - { + assert(it->item.value->type == DICT); + tll_foreach(it->item.value->dict.pairs, v_it) { struct dict_pair p = { .key = v_it->item.key, .value = v_it->item.value, }; - if (dict_has_key(node, v_it->item.key)) { - /* Prefer value in target dictionary, over the - * value from the anchor */ - yml_destroy(v_it->item.key); - yml_destroy(v_it->item.value); - } else { - tll_push_back(node->dict.pairs, p); - } + /* TODO: handle this. Is it an error? Or should we + * replace the existing key/value pair */ + assert(!dict_has_key(node, v_it->item.key)); + + tll_push_back(node->dict.pairs, p); } /* Destroy list here, *without* freeing nodes (since @@ -299,12 +269,13 @@ post_process(struct yml_node *node, char **error) } break; } - - return true; } static const char * -format_error(enum yml_error err, const struct yml_node *parent, const struct yml_node *node, const char *anchor) +format_error(enum yml_error err, + const struct yml_node *parent, + const struct yml_node *node, + const char *anchor) { static char err_str[512]; @@ -315,9 +286,11 @@ format_error(enum yml_error err, const struct yml_node *parent, const struct yml case YML_ERR_DUPLICATE_KEY: { /* Find parent's key (i.e its name) */ - if (parent->parent != NULL && parent->parent->type == DICT && node->type == SCALAR) { - tll_foreach(parent->parent->dict.pairs, pair) - { + if (parent->parent != NULL && + parent->parent->type == DICT && + node->type == SCALAR) + { + tll_foreach(parent->parent->dict.pairs, pair) { if (pair->item.value != parent) continue; @@ -327,14 +300,17 @@ format_error(enum yml_error err, const struct yml_node *parent, const struct yml assert(pair->item.key->type == SCALAR); assert(node->type == SCALAR); - snprintf(err_str, sizeof(err_str), "%s: duplicate key: '%s'", pair->item.key->scalar.value, + snprintf(err_str, sizeof(err_str), + "%s: duplicate key: '%s'", + pair->item.key->scalar.value, node->scalar.value); return err_str; } } if (node->type == SCALAR) { - snprintf(err_str, sizeof(err_str), "duplicate key: %s", node->scalar.value); + snprintf(err_str, sizeof(err_str), + "duplicate key: %s", node->scalar.value); } else snprintf(err_str, sizeof(err_str), "duplicate key"); break; @@ -342,20 +318,22 @@ format_error(enum yml_error err, const struct yml_node *parent, const struct yml case YML_ERR_INVALID_ANCHOR: if (parent->parent != NULL && parent->parent->type == DICT) { - tll_foreach(parent->parent->dict.pairs, pair) - { + tll_foreach(parent->parent->dict.pairs, pair) { if (pair->item.value != parent) continue; if (pair->item.key->type != SCALAR) break; - snprintf(err_str, sizeof(err_str), "%s: invalid anchor: %s", pair->item.key->scalar.value, + snprintf(err_str, sizeof(err_str), + "%s: invalid anchor: %s", + pair->item.key->scalar.value, anchor != NULL ? anchor : ""); return err_str; } } - snprintf(err_str, sizeof(err_str), "invalid anchor: %s", anchor != NULL ? anchor : ""); + snprintf(err_str, sizeof(err_str), "invalid anchor: %s", + anchor != NULL ? anchor : ""); break; case YML_ERR_UNKNOWN: @@ -366,47 +344,6 @@ format_error(enum yml_error err, const struct yml_node *parent, const struct yml return err_str; } -static char * -replace_env_variables(const char *str, size_t len) -{ - char *result = strndup(str, len); - char *start, *key; - const char *end, *env_value; - const char* prefix = "${"; - const char* suffix = "}"; - const size_t pref_len = 2; - const size_t suff_len = 1; - size_t key_len; - - while ((start = strstr(result, prefix)) != NULL && - (end = strstr(start, suffix)) != NULL) - { - key_len = end - start - pref_len; - key = strndup(start + pref_len, key_len); - env_value = getenv(key); - - if (env_value) { - size_t result_len = strlen(result); - size_t new_len = result_len - key_len - pref_len - suff_len + strlen(env_value); - char *new_result = malloc(new_len + 1); - - strncpy(new_result, result, start - result); - new_result[start - result] = '\0'; - strcat(new_result, env_value); - strcat(new_result, end + 1); - - free(result); - result = new_result; - } else { - memmove(start, end + 1, strlen(end + 1) + 1); - } - - free(key); - } - - return result; -} - struct yml_node * yml_load(FILE *yml, char **error) { @@ -416,7 +353,7 @@ yml_load(FILE *yml, char **error) yaml_parser_set_input_file(&yaml, yml); bool done = false; - int indent UNUSED = 0; + int indent = 0; struct yml_node *root = malloc(sizeof(*root)); root->type = ROOT; @@ -431,12 +368,19 @@ yml_load(FILE *yml, char **error) yaml_event_t event; if (!yaml_parser_parse(&yaml, &event)) { if (error != NULL) { - int cnt = snprintf(NULL, 0, "%zu:%zu: %s %s", yaml.problem_mark.line + 1, yaml.problem_mark.column, - yaml.problem, yaml.context != NULL ? yaml.context : ""); + int cnt = snprintf( + NULL, 0, "%zu:%zu: %s %s", + yaml.problem_mark.line + 1, + yaml.problem_mark.column, + yaml.problem, + yaml.context != NULL ? yaml.context : ""); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: %s %s", yaml.problem_mark.line + 1, yaml.problem_mark.column, - yaml.problem, yaml.context != NULL ? yaml.context : ""); + snprintf(*error, cnt + 1, "%zu:%zu: %s %s", + yaml.problem_mark.line + 1, + yaml.problem_mark.column, + yaml.problem, + yaml.context != NULL ? yaml.context : ""); } goto err_no_error_formatting; @@ -487,7 +431,9 @@ yml_load(FILE *yml, char **error) } if (!got_match) { - error_str = format_error(YML_ERR_INVALID_ANCHOR, n, NULL, (const char *)event.data.alias.anchor); + error_str = format_error( + YML_ERR_INVALID_ANCHOR, n, NULL, + (const char *)event.data.alias.anchor); yaml_event_delete(&event); goto err; } @@ -497,7 +443,8 @@ yml_load(FILE *yml, char **error) case YAML_SCALAR_EVENT: { struct yml_node *new_scalar = calloc(1, sizeof(*new_scalar)); new_scalar->type = SCALAR; - new_scalar->scalar.value = replace_env_variables((const char *)event.data.scalar.value, event.data.scalar.length); + new_scalar->scalar.value = strndup( + (const char*)event.data.scalar.value, event.data.scalar.length); enum yml_error err = add_node(n, new_scalar, event.start_mark); if (err != YML_ERR_NONE) { @@ -579,21 +526,28 @@ yml_load(FILE *yml, char **error) yaml_parser_delete(&yaml); - if (!post_process(root, error)) { - yml_destroy(root); - return NULL; - } + post_process(root); return root; err: if (error_str != NULL) { - int cnt = snprintf(NULL, 0, "%zu:%zu: %s", yaml.mark.line + 1, yaml.mark.column, error_str); + int cnt = snprintf( + NULL, 0, "%zu:%zu: %s", + yaml.mark.line + 1, + yaml.mark.column, + error_str); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: %s", yaml.mark.line + 1, yaml.mark.column, error_str); + snprintf( + *error, cnt + 1, "%zu:%zu: %s", + yaml.mark.line + 1, + yaml.mark.column, + error_str); } else { - int cnt = snprintf(NULL, 0, "%zu:%zu: unknown error", yaml.mark.line + 1, yaml.mark.column); + int cnt = snprintf(NULL, 0, "%zu:%zu: unknown error", + yaml.mark.line + 1, yaml.mark.column); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: unknown error", yaml.mark.line + 1, yaml.mark.column); + snprintf(*error, cnt + 1, "%zu:%zu: unknown error", + yaml.mark.line + 1, yaml.mark.column); } err_no_error_formatting: @@ -625,8 +579,7 @@ yml_destroy(struct yml_node *node) break; case DICT: - tll_foreach(node->dict.pairs, it) - { + tll_foreach(node->dict.pairs, it) { yml_destroy(it->item.key); yml_destroy(it->item.value); } @@ -655,11 +608,9 @@ yml_is_list(const struct yml_node *node) return node->type == LIST; } -static struct yml_node const * -yml_get_(struct yml_node const *node, char const *_path, bool value) + const struct yml_node * +yml_get_value(const struct yml_node *node, const char *_path) { - /* value: true for value, false for key */ - if (node != NULL && node->type == ROOT) node = node->root.root; @@ -668,21 +619,18 @@ yml_get_(struct yml_node const *node, char const *_path, bool value) char *path = strdup(_path); - for (const char *part = strtok(path, "."), *next_part = strtok(NULL, "."); part != NULL; - part = next_part, next_part = strtok(NULL, ".")) { + for (const char *part = strtok(path, "."), *next_part = strtok(NULL, "."); + part != NULL; + part = next_part, next_part = strtok(NULL, ".")) + { assert(yml_is_dict(node)); - tll_foreach(node->dict.pairs, it) - { + tll_foreach(node->dict.pairs, it) { assert(yml_is_scalar(it->item.key)); if (strcmp(it->item.key->scalar.value, part) == 0) { if (next_part == NULL) { free(path); - - if (value) - return it->item.value; - else - return it->item.key; + return it->item.value; } node = it->item.value; @@ -695,24 +643,11 @@ yml_get_(struct yml_node const *node, char const *_path, bool value) return NULL; } -const struct yml_node * -yml_get_value(const struct yml_node *node, const char *_path) -{ - return yml_get_(node, _path, true); -} - -struct yml_node const * -yml_get_key(struct yml_node const *node, char const *_path) -{ - return yml_get_(node, _path, false); -} - struct yml_list_iter yml_list_iter(const struct yml_node *list) { assert(yml_is_list(list)); - tll_foreach(list->list.values, it) - { + tll_foreach(list->list.values, it) { return (struct yml_list_iter){ .node = it->item, .private = it, @@ -745,7 +680,9 @@ yml_list_length(const struct yml_node *list) assert(yml_is_list(list)); size_t length = 0; - for (struct yml_list_iter it = yml_list_iter(list); it.node != NULL; yml_list_next(&it), length++) + for (struct yml_list_iter it = yml_list_iter(list); + it.node != NULL; + yml_list_next(&it), length++) ; return length; @@ -756,8 +693,7 @@ yml_dict_iter(const struct yml_node *dict) { assert(yml_is_dict(dict)); - tll_foreach(dict->dict.pairs, it) - { + tll_foreach(dict->dict.pairs, it) { return (struct yml_dict_iter){ .key = it->item.key, .value = it->item.value, @@ -765,7 +701,7 @@ yml_dict_iter(const struct yml_node *dict) }; } - return (struct yml_dict_iter){ + return (struct yml_dict_iter) { .key = NULL, .value = NULL, .private1 = NULL, @@ -836,12 +772,18 @@ _as_bool(const struct yml_node *value, bool *ret) return false; const char *v = yml_value_as_string(value); - if (strcasecmp(v, "y") == 0 || strcasecmp(v, "yes") == 0 || strcasecmp(v, "true") == 0 - || strcasecmp(v, "on") == 0) { + if (strcasecmp(v, "y") == 0 || + strcasecmp(v, "yes") == 0 || + strcasecmp(v, "true") == 0 || + strcasecmp(v, "on") == 0) + { *ret = true; return true; - } else if (strcasecmp(v, "n") == 0 || strcasecmp(v, "no") == 0 || strcasecmp(v, "false") == 0 - || strcasecmp(v, "off") == 0) { + } else if (strcasecmp(v, "n") == 0 || + strcasecmp(v, "no") == 0 || + strcasecmp(v, "false") == 0 || + strcasecmp(v, "off") == 0) + { *ret = false; return true; } @@ -888,8 +830,7 @@ _print_node(const struct yml_node *n, int indent) break; case DICT: - tll_foreach(n->dict.pairs, it) - { + tll_foreach(n->dict.pairs, it) { _print_node(it->item.key, indent); printf(": "); @@ -904,8 +845,7 @@ _print_node(const struct yml_node *n, int indent) break; case LIST: - tll_foreach(n->list.values, it) - { + tll_foreach(n->list.values, it) { printf("%*s- ", indent, ""); if (it->item->type != SCALAR) { printf("\n"); diff --git a/yml.h b/yml.h index 784252c..476d469 100644 --- a/yml.h +++ b/yml.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include struct yml_node; @@ -11,8 +11,8 @@ bool yml_is_scalar(const struct yml_node *node); bool yml_is_dict(const struct yml_node *node); bool yml_is_list(const struct yml_node *node); -const struct yml_node *yml_get_value(const struct yml_node *node, const char *path); -const struct yml_node *yml_get_key(struct yml_node const *node, char const *path); +const struct yml_node *yml_get_value( + const struct yml_node *node, const char *path); struct yml_list_iter { const struct yml_node *node;