diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 1703a3d..a5d4e9c 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -19,17 +19,13 @@ 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 + - https://codeberg.org/dnkl/yambar # triggers: # - action: email @@ -37,10 +33,10 @@ sources: # to: tasks: - - fcft: | - cd yambar/subprojects - git clone https://codeberg.org/dnkl/fcft.git - cd ../.. + - codespell: | + pip install codespell + cd yambar + ~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd - setup: | mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared meson --buildtype=debug -Db_coverage=true yambar bld/debug diff --git a/.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/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..06df201 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,98 @@ +image: alpine:latest + +stages: + - info + - build + +variables: + GIT_SUBMODULE_STRATEGY: normal + +before_script: + - apk update + - apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc + - apk add pixman-dev freetype-dev fontconfig-dev + - apk add libxcb-dev xcb-util-wm-dev xcb-util-cursor-dev yaml-dev + - apk add wayland-dev wayland-protocols wlroots-dev + - apk add json-c-dev libmpdclient-dev alsa-lib-dev + - apk add ttf-dejavu + - apk add git + +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 + +codespell: + image: alpine:latest + stage: build + script: + - apk add python3 + - apk add py3-pip + - pip install codespell + - codespell README.md CHANGELOG.md *.c *.h doc/*.scd 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..ab2e3fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,5 @@ # 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) @@ -12,445 +7,22 @@ * [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)). + (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)). + (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)). + (https://codeberg.org/dnkl/yambar/issues/84). * river: support for the river-status protocol, version 2 (‘urgent’ views). * `online` tag to the `alsa` module. @@ -462,16 +34,17 @@ * network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags. * network: `poll-interval` option (for the new `signal` and `*-bitrate` tags). +* tags: percentage formatter, for range tags: `{tag_name:%}`. * tags: percentage tag formatter, for range tags: `{tag_name:%}`. * tags: kb/mb/gb, and kib/mib/gib tag formatters. -* clock: add a config option to show UTC time. + ### 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)). + (https://codeberg.org/dnkl/yambar/issues/86). ### Fixed @@ -481,7 +54,7 @@ * Regression: `{where}` tag not being expanded in progress-bar `on-click` handlers. * `alsa` module causing yambar to use 100% CPU if the ALSA device is - disconnected ([#61](https://codeberg.org/dnkl/yambar/issues/61)). + disconnected (https://codeberg.org/dnkl/yambar/issues/61). ### Contributors @@ -497,39 +70,39 @@ * Text shaping support. * Support for middle and right mouse buttons, mouse wheel and trackpad - scrolling ([#39](https://codeberg.org/dnkl/yambar/issues/39)). + scrolling (https://codeberg.org/dnkl/yambar/issues/39). * script: polling mode. See the new `poll-interval` option - ([#67](https://codeberg.org/dnkl/yambar/issues/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)). + each module (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)). + (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)). + (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)). + (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)). + (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)). + (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)). + (https://codeberg.org/dnkl/yambar/issues/45). * Crash when a string particle contained `{}` - ([#48](https://codeberg.org/dnkl/yambar/issues/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)). + `9` (https://codeberg.org/dnkl/yambar/issues/60). ### Contributors @@ -544,7 +117,7 @@ * 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 +125,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 +134,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 +154,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..e4b343b 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,8 +1,7 @@ pkgname=yambar -pkgver=1.11.0 +pkgver=1.7.0 pkgrel=1 pkgdesc="Simplistic and highly configurable status panel for X and Wayland" -changelog=CHANGELOG.md arch=('x86_64' 'aarch64') url=https://codeberg.org/dnkl/yambar license=(mit) @@ -16,9 +15,7 @@ depends=( 'libudev.so' 'json-c' 'libmpdclient' - 'libpulse' - 'pipewire' - 'fcft>=3.0.0' 'fcft<4.0.0') + 'fcft>=2.4.0') optdepends=('xcb-util-errors: better X error messages') source=() diff --git a/PKGBUILD.wayland-only b/PKGBUILD.wayland-only index 266fc06..d0963e4 100644 --- a/PKGBUILD.wayland-only +++ b/PKGBUILD.wayland-only @@ -1,5 +1,5 @@ pkgname=yambar-wayland -pkgver=1.11.0 +pkgver=1.7.0 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.4.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..f61d195 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,6 @@ -[![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) +[![Packaging status](https://repology.org/badge/vertical-allrepos/yambar.svg)](https://repology.org/project/yambar/versions) ## Index @@ -80,9 +57,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 +76,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)) @@ -128,7 +98,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..47fae95 100644 --- a/bar/backend.h +++ b/bar/backend.h @@ -7,8 +7,10 @@ struct backend { bool (*setup)(struct bar *bar); void (*cleanup)(struct bar *bar); - void (*loop)(struct bar *bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)); + void (*loop)(struct bar *bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y)); void (*commit)(const struct bar *bar); void (*refresh)(const struct bar *bar); void (*set_cursor)(struct bar *bar, const char *cursor); diff --git a/bar/bar.c b/bar/bar.c index 109f210..2399368 100644 --- a/bar/bar.c +++ b/bar/bar.c @@ -1,15 +1,15 @@ #include "bar.h" #include "private.h" -#include -#include -#include -#include -#include #include +#include #include +#include +#include #include +#include #include +#include #include @@ -18,17 +18,17 @@ #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 @@ -75,8 +75,9 @@ expose(const struct bar *_bar) const struct private *bar = _bar->private; pixman_image_t *pix = bar->pix; - pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, &bar->background, 1, - &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, pix, &bar->background, 1, + &(pixman_rectangle16_t){0, 0, bar->width, bar->height_with_border}); pixman_image_fill_rectangles( PIXMAN_OP_OVER, pix, &bar->border.color, 4, @@ -85,15 +86,20 @@ expose(const struct bar *_bar) {0, 0, bar->border.left_width, bar->height_with_border}, /* Right */ - {bar->width - bar->border.right_width, 0, bar->border.right_width, bar->height_with_border}, + {bar->width - bar->border.right_width, + 0, bar->border.right_width, bar->height_with_border}, /* Top */ - {bar->border.left_width, 0, bar->width - bar->border.left_width - bar->border.right_width, + {bar->border.left_width, + 0, + bar->width - bar->border.left_width - bar->border.right_width, bar->border.top_width}, /* Bottom */ - {bar->border.left_width, bar->height_with_border - bar->border.bottom_width, - bar->width - bar->border.left_width - bar->border.right_width, bar->border.bottom_width}, + {bar->border.left_width, + bar->height_with_border - bar->border.bottom_width, + bar->width - bar->border.left_width - bar->border.right_width, + bar->border.bottom_width}, }); for (size_t i = 0; i < bar->left.count; i++) { @@ -128,14 +134,6 @@ expose(const struct bar *_bar) int y = bar->border.top_width; int x = bar->border.left_width + bar->left_margin - bar->left_spacing; - pixman_region32_t clip; - pixman_region32_init_rect( - &clip, bar->border.left_width + bar->left_margin, bar->border.top_width, - (bar->width - bar->left_margin - bar->right_margin - bar->border.left_width - bar->border.right_width), - bar->height); - pixman_image_set_clip_region32(pix, &clip); - pixman_region32_fini(&clip); - for (size_t i = 0; i < bar->left.count; i++) { const struct exposable *e = bar->left.exps[i]; e->expose(e, pix, x + bar->left_spacing, y, bar->height); @@ -151,7 +149,11 @@ expose(const struct bar *_bar) x += bar->left_spacing + e->width + bar->right_spacing; } - x = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width); + x = bar->width - ( + right_width + + bar->left_spacing + + bar->right_margin + + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { const struct exposable *e = bar->right.exps[i]; @@ -163,6 +165,7 @@ expose(const struct bar *_bar) bar->backend.iface->commit(_bar); } + static void refresh(const struct bar *bar) { @@ -185,12 +188,15 @@ output_name(const struct bar *bar) } static void -on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, + int x, int y) { struct private *bar = _bar->private; - if ((y < bar->border.top_width || y >= (bar->height_with_border - bar->border.bottom_width)) - || (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) { + if ((y < bar->border.top_width || + y >= (bar->height_with_border - bar->border.bottom_width)) || + (x < bar->border.left_width || x >= (bar->width - bar->border.right_width))) + { set_cursor(_bar, "left_ptr"); return; } @@ -232,7 +238,10 @@ on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn, int x, mx += e->width + bar->right_spacing; } - mx = bar->width - (right_width + bar->left_spacing + bar->right_margin + bar->border.right_width); + mx = bar->width - (right_width + + bar->left_spacing + + bar->right_margin + + bar->border.right_width); for (size_t i = 0; i < bar->right.count; i++) { struct exposable *e = bar->right.exps[i]; @@ -273,7 +282,8 @@ run(struct bar *_bar) { struct private *bar = _bar->private; - bar->height_with_border = bar->height + bar->border.top_width + bar->border.bottom_width; + bar->height_with_border = + bar->height + bar->border.top_width + bar->border.bottom_width; if (!bar->backend.iface->setup(_bar)) { bar->backend.iface->cleanup(_bar); @@ -283,7 +293,6 @@ run(struct bar *_bar) } set_cursor(_bar, "left_ptr"); - expose(_bar); /* Start modules */ thrd_t thrd_left[max(bar->left.count, 1)]; @@ -323,26 +332,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; } diff --git a/bar/bar.h b/bar/bar.h index ce91247..c9f94f4 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 { @@ -18,7 +17,7 @@ struct 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_layer { BAR_LAYER_TOP, BAR_LAYER_BOTTOM }; enum bar_backend { BAR_BACKEND_AUTO, BAR_BACKEND_XCB, BAR_BACKEND_WAYLAND }; struct bar_config { @@ -27,7 +26,6 @@ struct bar_config { const char *monitor; enum bar_layer layer; enum bar_location location; - enum font_shaping font_shaping; int height; int left_spacing, right_spacing; int left_margin, right_margin; diff --git a/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..f216c99 100644 --- a/bar/private.h +++ b/bar/private.h @@ -3,8 +3,7 @@ #include "../bar/bar.h" #include "backend.h" -struct private -{ +struct private { /* From bar_config */ char *monitor; enum bar_layer layer; diff --git a/bar/wayland.c b/bar/wayland.c index 86ab252..270b4ff 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -1,25 +1,24 @@ #include "wayland.h" -#include -#include -#include -#include #include #include #include +#include #include +#include +#include -#include -#include #include +#include +#include #include #include #include #include -#include #include +#include #define LOG_MODULE "bar:wayland" #define LOG_ENABLE_DBG 0 @@ -28,10 +27,6 @@ #include "private.h" -#if !defined(MFD_NOEXEC_SEAL) -#define MFD_NOEXEC_SEAL 0 -#endif - struct buffer { bool busy; size_t width; @@ -50,7 +45,6 @@ struct monitor { struct wl_output *output; struct zxdg_output_v1 *xdg; char *name; - uint32_t wl_name; int x; int y; @@ -101,7 +95,6 @@ struct wayland_backend { tll(struct monitor) monitors; const struct monitor *monitor; - char *last_mapped_monitor; int scale; @@ -116,15 +109,16 @@ 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, + enum mouse_button btn, int x, int y); }; static void @@ -156,7 +150,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 +160,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 +172,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 +212,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 +228,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,7 +245,8 @@ 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; @@ -252,7 +258,8 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, str } 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,12 +268,14 @@ 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, MOUSE_BTN_NONE, + seat->pointer.x, seat->pointer.y); } static void -wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, - uint32_t state) +wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { struct seat *seat = data; struct wayland_backend *backend = seat->backend; @@ -277,31 +286,21 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, ui enum mouse_button btn; switch (button) { - case BTN_LEFT: - btn = MOUSE_BTN_LEFT; - break; - case BTN_MIDDLE: - btn = MOUSE_BTN_MIDDLE; - break; - case BTN_RIGHT: - btn = MOUSE_BTN_RIGHT; - break; - case BTN_SIDE: - btn = MOUSE_BTN_PREVIOUS; - break; - case BTN_EXTRA: - btn = MOUSE_BTN_NEXT; - break; + case BTN_LEFT: btn = MOUSE_BTN_LEFT; break; + case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break; + case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break; default: return; } - backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); } } static void -wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) +wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; @@ -317,18 +316,24 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32 const double amount = wl_fixed_to_double(value); - if ((backend->aggregated_scroll > 0 && amount < 0) || (backend->aggregated_scroll < 0 && amount > 0)) { + if ((backend->aggregated_scroll > 0 && amount < 0) || + (backend->aggregated_scroll < 0 && amount > 0)) + { backend->aggregated_scroll = amount; } else backend->aggregated_scroll += amount; - enum mouse_button btn = backend->aggregated_scroll > 0 ? MOUSE_BTN_WHEEL_DOWN : MOUSE_BTN_WHEEL_UP; + enum mouse_button btn = backend->aggregated_scroll > 0 + ? MOUSE_BTN_WHEEL_DOWN + : MOUSE_BTN_WHEEL_UP; const double step = bar->trackpad_sensitivity; const double adjust = backend->aggregated_scroll > 0 ? -step : step; while (fabs(backend->aggregated_scroll) >= step) { - backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, + seat->pointer.x, seat->pointer.y); backend->aggregated_scroll += adjust; } } @@ -342,12 +347,14 @@ wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) } static void -wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) +wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) { } static void -wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) +wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; @@ -358,7 +365,8 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, u } 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; @@ -367,12 +375,16 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axi struct wayland_backend *backend = seat->backend; backend->have_discrete = true; - enum mouse_button btn = discrete > 0 ? MOUSE_BTN_WHEEL_DOWN : MOUSE_BTN_WHEEL_UP; + enum mouse_button btn = discrete > 0 + ? MOUSE_BTN_WHEEL_DOWN + : MOUSE_BTN_WHEEL_UP; int count = abs(discrete); for (int32_t i = 0; i < count; i++) { - backend->bar_on_mouse(backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y); + backend->bar_on_mouse( + backend->bar, ON_MOUSE_CLICK, btn, + seat->pointer.x, seat->pointer.y); } } @@ -389,14 +401,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 +450,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 +461,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 +482,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,61 +511,38 @@ 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); @@ -579,7 +550,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) { } @@ -597,12 +569,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 +586,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 +595,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 +605,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 +623,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,7 +635,8 @@ 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) { @@ -663,10 +644,12 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha 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 +659,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 +669,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 +679,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 +690,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 +703,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 +726,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 +742,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 +769,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 +791,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 +857,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,7 +883,8 @@ update_size(struct wayland_backend *backend) const struct monitor *mon = backend->monitor; const int scale = mon != NULL ? mon->scale : guess_scale(backend); - assert(backend->surface != NULL); + if (backend->scale == scale) + return true; backend->scale = scale; @@ -1024,21 +894,29 @@ update_size(struct wayland_backend *backend) bar->height = height - bar->border.top_width - bar->border.bottom_width; bar->height_with_border = height; - zwlr_layer_surface_v1_set_size(backend->layer_surface, 0, bar->height_with_border / scale); + zwlr_layer_surface_v1_set_size( + backend->layer_surface, 0, bar->height_with_border / scale); zwlr_layer_surface_v1_set_exclusive_zone( backend->layer_surface, - (bar->height_with_border + (bar->location == BAR_TOP ? bar->border.bottom_margin : bar->border.top_margin)) - / scale); + (bar->height_with_border + (bar->location == BAR_TOP + ? bar->border.bottom_margin + : bar->border.top_margin)) + / scale); - zwlr_layer_surface_v1_set_margin(backend->layer_surface, bar->border.top_margin / scale, - bar->border.right_margin / scale, bar->border.bottom_margin / scale, - bar->border.left_margin / scale); + zwlr_layer_surface_v1_set_margin( + backend->layer_surface, + bar->border.top_margin / scale, + bar->border.right_margin / scale, + bar->border.bottom_margin / scale, + bar->border.left_margin / scale + ); /* Trigger a 'configure' event, after which we'll have the width */ wl_surface_commit(backend->surface); wl_display_roundtrip(backend->display); - if (backend->width == -1 || backend->height != bar->height_with_border) { + if (backend->width == -1 || + backend->height != bar->height_with_border) { LOG_ERR("failed to get panel width"); return false; } @@ -1055,6 +933,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 +980,48 @@ 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) { + enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM + ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM + : ZWLR_LAYER_SHELL_V1_LAYER_TOP; + + backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + backend->layer_shell, backend->surface, + backend->monitor != NULL ? backend->monitor->output : NULL, + layer, "panel"); + + if (backend->layer_surface == NULL) { + LOG_ERR("failed to create layer shell surface"); + return false; + } + + zwlr_layer_surface_v1_add_listener( + backend->layer_surface, &layer_surface_listener, backend); + + /* Aligned to top, maximum width */ + enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP + ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + zwlr_layer_surface_v1_set_anchor( + backend->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + top_or_bottom); + + 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 +1041,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 +1051,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 +1088,25 @@ cleanup(struct bar *_bar) /* Destroyed when freeing buffer list */ bar->pix = NULL; + } static void -loop(struct bar *_bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)) +loop(struct bar *_bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y)) { struct private *bar = _bar->private; struct wayland_backend *backend = bar->backend.data; - 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 +1118,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 +1182,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 +1194,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 +1204,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 +1225,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 +1237,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 +1252,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 +1266,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 +1279,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 +1301,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); @@ -1445,7 +1313,7 @@ set_cursor(struct bar *_bar, const char *cursor) } static const char * -bar_output_name(const struct bar *_bar) +output_name(const struct bar *_bar) { const struct private *bar = _bar->private; const struct wayland_backend *backend = bar->backend.data; @@ -1460,5 +1328,5 @@ const struct backend wayland_backend_iface = { .commit = &commit, .refresh = &refresh, .set_cursor = &set_cursor, - .output_name = &bar_output_name, + .output_name = &output_name, }; diff --git a/bar/xcb.c b/bar/xcb.c index ae52bf3..4b0e7c1 100644 --- a/bar/xcb.c +++ b/bar/xcb.c @@ -1,16 +1,16 @@ #include "xcb.h" -#include #include +#include +#include #include #include -#include #include +#include #include #include -#include #include #include #include @@ -39,6 +39,7 @@ struct xcb_backend { void *client_pixmap; size_t client_pixmap_size; pixman_image_t *pix; + }; void * @@ -54,8 +55,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 +76,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 +90,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 +111,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 +155,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 +232,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,8 +310,10 @@ cleanup(struct bar *_bar) } static void -loop(struct bar *_bar, void (*expose)(const struct bar *bar), - void (*on_mouse)(struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y)) +loop(struct bar *_bar, + void (*expose)(const struct bar *bar), + void (*on_mouse)(struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y)) { struct private *bar = _bar->private; struct xcb_backend *backend = bar->backend.data; @@ -277,7 +323,10 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), const int fd = xcb_get_file_descriptor(backend->conn); while (true) { - struct pollfd fds[] = {{.fd = _bar->abort_fd, .events = POLLIN}, {.fd = fd, .events = POLLIN}}; + struct pollfd fds[] = { + {.fd = _bar->abort_fd, .events = POLLIN}, + {.fd = fd, .events = POLLIN} + }; poll(fds, sizeof(fds) / sizeof(fds[0]), -1); @@ -286,14 +335,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)); @@ -316,12 +369,9 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar), const xcb_button_release_event_t *evt = (void *)e; switch (evt->detail) { - case 1: - case 2: - case 3: - case 4: - case 5: - on_mouse(_bar, ON_MOUSE_CLICK, evt->detail, evt->event_x, evt->event_y); + case 1: case 2: case 3: case 4: case 5: + on_mouse(_bar, ON_MOUSE_CLICK, + evt->detail, evt->event_x, evt->event_y); break; } break; @@ -355,9 +405,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 +420,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,7 +458,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); + xcb_change_window_attributes( + backend->conn, backend->win, XCB_CW_CURSOR, &backend->cursor); } static const char * 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..65b3100 100644 --- a/completions/zsh/_yambar +++ b/completions/zsh/_yambar @@ -8,6 +8,6 @@ _arguments \ '(-c --config)'{-c,--config}'[alternative configuration file]:filename:_files' \ '(-C --validate)'{-C,--validate}'[verify configuration then quit]' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running]:pidfile:_files' \ - '(-d --log-level)'{-d,--log-level}'[log level (warning)]:loglevel:(info warning error none)' \ + '(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-s --log-no-syslog)'{-s,--log-no-syslog}'[disable syslog logging]' diff --git a/config-verify.c b/config-verify.c index ed7d2f5..67b21e1 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,17 +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)); + LOG_ERR("%s: value is not an integer: '%s'", + conf_err_prefix(chain, node), yml_value_as_string(node)); return false; } @@ -63,7 +56,8 @@ conf_verify_bool(keychain_t *chain, const struct yml_node *node) if (yml_value_is_bool(node)) return true; - LOG_ERR("%s: value is not a boolean: '%s'", conf_err_prefix(chain, node), yml_value_as_string(node)); + LOG_ERR("%s: value is not a boolean: '%s'", + conf_err_prefix(chain, node), yml_value_as_string(node)); return false; } @@ -76,7 +70,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 +82,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 +104,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 +120,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,43 +163,21 @@ 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); + return true; static const struct attr_info info[] = { - {"left", false, &verify_on_click_path}, {"middle", false, &verify_on_click_path}, - {"right", false, &verify_on_click_path}, {"wheel-up", false, &verify_on_click_path}, - {"wheel-down", false, &verify_on_click_path}, {"previous", false, &verify_on_click_path}, - {"next", false, &verify_on_click_path}, {NULL, false, NULL}, + {"left", false, &conf_verify_string}, + {"middle", false, &conf_verify_string}, + {"right", false, &conf_verify_string}, + {"wheel-up", false, &conf_verify_string}, + {"wheel-down", false, &conf_verify_string}, + {NULL, false, NULL}, }; return conf_verify_dict(chain, node, info); @@ -216,30 +196,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 +224,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 +240,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 +258,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 +276,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 +292,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 +315,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 +343,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 +366,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 +381,18 @@ 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}, + {"left-width", false, &conf_verify_int}, + {"right-width", false, &conf_verify_int}, + {"top-width", false, &conf_verify_int}, + {"bottom-width", false, &conf_verify_int}, + {"color", false, &conf_verify_color}, + {"margin", false, &conf_verify_int}, + {"left-margin", false, &conf_verify_int}, + {"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); @@ -416,7 +407,7 @@ verify_bar_location(keychain_t *chain, const struct yml_node *node) static bool verify_bar_layer(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum(chain, node, (const char *[]){"overlay", "top", "bottom", "background"}, 4); + return conf_verify_enum(chain, node, (const char *[]){"top", "bottom"}, 2); } bool @@ -431,31 +422,30 @@ 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}, + {"trackpad-sensitivity", false, &conf_verify_int}, {NULL, false, NULL}, }; diff --git a/config-verify.h b/config-verify.h index 8ad44ce..5afe3f6 100644 --- a/config-verify.h +++ b/config-verify.h @@ -26,14 +26,16 @@ 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, @@ -42,7 +44,6 @@ bool conf_verify_dict(keychain_t *chain, const struct yml_node *node, bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node); bool conf_verify_color(keychain_t *chain, const struct yml_node *node); bool conf_verify_font(keychain_t *chain, const struct yml_node *node); -bool conf_verify_font_shaping(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle(keychain_t *chain, const struct yml_node *node); bool conf_verify_particle_list_items(keychain_t *chain, const struct yml_node *node); diff --git a/config.c b/config.c index 0f80364..7f9583e 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,53 +131,28 @@ 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; + 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}; + const 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); + const char *legacy = yml_value_as_string(on_click); + if (legacy != NULL) 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)) { + 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); + const char *template = yml_value_as_string(it.value); if (strcmp(key, "left") == 0) on_click_templates[MOUSE_BTN_LEFT] = template; @@ -245,10 +164,6 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited) on_click_templates[MOUSE_BTN_WHEEL_UP] = template; else if (strcmp(key, "wheel-down") == 0) on_click_templates[MOUSE_BTN_WHEEL_DOWN] = template; - else if (strcmp(key, "previous") == 0) - on_click_templates[MOUSE_BTN_PREVIOUS] = template; - else if (strcmp(key, "next") == 0) - on_click_templates[MOUSE_BTN_NEXT] = template; else assert(false); } @@ -266,14 +181,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_templates, font, foreground, deco); const struct particle_iface *iface = plugin_load_particle(type); @@ -290,7 +205,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 +215,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); @@ -317,18 +232,15 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *layer = yml_get_value(bar, "layer"); if (layer != NULL) { const char *tmp = yml_value_as_string(layer); - if (strcmp(tmp, "overlay") == 0) - conf.layer = BAR_LAYER_OVERLAY; - else if (strcmp(tmp, "top") == 0) + 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,8 +265,11 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) if (right_margin != NULL) conf.right_margin = yml_value_as_int(right_margin); - const struct yml_node *trackpad_sensitivity = yml_get_value(bar, "trackpad-sensitivity"); - conf.trackpad_sensitivity = trackpad_sensitivity != NULL ? yml_value_as_int(trackpad_sensitivity) : 30; + const struct yml_node *trackpad_sensitivity = + yml_get_value(bar, "trackpad-sensitivity"); + conf.trackpad_sensitivity = trackpad_sensitivity != NULL + ? yml_value_as_int(trackpad_sensitivity) + : 30; const struct yml_node *border = yml_get_value(bar, "border"); if (border != NULL) { @@ -371,8 +286,10 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) const struct yml_node *bottom_margin = yml_get_value(border, "bottom-margin"); if (width != NULL) - conf.border.left_width = conf.border.right_width = conf.border.top_width = conf.border.bottom_width - = yml_value_as_int(width); + conf.border.left_width = + conf.border.right_width = + conf.border.top_width = + conf.border.bottom_width = yml_value_as_int(width); if (left_width != NULL) conf.border.left_width = yml_value_as_int(left_width); @@ -387,8 +304,10 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend) conf.border.color = conf_to_color(color); if (margin != NULL) - conf.border.left_margin = conf.border.right_margin = conf.border.top_margin = conf.border.bottom_margin - = yml_value_as_int(margin); + conf.border.left_margin = + conf.border.right_margin = + conf.border.top_margin = + conf.border.bottom_margin = yml_value_as_int(margin); if (left_margin != NULL) conf.border.left_margin = yml_value_as_int(left_margin); @@ -408,7 +327,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 +335,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 +356,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 +370,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..a1550c4 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -1,86 +1,19 @@ 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-alsa.5.scd', 'yambar-modules-backlight.5.scd', + 'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd', + 'yambar-modules-foreign-toplevel.5.scd', + 'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd', + 'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd', + 'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd', + 'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd', + 'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd', + 'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd', + 'yambar-particles.5.scd', 'yambar-tags.5.scd'] parts = man_src.split('.') name = parts[-3] section = parts[-2] @@ -90,7 +23,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 index 14804e5..58a4343 100644 --- a/doc/yambar-modules-alsa.5.scd +++ b/doc/yambar-modules-alsa.5.scd @@ -7,21 +7,16 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | online : bool : True when the ALSA device has successfully been opened -| 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 +: Volume level, 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. +: Volume level, as a percentage | muted : bool : True if muted, otherwise false @@ -32,7 +27,7 @@ alsa - Monitors an alsa soundcard for volume and mute/unmute changes [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | card : string : yes diff --git a/doc/yambar-modules-backlight.5.scd b/doc/yambar-modules-backlight.5.scd index 6fa3a9a..7c1e6c6 100644 --- a/doc/yambar-modules-backlight.5.scd +++ b/doc/yambar-modules-backlight.5.scd @@ -11,7 +11,7 @@ _/sys/class/backlight_, and uses *udev* to monitor for changes. [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | brightness : range : The current brightness level, in absolute value diff --git a/doc/yambar-modules-battery.5.scd b/doc/yambar-modules-battery.5.scd index aab2106..42b5e00 100644 --- a/doc/yambar-modules-battery.5.scd +++ b/doc/yambar-modules-battery.5.scd @@ -8,22 +8,11 @@ battery - This module reads battery status This module reads battery status from _/sys/class/power_supply_ and uses *udev* to monitor for changes. -Note that it is common (and "normal") for batteries to be in the state -*unknown* under certain conditions. - -For example, some have been seen to enter the *unknown* state when -charging and the capacity reaches ~90%. The battery then stays in -*unknown*, rather than *charging*, until it has been fully charged and -enters the state *full*. - -This does not happen with all batteries, and other batteries may enter -the state *unknown* under other conditions. - # TAGS [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | name : string : Battery device name @@ -49,7 +38,7 @@ the state *unknown* under other conditions. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | name : string : yes @@ -57,20 +46,7 @@ the state *unknown* under other conditions. | 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. +: How often, in seconds, to poll for capacity changes (default=*60*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting). # EXAMPLES @@ -79,7 +55,7 @@ bar: left: - battery: name: BAT0 - poll-interval: 30000 + poll-interval: 30 content: string: {text: "BAT: {capacity}% {estimate}"} ``` diff --git a/doc/yambar-modules-clock.5.scd b/doc/yambar-modules-clock.5.scd index bf3506b..05e18fc 100644 --- a/doc/yambar-modules-clock.5.scd +++ b/doc/yambar-modules-clock.5.scd @@ -7,7 +7,7 @@ clock - This module provides the current date and time [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | time : string : Current time, formatted using the _time-format_ attribute @@ -20,7 +20,7 @@ clock - This module provides the current date and time [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | time-format : string : no @@ -29,10 +29,6 @@ clock - This module provides the current date and time : string : no : *strftime* formatter for the _date_ date (default=*%x*) -| utc -: bool -: no -: Use GMT instead of local timezone (default=false) # EXAMPLES diff --git a/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 index 8b1d576..6a65ec3 100644 --- a/doc/yambar-modules-foreign-toplevel.5.scd +++ b/doc/yambar-modules-foreign-toplevel.5.scd @@ -21,7 +21,7 @@ Note: Wayland only. [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | app-id : string : The application ID (typically the application name) @@ -47,7 +47,7 @@ Note: Wayland only. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | content : particle : yes @@ -67,9 +67,10 @@ bar: - foreign-toplevel: content: map: - conditions: - ~activated: {empty: {}} - activated: + tag: activated + values: + false: {empty: {}} + true: - string: {text: "{app-id}: {title}"} ``` diff --git a/doc/yambar-modules-i3.5.scd b/doc/yambar-modules-i3.5.scd index 2014a3c..8bf4a75 100644 --- a/doc/yambar-modules-i3.5.scd +++ b/doc/yambar-modules-i3.5.scd @@ -22,13 +22,10 @@ with the _application_ and _title_ tags to replace the X11-only [[ *Name* :[ *Type* -:< *Description* +:[ *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) @@ -38,9 +35,6 @@ with the _application_ and _title_ tags to replace the X11-only | urgent : bool : True if the workspace has the urgent flag set -| empty -: bool -: True if the workspace is empty (Sway only) | state : string : One of *urgent*, *focused*, *unfocused* or *invisible* (note: @@ -60,7 +54,7 @@ with the _application_ and _title_ tags to replace the X11-only [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | content : associative array : yes @@ -70,11 +64,7 @@ with the _application_ and _title_ tags to replace the X11-only | sort : enum : no -: How to sort the list of workspaces; one of _none_, _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. +: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_. | persistent : list of strings : no @@ -105,9 +95,10 @@ bar: content: "": map: + tag: state default: {string: {text: "{name}"}} - conditions: - state == focused: {string: {text: "{name}*"}} + values: + focused: {string: {text: "{name}*"}} current: { string: {text: "{application}: {title}"}} ``` 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 index d89407a..93e776b 100644 --- a/doc/yambar-modules-mpd.5.scd +++ b/doc/yambar-modules-mpd.5.scd @@ -7,7 +7,7 @@ mpd - This module provides MPD status such as currently playing artist/album/son [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | state : string : One of *offline*, *stopped*, *paused* or *playing* @@ -20,9 +20,6 @@ mpd - This module provides MPD status such as currently playing artist/album/son | consume : bool : True if the *consume* flag is set -| single -: bool -: True if the *single* flag is set | volume : range : Volume of MPD in percentage @@ -35,9 +32,6 @@ mpd - This module provides MPD status such as currently playing artist/album/son | title : string : Title of currently playing song (also valid in *paused* state) -| file -: string -: Filename or URL of currently playing song (also valid in *paused* state) | pos : string : *%M:%S*-formatted string describing the song's current position @@ -59,7 +53,7 @@ mpd - This module provides MPD status such as currently playing artist/album/son [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | host : string : yes diff --git a/doc/yambar-modules-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 index afbbae3..8ac46a1 100644 --- a/doc/yambar-modules-network.5.scd +++ b/doc/yambar-modules-network.5.scd @@ -6,31 +6,20 @@ 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. +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 per network interface. +address. # TAGS [[ *Name* :[ *Type* -:< *Description* +:[ *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 @@ -56,21 +45,12 @@ address per network interface. | 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 +: RX bitrate, in Mbit/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 +: TX bitrate in Mbit/s # CONFIGURATION @@ -78,46 +58,26 @@ address per network interface. [[ *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_ +:[ *Description* +| name +: string +: yes +: Name of network interface to monitor | 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. +: Periodically (in seconds) update the signal and rx+tx bitrate tags. # EXAMPLES -Display all Ethernet (including WLAN) devices. This excludes loopback, -bridges etc. - ``` bar: left: - network: + name: wlp3s0 content: - map: - conditions: - type == ether || type == wlan: - map: - default: - string: {text: "{name}: {state} ({ipv4})"} - conditions: - ipv4 == "": - string: {text: "{name}: {state}"} + string: {text: "{name}: {state} ({ipv4})"} ``` # SEE ALSO 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 index 0a193d4..4b984fa 100644 --- a/doc/yambar-modules-removables.5.scd +++ b/doc/yambar-modules-removables.5.scd @@ -12,7 +12,7 @@ instantiates the provided _content_ particle for each detected drive. [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | vendor : string : Name of the drive vendor @@ -22,10 +22,6 @@ instantiates the provided _content_ particle for each detected drive. | optical : bool : True if the drive is an optical drive (CD-ROM, DVD-ROM etc) -| audio -: bool -: True if an optical drive has an audio CD inserted (i.e. this - property is always false for non-optical drives). | device : string : Volume device name (typically */dev/sd?*) @@ -48,7 +44,7 @@ instantiates the provided _content_ particle for each detected drive. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | left-spacing : int : no @@ -74,12 +70,13 @@ bar: - removables: content: map: - conditions: - ~mounted: + tag: mounted + values: + false: string: on-click: udisksctl mount -b {device} text: "{label}" - mounted: + true: string: on-click: udisksctl unmount -b {device} text: "{label}" diff --git a/doc/yambar-modules-river.5.scd b/doc/yambar-modules-river.5.scd index 3bf3b61..96be605 100644 --- a/doc/yambar-modules-river.5.scd +++ b/doc/yambar-modules-river.5.scd @@ -13,18 +13,17 @@ 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". +the seats' name and the title of the seats' currently focused view. It also specifies a _content_ template particle, which is instantiated once for all 32 river tags. This means you probably want to use a *map* particle to hide unused river tags. -# TAGS (for the "content" particle) +# TAGS [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | id : int : River tag number @@ -42,35 +41,20 @@ once for all 32 river tags. This means you probably want to use a : True if the river tag has views (i.e. windows). | state : string -: Set to *urgent* if _urgent_ is true, *focused* if _focused_ is true, - *unfocused* if _visible_ is true, but _focused_ is false, or - *invisible* if the river tag is not visible on any monitors. - - -# TAGS (for the "title" particle) - -[[ *Name* -:[ *Type* -:< *Description* +: Set to *urgent* if _urgent_ is true, *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors. | seat : string -: The name of the seat. +: The name of the seat (*title* particle only, see CONFIGURATION) | 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. +: The seat's focused view's title (*title* particle only, see CONFIGURATION) # CONFIGURATION [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | title : particle : no @@ -92,12 +76,13 @@ once for all 32 river tags. This means you probably want to use a bar: left: - river: - title: {string: { text: "{seat} - {title} ({layout}/{mode})" }} + title: {string: { text: "{seat} - {title}" }} content: map: - conditions: - ~occupied: {empty: {}} - occupied: + tag: occupied + values: + false: {empty: {}} + true: string: margin: 5 text: "{id}: {state}" diff --git a/doc/yambar-modules-script.5.scd b/doc/yambar-modules-script.5.scd index 48722cf..26b210f 100644 --- a/doc/yambar-modules-script.5.scd +++ b/doc/yambar-modules-script.5.scd @@ -16,7 +16,7 @@ configurable amount of time. In continuous mode, the script is executed once. It will typically run in a loop, sending an updated tag set whenever it needs, or wants to. The last tag set is used (displayed) by yambar until a new tag set -is received. This mode is intended to be used by scripts that depend +is received. This mode is intended to be used by scripts that depends on non-polling methods to update their state. Tag sets, or _transactions_, are separated by an empty line @@ -66,21 +66,19 @@ User defined. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | path : string : yes -: Path to script/binary to execute. Must either be an absolute path, - or start with *~/*. +: Path to script/binary to execute. Must be an absolute path. | 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. +: Number of seconds between each script run. If unset, continuous mode + is used. # EXAMPLES @@ -115,36 +113,6 @@ bar: content: {string: {text: "{test}"}} ``` -Another example use case of this module could be to display currently playing -song or other media from players that support MPRIS (Media Player Remote -Interfacing Specification): - -``` -bar: - center: - - script: - path: /usr/bin/playerctl - args: - - "--follow" - - "metadata" - - "-f" - - | - status|string|{{status}} - artist|string|{{artist}} - title|string|{{title}} - content: - map: - 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 index bcf948c..567f11a 100644 --- a/doc/yambar-modules-sway-xkb.5.scd +++ b/doc/yambar-modules-sway-xkb.5.scd @@ -16,7 +16,7 @@ instantiated from this template, and represents an input device. [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | id : string : Input device identifier @@ -29,7 +29,7 @@ instantiated from this template, and represents an input device. [[ *Name* :[ *Type* :[ *Req* -:< *Description* +:[ *Description* | identifiers : list of strings : yes diff --git a/doc/yambar-modules-xkb.5.scd b/doc/yambar-modules-xkb.5.scd index ef03097..cb9b81c 100644 --- a/doc/yambar-modules-xkb.5.scd +++ b/doc/yambar-modules-xkb.5.scd @@ -14,7 +14,7 @@ Note: this module is X11 only. It does not work in Wayland. [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | name : string : Name of currently selected layout, long version (e.g. "English (US)") diff --git a/doc/yambar-modules-xwindow.5.scd b/doc/yambar-modules-xwindow.5.scd index 1aadbf7..b4c8e66 100644 --- a/doc/yambar-modules-xwindow.5.scd +++ b/doc/yambar-modules-xwindow.5.scd @@ -16,7 +16,7 @@ _title_ tags. [[ *Name* :[ *Type* -:< *Description* +:[ *Description* | application : string : Name of the application that owns the currently focused window diff --git a/doc/yambar-modules.5.scd b/doc/yambar-modules.5.scd index 1ec4871..266d9b7 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 @@ -142,28 +145,14 @@ Available modules have their own pages: *yambar-modules-clock*(5) -*yambar-modules-cpu*(5) - -*yambar-modules-disk-io*(5) - -*yambar-modules-dwl*(5) - -*yambar-modules-foreign-toplevel*(5) - *yambar-modules-i3*(5) *yambar-modules-label*(5) -*yambar-modules-mem*(5) - *yambar-modules-mpd*(5) *yambar-modules-network*(5) -*yambar-modules-pipewire*(5) - -*yambar-modules-pulse*(5) - *yambar-modules-removables*(5) *yambar-modules-river*(5) @@ -174,10 +163,6 @@ Available modules have their own pages: *yambar-modules-sway*(5) -*yambar-modules-niri-language*(5) - -*yambar-modules-niri-workspaces*(5) - *yambar-modules-xkb*(5) *yambar-modules-xwindow*(5) diff --git a/doc/yambar-particles.5.scd b/doc/yambar-particles.5.scd index 325ef89..4ded3c1 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 @@ -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..49a08ab 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 @@ -58,77 +58,39 @@ 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 +:[ *Description* +:[ *Applies to*] | hex : format -: All tag types : Renders a tag's value in hex +: All tag types | oct : format -: All tag types : Renders a tag's value in octal +: All tag types | % : 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 +: Range tags | kb, mb, gb : format +: Renders a tag's value (in decimal) divided by 1024, 1024^2 or + 1024^3. Note: no unit suffix is appended) : 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 +: Same as *kb*, *mb* and *gb*, but divide by 1000^n instead of 1024^n. : 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 +: Range tags | max : selector -: Range tags : Renders a range tag's maximum value +: Range tags | unit : selector -: Realtime tags : Renders a realtime tag's unit (e.g. "s", or "ms") +: Realtime tags -# 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 -``` diff --git a/doc/yambar.1.scd b/doc/yambar.1.scd index 2aaa46f..a34f13c 100644 --- a/doc/yambar.1.scd +++ b/doc/yambar.1.scd @@ -25,11 +25,11 @@ 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. + as been specified, the file is unlinked exit. *-d*,*--log-level*={*info*,*warning*,*error*,*none*} Log level, used both for log output on stderr as well as - syslog. Default: _warning_. + syslog. Default: _info_. *-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..47041fb 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 @@ -54,8 +48,7 @@ types that are frequently used: | layer : string : no -: Layer to put bar on. One of _overlay_, _top_, _bottom_ or - _background_. Wayland only. Default: _bottom_. +: Layer to put bar on. One of _top_ or _bottom_. Wayland only | left-spacing : int : no @@ -131,17 +124,7 @@ 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 @@ -178,8 +161,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..eab6c46 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}} @@ -46,67 +46,82 @@ bar: foreground: 000000ff deco: {stack: [background: {color: bc2b3fff}, <<: *std_underline]} - map: &i3_mode + tag: mode default: - string: margin: 5 text: "{mode}" deco: {background: {color: cc421dff}} - empty: {right-margin: 7} - conditions: - mode == default: {empty: {}} + values: + 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: + "": + - map: {<<: *i3_mode} + - string: {text: "{title}"} + default: + list: + spacing: 0 + items: + - map: {<<: *i3_mode} + - string: {text: "{application}", max: 10, foreground: ffa0a0ff} + - string: {text: ": "} + - string: {text: "{title}", max: 35} - - foreign-toplevel: - content: - map: - conditions: - ~activated: {empty: {}} - activated: - - string: {text: "{app-id}", foreground: ffa0a0ff} - - string: {text: ": {title}"} center: - mpd: host: /run/mpd/socket @@ -115,28 +130,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 +165,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 +191,75 @@ 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} + - string: {text: "{ssid}"} + values: + "": + - string: {text: , font: *awesome, foreground: ffffff66} + - string: {text: "{ssid}", foreground: ffffff66} - alsa: card: hw:PCH mixer: Master content: map: - conditions: - ~online: {string: {text: , font: *awesome, foreground: ff0000ff}} - online: + tag: online + values: + false: {string: {text: , font: *awesome, foreground: ff0000ff}} + true: map: on-click: /bin/sh -c "amixer -q sset Speaker unmute && amixer -q sset Headphone unmute && amixer -q sset Master toggle" - conditions: - muted: {string: {text: , font: *awesome, foreground: ffffff66}} - ~muted: + tag: muted + values: + true: {string: {text: , font: *awesome, foreground: ffffff66}} + false: ramp: - tag: percent + 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,20 +274,13 @@ 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": + not charging: - ramp: tag: capacity items: @@ -284,6 +305,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 index 4999548..395a790 100755 --- a/examples/scripts/dwl-tags.sh +++ b/examples/scripts/dwl-tags.sh @@ -19,7 +19,7 @@ # # Now the fun part # -# Example configuration: +# Exemple configuration: # # - script: # path: /absolute/path/to/dwl-tags.sh @@ -31,33 +31,39 @@ # content: # - map: # margin: 4 -# conditions: -# tag_0_occupied: +# tag: tag_0_occupied +# values: +# true: # map: -# conditions: -# tag_0_focused: {string: {text: "{tag_0}", <<: *focused}} -# ~tag_0_focused: {string: {text: "{tag_0}", <<: *occupied}} -# ~tag_0_occupied: +# tag: tag_0_focused +# values: +# true: {string: {text: "{tag_0}", <<: *focused}} +# false: {string: {text: "{tag_0}", <<: *occupied}} +# false: # map: -# conditions: -# tag_0_focused: {string: {text: "{tag_0}", <<: *focused}} -# ~tag_0_focused: {string: {text: "{tag_0}", <<: *default}} -# ... +# tag: tag_0_focused +# values: +# true: {string: {text: "{tag_0}", <<: *focused}} +# false: {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: +# tag: tag_8_occupied +# values: +# true: # map: +# tag: tag_8_focused # values: -# tag_8_focused: {string: {text: "{tag_8}", <<: *focused}} -# ~tag_8_focused: {string: {text: "{tag_8}", <<: *default}} +# true: {string: {text: "{tag_8}", <<: *focused}} +# false: {string: {text: "{tag_8}", <<: *occupied}} +# false: +# map: +# tag: tag_8_focused +# values: +# true: {string: {text: "{tag_8}", <<: *focused}} +# false: {string: {text: "{tag_8}", <<: *default}} # - list: # spacing: 3 # items: @@ -121,7 +127,7 @@ while true; do inotifywait -qq --event modify "${fname}" # Get info from the file - output="$(tail -n6 "${fname}")" + output="$(tail -n4 "${fname}")" title="$(echo "${output}" | grep title | cut -d ' ' -f 3- )" #selmon="$(echo "${output}" | grep 'selmon')" layout="$(echo "${output}" | grep layout | cut -d ' ' -f 3- )" @@ -136,3 +142,4 @@ done unset -v output title layout activetags selectedtags unset -v tags name + diff --git a/examples/scripts/pacman.sh b/examples/scripts/pacman.sh index 5026b5a..a20fd6b 100755 --- a/examples/scripts/pacman.sh +++ b/examples/scripts/pacman.sh @@ -12,21 +12,22 @@ # {aur} int number of aur packages # {pkg} int sum of both # -# Examples configuration: +# Exemples configuration: # - script: # path: /absolute/path/to/pacman.sh -# args: [] +# args: [] # content: { string: { text: "{pacman} + {aur} = {pkg}" } } # # To display a message when there is no update: # - script: # path: /absolute/path/to/pacman.sh -# args: [] +# args: [] # content: # map: +# tag: pkg # default: { string: { text: "{pacman} + {aur} = {pkg}" } } -# conditions: -# pkg == 0: {string: {text: no updates}} +# values: +# 0: {string: {text: no updates}} declare interval aur_helper pacman_num aur_num pkg_num @@ -47,9 +48,9 @@ while true; do # Change interval # NUMBER[SUFFIXE] # Possible suffix: - # "s" seconds / "m" minutes / "h" hours / "d" days + # "s" seconds / "m" minutes / "h" hours / "d" days interval="1h" - + # Change your aur manager aur_helper="paru" @@ -62,7 +63,7 @@ while true; do else aur_num=$("${aur_helper}" -Qmu | wc -l) fi - + pkg_num=$(( pacman_num + aur_num )) printf -- '%s\n' "pacman|int|${pacman_num}" @@ -76,3 +77,4 @@ 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..13affaa 100644 --- a/external/river-status-unstable-v1.xml +++ b/external/river-status-unstable-v1.xml @@ -16,7 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - + A global factory for objects that receive status information specific to river. It could be used to implement, for example, a status bar. @@ -47,7 +47,7 @@ - + This interface allows clients to receive information about the current windowing state of an output. @@ -83,24 +83,9 @@ - - - - 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 @@ -136,13 +121,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/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..458f4a1 100755 --- a/generate-version.sh +++ b/generate-version.sh @@ -13,14 +13,7 @@ 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}" diff --git a/log.c b/log.c index ba4ebd9..467b6fd 100644 --- a/log.c +++ b/log.c @@ -1,6 +1,5 @@ #include "log.h" -#include #include #include #include @@ -10,12 +9,13 @@ #include #include #include +#include #define ALEN(v) (sizeof(v) / sizeof((v)[0])) #define UNUSED __attribute__((unused)) static bool colorize = false; -static bool do_syslog = false; +static bool do_syslog = true; static enum log_class log_level = LOG_CLASS_NONE; static const struct { @@ -32,28 +32,21 @@ static const struct { }; void -log_init(enum log_colorize _colorize, bool _do_syslog, enum log_facility syslog_facility, enum log_class _log_level) +log_init(enum log_colorize _colorize, bool _do_syslog, + enum log_facility syslog_facility, enum log_class _log_level) { static const int facility_map[] = { [LOG_FACILITY_USER] = LOG_USER, [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - /* Don't use colors if NO_COLOR is defined and not empty */ - const char *no_color_str = getenv("NO_COLOR"); - const bool no_color = no_color_str != NULL && no_color_str[0] != '\0'; - - colorize = _colorize == LOG_COLORIZE_NEVER - ? false - : _colorize == LOG_COLORIZE_ALWAYS - ? true - : !no_color && isatty(STDERR_FILENO); + colorize = _colorize == LOG_COLORIZE_NEVER ? false : _colorize == LOG_COLORIZE_ALWAYS ? true : isatty(STDERR_FILENO); do_syslog = _do_syslog; log_level = _log_level; int slvl = log_level_map[_log_level].syslog_equivalent; if (do_syslog && slvl != -1) { - openlog(NULL, /*LOG_PID*/ 0, facility_map[syslog_facility]); + openlog(NULL, /*LOG_PID*/0, facility_map[syslog_facility]); setlogmask(LOG_UPTO(slvl)); } } @@ -66,8 +59,8 @@ log_deinit(void) } static void -_log(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, int sys_errno, - va_list va) +_log(enum log_class log_class, const char *module, const char *file, int lineno, + const char *fmt, int sys_errno, va_list va) { assert(log_class > LOG_CLASS_NONE); assert(log_class < ALEN(log_level_map)); @@ -97,8 +90,9 @@ _log(enum log_class log_class, const char *module, const char *file, int lineno, } static void -_sys_log(enum log_class log_class, const char *module, const char UNUSED *file, int UNUSED lineno, const char *fmt, - int sys_errno, va_list va) +_sys_log(enum log_class log_class, const char *module, + const char UNUSED *file, int UNUSED lineno, + const char *fmt, int sys_errno, va_list va) { assert(log_class > LOG_CLASS_NONE); assert(log_class < ALEN(log_level_map)); @@ -106,9 +100,6 @@ _sys_log(enum log_class log_class, const char *module, const char UNUSED *file, 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; @@ -123,7 +114,8 @@ _sys_log(enum log_class log_class, const char *module, const char UNUSED *file, } void -log_msg_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) +log_msg_va(enum log_class log_class, const char *module, + const char *file, int lineno, const char *fmt, va_list va) { va_list va2; va_copy(va2, va); @@ -133,7 +125,8 @@ log_msg_va(enum log_class log_class, const char *module, const char *file, int l } void -log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) +log_msg(enum log_class log_class, const char *module, + const char *file, int lineno, const char *fmt, ...) { va_list va; va_start(va, fmt); @@ -142,13 +135,17 @@ log_msg(enum log_class log_class, const char *module, const char *file, int line } void -log_errno_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) +log_errno_va(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, va_list va) { log_errno_provided_va(log_class, module, file, lineno, errno, fmt, va); } void -log_errno(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) +log_errno(enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) { va_list va; va_start(va, fmt); @@ -157,7 +154,8 @@ log_errno(enum log_class log_class, const char *module, const char *file, int li } void -log_errno_provided_va(enum log_class log_class, const char *module, const char *file, int lineno, int errno_copy, +log_errno_provided_va(enum log_class log_class, const char *module, + const char *file, int lineno, int errno_copy, const char *fmt, va_list va) { va_list va2; @@ -168,7 +166,8 @@ log_errno_provided_va(enum log_class log_class, const char *module, const char * } void -log_errno_provided(enum log_class log_class, const char *module, const char *file, int lineno, int errno_copy, +log_errno_provided(enum log_class log_class, const char *module, + const char *file, int lineno, int errno_copy, const char *fmt, ...) { va_list va; diff --git a/log.h b/log.h index 48f16fe..94fd178 100644 --- a/log.h +++ b/log.h @@ -1,43 +1,68 @@ #pragma once -#include #include +#include enum log_colorize { LOG_COLORIZE_NEVER, LOG_COLORIZE_ALWAYS, LOG_COLORIZE_AUTO }; enum log_facility { LOG_FACILITY_USER, LOG_FACILITY_DAEMON }; -enum log_class { LOG_CLASS_NONE, LOG_CLASS_ERROR, LOG_CLASS_WARNING, LOG_CLASS_INFO, LOG_CLASS_DEBUG }; +enum log_class { + LOG_CLASS_NONE, + LOG_CLASS_ERROR, + LOG_CLASS_WARNING, + LOG_CLASS_INFO, + LOG_CLASS_DEBUG +}; -void log_init(enum log_colorize colorize, bool do_syslog, enum log_facility syslog_facility, enum log_class log_level); +void log_init(enum log_colorize colorize, bool do_syslog, + enum log_facility syslog_facility, enum log_class log_level); void log_deinit(void); -void log_msg(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) - __attribute__((format(printf, 5, 6))); +void log_msg( + enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) __attribute__((format (printf, 5, 6))); -void log_errno(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, ...) - __attribute__((format(printf, 5, 6))); +void log_errno( + enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, ...) __attribute__((format (printf, 5, 6))); -void log_errno_provided(enum log_class log_class, const char *module, const char *file, int lineno, int _errno, - const char *fmt, ...) __attribute__((format(printf, 6, 7))); +void log_errno_provided( + enum log_class log_class, const char *module, + const char *file, int lineno, int _errno, + const char *fmt, ...) __attribute__((format (printf, 6, 7))); + +void log_msg_va( + enum log_class log_class, const char *module, + const char *file, int lineno, const char *fmt, va_list va) __attribute__((format (printf, 5, 0))); +void log_errno_va( + enum log_class log_class, const char *module, + const char *file, int lineno, + const char *fmt, va_list va) __attribute__((format (printf, 5, 0))); +void log_errno_provided_va( + enum log_class log_class, const char *module, + const char *file, int lineno, int _errno, + const char *fmt, va_list va) __attribute__((format (printf, 6, 0))); -void log_msg_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, va_list va) - __attribute__((format(printf, 5, 0))); -void log_errno_va(enum log_class log_class, const char *module, const char *file, int lineno, const char *fmt, - va_list va) __attribute__((format(printf, 5, 0))); -void log_errno_provided_va(enum log_class log_class, const char *module, const char *file, int lineno, int _errno, - const char *fmt, va_list va) __attribute__((format(printf, 6, 0))); int log_level_from_string(const char *str); const char *log_level_string_hint(void); -#define LOG_ERR(...) log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERRNO(...) log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERRNO_P(_errno, ...) \ - log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, _errno, __VA_ARGS__) -#define LOG_WARN(...) log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_INFO(...) log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERR(...) \ + log_msg(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERRNO(...) \ + log_errno(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERRNO_P(_errno, ...) \ + log_errno_provided(LOG_CLASS_ERROR, LOG_MODULE, __FILE__, __LINE__, \ + _errno, __VA_ARGS__) +#define LOG_WARN(...) \ + log_msg(LOG_CLASS_WARNING, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_INFO(...) \ + log_msg(LOG_CLASS_INFO, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG -#define LOG_DBG(...) log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) + #define LOG_DBG(...) \ + log_msg(LOG_CLASS_DEBUG, LOG_MODULE, __FILE__, __LINE__, __VA_ARGS__) #else -#define LOG_DBG(...) + #define LOG_DBG(...) #endif diff --git a/main.c b/main.c index c355843..e92e2e5 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; @@ -131,7 +131,7 @@ print_usage(const char *prog_name) " -c,--config=FILE alternative configuration file\n" " -C,--validate verify configuration then quit\n" " -p,--print-pid=FILE|FD print PID to file or FD\n" - " -d,--log-level={info|warning|error|none} log level (warning)\n" + " -d,--log-level={info|warning|error|none} log level (info)\n" " -l,--log-colorize=[never|always|auto] enable/disable colorization of log output on stderr\n" " -s,--log-no-syslog disable syslog logging\n" " -v,--version show the version number and quit\n"); @@ -147,8 +147,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 +178,16 @@ int main(int argc, char *const *argv) { static const struct option longopts[] = { - {"backend", required_argument, 0, 'b'}, - {"config", required_argument, 0, 'c'}, - {"validate", no_argument, 0, 'C'}, - {"print-pid", required_argument, 0, 'p'}, - {"log-level", required_argument, 0, 'd'}, - {"log-colorize", optional_argument, 0, 'l'}, - {"log-no-syslog", no_argument, 0, 's'}, - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {NULL, no_argument, 0, 0}, + {"backend", required_argument, 0, 'b'}, + {"config", required_argument, 0, 'c'}, + {"validate", no_argument, 0, 'C'}, + {"print-pid", required_argument, 0, 'p'}, + {"log-level", required_argument, 0, 'd'}, + {"log-colorize", optional_argument, 0, 'l'}, + {"log-no-syslog", no_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {NULL, no_argument, 0, 0}, }; bool unlink_pid_file = false; @@ -196,7 +197,7 @@ main(int argc, char *const *argv) char *config_path = NULL; enum bar_backend backend = BAR_BACKEND_AUTO; - enum log_class log_level = LOG_CLASS_WARNING; + enum log_class log_level = LOG_CLASS_INFO; enum log_colorize log_colorize = LOG_COLORIZE_AUTO; bool log_syslog = true; @@ -222,8 +223,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; } @@ -242,7 +244,11 @@ main(int argc, char *const *argv) case 'd': { int lvl = log_level_from_string(optarg); if (lvl < 0) { - fprintf(stderr, "-d,--log-level: %s: argument must be one of %s\n", optarg, log_level_string_hint()); + fprintf( + stderr, + "-d,--log-level: %s: argument must be one of %s\n", + optarg, + log_level_string_hint()); return EXIT_FAILURE; } log_level = lvl; @@ -286,10 +292,12 @@ 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); + _Static_assert((int)LOG_CLASS_ERROR == (int)FCFT_LOG_CLASS_ERROR, + "fcft log level enum offset"); + _Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS, + "fcft colorize enum mismatch"); + fcft_log_init( + (enum fcft_log_colorize)log_colorize, log_syslog, (enum fcft_log_class)log_level); const struct sigaction sa = {.sa_handler = &signal_handler}; sigaction(SIGINT, &sa, NULL); @@ -366,7 +374,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 */ diff --git a/meson.build b/meson.build index 67d3096..b7065e6 100644 --- a/meson.build +++ b/meson.build @@ -1,28 +1,20 @@ project('yambar', 'c', - version: '1.11.0', + version: '1.7.0', 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.4.0', '<3.0.0'], fallback: 'fcft') add_project_arguments( - cc_flags + ['-D_GNU_SOURCE'] + (is_debug_build ? ['-D_DEBUG'] : []) + (backend_x11 ? ['-DENABLE_X11'] : []) + @@ -95,20 +84,16 @@ 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') @@ -116,16 +101,14 @@ 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: [env, 'LC_ALL=C', generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@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 +117,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 +153,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..e525c87 100644 --- a/module.h +++ b/module.h @@ -27,7 +27,7 @@ struct module { * specified number of milliseconds */ bool (*refresh_in)(struct module *mod, long milli_seconds); - const char *(*description)(const struct module *mod); + const char *(*description)(struct module *mod); }; struct module *module_common_new(void); @@ -35,9 +35,9 @@ void module_default_destroy(struct module *mod); struct exposable *module_begin_expose(struct module *mod); /* List of attributes *all* modules implement */ -#define MODULE_COMMON_ATTRS \ - {"content", true, &conf_verify_particle}, {"anchors", false, NULL}, {"font", false, &conf_verify_font}, \ - {"foreground", false, &conf_verify_color}, \ - { \ - NULL, false, NULL \ - } +#define MODULE_COMMON_ATTRS \ + {"content", true, &conf_verify_particle}, \ + {"anchors", false, NULL}, \ + {"font", false, &conf_verify_font}, \ + {"foreground", false, &conf_verify_color}, \ + {NULL, false, NULL} diff --git a/modules/alsa.c b/modules/alsa.c index 8d7cd25..f22b340 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -1,8 +1,8 @@ -#include #include #include -#include +#include #include +#include #include @@ -10,10 +10,10 @@ #define LOG_MODULE "alsa" #define LOG_ENABLE_DBG 0 +#include "../log.h" #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" -#include "../log.h" #include "../plugin.h" enum channel_type { CHANNEL_PLAYBACK, CHANNEL_CAPTURE }; @@ -23,14 +23,11 @@ struct channel { 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; @@ -45,18 +42,10 @@ struct private long playback_vol_min; long playback_vol_max; - bool has_playback_db; - long playback_db_min; - long playback_db_max; - bool has_capture_volume; long capture_vol_min; long capture_vol_max; - long has_capture_db; - long capture_db_min; - long capture_db_max; - const struct channel *volume_chan; const struct channel *muted_chan; }; @@ -71,8 +60,7 @@ static void destroy(struct module *mod) { struct private *m = mod->private; - tll_foreach(m->channels, it) - { + tll_foreach(m->channels, it) { channel_free(&it->item); tll_remove(m->channels, it); } @@ -86,10 +74,10 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { static char desc[32]; - const struct private *m = mod->private; + struct private *m = mod->private; snprintf(desc, sizeof(desc), "alsa(%s)", m->card); return desc; } @@ -106,53 +94,30 @@ content(struct module *mod) bool muted = muted_chan != NULL ? muted_chan->muted : false; long vol_min = 0, vol_max = 0, vol_cur = 0; - long db_min = 0, db_max = 0, db_cur = 0; - bool use_db = false; if (volume_chan != NULL) { if (volume_chan->type == CHANNEL_PLAYBACK) { - db_min = m->playback_db_min; - db_max = m->playback_db_max; vol_min = m->playback_vol_min; vol_max = m->playback_vol_max; } else { - db_min = m->capture_db_min; - db_max = m->capture_db_max; vol_min = m->capture_vol_min; vol_max = m->capture_vol_max; } vol_cur = volume_chan->vol_cur; - db_cur = volume_chan->db_cur; - use_db = volume_chan->use_db; } - int percent; - - 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; - } + int percent = vol_max - vol_min > 0 + ? round(100. * vol_cur / (vol_max - vol_min)) + : 0; struct tag_set tags = { .tags = (struct tag *[]){ tag_new_bool(mod, "online", m->online), tag_new_int_range(mod, "volume", vol_cur, vol_min, vol_max), - tag_new_int_range(mod, "dB", db_cur, db_min, db_max), tag_new_int_range(mod, "percent", percent, 0, 100), tag_new_bool(mod, "muted", muted), }, - .count = 5, + .count = 4, }; mtx_unlock(&mod->lock); @@ -167,98 +132,66 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) { struct private *m = mod->private; - mtx_lock(&mod->lock); - /* If volume level can be changed (i.e. this isn't just a switch; * e.g. a digital channel), get current channel levels */ - tll_foreach(m->channels, it) - { + tll_foreach(m->channels, it) { struct channel *chan = &it->item; - const bool has_volume = chan->type == CHANNEL_PLAYBACK ? m->has_playback_volume : m->has_capture_volume; - const bool has_db = chan->type == CHANNEL_PLAYBACK ? m->has_playback_db : m->has_capture_db; + const bool has_volume = chan->type == CHANNEL_PLAYBACK + ? m->has_playback_volume : m->has_capture_volume; + const long min = chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_min : m->capture_vol_min; + const long max = chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_max : m->capture_vol_max; - if (!has_volume && !has_db) + if (!has_volume) continue; - if (has_db) { - chan->use_db = true; - - const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_db_min : m->capture_db_min; - const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_db_max : m->capture_db_max; - assert(min <= max); - - int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_dB(elem, chan->id, &chan->db_cur) - : snd_mixer_selem_get_capture_dB(elem, chan->id, &chan->db_cur); - - if (r < 0) { - LOG_ERR("%s,%s: %s: failed to get current dB", m->card, m->mixer, chan->name); - } - - if (chan->db_cur < min) { - LOG_WARN("%s,%s: %s: current dB is less than the indicated minimum: " - "%ld < %ld", - m->card, m->mixer, chan->name, chan->db_cur, min); - chan->db_cur = min; - } - - if (chan->db_cur > max) { - LOG_WARN("%s,%s: %s: current dB is greater than the indicated maximum: " - "%ld > %ld", - m->card, m->mixer, chan->name, chan->db_cur, max); - chan->db_cur = max; - } - - assert(chan->db_cur >= min); - assert(chan->db_cur <= max); - - LOG_DBG("%s,%s: %s: dB: %ld", m->card, m->mixer, chan->name, chan->db_cur); - } else - chan->use_db = false; - - const long min = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_min : m->capture_vol_min; - const long max = chan->type == CHANNEL_PLAYBACK ? m->playback_vol_max : m->capture_vol_max; assert(min <= max); - int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_volume(elem, chan->id, &chan->vol_cur) - : snd_mixer_selem_get_capture_volume(elem, chan->id, &chan->vol_cur); + int r = chan->type == CHANNEL_PLAYBACK + ? snd_mixer_selem_get_playback_volume(elem, chan->id, &chan->vol_cur) + : snd_mixer_selem_get_capture_volume(elem, chan->id, &chan->vol_cur); if (r < 0) { - LOG_ERR("%s,%s: %s: failed to get current volume", m->card, m->mixer, chan->name); + LOG_ERR("%s,%s: %s: failed to get current volume", + m->card, m->mixer, chan->name); } if (chan->vol_cur < min) { - LOG_WARN("%s,%s: %s: current volume is less than the indicated minimum: " - "%ld < %ld", - m->card, m->mixer, chan->name, chan->vol_cur, min); + LOG_WARN( + "%s,%s: %s: current volume is less than the indicated minimum: " + "%ld < %ld", m->card, m->mixer, chan->name, chan->vol_cur, min); chan->vol_cur = min; } if (chan->vol_cur > max) { - LOG_WARN("%s,%s: %s: current volume is greater than the indicated maximum: " - "%ld > %ld", - m->card, m->mixer, chan->name, chan->vol_cur, max); + LOG_WARN( + "%s,%s: %s: current volume is greater than the indicated maximum: " + "%ld > %ld", m->card, m->mixer, chan->name, chan->vol_cur, max); chan->vol_cur = max; } assert(chan->vol_cur >= min); - assert(chan->vol_cur <= max); + assert(chan->vol_cur <= max ); - LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer, chan->name, chan->vol_cur); + LOG_DBG("%s,%s: %s: volume: %ld", + m->card, m->mixer, chan->name, chan->vol_cur); } /* Get channels’ muted state */ - tll_foreach(m->channels, it) - { + tll_foreach(m->channels, it) { struct channel *chan = &it->item; int unmuted; - int r = chan->type == CHANNEL_PLAYBACK ? snd_mixer_selem_get_playback_switch(elem, chan->id, &unmuted) - : snd_mixer_selem_get_capture_switch(elem, chan->id, &unmuted); + int r = chan->type == CHANNEL_PLAYBACK + ? snd_mixer_selem_get_playback_switch(elem, chan->id, &unmuted) + : snd_mixer_selem_get_capture_switch(elem, chan->id, &unmuted); if (r < 0) { - LOG_WARN("%s,%s: %s: failed to get muted state", m->card, m->mixer, chan->name); + LOG_WARN("%s,%s: %s: failed to get muted state", + m->card, m->mixer, chan->name); unmuted = 1; } @@ -266,9 +199,10 @@ update_state(struct module *mod, snd_mixer_elem_t *elem) LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer, chan->name, !unmuted); } + mtx_lock(&mod->lock); m->online = true; - mtx_unlock(&mod->lock); + mod->bar->refresh(mod->bar); } @@ -294,8 +228,10 @@ run_while_online(struct module *mod) return ret; } - if (snd_mixer_attach(handle, m->card) != 0 || snd_mixer_selem_register(handle, NULL, NULL) != 0 - || snd_mixer_load(handle) != 0) { + if (snd_mixer_attach(handle, m->card) != 0 || + snd_mixer_selem_register(handle, NULL, NULL) != 0 || + snd_mixer_load(handle) != 0) + { LOG_ERR("failed to attach to card"); ret = RUN_FAILED_CONNECT; goto err; @@ -306,7 +242,7 @@ run_while_online(struct module *mod) snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, m->mixer); - snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid); + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); if (elem == NULL) { LOG_ERR("failed to find mixer"); goto err; @@ -315,53 +251,45 @@ run_while_online(struct module *mod) /* Get playback volume range */ m->has_playback_volume = snd_mixer_selem_has_playback_volume(elem) > 0; if (m->has_playback_volume) { - if (snd_mixer_selem_get_playback_volume_range(elem, &m->playback_vol_min, &m->playback_vol_max) < 0) { - LOG_ERR("%s,%s: failed to get playback volume range", m->card, m->mixer); + if (snd_mixer_selem_get_playback_volume_range( + elem, &m->playback_vol_min, &m->playback_vol_max) < 0) + { + LOG_ERR("%s,%s: failed to get playback volume range", + m->card, m->mixer); assert(m->playback_vol_min == 0); assert(m->playback_vol_max == 0); } if (m->playback_vol_min > m->playback_vol_max) { - LOG_WARN("%s,%s: indicated minimum playback volume is greater than the " - "maximum: %ld > %ld", - m->card, m->mixer, m->playback_vol_min, m->playback_vol_max); + LOG_WARN( + "%s,%s: indicated minimum playback volume is greater than the " + "maximum: %ld > %ld", + m->card, m->mixer, m->playback_vol_min, m->playback_vol_max); m->playback_vol_min = m->playback_vol_max; } } - if (snd_mixer_selem_get_playback_dB_range(elem, &m->playback_db_min, &m->playback_db_max) < 0) { - LOG_WARN("%s,%s: failed to get playback dB range, " - "will use raw volume values instead", - m->card, m->mixer); - 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); + if (snd_mixer_selem_get_capture_volume_range( + elem, &m->capture_vol_min, &m->capture_vol_max) < 0) + { + LOG_ERR("%s,%s: failed to get capture volume range", + m->card, m->mixer); assert(m->capture_vol_min == 0); assert(m->capture_vol_max == 0); } if (m->capture_vol_min > m->capture_vol_max) { - LOG_WARN("%s,%s: indicated minimum capture volume is greater than the " - "maximum: %ld > %ld", - m->card, m->mixer, m->capture_vol_min, m->capture_vol_max); + LOG_WARN( + "%s,%s: indicated minimum capture volume is greater than the " + "maximum: %ld > %ld", + m->card, m->mixer, m->capture_vol_min, m->capture_vol_max); m->capture_vol_min = m->capture_vol_max; } } - if (snd_mixer_selem_get_capture_dB_range(elem, &m->capture_db_min, &m->capture_db_max) < 0) { - LOG_WARN("%s,%s: failed to get capture dB range, " - "will use raw volume values instead", - m->card, m->mixer); - 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; @@ -371,7 +299,7 @@ run_while_online(struct module *mod) struct channel chan = { .id = i, .type = is_playback ? CHANNEL_PLAYBACK : CHANNEL_CAPTURE, - .name = strdup(snd_mixer_selem_channel_name(i)), + .name = strdup(snd_mixer_selem_channel_name( i)), }; tll_push_back(m->channels, chan); } @@ -384,13 +312,13 @@ run_while_online(struct module *mod) char channels_str[1024]; int channels_idx = 0; - tll_foreach(m->channels, it) - { + tll_foreach(m->channels, it) { const struct channel *chan = &it->item; - channels_idx += snprintf(&channels_str[channels_idx], sizeof(channels_str) - channels_idx, - channels_idx == 0 ? "%s (%s)" : ", %s (%s)", chan->name, - chan->type == CHANNEL_PLAYBACK ? "🔊" : "🎤"); + channels_idx += snprintf( + &channels_str[channels_idx], sizeof(channels_str) - channels_idx, + channels_idx == 0 ? "%s (%s)" : ", %s (%s)", + chan->name, chan->type == CHANNEL_PLAYBACK ? "🔊" : "🎤"); assert(channels_idx <= sizeof(channels_str)); } @@ -400,8 +328,7 @@ run_while_online(struct module *mod) bool volume_channel_is_valid = m->volume_name == NULL; bool muted_channel_is_valid = m->muted_name == NULL; - tll_foreach(m->channels, it) - { + tll_foreach(m->channels, it) { const struct channel *chan = &it->item; if (m->volume_name != NULL && strcmp(chan->name, m->volume_name) == 0) { m->volume_chan = chan; @@ -434,14 +361,15 @@ run_while_online(struct module *mod) update_state(mod, elem); LOG_INFO( - "%s,%s: %s range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", m->card, m->mixer, - 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); + "%s,%s: volume range=%ld-%ld, current=%ld%s (sources: volume=%s, muted=%s)", + m->card, m->mixer, + m->volume_chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_min : m->capture_vol_min, + m->volume_chan->type == CHANNEL_PLAYBACK + ? m->playback_vol_max : m->capture_vol_max, + m->volume_chan->vol_cur, + m->muted_chan->muted ? " (muted)" : "", + m->volume_chan->name, m->muted_chan->name); mod->bar->refresh(mod->bar); @@ -555,7 +483,8 @@ run(struct module *mod) bool have_create_event = false; while (!have_create_event) { - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, {.fd = ifd, .events = POLLIN}}; + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, + {.fd = ifd, .events = POLLIN}}; int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (r < 0) { @@ -597,7 +526,7 @@ run(struct module *mod) break; /* Consume inotify data */ - for (const char *ptr = buf; ptr < buf + len;) { + for (const char *ptr = buf; ptr < buf + len; ) { const struct inotify_event *e = (const struct inotify_event *)ptr; if (e->mask & IN_CREATE) { @@ -615,20 +544,23 @@ out: if (wd >= 0) inotify_rm_watch(ifd, wd); if (ifd >= 0) - close(ifd); + close (ifd); return ret; } static struct module * -alsa_new(const char *card, const char *mixer, const char *volume_channel_name, const char *muted_channel_name, +alsa_new(const char *card, const char *mixer, + const char *volume_channel_name, const char *muted_channel_name, struct particle *label) { struct private *priv = calloc(1, sizeof(*priv)); priv->label = label; priv->card = strdup(card); priv->mixer = strdup(mixer); - priv->volume_name = volume_channel_name != NULL ? strdup(volume_channel_name) : NULL; - priv->muted_name = muted_channel_name != NULL ? strdup(muted_channel_name) : NULL; + priv->volume_name = + volume_channel_name != NULL ? strdup(volume_channel_name) : NULL; + priv->muted_name = + muted_channel_name != NULL ? strdup(muted_channel_name) : NULL; struct module *mod = module_common_new(); mod->private = priv; @@ -648,9 +580,12 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *muted = yml_get_value(node, "muted"); const struct yml_node *content = yml_get_value(node, "content"); - return alsa_new(yml_value_as_string(card), yml_value_as_string(mixer), - volume != NULL ? yml_value_as_string(volume) : NULL, - muted != NULL ? yml_value_as_string(muted) : NULL, conf_to_particle(content, inherited)); + return alsa_new( + yml_value_as_string(card), + yml_value_as_string(mixer), + volume != NULL ? yml_value_as_string(volume) : NULL, + muted != NULL ? yml_value_as_string(muted) : NULL, + conf_to_particle(content, inherited)); } static bool diff --git a/modules/backlight.c b/modules/backlight.c index 1495c5c..e7bca2e 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; @@ -41,7 +39,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "backlight"; } @@ -112,13 +110,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 +124,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 +134,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 +144,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 +178,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 +207,7 @@ run(struct module *mod) udev_unref(udev); close(current_fd); - return ret; + return 0; } static struct module * @@ -244,7 +232,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..4a55141 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_NOTCHARGING, 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) { @@ -130,10 +58,10 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { static char desc[32]; - const struct private *m = mod->private; + struct private *m = mod->private; snprintf(desc, sizeof(desc), "bat(%s)", m->battery); return desc; } @@ -145,22 +73,20 @@ content(struct module *mod) mtx_lock(&mod->lock); - assert(m->state == STATE_FULL || m->state == STATE_NOTCHARGING || m->state == STATE_CHARGING - || m->state == STATE_DISCHARGING || m->state == STATE_UNKNOWN); + assert(m->state == STATE_FULL || + m->state == STATE_NOTCHARGING || + m->state == STATE_CHARGING || + m->state == STATE_DISCHARGING); 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) @@ -172,14 +98,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) 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; @@ -219,18 +146,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 +168,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 +185,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 +200,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 +238,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 +251,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 +292,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 +306,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 +321,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 +333,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 +350,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,7 +357,7 @@ 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) @@ -446,32 +367,24 @@ update_status(struct module *mod) else if (strcmp(status, "Discharging") == 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 +398,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 +422,31 @@ 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)) + bar->refresh(bar); } out: @@ -593,17 +458,13 @@ 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; @@ -620,29 +481,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 +493,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..b0db44e 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 @@ -39,7 +36,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "clock"; } @@ -49,7 +46,7 @@ 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 +55,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, }; @@ -68,6 +66,7 @@ content(struct module *mod) return exposable; } +#include static int run(struct module *mod) { @@ -75,8 +74,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 +87,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 +115,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,7 +160,8 @@ 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; @@ -177,11 +178,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 +191,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 index ccd6d5b..ef45b3b 100644 --- a/modules/foreign-toplevel.c +++ b/modules/foreign-toplevel.c @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #include @@ -11,8 +11,8 @@ #define LOG_MODULE "foreign-toplevel" #define LOG_ENABLE_DBG 0 #include "../log.h" -#include "../particles/dynlist.h" #include "../plugin.h" +#include "../particles/dynlist.h" #include "wlr-foreign-toplevel-management-unstable-v1.h" #include "xdg-output-unstable-v1.h" @@ -46,8 +46,7 @@ struct toplevel { tll(const struct output *) outputs; }; -struct private -{ +struct private { struct particle *template; uint32_t manager_wl_name; struct zwlr_foreign_toplevel_manager_v1 *manager; @@ -93,7 +92,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "toplevel"; } @@ -111,8 +110,7 @@ content(struct module *mod) const char *current_output = mod->bar->output_name(mod->bar); - tll_foreach(m->toplevels, it) - { + tll_foreach(m->toplevels, it) { const struct toplevel *top = &it->item; bool show = false; @@ -120,10 +118,11 @@ content(struct module *mod) if (m->all_monitors) show = true; else if (current_output != NULL) { - tll_foreach(top->outputs, it2) - { + tll_foreach(top->outputs, it2) { const struct output *output = it2->item; - if (output->name != NULL && strcmp(output->name, current_output) == 0) { + if (output->name != NULL && + strcmp(output->name, current_output) == 0) + { show = true; break; } @@ -159,18 +158,22 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", + iface, wanted, version); return false; } static void -xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) +xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, + int32_t x, int32_t y) { } static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, + int32_t width, int32_t height) { } @@ -180,7 +183,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->mod; @@ -194,7 +198,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) { } @@ -233,7 +238,8 @@ app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *a } static void -output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *wl_output) +output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_output *wl_output) { struct toplevel *top = data; struct module *mod = top->mod; @@ -242,8 +248,7 @@ output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct mtx_lock(&mod->lock); const struct output *output = NULL; - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -255,8 +260,7 @@ output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct goto out; } - tll_foreach(top->outputs, it) - { + tll_foreach(top->outputs, it) { if (it->item == output) { LOG_ERR("output-enter event on output we're already on"); goto out; @@ -271,7 +275,8 @@ out: } static void -output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *wl_output) +output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_output *wl_output) { struct toplevel *top = data; struct module *mod = top->mod; @@ -280,8 +285,7 @@ output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct mtx_lock(&mod->lock); const struct output *output = NULL; - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -294,10 +298,10 @@ output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct } bool output_removed = false; - tll_foreach(top->outputs, it) - { + tll_foreach(top->outputs, it) { if (it->item == output) { - LOG_DBG("unmapped: %s:%s from %s", top->app_id, top->title, output->name); + LOG_DBG("unmapped: %s:%s from %s", + top->app_id, top->title, output->name); tll_remove(top->outputs, it); output_removed = true; break; @@ -314,7 +318,8 @@ out: } static void -state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *states) +state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, + struct wl_array *states) { struct toplevel *top = data; @@ -324,21 +329,12 @@ state(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_arra bool fullscreen = false; enum zwlr_foreign_toplevel_handle_v1_state *state; - wl_array_for_each(state, states) - { + wl_array_for_each(state, states) { switch (*state) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: - maximized = true; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: - minimized = true; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: - activated = true; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: - fullscreen = true; - break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: maximized = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: minimized = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: activated = true; break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: fullscreen = true; break; } } @@ -368,8 +364,7 @@ closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) struct private *m = mod->private; mtx_lock(&mod->lock); - tll_foreach(m->toplevels, it) - { + tll_foreach(m->toplevels, it) { if (it->item.handle == handle) { toplevel_free(top); tll_remove(m->toplevels, it); @@ -377,13 +372,12 @@ closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) } } mtx_unlock(&mod->lock); - - const struct bar *bar = mod->bar; - bar->refresh(bar); } static void -parent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct zwlr_foreign_toplevel_handle_v1 *parent) +parent(void *data, + struct zwlr_foreign_toplevel_handle_v1 *handle, + struct zwlr_foreign_toplevel_handle_v1 *parent) { } @@ -399,7 +393,9 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_listener = }; static void -toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zwlr_foreign_toplevel_handle_v1 *handle) +toplevel(void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager, + struct zwlr_foreign_toplevel_handle_v1 *handle) { struct module *mod = data; struct private *m = mod->private; @@ -413,13 +409,15 @@ toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zw { tll_push_back(m->toplevels, toplevel); - zwlr_foreign_toplevel_handle_v1_add_listener(handle, &toplevel_listener, &tll_back(m->toplevels)); + zwlr_foreign_toplevel_handle_v1_add_listener( + handle, &toplevel_listener, &tll_back(m->toplevels)); } mtx_unlock(&mod->lock); } static void -finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager) +finished(void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager) { struct module *mod = data; struct private *m = mod->private; @@ -444,12 +442,15 @@ output_xdg_output(struct output *output) if (output->xdg_output != NULL) return; - output->xdg_output = zxdg_output_manager_v1_get_xdg_output(m->xdg_output_manager, output->wl_output); - zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + m->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener( + output->xdg_output, &xdg_output_listener, output); } static void -handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { struct module *mod = data; struct private *m = mod->private; @@ -469,7 +470,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha struct output output = { .mod = mod, .wl_name = name, - .wl_output = wl_registry_bind(registry, name, &wl_output_interface, required), + .wl_output = wl_registry_bind( + registry, name, &wl_output_interface, required), }; mtx_lock(&mod->lock); @@ -483,10 +485,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(&mod->lock); - tll_foreach(m->outputs, it) output_xdg_output(&it->item); + tll_foreach(m->outputs, it) + output_xdg_output(&it->item); mtx_unlock(&mod->lock); } } @@ -499,19 +503,16 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) mtx_lock(&mod->lock); - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { const struct output *output = &it->item; if (output->wl_name == name) { /* Loop all toplevels */ - tll_foreach(m->toplevels, it2) - { + tll_foreach(m->toplevels, it2) { /* And remove this output from their list of tracked * outputs */ - tll_foreach(it2->item.outputs, it3) - { + tll_foreach(it2->item.outputs, it3) { if (it3->item == output) { tll_remove(it2->item.outputs, it3); break; @@ -547,8 +548,9 @@ run(struct module *mod) goto out; } - if ((registry = wl_display_get_registry(display)) == NULL - || wl_registry_add_listener(registry, ®istry_listener, mod) != 0) { + if ((registry = wl_display_get_registry(display)) == NULL || + wl_registry_add_listener(registry, ®istry_listener, mod) != 0) + { LOG_ERR("failed to get Wayland registry"); goto out; } @@ -556,14 +558,18 @@ run(struct module *mod) wl_display_roundtrip(display); if (m->manager_wl_name == 0) { - LOG_ERR("compositor does not implement the foreign-toplevel-manager interface"); + LOG_ERR( + "compositor does not implement the foreign-toplevel-manager interface"); goto out; } - m->manager = wl_registry_bind(registry, m->manager_wl_name, &zwlr_foreign_toplevel_manager_v1_interface, - required_manager_interface_version); + m->manager = wl_registry_bind( + registry, m->manager_wl_name, + &zwlr_foreign_toplevel_manager_v1_interface, + required_manager_interface_version); - zwlr_foreign_toplevel_manager_v1_add_listener(m->manager, &manager_listener, mod); + zwlr_foreign_toplevel_manager_v1_add_listener( + m->manager, &manager_listener, mod); while (true) { wl_display_flush(display); @@ -597,14 +603,12 @@ run(struct module *mod) } out: - tll_foreach(m->toplevels, it) - { + tll_foreach(m->toplevels, it) { toplevel_free(&it->item); tll_remove(m->toplevels, it); } - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { output_free(&it->item); tll_remove(m->outputs, it); } @@ -642,7 +646,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 *all_monitors = yml_get_value(node, "all-monitors"); - return ftop_new(conf_to_particle(c, inherited), all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); + return ftop_new( + conf_to_particle(c, inherited), + all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); } static bool diff --git a/modules/i3-common.c b/modules/i3-common.c index 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..5e5c80a 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,7 +29,6 @@ struct ws_content { }; struct workspace { - int id; char *name; int name_as_int; /* -1 if name is not a decimal number */ bool persistent; @@ -38,7 +37,6 @@ struct workspace { bool visible; bool focused; bool urgent; - bool empty; struct { unsigned id; @@ -48,8 +46,7 @@ struct workspace { } window; }; -struct private -{ +struct private { int left_spacing; int right_spacing; @@ -62,7 +59,6 @@ struct private size_t count; } ws_content; - bool strip_workspace_numbers; enum sort_mode sort_mode; tll(struct workspace) workspaces; @@ -74,22 +70,6 @@ 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; @@ -105,18 +85,14 @@ 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,52 +101,33 @@ workspace_from_json(const struct json_object *json, struct workspace *ws) const char *name_as_string = json_object_get_string(name); - const size_t node_count = focus != NULL ? json_object_array_length(focus) : 0; - - const bool is_empty = node_count == 0; - int name_as_int = workspace_name_as_int(name_as_string); - - *ws = (struct workspace){ - .id = json_object_get_int(id), + *ws = (struct workspace) { .name = strdup(name_as_string), - .name_as_int = name_as_int, + .name_as_int = workspace_name_as_int(name_as_string), .persistent = false, .output = strdup(json_object_get_string(output)), .visible = json_object_get_boolean(visible), .focused = json_object_get_boolean(focused), .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->name); ws->name = NULL; + free(ws->output); ws->output = NULL; + free(ws->window.title); ws->window.title = NULL; + free(ws->window.application); ws->window.application = NULL; } static void workspaces_free(struct private *m, bool free_persistent) { - tll_foreach(m->workspaces, it) - { + tll_foreach(m->workspaces, it) { if (free_persistent || !it->item.persistent) { workspace_free(&it->item); tll_remove(m->workspaces, it); @@ -178,6 +135,7 @@ workspaces_free(struct private *m, bool free_persistent) } } + static void workspace_add(struct private *m, struct workspace ws) { @@ -186,26 +144,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 +155,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 +169,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 +191,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 +206,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 +217,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 +230,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)) { @@ -323,37 +249,12 @@ 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)) + struct json_object *name; + if (!json_object_object_get_ex(ws_json, "name", &name)) 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; - } - } - } - } + const char *name_as_string = json_object_get_string(name); + struct workspace *already_exists = workspace_lookup(m, name_as_string); if (already_exists != NULL) { bool persistent = already_exists->persistent; @@ -375,7 +276,7 @@ workspace_update_or_add(struct private *m, const struct json_object *ws_json) } 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; @@ -402,7 +303,7 @@ err: } 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,20 +319,23 @@ 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); @@ -441,34 +345,36 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void } else if (is_empty) { - struct workspace *ws = workspace_lookup(m, current_id); + struct workspace *ws = workspace_lookup(m, current_name); assert(ws != NULL); if (!ws->persistent) - workspace_del(m, current_id); + workspace_del(m, current_name); else { - workspace_free_persistent(ws); - ws->empty = true; + workspace_free(ws); + ws->name = strdup(current_name); + assert(ws->persistent); } } 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) ws->visible = false; @@ -479,67 +385,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 +399,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 +413,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 +435,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 +446,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 +457,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 +494,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 +539,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 +581,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; @@ -752,19 +598,10 @@ run(struct module *mod) for (size_t i = 0; i < m->persistent_count; i++) { const char *name_as_string = m->persistent_workspaces[i]; - int name_as_int = workspace_name_as_int(name_as_string); - if (m->strip_workspace_numbers) { - const char *colon = strchr(name_as_string, ':'); - if (colon != NULL) - name_as_string = colon++; - } - struct workspace ws = { - .id = -1, .name = strdup(name_as_string), - .name_as_int = name_as_int, + .name_as_int = workspace_name_as_int(name_as_string), .persistent = true, - .empty = true, }; workspace_add(m, ws); } @@ -824,7 +661,7 @@ ws_content_for_name(struct private *m, const char *name) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "i3/sway"; } @@ -840,47 +677,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 +708,7 @@ content(struct module *mod) tag_new_string(mod, "mode", m->mode), }, - .count = 10, + .count = 8, }; if (ws->focused) { @@ -898,9 +718,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 +733,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 +744,10 @@ 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, + size_t persistent_count, + const char *persistent_workspaces[static persistent_count]) { struct private *m = calloc(1, sizeof(*m)); @@ -938,11 +763,11 @@ 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])); + 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]); @@ -965,26 +790,28 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *right_spacing = yml_get_value(node, "right-spacing"); const struct yml_node *sort = yml_get_value(node, "sort"); const struct yml_node *persistent = yml_get_value(node, "persistent"); - const struct yml_node *strip_workspace_number = yml_get_value(node, "strip-workspace-numbers"); - int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; - int right = spacing != NULL ? yml_value_as_int(spacing) - : right_spacing != NULL ? yml_value_as_int(right_spacing) - : 0; + int left = spacing != NULL ? yml_value_as_int(spacing) : + left_spacing != NULL ? yml_value_as_int(left_spacing) : 0; + int right = spacing != NULL ? yml_value_as_int(spacing) : + right_spacing != NULL ? yml_value_as_int(right_spacing) : 0; const char *sort_value = sort != NULL ? yml_value_as_string(sort) : NULL; - enum sort_mode sort_mode = sort_value == NULL ? SORT_NONE - : strcmp(sort_value, "none") == 0 ? SORT_NONE - : strcmp(sort_value, "native") == 0 ? SORT_NATIVE - : strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING - : SORT_DESCENDING; + enum sort_mode sort_mode = + sort_value == NULL ? SORT_NONE : + strcmp(sort_value, "none") == 0 ? SORT_NONE : + strcmp(sort_value, "ascending") == 0 ? SORT_ASCENDING : SORT_DESCENDING; - const size_t persistent_count = persistent != NULL ? yml_list_length(persistent) : 0; + const size_t persistent_count = + persistent != NULL ? yml_list_length(persistent) : 0; const char *persistent_workspaces[persistent_count]; if (persistent != NULL) { size_t idx = 0; - for (struct yml_list_iter it = yml_list_iter(persistent); it.node != NULL; yml_list_next(&it), idx++) { + for (struct yml_list_iter it = yml_list_iter(persistent); + it.node != NULL; + yml_list_next(&it), idx++) + { persistent_workspaces[idx] = yml_value_as_string(it.node); } } @@ -992,27 +819,36 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) struct i3_workspaces workspaces[yml_dict_length(c)]; size_t idx = 0; - for (struct yml_dict_iter it = yml_dict_iter(c); it.key != NULL; yml_dict_next(&it), idx++) { + for (struct yml_dict_iter it = yml_dict_iter(c); + it.key != NULL; + yml_dict_next(&it), idx++) + { workspaces[idx].name = yml_value_as_string(it.key); workspaces[idx].content = conf_to_particle(it.value, inherited); } - return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, persistent_count, persistent_workspaces, - (strip_workspace_number != NULL ? yml_value_as_bool(strip_workspace_number) : false)); + return i3_new(workspaces, yml_dict_length(c), left, right, sort_mode, + persistent_count, persistent_workspaces); } 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,7 +864,8 @@ verify_content(keychain_t *chain, const struct yml_node *node) static bool verify_sort(keychain_t *chain, const struct yml_node *node) { - return conf_verify_enum(chain, node, (const char *[]){"none", "native", "ascending", "descending"}, 4); + return conf_verify_enum( + chain, node, (const char *[]){"none", "ascending", "descending"}, 3); } static bool @@ -1041,12 +878,11 @@ 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 +897,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..01fce76 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) @@ -20,7 +22,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "label"; } 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..dbaa7a7 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,10 +49,10 @@ 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 + mod_data += { + 'river': [[wl_proto_src + wl_proto_headers + river_proto_src + river_proto_headers], [dynlist]], + } -if plugin_foreign_toplevel_enabled ftop_proto_headers = [] ftop_proto_src = [] @@ -193,7 +71,9 @@ if plugin_foreign_toplevel_enabled command: [wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@']) endforeach - mod_data += {'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [m, dynlist, wayland_client]]} + mod_data += { + 'foreign-toplevel': [[wl_proto_src + wl_proto_headers + ftop_proto_headers + ftop_proto_src], [dynlist]], + } endif foreach mod, data : mod_data diff --git a/modules/mpd.c b/modules/mpd.c index e70e41f..bebd401 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); @@ -86,7 +84,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "mpd"; } @@ -132,11 +130,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 +152,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 +169,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 +223,7 @@ wait_for_socket_create(const struct module *mod) struct stat st; if (stat(m->host, &st) == 0 && S_ISSOCK(st.st_mode)) { - int s = socket(AF_UNIX, SOCK_STREAM | 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 +234,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 +246,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 +263,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 +273,7 @@ wait_for_socket_create(const struct module *mod) break; } - ptr += sizeof(*e) + e->len; + ptr += sizeof(*e) + e->len; } } @@ -308,7 +296,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 +315,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 +328,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 +338,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 +377,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 +387,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 +414,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 +441,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 +459,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 +477,7 @@ run(struct module *mod) m->conn = NULL; } - return aborted ? 0 : ret; + return 0; } struct refresh_context { @@ -578,7 +532,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 +574,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; } @@ -649,8 +605,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 +616,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"host", true, &conf_verify_string}, - {"port", false, &conf_verify_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..bf32c57 100644 --- a/modules/network.c +++ b/modules/network.c @@ -1,47 +1,35 @@ -#include -#include -#include #include #include +#include #include #include +#include #include +#include +#include #include #include -#include - -#include -#include #include -#include #include -#include #include -#include +#include #include +#include #include #define LOG_MODULE "network" -#define LOG_ENABLE_DBG 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; -}; +#define UNUSED __attribute__((unused)) struct af_addr { int family; @@ -51,17 +39,26 @@ struct af_addr { } addr; }; -struct iface { - char *name; - char *type; /* ARPHRD_NNN */ - char *kind; /* IFLA_LINKINFO::IFLA_INFO_KIND */ +struct private { + char *iface; + struct particle *label; + int poll_interval; - uint32_t get_stats_seq_nr; + int genl_sock; + int rt_sock; - int index; + struct { + uint16_t family_id; + uint32_t get_interface_seq_nr; + uint32_t get_station_seq_nr; + } nl80211; + + bool get_addresses; + + int ifindex; uint8_t mac[6]; bool carrier; - uint8_t state; /* IFLA_OPERSTATE */ + uint8_t state; /* IFLA_OPERSTATE */ /* IPv4 and IPv6 addresses */ tll(struct af_addr) addrs; @@ -71,47 +68,8 @@ struct iface { 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 particle *label; - int poll_interval; - - int left_spacing; - int right_spacing; - - bool get_addresses; - - int genl_sock; - int rt_sock; - int urandom_fd; - - struct { - uint16_t family_id; - uint32_t get_interface_seq_nr; - uint32_t get_scan_seq_nr; - } nl80211; - - tll(struct iface) ifaces; -}; - -static void -free_iface(struct iface iface) -{ - tll_free(iface.addrs); - free(iface.ssid); - free(iface.kind); - free(iface.type); - free(iface.name); -} - static void destroy(struct module *mod) { @@ -121,22 +79,22 @@ destroy(struct module *mod) 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->ssid); + free(m->iface); free(m); + module_default_destroy(mod); } static const char * -description(const struct module *mod) +description(struct module *mod) { - return "network"; + static char desc[32]; + struct private *m = mod->private; + + snprintf(desc, sizeof(desc), "net(%s)", m->iface); + return desc; } static struct exposable * @@ -146,97 +104,56 @@ 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), + tag_new_string(mod, "ssid", m->ssid), + tag_new_int(mod, "signal", m->signal_strength_dbm), + tag_new_int(mod, "rx-bitrate", m->rx_bitrate / 1000 / 1000), + tag_new_int(mod, "tx-bitrate", m->tx_bitrate / 1000 / 1000), + }, + .count = 11, + }; + 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 */ @@ -250,7 +167,7 @@ nl_pid_value(void) static int netlink_connect_rt(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; @@ -274,14 +191,15 @@ netlink_connect_rt(void) static int netlink_connect_genl(void) { - int sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_GENERIC); + int sock = socket(AF_NETLINK, SOCK_RAW, 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(), + .nl_family = AF_NETLINK, + .nl_pid = nl_pid_value(), /* no multicast notifications by default, will be added later */ }; @@ -297,8 +215,10 @@ netlink_connect_genl(void) static bool send_nlmsg(int sock, const void *nlmsg, size_t len) { - int r = sendto(sock, nlmsg, len, 0, (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, - sizeof(struct sockaddr_nl)); + int r = sendto( + sock, nlmsg, len, 0, + (struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK}, + sizeof(struct sockaddr_nl)); return r == len; } @@ -324,56 +244,14 @@ send_rt_request(struct private *m, int request) }; if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink RT request (%d)", request); + LOG_ERRNO("%s: failed to send netlink RT request (%d)", + m->iface, request); return false; } return true; } -static bool -send_rt_getstats_request(struct private *m, struct iface *iface) -{ - if (iface->get_stats_seq_nr > 0) { - LOG_DBG("%s: RT get-stats request already in progress", iface->name); - return true; - } - - LOG_DBG("%s: sending RT get-stats request", iface->name); - - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); - return false; - } - - struct { - struct nlmsghdr hdr; - struct if_stats_msg rt; - } 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) { @@ -412,11 +290,14 @@ send_ctrl_get_family_request(struct private *m) }, }; - _Static_assert(sizeof(req.msg.family_name_attr) == NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)), - ""); + _Static_assert( + sizeof(req.msg.family_name_attr) == + NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)), + ""); if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink ctrl-get-family request"); + LOG_ERRNO("%s: failed to send netlink ctrl-get-family request", + m->iface); return false; } @@ -424,70 +305,9 @@ send_ctrl_get_family_request(struct private *m) } static bool -send_nl80211_request(struct private *m, uint8_t cmd, uint32_t seq) +send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, 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) + if (m->ifindex < 0) return false; const struct { @@ -503,14 +323,14 @@ send_nl80211_get_station(struct private *m, struct iface *iface) .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_flags = flags, + .nlmsg_seq = seq, .nlmsg_pid = nl_pid_value(), }, .msg = { .genl = { - .cmd = NL80211_CMD_GET_STATION, + .cmd = cmd, .version = 1, }, @@ -520,13 +340,14 @@ send_nl80211_get_station(struct private *m, struct iface *iface) .nla_len = sizeof(req.msg.ifindex), }, - .index = iface->index, + .index = m->ifindex, }, }, }; if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) { - LOG_ERRNO("failed to send netlink nl80211 get-inteface request"); + LOG_ERRNO("%s: failed to send netlink nl80211 get-inteface request", + m->iface); return false; } @@ -534,197 +355,131 @@ send_nl80211_get_station(struct private *m, struct iface *iface) } static bool -send_nl80211_get_scan(struct private *m) +send_nl80211_get_interface(struct private *m) { - if (m->nl80211.get_scan_seq_nr > 0) { - LOG_ERR("nl80211 get-scan request already in progress"); + if (m->nl80211.get_interface_seq_nr > 0) { + LOG_DBG( + "%s: nl80211 get-interface request already in progress", m->iface); return true; } - uint32_t seq; - if (read(m->urandom_fd, &seq, sizeof(seq)) != sizeof(seq)) { - LOG_ERRNO("failed to read from /dev/urandom"); + LOG_DBG("%s: sending nl80211 get-interface request", m->iface); + + uint32_t seq = time(NULL); + if (send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, NLM_F_REQUEST, seq)) { + m->nl80211.get_interface_seq_nr = seq; + return true; + } else return false; +} + +static bool +send_nl80211_get_station(struct private *m) +{ + if (m->nl80211.get_station_seq_nr > 0) { + LOG_DBG( + "%s: nl80211 get-station request already in progress", m->iface); + return true; } - LOG_DBG("sending nl80211 get-scan request %d", seq); + LOG_DBG("%s: sending nl80211 get-station request", m->iface); - if (!send_nl80211_request(m, NL80211_CMD_GET_SCAN, seq)) + uint32_t seq = time(NULL); + if (send_nl80211_request( + m, NL80211_CMD_GET_STATION, NLM_F_REQUEST | NLM_F_DUMP, seq)) + { + m->nl80211.get_station_seq_nr = seq; + return true; + } else return false; - - 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) +find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len) { - const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; - const uint8_t *end = (const uint8_t *)genl + len; + struct private *m = mod->private; - 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; + for (const struct rtattr *attr = IFLA_RTA(msg); + RTA_OK(attr, len); + attr = RTA_NEXT(attr, len)) + { + switch (attr->rta_type) { + case IFLA_IFNAME: + if (strcmp((const char *)RTA_DATA(attr), m->iface) == 0) { + LOG_INFO("%s: ifindex=%d", m->iface, msg->ifi_index); + + mtx_lock(&mod->lock); + m->ifindex = msg->ifi_index; + mtx_unlock(&mod->lock); + + send_nl80211_get_interface(m); + send_nl80211_get_station(m); + return true; + } - 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 +488,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 +538,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 +560,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,15 +574,64 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s mod->bar->refresh(mod->bar); } +static bool +foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len, + bool (*cb)(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len)) +{ + const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN; + const uint8_t *end = (const uint8_t *)genl + len; + + for (const struct nlattr *attr = (const struct nlattr *)raw; + raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) + { + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + bool nested = (attr->nla_type & NLA_F_NESTED) != 0;; + const void *payload = raw + NLA_HDRLEN; + + if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN)) + return false; + } + + return true; +} + +static bool +foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len, + bool (*cb)(struct module *mod, uint16_t type, + bool nested, const void *payload, size_t len, + void *ctx), + void *ctx) +{ + const uint8_t *raw = parent_payload; + const uint8_t *end = parent_payload + len; + + for (const struct nlattr *attr = (const struct nlattr *)raw; + raw < end; + raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) + { + uint16_t type = attr->nla_type & NLA_TYPE_MASK; + bool nested = (attr->nla_type & NLA_F_NESTED) != 0; + const void *payload = raw + NLA_HDRLEN; + + if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx)) + return false; + } + + return true; +} + struct mcast_group { uint32_t id; 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) +parse_mcast_group(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { + struct private *m = mod->private; struct mcast_group *ctx = _ctx; switch (type) { @@ -857,9 +646,8 @@ parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool n } default: - LOG_WARN("unrecognized GENL MCAST GRP attribute: " - "type=%hu, nested=%d, len=%zu", - type, nested, len); + LOG_WARN("%s: unrecognized GENL MCAST GRP attribute: " + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -867,13 +655,13 @@ parse_mcast_group(struct module *mod, struct iface *iface, uint16_t type, bool n } static bool -parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, - void *_ctx) +parse_mcast_groups(struct module *mod, 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); + foreach_nlattr_nested(mod, payload, len, &parse_mcast_group, &group); LOG_DBG("MCAST: %s -> %u", group.name, group.id); @@ -883,7 +671,9 @@ parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool * CONNECT/DISCONNECT events. */ - int r = setsockopt(m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group.id, sizeof(int)); + int r = setsockopt( + m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group.id, sizeof(int)); if (r < 0) LOG_ERRNO("failed to joint the nl80211 MLME mcast group"); @@ -893,8 +683,8 @@ parse_mcast_groups(struct module *mod, struct iface *iface, uint16_t type, bool } static bool -handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, size_t len, - void *_ctx) +handle_genl_ctrl(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) { struct private *m = mod->private; @@ -906,17 +696,16 @@ handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool ne } case CTRL_ATTR_FAMILY_NAME: - // LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); + //LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len); break; case CTRL_ATTR_MCAST_GROUPS: - foreach_nlattr_nested(mod, NULL, payload, len, &parse_mcast_groups, NULL); + foreach_nlattr_nested(mod, payload, len, &parse_mcast_groups, NULL); break; default: - LOG_DBG("unrecognized GENL CTRL attribute: " - "type=%hu, nested=%d, len=%zu", - type, nested, len); + LOG_DBG("%s: unrecognized GENL CTRL attribute: " + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -924,49 +713,43 @@ handle_genl_ctrl(struct module *mod, struct iface *iface, uint16_t type, bool ne } static bool -find_nl80211_iface(struct module *mod, struct iface *_iface, uint16_t type, bool nested, const void *payload, - size_t len, void *ctx) +check_for_nl80211_ifindex(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) { 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 *(uint32_t *)payload == m->ifindex; } 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) +nl80211_is_for_us(struct module *mod, const struct genlmsghdr *genl, + size_t msg_size) { + return foreach_nlattr(mod, genl, msg_size, &check_for_nl80211_ifindex); +} + +static bool +handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) +{ + struct private *m = mod->private; + switch (type) { case NL80211_ATTR_IFINDEX: - assert(*(uint32_t *)payload == iface->index); + assert(*(uint32_t *)payload == m->ifindex); 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); + free(m->ssid); + m->ssid = strndup(ssid, len); mtx_unlock(&mod->lock); mod->bar->refresh(mod->bar); @@ -975,8 +758,7 @@ handle_nl80211_new_interface(struct module *mod, struct iface *iface, uint16_t t default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -988,9 +770,10 @@ struct rate_info_ctx { }; static bool -handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) +handle_nl80211_rate_info(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { + struct private *m UNUSED = mod->private; struct rate_info_ctx *ctx = _ctx; switch (type) { @@ -1011,8 +794,7 @@ handle_nl80211_rate_info(struct module *mod, struct iface *iface, uint16_t type, default: LOG_DBG("%s: unrecognized nl80211 rate info attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -1024,9 +806,10 @@ struct station_info_ctx { }; static bool -handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) +handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len, void *_ctx) { + struct private *m = mod->private; struct station_info_ctx *ctx = _ctx; switch (type) { @@ -1034,21 +817,23 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty LOG_DBG("signal strength (last): %hhd dBm", *(uint8_t *)payload); break; - case NL80211_STA_INFO_SIGNAL_AVG: + case NL80211_STA_INFO_SIGNAL_AVG: { LOG_DBG("signal strength (average): %hhd dBm", *(uint8_t *)payload); mtx_lock(&mod->lock); - iface->signal_strength_dbm = *(int8_t *)payload; + m->signal_strength_dbm = *(int8_t *)payload; mtx_unlock(&mod->lock); ctx->update_bar = true; break; + } case NL80211_STA_INFO_TX_BITRATE: { struct rate_info_ctx rctx = {0}; - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); + foreach_nlattr_nested( + mod, payload, len, &handle_nl80211_rate_info, &rctx); LOG_DBG("TX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.); mtx_lock(&mod->lock); - iface->tx_bitrate = rctx.bitrate; + m->tx_bitrate = rctx.bitrate; mtx_unlock(&mod->lock); ctx->update_bar = true; break; @@ -1056,11 +841,12 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty case NL80211_STA_INFO_RX_BITRATE: { struct rate_info_ctx rctx = {0}; - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_rate_info, &rctx); + foreach_nlattr_nested( + mod, 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; + m->rx_bitrate = rctx.bitrate; mtx_unlock(&mod->lock); ctx->update_bar = true; break; @@ -1068,8 +854,7 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty default: LOG_DBG("%s: unrecognized nl80211 station info attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -1077,16 +862,16 @@ handle_nl80211_station_info(struct module *mod, struct iface *iface, uint16_t ty } static bool -handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload, - size_t len, void *_ctx) +handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested, + const void *payload, size_t len) { - switch (type) { - case NL80211_ATTR_IFINDEX: - break; + struct private *m UNUSED = mod->private; + switch (type) { case NL80211_ATTR_STA_INFO: { struct station_info_ctx ctx = {0}; - foreach_nlattr_nested(mod, iface, payload, len, &handle_nl80211_station_info, &ctx); + foreach_nlattr_nested( + mod, payload, len, &handle_nl80211_station_info, &ctx); if (ctx.update_bar) mod->bar->refresh(mod->bar); @@ -1095,101 +880,7 @@ handle_nl80211_new_station(struct module *mod, struct iface *iface, uint16_t typ default: LOG_DBG("%s: unrecognized nl80211 attribute: " - "type=%hu, nested=%d, len=%zu", - iface->name, type, nested, len); - 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); + "type=%hu, nested=%d, len=%zu", m->iface, type, nested, len); break; } @@ -1234,42 +925,6 @@ 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) { @@ -1277,12 +932,15 @@ parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) /* Process response */ for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { - switch (hdr->nlmsg_type) { case NLMSG_DONE: + if (m->ifindex == -1) { + LOG_ERR("%s: failed to find interface", m->iface); + return false; + } /* Request initial list of IPv4/6 addresses */ - if (m->get_addresses) { + if (m->get_addresses && m->ifindex != -1) { m->get_addresses = false; send_rt_request(m, RTM_GETADDR); } @@ -1305,20 +963,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(-err->error, "%s: netlink RT reply", m->iface); return false; } default: - LOG_WARN("unrecognized netlink message type: 0x%x", hdr->nlmsg_type); + LOG_WARN( + "%s: unrecognized netlink message type: 0x%x", + m->iface, hdr->nlmsg_type); return false; } } @@ -1330,49 +985,39 @@ 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) { + if (hdr->nlmsg_seq == m->nl80211.get_station_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); + m->nl80211.get_station_seq_nr = 0; } } + else if (hdr->nlmsg_type == GENL_ID_CTRL) { + const struct genlmsghdr *genl = NLMSG_DATA(hdr); + const size_t msg_size = NLMSG_PAYLOAD(hdr, 0); + foreach_nlattr(mod, genl, msg_size, &handle_genl_ctrl); + } + 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; + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: got interface information", m->iface); + foreach_nlattr( + mod, genl, msg_size, &handle_nl80211_new_interface); - 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); + LOG_INFO("%s: SSID: %s", m->iface, m->ssid); + } break; case NL80211_CMD_CONNECT: @@ -1386,41 +1031,37 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) * * Thus, we need to explicitly request an update. */ - LOG_DBG("connected, requesting interface information"); - send_nl80211_get_interface(m); + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: connected, requesting interface information", + m->iface); + send_nl80211_get_interface(m); + send_nl80211_get_station(m); + } break; case NL80211_CMD_DISCONNECT: - if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: disconnected, resetting SSID etc", m->iface); - 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); + mtx_lock(&mod->lock); + free(m->ssid); + m->ssid = NULL; + m->signal_strength_dbm = 0; + m->rx_bitrate = m->tx_bitrate = 0; + mtx_unlock(&mod->lock); + } break; case NL80211_CMD_NEW_STATION: - if (foreach_nlattr(mod, NULL, genl, msg_size, &find_nl80211_iface, &iface)) - continue; + if (nl80211_is_for_us(mod, genl, msg_size)) { + LOG_DBG("%s: got station information", m->iface); + foreach_nlattr(mod, genl, msg_size, &handle_nl80211_new_station); + } - LOG_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); + LOG_INFO("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s", + m->iface, m->signal_strength_dbm, + m->rx_bitrate / 1000 / 1000, + m->tx_bitrate / 1000 / 1000); break; default: @@ -1437,13 +1078,14 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len) ; /* iface is not an nl80211 device */ else if (nl_errno == ENOENT) ; /* iface down? */ - else { - LOG_ERRNO_P(nl_errno, "nl80211 reply (seq-nr: %u)", hdr->nlmsg_seq); - } + else + LOG_ERRNO_P(nl_errno, "%s: nl80211 reply", m->iface); } else { - LOG_WARN("unrecognized netlink message type: 0x%x", hdr->nlmsg_type); + LOG_WARN( + "%s: unrecognized netlink message type: 0x%x", + m->iface, hdr->nlmsg_type); return false; } } @@ -1461,20 +1103,17 @@ run(struct module *mod) 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"); + LOG_ERRNO("%s: failed to create poll timer FD", m->iface); goto out; } - 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}, + .it_value = {.tv_sec = m->poll_interval}, + .it_interval = {.tv_sec = m->poll_interval}, }; if (timerfd_settime(timer_fd, 0, &poll_time, NULL) < 0) { - LOG_ERRNO("failed to arm poll timer"); + LOG_ERRNO("%s: failed to arm poll timer", m->iface); goto out; } } @@ -1485,7 +1124,9 @@ run(struct module *mod) if (m->rt_sock < 0 || m->genl_sock < 0) goto out; - if (!send_rt_request(m, RTM_GETLINK) || !send_ctrl_get_family_request(m)) { + if (!send_rt_request(m, RTM_GETLINK) || + !send_ctrl_get_family_request(m)) + { goto out; } @@ -1503,13 +1144,15 @@ run(struct module *mod) if (fds[0].revents & (POLLIN | POLLHUP)) break; - if ((fds[1].revents & POLLHUP) || (fds[2].revents & POLLHUP)) { - LOG_ERR("disconnected from netlink socket"); + if ((fds[1].revents & POLLHUP) || + (fds[2].revents & POLLHUP)) + { + LOG_ERR("%s: disconnected from netlink socket", m->iface); break; } if (fds[3].revents & POLLHUP) { - LOG_ERR("disconnected from timer FD"); + LOG_ERR("%s: disconnected from timer FD", m->iface); break; } @@ -1552,17 +1195,13 @@ run(struct module *mod) break; } - tll_foreach(m->ifaces, it) - { - send_nl80211_get_station(m, &it->item); - send_rt_getstats_request(m, &it->item); - }; + send_nl80211_get_station(m); } } ret = 0; -out: + out: if (m->rt_sock >= 0) close(m->rt_sock); if (m->genl_sock >= 0) @@ -1574,25 +1213,19 @@ out: } 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 poll_interval) { - 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->get_addresses = true; + priv->ifindex = -1; + priv->state = IF_OPER_DOWN; struct module *mod = module_common_new(); mod->private = priv; @@ -1606,43 +1239,21 @@ network_new(struct particle *label, int poll_interval, int left_spacing, int rig 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), + poll != NULL ? yml_value_as_int(poll) : 0); } 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}, + {"poll-interval", false, &conf_verify_int}, 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..a676ceb 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,10 +16,10 @@ #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" @@ -36,8 +35,8 @@ struct partition { char *label; uint64_t size; - bool audio_cd; + /*tll(char *) mount_points;*/ mount_point_list_t mount_points; }; @@ -54,8 +53,7 @@ struct block_device { tll(struct partition) partitions; }; -struct private -{ +struct private { struct particle *label; int left_spacing; int right_spacing; @@ -76,7 +74,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 +90,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); @@ -100,7 +100,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "removables"; } @@ -112,23 +112,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)]; 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 +141,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 +155,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 +199,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 +220,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 +234,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 +252,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 +264,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 +287,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 +324,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 +335,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 +350,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 +386,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 +408,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 +467,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 +476,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 +504,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 +529,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 +545,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 +553,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 +571,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 +584,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; @@ -715,22 +617,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)]; 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 +649,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..f9854f2 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 #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 { @@ -32,9 +30,6 @@ struct output { uint32_t occupied; uint32_t focused; uint32_t urgent; - - /* Layout */ - char *layout; }; struct seat { @@ -44,21 +39,19 @@ 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; }; @@ -75,7 +68,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "river"; } @@ -89,22 +82,18 @@ content(struct module *mod) mtx_lock(&m->mod->lock); - if (!m->is_running) { - mtx_unlock(&m->mod->lock); - return dynlist_exposable_new(NULL, 0, 0, 0); - } - uint32_t urgent = 0; uint32_t occupied = 0; uint32_t output_focused = 0; uint32_t seat_focused = 0; - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { const struct output *output = &it->item; - if (!m->all_monitors && output_bar_is_on != NULL && output->name != NULL - && strcmp(output->name, output_bar_is_on) != 0) { + if (!m->all_monitors && + output_bar_is_on != NULL && output->name != NULL && + strcmp(output->name, output_bar_is_on) != 0) + { continue; } @@ -112,8 +101,7 @@ content(struct module *mod) urgent |= output->urgent; occupied |= output->occupied; - tll_foreach(m->seats, it2) - { + tll_foreach(m->seats, it2) { const struct seat *seat = &it2->item; if (seat->output == output) { seat_focused |= output->focused; @@ -121,7 +109,8 @@ content(struct module *mod) } } - const size_t seat_count = m->title != NULL ? tll_length(m->seats) : 0; + const size_t seat_count = m->title != NULL && !m->is_starting_up + ? tll_length(m->seats) : 0; struct exposable *tag_parts[32 + seat_count]; for (unsigned i = 0; i < 32; i++) { @@ -134,11 +123,14 @@ content(struct module *mod) bool is_urgent = urgent & (1u << i); bool is_occupied = occupied & (1u << i); - const char *state = is_urgent ? "urgent" : is_visible ? is_focused ? "focused" : "unfocused" : "invisible"; + const char *state = + is_urgent ? "urgent" : + is_visible ? is_focused ? "focused" : "unfocused" : + "invisible"; #if 0 LOG_DBG("tag: #%u, visible=%d, focused=%d, occupied=%d, state=%s", - i, is_visible, is_focused, is_occupied & (1u << i), state); + i, visible, focused, occupied & (1u << i), state); #endif struct tag_set tags = { @@ -157,21 +149,17 @@ content(struct module *mod) tag_set_destroy(&tags); } - if (m->title != NULL) { + if (m->title != NULL && !m->is_starting_up) { size_t i = 32; - tll_foreach(m->seats, it) - { + 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); @@ -189,21 +177,15 @@ verify_iface_version(const char *iface, uint32_t version, uint32_t wanted) if (version >= wanted) return true; - LOG_ERR("%s: need interface version %u, but compositor only implements %u", iface, wanted, version); + LOG_ERR("%s: need interface version %u, but compositor only implements %u", + iface, wanted, version); return false; } static void output_destroy(struct output *output) { - tll_foreach(output->m->seats, it) - { - 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) @@ -217,7 +199,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,7 +206,8 @@ 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; @@ -242,7 +224,8 @@ focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1 } static void -view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, struct wl_array *tags) +view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + struct wl_array *tags) { struct output *output = data; struct module *mod = output->m->mod; @@ -254,7 +237,9 @@ 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); } @@ -263,7 +248,8 @@ view_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, s } static void -urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, uint32_t tags) +urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, + uint32_t tags) { struct output *output = data; struct module *mod = output->m->mod; @@ -276,59 +262,23 @@ urgent_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, mod->bar->refresh(mod->bar); } -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) -static void -layout_name(void *data, struct zriver_output_status_v1 *zriver_output_status_v1, const char *name) -{ - struct output *output = data; - struct module *mod = output->m->mod; - - mtx_lock(&mod->lock); - { - free(output->layout); - output->layout = name != NULL ? strdup(name) : NULL; - } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); -} -#endif - -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) -static void -layout_name_clear(void *data, struct zriver_output_status_v1 *zriver_output_status_v1) -{ - struct output *output = data; - struct module *mod = output->m->mod; - - mtx_lock(&mod->lock); - { - free(output->layout); - output->layout = NULL; - } - mtx_unlock(&mod->lock); - mod->bar->refresh(mod->bar); -} -#endif static const struct zriver_output_status_v1_listener river_status_output_listener = { .focused_tags = &focused_tags, .view_tags = &view_tags, .urgent_tags = &urgent_tags, -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_SINCE_VERSION) - .layout_name = &layout_name, -#endif -#if defined(ZRIVER_OUTPUT_STATUS_V1_LAYOUT_NAME_CLEAR_SINCE_VERSION) - .layout_name_clear = &layout_name_clear, -#endif }; static void -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 +288,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 +304,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,46 +318,44 @@ 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) +focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + struct wl_output *wl_output) { struct seat *seat = data; struct private *m = seat->m; struct module *mod = m->mod; struct output *output = NULL; - tll_foreach(m->outputs, it) - { + tll_foreach(m->outputs, it) { if (it->item.wl_output == wl_output) { output = &it->item; break; @@ -426,7 +376,8 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, } static void -unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *wl_output) +unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, + struct wl_output *wl_output) { struct seat *seat = data; struct private *m = seat->m; @@ -435,8 +386,7 @@ 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; @@ -454,7 +404,8 @@ 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; @@ -469,9 +420,11 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, co const char *output_bar_is_on = mod->bar->output_name(mod->bar); - if (seat->m->all_monitors - || (output_bar_is_on != NULL && seat->output != NULL && seat->output->name != NULL - && strcmp(output_bar_is_on, seat->output->name) == 0)) { + if (seat->m->all_monitors || + (output_bar_is_on != NULL && + seat->output != NULL && seat->output->name != NULL && + strcmp(output_bar_is_on, seat->output->name) == 0)) + { mtx_lock(&mod->lock); { free(seat->title); @@ -482,37 +435,15 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, co } } -#if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) -static void -mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *name) -{ - struct seat *seat = data; - struct module *mod = seat->m->mod; - - mtx_lock(&mod->lock); - { - free(seat->mode); - seat->mode = strdup(name); - mtx_unlock(&mod->lock); - } - mod->bar->refresh(mod->bar); - - LOG_DBG("seat: %s, current mode: %s", seat->name, seat->mode); -} - -#endif - static const struct zriver_seat_status_v1_listener river_seat_status_listener = { .focused_output = &focused_output, .unfocused_output = &unfocused_output, .focused_view = &focused_view, -#if defined(ZRIVER_SEAT_STATUS_V1_MODE_SINCE_VERSION) - .mode = &mode, -#endif }; static void -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,28 +468,29 @@ 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; @@ -567,7 +499,8 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha 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; @@ -580,8 +513,7 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha 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); + instantiate_output(&tll_back(m->outputs)); mtx_unlock(&m->mod->lock); } @@ -590,10 +522,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 +536,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,7 +547,7 @@ 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); } @@ -621,11 +556,14 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const cha 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 +574,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 +583,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 +614,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 +628,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 +674,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,6 +689,9 @@ out: wl_registry_destroy(registry); if (display != NULL) wl_display_disconnect(display); + + if (unlock_at_exit) + mtx_unlock(&mod->lock); return ret; } @@ -749,6 +702,7 @@ river_new(struct particle *template, struct particle *title, bool all_monitors) m->template = template; m->title = title; m->all_monitors = all_monitors; + m->is_starting_up = true; struct module *mod = module_common_new(); mod->private = m; @@ -767,8 +721,10 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) const struct yml_node *title = yml_get_value(node, "title"); const struct yml_node *all_monitors = yml_get_value(node, "all-monitors"); - return river_new(conf_to_particle(c, inherited), title != NULL ? conf_to_particle(title, inherited) : NULL, - all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); + return river_new( + conf_to_particle(c, inherited), + title != NULL ? conf_to_particle(title, inherited) : NULL, + all_monitors != NULL ? yml_value_as_bool(all_monitors) : false); } static bool diff --git a/modules/script.c b/modules/script.c index 9f9b40a..f323635 100644 --- a/modules/script.c +++ b/modules/script.c @@ -1,31 +1,27 @@ -#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; @@ -63,10 +59,10 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { static char desc[32]; - const struct private *m = mod->private; + struct private *m = mod->private; char *path = strdup(m->path); snprintf(desc, sizeof(desc), "script(%s)", basename(path)); @@ -111,8 +107,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 +162,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 +183,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; } @@ -193,7 +196,9 @@ process_line(struct module *mod, const char *line, size_t len) long end = 0; for (size_t i = 0; i < end_len; i++) { if (!(_end[i] >= '0' && _end[i] <= '9')) { - LOG_ERR("tag range end is not an integer: %.*s", (int)end_len, _end); + LOG_ERR( + "tag range end is not an integer: %.*s", + (int)end_len, _end); goto bad_tag; } @@ -215,7 +220,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 +295,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,21 +312,21 @@ 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; } @@ -389,7 +395,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 +427,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 +443,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 +457,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: @@ -517,7 +538,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 +552,7 @@ execute_script(struct module *mod) /* Don't spinning */ thrd_yield(); - usleep(100000); /* 100ms */ + usleep(100000); /* 100ms */ } if (waited_pid == pid) { @@ -558,19 +581,16 @@ run(struct module *mod) break; if (m->aborted) break; - if (m->poll_interval <= 0) + if (m->poll_interval < 0) break; struct timeval now; - if (gettimeofday(&now, NULL) < 0) { + 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 poll_interval = {.tv_sec = m->poll_interval}; struct timeval timeout; timeradd(&now, &poll_interval, &timeout); @@ -613,10 +633,11 @@ run(struct module *mod) } static struct module * -script_new(char *path, size_t argc, const char *const argv[static argc], int poll_interval, struct particle *_content) +script_new(const char *path, size_t argc, const char *const argv[static argc], + int poll_interval, struct particle *_content) { struct private *m = calloc(1, sizeof(*m)); - m->path = path; + m->path = strdup(path); m->content = _content; m->argc = argc; m->argv = malloc(argc * sizeof(m->argv[0])); @@ -636,7 +657,7 @@ script_new(char *path, size_t argc, const char *const argv[static argc], int pol 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"); @@ -646,31 +667,18 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited) if (args != NULL) { size_t i = 0; - for (struct yml_list_iter iter = yml_list_iter(args); iter.node != NULL; yml_list_next(&iter), i++) { + for (struct yml_list_iter iter = yml_list_iter(args); + iter.node != NULL; + yml_list_next(&iter), i++) + { argv[i] = yml_value_as_string(iter.node); } } - 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, + poll_interval != NULL ? yml_value_as_int(poll_interval) : -1, + conf_to_particle(c, inherited)); } static bool @@ -680,12 +688,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 +702,13 @@ 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}, + {"poll-interval", false, &conf_verify_int}, MODULE_COMMON_ATTRS, }; diff --git a/modules/sway-xkb.c b/modules/sway-xkb.c index 1507241..f4e7577 100644 --- a/modules/sway-xkb.c +++ b/modules/sway-xkb.c @@ -3,15 +3,15 @@ #define LOG_MODULE "sway-xkb" #define LOG_ENABLE_DBG 0 +#include "../log.h" #include "../bar/bar.h" #include "../config-verify.h" #include "../config.h" -#include "../log.h" #include "../particles/dynlist.h" #include "../plugin.h" -#include "i3-common.h" #include "i3-ipc.h" +#include "i3-common.h" #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -21,8 +21,7 @@ struct input { char *layout; }; -struct private -{ +struct private { struct particle *template; int left_spacing; int right_spacing; @@ -56,7 +55,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "sway-xkb"; } @@ -68,7 +67,6 @@ content(struct module *mod) mtx_lock(&mod->lock); - assert(m->num_existing_inputs <= m->num_inputs); struct exposable *particles[max(m->num_existing_inputs, 1)]; for (size_t i = 0, j = 0; i < m->num_inputs; i++) { @@ -90,11 +88,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; @@ -121,11 +120,9 @@ handle_input_reply(int sock, int type, const struct json_object *json, void *_mo struct input *input = NULL; for (size_t i = 0; i < m->num_inputs; i++) { struct input *maybe_input = &m->inputs[i]; - if (strcmp(maybe_input->identifier, id) == 0 && !maybe_input->exists) { + if (strcmp(maybe_input->identifier, id) == 0) { input = maybe_input; - LOG_DBG("adding: %s", id); - mtx_lock(&mod->lock); input->exists = true; m->num_existing_inputs++; @@ -141,7 +138,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 +158,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; @@ -209,36 +207,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 +267,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 +295,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; @@ -340,32 +329,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 +374,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 +390,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..5c2c1f9 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; @@ -74,7 +73,7 @@ destroy(struct module *mod) } static const char * -description(const struct module *mod) +description(struct module *mod) { return "xkb"; } @@ -118,8 +117,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 +142,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 +159,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 +181,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 +209,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 +232,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 +249,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 +259,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 +279,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 +290,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 +312,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 +332,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 +353,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 +393,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 +418,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 +428,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 +456,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 +465,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 +483,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 +501,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 +602,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); diff --git a/modules/xwindow.c b/modules/xwindow.c index c730128..3c8655e 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; @@ -40,7 +37,7 @@ struct private }; static const char * -description(const struct module *mod) +description(struct module *mod) { return "xwindow"; } @@ -49,19 +46,23 @@ 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 +84,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 +103,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 +133,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 +162,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 +205,7 @@ update_title(struct module *mod) free(r1); free(r2); free(r3); -} + } static int run(struct module *mod) @@ -221,16 +225,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 +246,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 +266,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 +292,7 @@ run(struct module *mod) xcb_destroy_window(m->conn, m->monitor_win); xcb_disconnect(m->conn); - return ret; + return 0; } static struct exposable * diff --git a/particle.c b/particle.c index f35b5d1..468bd28 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) @@ -29,22 +28,23 @@ particle_default_destroy(struct particle *particle) } struct particle * -particle_common_new(int left_margin, int right_margin, char **on_click_templates, struct fcft_font *font, - enum font_shaping font_shaping, pixman_color_t foreground, struct deco *deco) +particle_common_new(int left_margin, int right_margin, + const char **on_click_templates, + struct fcft_font *font, pixman_color_t foreground, + struct deco *deco) { struct particle *p = calloc(1, sizeof(*p)); p->left_margin = left_margin; p->right_margin = right_margin; p->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]; + p->on_click_templates[i] = strdup(on_click_templates[i]); } } } @@ -61,11 +61,13 @@ exposable_default_destroy(struct exposable *exposable) } void -exposable_render_deco(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height) +exposable_render_deco(const struct exposable *exposable, + pixman_image_t *pix, int x, int y, int height) { const struct deco *deco = exposable->particle->deco; if (deco != NULL) deco->expose(deco, pix, x, y, exposable->width, height); + } static bool @@ -110,7 +112,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,7 +150,8 @@ err: } void -exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, +exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, + enum mouse_event event, enum mouse_button btn, int x, int y) { #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG @@ -158,16 +163,18 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mo [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]); + LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)", + exposable, event == ON_MOUSE_MOTION ? "motion" : "click", + button_name[btn], x, y, exposable->on_click[btn]); #endif /* If we have a handler, change cursor to a hand */ - const char *cursor - = (exposable->particle != NULL && exposable->particle->have_on_click_template) ? "hand2" : "left_ptr"; + const char *cursor = + (exposable->particle != NULL && + exposable->particle->have_on_click_template) + ? "hand2" + : "left_ptr"; bar->set_cursor(bar, cursor); /* If this is a mouse click, and we have a handler, execute it */ @@ -235,7 +242,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 +250,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 +267,19 @@ 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++) + if (i != pipe_fds[1]) + close(i); + execvp(argv[0], argv); fail: @@ -300,7 +317,10 @@ exposable_common_new(const struct particle *particle, const struct tag_set *tags exposable->particle = particle; if (particle != NULL && particle->have_on_click_template) { - tags_expand_templates(exposable->on_click, (const char **)particle->on_click_templates, MOUSE_BTN_COUNT, tags); + tags_expand_templates( + exposable->on_click, + (const char **)particle->on_click_templates, + MOUSE_BTN_COUNT, tags); } exposable->destroy = &exposable_default_destroy; exposable->on_mouse = &exposable_default_on_mouse; diff --git a/particle.h b/particle.h index bc8648d..4ca520f 100644 --- a/particle.h +++ b/particle.h @@ -5,7 +5,6 @@ #include "color.h" #include "decoration.h" -#include "font-shaping.h" #include "tag.h" enum mouse_event { @@ -20,8 +19,6 @@ enum mouse_button { MOUSE_BTN_RIGHT, MOUSE_BTN_WHEEL_UP, MOUSE_BTN_WHEEL_DOWN, - MOUSE_BTN_PREVIOUS, - MOUSE_BTN_NEXT, MOUSE_BTN_COUNT, }; @@ -38,13 +35,14 @@ struct particle { 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); }; + struct exposable { const struct particle *particle; void *private; @@ -54,31 +52,36 @@ struct exposable { void (*destroy)(struct exposable *exposable); int (*begin_expose)(struct exposable *exposable); - void (*expose)(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); + void (*expose)(const struct exposable *exposable, pixman_image_t *pix, + int x, int y, int height); - void (*on_mouse)(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, - int y); + void (*on_mouse)(struct exposable *exposable, struct bar *bar, + enum mouse_event event, enum mouse_button btn, int x, int y); }; -struct particle *particle_common_new(int left_margin, int right_margin, char *on_click_templates[], - struct fcft_font *font, enum font_shaping font_shaping, pixman_color_t foreground, - struct deco *deco); +struct particle *particle_common_new( + int left_margin, int right_margin, const char *on_click_templates[], + struct fcft_font *font, pixman_color_t foreground, struct deco *deco); void particle_default_destroy(struct particle *particle); -struct exposable *exposable_common_new(const struct particle *particle, const struct tag_set *tags); +struct exposable *exposable_common_new( + const struct particle *particle, const struct tag_set *tags); void exposable_default_destroy(struct exposable *exposable); -void exposable_render_deco(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); +void exposable_render_deco( + const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height); -void exposable_default_on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, - enum mouse_button btn, int x, int y); +void exposable_default_on_mouse( + struct exposable *exposable, struct bar *bar, + enum mouse_event event, enum mouse_button btn, int x, int y); /* List of attributes *all* particles implement */ -#define PARTICLE_COMMON_ATTRS \ - {"margin", false, &conf_verify_unsigned}, {"left-margin", false, &conf_verify_unsigned}, \ - {"right-margin", false, &conf_verify_unsigned}, {"on-click", false, &conf_verify_on_click}, \ - {"font", false, &conf_verify_font}, {"font-shaping", false, &conf_verify_font_shaping}, \ - {"foreground", false, &conf_verify_color}, {"deco", false, &conf_verify_decoration}, \ - { \ - NULL, false, NULL \ - } +#define PARTICLE_COMMON_ATTRS \ + {"margin", false, &conf_verify_int}, \ + {"left-margin", false, &conf_verify_int}, \ + {"right-margin", false, &conf_verify_int}, \ + {"on-click", false, &conf_verify_on_click}, \ + {"font", false, &conf_verify_font}, \ + {"foreground", false, &conf_verify_color}, \ + {"deco", false, &conf_verify_decoration}, \ + {NULL, false, NULL} diff --git a/particles/dynlist.c b/particles/dynlist.c index fcd0066..5b64dbe 100644 --- a/particles/dynlist.c +++ b/particles/dynlist.c @@ -1,14 +1,13 @@ #include "dynlist.h" -#include #include +#include #define LOG_MODULE "dynlist" #include "../log.h" #include "../particle.h" -struct private -{ +struct private { int left_spacing; int right_spacing; @@ -72,13 +71,13 @@ 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, enum mouse_button btn, int x, int y) { const struct private *e = exposable->private; @@ -87,16 +86,17 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e return; } - int px = /*p->left_margin;*/ 0; + int px = /*p->left_margin;*/0; for (size_t i = 0; i < e->count; i++) { if (x >= px && x < px + e->exposables[i]->width) { if (e->exposables[i]->on_mouse != NULL) { - e->exposables[i]->on_mouse(e->exposables[i], bar, event, btn, x - px, y); + e->exposables[i]->on_mouse( + e->exposables[i], bar, event, btn, x - px, y); } return; } - 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"); @@ -104,7 +104,8 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e } 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..5c0be16 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; } diff --git a/particles/list.c b/particles/list.c index 83b5d0c..19ff15b 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) { @@ -80,18 +80,21 @@ 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, enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -101,12 +104,13 @@ 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, btn, 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) */ @@ -152,8 +156,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 +183,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 +208,9 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"items", true, &conf_verify_particle_list_items}, - {"spacing", false, &conf_verify_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..980bdd1 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; @@ -288,16 +57,21 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int struct eprivate *e = exposable->private; exposable_render_deco(exposable, pix, x, y, height); - e->exposable->expose(e->exposable, pix, x + exposable->particle->left_margin, y, height); + e->exposable->expose( + e->exposable, pix, x + exposable->particle->left_margin, y, height); } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -318,12 +92,21 @@ 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; @@ -361,25 +144,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 +176,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 +201,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..ebc51c3 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; @@ -75,7 +74,8 @@ begin_expose(struct exposable *exposable) /* Margins */ if (have_at_least_one) { - exposable->width += exposable->particle->left_margin + exposable->particle->right_margin; + exposable->width += exposable->particle->left_margin + + exposable->particle->right_margin; } else assert(exposable->width == 0); @@ -97,7 +97,8 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; @@ -160,14 +161,18 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, e original[i] = exposable->on_click[i]; if (event == ON_MOUSE_CLICK) { - long where = clickable_width > 0 ? 100 * (x - x_offset) / clickable_width : 0; + long where = clickable_width > 0 + ? 100 * (x - x_offset) / clickable_width + : 0; struct tag_set tags = { .tags = (struct tag *[]){tag_new_int(NULL, "where", where)}, .count = 1, }; - tags_expand_templates(exposable->on_click, (const char **)exposable->on_click, MOUSE_BTN_COUNT, &tags); + tags_expand_templates( + exposable->on_click, (const char **)exposable->on_click, + MOUSE_BTN_COUNT, &tags); tag_set_destroy(&tags); } @@ -193,17 +198,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])); @@ -252,7 +259,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 +271,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 +304,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 +323,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"tag", true, &conf_verify_string}, - {"length", true, &conf_verify_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..1d4cb59 100644 --- a/particles/ramp.c +++ b/particles/ramp.c @@ -1,24 +1,19 @@ -#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.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; }; @@ -58,16 +53,21 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int struct eprivate *e = exposable->private; exposable_render_deco(exposable, pix, x, y, height); - e->exposable->expose(e->exposable, pix, x + exposable->particle->left_margin, y, height); + e->exposable->expose( + e->exposable, pix, x + exposable->particle->left_margin, y, height); } static void -on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, enum mouse_button btn, int x, int y) +on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event, + enum mouse_button btn, int x, int y) { const struct particle *p = exposable->particle; const struct eprivate *e = exposable->private; - if ((event == ON_MOUSE_MOTION && exposable->particle->have_on_click_template) || exposable->on_click[btn] != NULL) { + if ((event == ON_MOUSE_MOTION && + exposable->particle->have_on_click_template) || + exposable->on_click[btn] != NULL) + { /* We have our own handler */ exposable_default_on_mouse(exposable, bar, event, btn, x, y); return; @@ -110,26 +110,23 @@ 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); + LOG_WARN( + "tag's minimum value is greater than its maximum: " + "tag=\"%s\", min=%ld, max=%ld", p->tag, min, max); min = max; } if (value < min) { - LOG_WARN("tag's value is less than its minimum value: " - "tag=\"%s\", min=%ld, value=%ld", - p->tag, min, value); + LOG_WARN( + "tag's value is less than its minimum value: " + "tag=\"%s\", min=%ld, value=%ld", p->tag, min, value); value = min; } if (value > max) { - LOG_WARN("tag's value is greater than its maximum value: " - "tag=\"%s\", max=%ld, value=%ld", - p->tag, max, value); + LOG_WARN( + "tag's value is greater than its maximum value: " + "tag=\"%s\", max=%ld, value=%ld", p->tag, max, value); value = max; } @@ -164,18 +161,14 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } static struct particle * -ramp_new(struct particle *common, const char *tag, struct particle *particles[], size_t count, bool use_custom_min, - long min, bool use_custom_max, long max) +ramp_new(struct particle *common, const char *tag, + struct particle *particles[], size_t count) { 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 +184,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 +206,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..c475d5b 100644 --- a/particles/string.c +++ b/particles/string.c @@ -1,13 +1,12 @@ -#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" @@ -18,8 +17,7 @@ struct text_run_cache { bool in_use; }; -struct private -{ +struct private { char *text; size_t max_len; @@ -52,7 +50,9 @@ begin_expose(struct exposable *exposable) struct eprivate *e = exposable->private; struct private *p = exposable->particle->private; - exposable->width = exposable->particle->left_margin + exposable->particle->right_margin; + exposable->width = + exposable->particle->left_margin + + exposable->particle->right_margin; if (e->cache_idx >= 0) { exposable->width += p->cache[e->cache_idx].width; @@ -87,17 +87,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 +111,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); } @@ -144,7 +149,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags) struct eprivate *e = calloc(1, sizeof(*e)); struct fcft_font *font = particle->font; - char32_t *wtext = NULL; + wchar_t *wtext = NULL; char *text = tags_expand_template(p->text, tags); e->glyphs = e->allocated_glyphs = NULL; @@ -168,22 +173,41 @@ instantiate(const struct particle *particle, const struct tag_set *tags) } } - /* Not in cache - we need to rasterize it. First, convert to char32_t */ - wtext = ambstoc32(text); - size_t chars = wtext != NULL ? c32len(wtext) : 0; + /* Not in cache - we need to rasterize it. First, convert to wchar */ + size_t chars = mbstowcs(NULL, text, 0); + if (chars == (size_t)-1) + goto done; + + wtext = malloc((chars + 1) * sizeof(wtext[0])); + mbstowcs(wtext, text, chars + 1); /* Truncate, if necessary */ - if (p->max_len > 0 && 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'; + if (p->max_len > 0) { + const size_t len = wcslen(wtext); + if (len > p->max_len) { + + size_t end = p->max_len; + if (end >= 1) { + /* "allocate" room for three dots at the end */ + end -= 1; + } + + if (p->max_len > 1) { + wtext[end] = L'…'; + wtext[end + 1] = L'\0'; + chars = end + 1; + } else { + wtext[end] = L'\0'; + chars = 0; + } + } } e->kern_x = calloc(chars, sizeof(e->kern_x[0])); - if (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 (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) { + struct fcft_text_run *run = fcft_text_run_rasterize( + font, chars, wtext, FCFT_SUBPIXEL_NONE); if (run != NULL) { int w = 0; @@ -201,7 +225,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) if (cache_idx < 0) { size_t new_size = p->cache_size + 1; - struct text_run_cache *new_cache = realloc(p->cache, new_size * sizeof(new_cache[0])); + struct text_run_cache *new_cache = realloc( + p->cache, new_size * sizeof(new_cache[0])); p->cache_size = new_size; p->cache = new_cache; @@ -225,7 +250,8 @@ instantiate(const struct particle *particle, const struct tag_set *tags) /* Convert text to glyph masks/images. */ for (size_t i = 0; i < chars; i++) { - const struct fcft_glyph *glyph = fcft_rasterize_char_utf32(font, wtext[i], FCFT_SUBPIXEL_NONE); + const struct fcft_glyph *glyph = fcft_glyph_rasterize( + font, wtext[i], FCFT_SUBPIXEL_NONE); if (glyph == NULL) continue; @@ -286,7 +312,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 +323,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node) { static const struct attr_info attrs[] = { {"text", true, &conf_verify_string}, - {"max", false, &conf_verify_unsigned}, + {"max", false, &conf_verify_int}, PARTICLE_COMMON_ATTRS, }; diff --git a/plugin.c b/plugin.c index 2ed0a4f..5e0e4bc 100644 --- a/plugin.c +++ b/plugin.c @@ -1,104 +1,52 @@ #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 +56,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 +71,60 @@ 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 +140,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 +165,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 +182,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/tag.c b/tag.c index d6609af..2943423 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)); @@ -510,23 +464,20 @@ tags_expand_template(const char *template, const struct tag_set *tags) FMT_HEX, FMT_OCT, FMT_PERCENT, - FMT_DIVIDE, - } format - = FMT_DEFAULT; + FMT_KBYTE, + FMT_MBYTE, + FMT_GBYTE, + FMT_KIBYTE, + FMT_MIBYTE, + FMT_GIBYTE, + } format = FMT_DEFAULT; enum { VALUE_VALUE, VALUE_MIN, VALUE_MAX, VALUE_UNIT, - } kind - = VALUE_VALUE; - - long digits = 0; - long decimals = 2; - long divider = 1; - bool zero_pad = false; - char *point = NULL; + } kind = VALUE_VALUE; for (size_t i = 0; i < MAX_TAG_ARGS; i++) { if (tag_args[i] == NULL) @@ -537,65 +488,25 @@ tags_expand_template(const char *template, const struct tag_set *tags) format = FMT_OCT; else if (strcmp(tag_args[i], "%") == 0) format = FMT_PERCENT; - else if (*tag_args[i] == '/') { - format = FMT_DIVIDE; - const char *divider_str = tag_args[i] + 1; - if (!is_number(divider_str, ÷r) || divider == 0) { - divider = 1; - LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str); - } - } - else if (strcmp(tag_args[i], "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], "kb") == 0) + format = FMT_KBYTE; + else if (strcmp(tag_args[i], "mb") == 0) + format = FMT_MBYTE; + else if (strcmp(tag_args[i], "gb") == 0) + format = FMT_GBYTE; + else if (strcmp(tag_args[i], "kib") == 0) + format = FMT_KIBYTE; + else if (strcmp(tag_args[i], "mib") == 0) + format = FMT_MIBYTE; + else if (strcmp(tag_args[i], "gib") == 0) + format = FMT_GIBYTE; else if (strcmp(tag_args[i], "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 + else LOG_WARN("invalid tag formatter: %s", tag_args[i]); } @@ -603,37 +514,15 @@ tags_expand_template(const char *template, const struct tag_set *tags) 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)); + snprintf(str, sizeof(str), format == FMT_HEX ? "%lx" : "%lo", + tag->as_int(tag)); sbuf_append(&formatted, str); break; } @@ -643,22 +532,29 @@ tags_expand_template(const char *template, const struct tag_set *tags) const long max = tag->max(tag); const long cur = tag->as_int(tag); - const char *fmt = zero_pad ? "%0*lu" : "%*lu"; char str[4]; - snprintf(str, sizeof(str), fmt, digits, (cur - min) * 100 / (max - min)); + snprintf(str, sizeof(str), "%lu", (cur - min) * 100 / (max - min)); sbuf_append(&formatted, str); break; } - case FMT_DIVIDE: { + case FMT_KBYTE: + case FMT_MBYTE: + case FMT_GBYTE: + case FMT_KIBYTE: + case FMT_MIBYTE: + case FMT_GIBYTE: { + const long divider = + format == FMT_KBYTE ? 1024 : + format == FMT_MBYTE ? 1024 * 1024 : + format == FMT_GBYTE ? 1024 * 1024 * 1024 : + format == FMT_KIBYTE ? 1000 : + format == FMT_MIBYTE ? 1000 * 1000 : + format == FMT_GIBYTE ? 1000 * 1000 * 1000 : + 1; + 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), "%lu", tag->as_int(tag) / divider); sbuf_append(&formatted, str); break; } @@ -671,33 +567,39 @@ tags_expand_template(const char *template, const struct tag_set *tags) const long max = tag->max(tag); long value = kind == VALUE_MIN ? min : max; - const char *fmt = 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_DEFAULT: fmt = "%ld"; break; + case FMT_HEX: fmt = "%lx"; break; + case FMT_OCT: fmt = "%lo"; break; + case FMT_PERCENT: value = (value - min) * 100 / (max - min); - fmt = zero_pad ? "%0*lu" : "%*lu"; + fmt = "%lu"; break; - case FMT_DIVIDE: { + case FMT_KBYTE: + case FMT_MBYTE: + case FMT_GBYTE: + case FMT_KIBYTE: + case FMT_MIBYTE: + case FMT_GIBYTE: { + const long divider = + format == FMT_KBYTE ? 1024 : + format == FMT_MBYTE ? 1024 * 1024 : + format == FMT_GBYTE ? 1024 * 1024 * 1024 : + format == FMT_KIBYTE ? 1000 : + format == FMT_MIBYTE ? 1000 * 1000 : + format == FMT_GIBYTE ? 1000 * 1000 * 1000 : + 1; value /= divider; - fmt = zero_pad ? "%0*lu" : "%*lu"; + fmt = "%lu"; 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 +608,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); @@ -730,7 +626,8 @@ tags_expand_template(const char *template, const struct tag_set *tags) } void -tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, const struct tag_set *tags) +tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, + const struct tag_set *tags) { for (size_t i = 0; i < nmemb; i++) expanded[i] = tags_expand_template(template[i], tags); diff --git a/tag.h b/tag.h index 6149b1e..d6bfe6a 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,21 @@ struct tag_set { }; struct tag *tag_new_int(struct module *owner, const char *name, long value); -struct tag *tag_new_int_range(struct module *owner, const char *name, long value, long min, long max); -struct tag *tag_new_int_realtime(struct module *owner, const char *name, long value, long min, long max, - enum tag_realtime_unit unit); +struct tag *tag_new_int_range( + struct module *owner, const char *name, long value, long min, long max); +struct tag *tag_new_int_realtime( + struct module *owner, const char *name, long value, long min, + long max, enum tag_realtime_unit unit); struct tag *tag_new_bool(struct module *owner, const char *name, bool value); struct tag *tag_new_float(struct module *owner, const char *name, double value); -struct tag *tag_new_string(struct module *owner, const char *name, const char *value); +struct tag *tag_new_string( + struct module *owner, const char *name, const char *value); const struct tag *tag_for_name(const struct tag_set *set, const char *name); void tag_set_destroy(struct tag_set *set); /* Utility functions */ char *tags_expand_template(const char *template, const struct tag_set *tags); -void tags_expand_templates(char *expanded[], const char *template[], size_t nmemb, const struct tag_set *tags); +void tags_expand_templates( + char *expanded[], const char *template[], size_t nmemb, + const struct tag_set *tags); diff --git a/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..52370ef 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); @@ -190,23 +188,25 @@ post_process(struct yml_node *node, char **error) break; case SCALAR: - // assert(strcmp(node->scalar.value, "<<") != 0); + //assert(strcmp(node->scalar.value, "<<") != 0); break; case LIST: - tll_foreach(node->list.values, it) if (!post_process(it->item, error)) return false; + tll_foreach(node->list.values, it) + if (!post_process(it->item, error)) + return false; break; case DICT: - tll_foreach(node->dict.pairs, it) - { - if (!post_process(it->item.key, error) || !post_process(it->item.value, error)) { + tll_foreach(node->dict.pairs, it) { + if (!post_process(it->item.key, error) || + !post_process(it->item.value, error)) + { return false; } } - tll_foreach(node->dict.pairs, it) - { + tll_foreach(node->dict.pairs, it) { if (it->item.key->type != SCALAR) continue; @@ -218,32 +218,29 @@ post_process(struct yml_node *node, char **error) * Merge value is a list (of dictionaries) * e.g. <<: [*foo, *bar] */ - tll_foreach(it->item.value->list.values, v_it) - { + tll_foreach(it->item.value->list.values, v_it) { if (v_it->item->type != DICT) { - int cnt = snprintf(NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", v_it->item->line, - v_it->item->column); + int cnt = snprintf( + NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", + v_it->item->line, v_it->item->column); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", v_it->item->line, - v_it->item->column); + snprintf( + *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", + v_it->item->line, v_it->item->column); return false; } - tll_foreach(v_it->item->dict.pairs, vv_it) - { + tll_foreach(v_it->item->dict.pairs, vv_it) { struct dict_pair p = { .key = vv_it->item.key, .value = vv_it->item.value, }; + /* 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 @@ -259,29 +256,27 @@ post_process(struct yml_node *node, char **error) * e.g. <<: *foo */ if (it->item.value->type != DICT) { - int cnt = snprintf(NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", it->item.value->line, - it->item.value->column); + int cnt = snprintf( + NULL, 0, "%zu:%zu: cannot merge non-dictionary anchor", + it->item.value->line, it->item.value->column); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", it->item.value->line, - it->item.value->column); + snprintf( + *error, cnt + 1, "%zu:%zu: cannot merge non-dictionary anchor", + it->item.value->line, it->item.value->column); return false; } - tll_foreach(it->item.value->dict.pairs, v_it) - { + tll_foreach(it->item.value->dict.pairs, v_it) { struct dict_pair p = { .key = v_it->item.key, .value = v_it->item.value, }; - 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 @@ -304,7 +299,10 @@ post_process(struct yml_node *node, char **error) } static const char * -format_error(enum yml_error err, const struct yml_node *parent, const struct yml_node *node, const char *anchor) +format_error(enum yml_error err, + const struct yml_node *parent, + const struct yml_node *node, + const char *anchor) { static char err_str[512]; @@ -315,9 +313,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 +327,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 +345,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 +371,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 +380,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 +395,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 +458,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 +470,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) { @@ -587,13 +561,23 @@ yml_load(FILE *yml, char **error) err: if (error_str != NULL) { - int cnt = snprintf(NULL, 0, "%zu:%zu: %s", yaml.mark.line + 1, yaml.mark.column, error_str); + int cnt = snprintf( + NULL, 0, "%zu:%zu: %s", + yaml.mark.line + 1, + yaml.mark.column, + error_str); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: %s", yaml.mark.line + 1, yaml.mark.column, error_str); + snprintf( + *error, cnt + 1, "%zu:%zu: %s", + yaml.mark.line + 1, + yaml.mark.column, + error_str); } else { - int cnt = snprintf(NULL, 0, "%zu:%zu: unknown error", yaml.mark.line + 1, yaml.mark.column); + int cnt = snprintf(NULL, 0, "%zu:%zu: unknown error", + yaml.mark.line + 1, yaml.mark.column); *error = malloc(cnt + 1); - snprintf(*error, cnt + 1, "%zu:%zu: unknown error", yaml.mark.line + 1, yaml.mark.column); + snprintf(*error, cnt + 1, "%zu:%zu: unknown error", + yaml.mark.line + 1, yaml.mark.column); } err_no_error_formatting: @@ -625,8 +609,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 +638,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 +649,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 +673,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 +710,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 +723,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 +731,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 +802,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 +860,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 +875,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;