mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-27 20:35:38 +02:00
Compare commits
65 commits
Author | SHA1 | Date | |
---|---|---|---|
|
43e1944607 | ||
|
ca0f565237 | ||
|
dfa0970b75 | ||
|
dcbb0f88ae | ||
|
6a97b364a0 | ||
|
87c74d54b7 | ||
|
0bcde5c453 | ||
|
56467d0ba3 | ||
|
7e76d53c0a | ||
|
dcf936fd9b | ||
|
e423776000 | ||
|
e68ed8d843 | ||
|
c27de56bea | ||
|
b5450c3918 | ||
|
5a515eae99 | ||
|
b486088f77 | ||
|
a242d3d569 | ||
|
2eb1cda733 | ||
|
21f374d2eb | ||
|
fc24ea225d | ||
|
e1f7c0292f | ||
|
d746d12f6a | ||
|
61d082c802 | ||
|
57711f0dbe | ||
|
b15714b38a | ||
|
3e0083c9f2 | ||
|
a367895dc6 | ||
|
650d1f13f9 | ||
|
e1b6a78f22 | ||
|
20d48a753b | ||
|
37ecc251a4 | ||
|
4826a52306 | ||
|
0f47cbb889 | ||
|
c3f7fe013d | ||
|
b81e41c3c4 | ||
|
060586dbbe | ||
|
c80bae7604 | ||
|
311c481bfe | ||
|
9498d7e445 | ||
|
2d651d1c0e | ||
|
1b2dee55ef | ||
|
700bf5b28c | ||
|
f8ba887dcd | ||
|
887e770202 | ||
|
54902f46ab | ||
|
699c563051 | ||
|
a5ae61b5df | ||
|
568eb1140f | ||
|
3e0a65f185 | ||
|
1a323c6d21 | ||
|
739dc30323 | ||
|
8422e7e0b1 | ||
|
9cc5e0f7a7 | ||
|
3431d5fc75 | ||
|
20659d3350 | ||
|
0bea49b75e | ||
|
70efd7d15c | ||
|
b8a93a2673 | ||
|
a467f56677 | ||
|
b3313cefc6 | ||
|
00234696fe | ||
|
3a7455913f | ||
|
547cef5afb | ||
|
6f3952819f | ||
|
f148c68736 |
60 changed files with 2759 additions and 228 deletions
|
@ -37,10 +37,6 @@ sources:
|
||||||
# to: <comitter>
|
# to: <comitter>
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- codespell: |
|
|
||||||
pip install codespell
|
|
||||||
cd yambar
|
|
||||||
~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd
|
|
||||||
- fcft: |
|
- fcft: |
|
||||||
cd yambar/subprojects
|
cd yambar/subprojects
|
||||||
git clone https://codeberg.org/dnkl/fcft.git
|
git clone https://codeberg.org/dnkl/fcft.git
|
||||||
|
|
|
@ -12,7 +12,7 @@ steps:
|
||||||
- python3 -m venv codespell-venv
|
- python3 -m venv codespell-venv
|
||||||
- source codespell-venv/bin/activate
|
- source codespell-venv/bin/activate
|
||||||
- pip install codespell
|
- pip install codespell
|
||||||
- codespell README.md CHANGELOG.md *.c *.h doc/*.scd
|
- codespell README.md CHANGELOG.md *.c *.h doc/*.scd bar decorations modules particles examples
|
||||||
- deactivate
|
- deactivate
|
||||||
|
|
||||||
- name: subprojects
|
- name: subprojects
|
||||||
|
|
59
CHANGELOG.md
59
CHANGELOG.md
|
@ -1,5 +1,6 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
* [Unreleased](#unreleased)
|
||||||
* [1.11.0](#1-11-0)
|
* [1.11.0](#1-11-0)
|
||||||
* [1.10.0](#1-10-0)
|
* [1.10.0](#1-10-0)
|
||||||
* [1.9.0](#1-9-0)
|
* [1.9.0](#1-9-0)
|
||||||
|
@ -11,6 +12,64 @@
|
||||||
* [1.5.0](#1-5-0)
|
* [1.5.0](#1-5-0)
|
||||||
|
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* environment variable substitution in config files ([#96][96]).
|
||||||
|
* Log output now respects the [`NO_COLOR`](http://no-color.org/)
|
||||||
|
environment variable.
|
||||||
|
* network: `type` tag ([#380][380]).
|
||||||
|
* network: `type` and `kind` tags ([#380][380]).
|
||||||
|
* tags: `/<N>` 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
|
## 1.11.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Yambar
|
# Yambar
|
||||||
|
|
||||||
[](https://repology.org/project/yambar/versions)
|
[](https://repology.org/project/yambar/versions)
|
||||||
|
|
||||||
|
|
||||||
## Index
|
## Index
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate total width of left/center/rigth groups.
|
* Calculate total width of left/center/right groups.
|
||||||
* Note: begin_expose() must have been called
|
* Note: begin_expose() must have been called
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -1223,7 +1223,7 @@ loop(struct bar *_bar, void (*expose)(const struct bar *bar),
|
||||||
bool do_expose = false;
|
bool do_expose = false;
|
||||||
|
|
||||||
/* Coalesce “refresh” commands */
|
/* Coalesce “refresh” commands */
|
||||||
size_t count = 0;
|
__attribute__((unused)) size_t count = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
ssize_t r = read(backend->pipe_fds[0], &command, sizeof(command));
|
ssize_t r = read(backend->pipe_fds[0], &command, sizeof(command));
|
||||||
|
|
|
@ -101,7 +101,7 @@ setup(struct bar *_bar)
|
||||||
backend->x = mon->x;
|
backend->x = mon->x;
|
||||||
backend->y = mon->y;
|
backend->y = mon->y;
|
||||||
bar->width = mon->width;
|
bar->width = mon->width;
|
||||||
backend->y += bar->location == BAR_TOP ? 0 : screen->height_in_pixels - bar->height_with_border;
|
backend->y += bar->location == BAR_TOP ? 0 : mon->height - bar->height_with_border;
|
||||||
|
|
||||||
found_monitor = true;
|
found_monitor = true;
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ refresh(const struct bar *_bar)
|
||||||
|
|
||||||
/* Send an event to handle refresh from main thread */
|
/* Send an event to handle refresh from main thread */
|
||||||
|
|
||||||
/* Note: docs say that all X11 events are 32 bytes, reglardless of
|
/* Note: docs say that all X11 events are 32 bytes, regardless of
|
||||||
* the size of the event structure */
|
* the size of the event structure */
|
||||||
xcb_expose_event_t *evt = calloc(32, 1);
|
xcb_expose_event_t *evt = calloc(32, 1);
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@ endif
|
||||||
if plugin_mpd_enabled
|
if plugin_mpd_enabled
|
||||||
plugin_pages += ['yambar-modules-mpd.5.scd']
|
plugin_pages += ['yambar-modules-mpd.5.scd']
|
||||||
endif
|
endif
|
||||||
|
if plugin_mpris_enabled
|
||||||
|
plugin_pages += ['yambar-modules-mpris.5.scd']
|
||||||
|
endif
|
||||||
if plugin_i3_enabled
|
if plugin_i3_enabled
|
||||||
plugin_pages += ['yambar-modules-i3.5.scd']
|
plugin_pages += ['yambar-modules-i3.5.scd']
|
||||||
plugin_pages += ['yambar-modules-sway.5.scd']
|
plugin_pages += ['yambar-modules-sway.5.scd']
|
||||||
|
@ -44,6 +47,12 @@ endif
|
||||||
if plugin_network_enabled
|
if plugin_network_enabled
|
||||||
plugin_pages += ['yambar-modules-network.5.scd']
|
plugin_pages += ['yambar-modules-network.5.scd']
|
||||||
endif
|
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
|
if plugin_pipewire_enabled
|
||||||
plugin_pages += ['yambar-modules-pipewire.5.scd']
|
plugin_pages += ['yambar-modules-pipewire.5.scd']
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -137,7 +137,7 @@ content:
|
||||||
|
|
||||||
# STACK
|
# STACK
|
||||||
|
|
||||||
This particles combines multiple decorations.
|
This particle combines multiple decorations.
|
||||||
|
|
||||||
## CONFIGURATION
|
## CONFIGURATION
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ currently present in the machine.
|
||||||
for the machine
|
for the machine
|
||||||
| is_disk
|
| is_disk
|
||||||
: boolean
|
: boolean
|
||||||
: whether or not the device is a disk (e.g. sda, sdb) or a partition
|
: 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.
|
(e.g., sda1, sda2, ...). "Total" is advertised as a disk.
|
||||||
| read_speed
|
| read_speed
|
||||||
: int
|
: int
|
||||||
: bytes read, in bytes/s
|
: bytes read, in bytes/s
|
||||||
|
|
|
@ -26,6 +26,9 @@ with the _application_ and _title_ tags to replace the X11-only
|
||||||
| name
|
| name
|
||||||
: string
|
: string
|
||||||
: The workspace name
|
: The workspace name
|
||||||
|
| output
|
||||||
|
: string
|
||||||
|
: The output (monitor) the workspace is on
|
||||||
| visible
|
| visible
|
||||||
: bool
|
: bool
|
||||||
: True if the workspace is currently visible (on any output)
|
: True if the workspace is currently visible (on any output)
|
||||||
|
|
|
@ -20,6 +20,9 @@ mpd - This module provides MPD status such as currently playing artist/album/son
|
||||||
| consume
|
| consume
|
||||||
: bool
|
: bool
|
||||||
: True if the *consume* flag is set
|
: True if the *consume* flag is set
|
||||||
|
| single
|
||||||
|
: bool
|
||||||
|
: True if the *single* flag is set
|
||||||
| volume
|
| volume
|
||||||
: range
|
: range
|
||||||
: Volume of MPD in percentage
|
: Volume of MPD in percentage
|
||||||
|
|
101
doc/yambar-modules-mpris.5.scd
Normal file
101
doc/yambar-modules-mpris.5.scd
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
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.<identity>'.
|
||||||
|
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)
|
|
@ -21,6 +21,16 @@ address per network interface.
|
||||||
| name
|
| name
|
||||||
: string
|
: string
|
||||||
: Network interface name
|
: 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
|
| index
|
||||||
: int
|
: int
|
||||||
: Network interface index
|
: Network interface index
|
||||||
|
@ -91,17 +101,23 @@ address per network interface.
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
Display all Ethernet (including WLAN) devices. This excludes loopback,
|
||||||
|
bridges etc.
|
||||||
|
|
||||||
```
|
```
|
||||||
bar:
|
bar:
|
||||||
left:
|
left:
|
||||||
- network:
|
- network:
|
||||||
content:
|
content:
|
||||||
map:
|
map:
|
||||||
default:
|
|
||||||
string: {text: "{name}: {state} ({ipv4})"}
|
|
||||||
conditions:
|
conditions:
|
||||||
ipv4 == "":
|
type == ether || type == wlan:
|
||||||
string: {text: "{name}: {state}"}
|
map:
|
||||||
|
default:
|
||||||
|
string: {text: "{name}: {state} ({ipv4})"}
|
||||||
|
conditions:
|
||||||
|
ipv4 == "":
|
||||||
|
string: {text: "{name}: {state}"}
|
||||||
```
|
```
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
34
doc/yambar-modules-niri-language.5.scd
Normal file
34
doc/yambar-modules-niri-language.5.scd
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
yambar-modules-niri-language(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
niri-language - This module provides information about niri's currently
|
||||||
|
selected language.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:< *Description*
|
||||||
|
| language
|
||||||
|
: string
|
||||||
|
: The currently selected language.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
No additional attributes supported, only the generic ones (see
|
||||||
|
*GENERIC CONFIGURATION* in *yambar-modules*(5))
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- niri-language:
|
||||||
|
content:
|
||||||
|
string: {text: "{language}"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
60
doc/yambar-modules-niri-workspaces.5.scd
Normal file
60
doc/yambar-modules-niri-workspaces.5.scd
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
yambar-modules-niri-workspaces(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
niri-workspaces - This module provides information about niri workspaces.
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module provides a map of each workspace present in niri.
|
||||||
|
|
||||||
|
Each workspace has its _id_, _name_, and its status (_focused_,
|
||||||
|
_active_, _empty_). The workspaces are sorted by their ids.
|
||||||
|
|
||||||
|
This module will *only* track the monitor where yambar was launched.
|
||||||
|
If you have a multi monitor setup, please launch yambar on each
|
||||||
|
individual monitor to track its workspaces.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:< *Description*
|
||||||
|
| id
|
||||||
|
: int
|
||||||
|
: The workspace id.
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: The name of the workspace.
|
||||||
|
| active
|
||||||
|
: bool
|
||||||
|
: True if the workspace is currently visible on the current output.
|
||||||
|
| focused
|
||||||
|
: bool
|
||||||
|
: True if the workspace is currently focused.
|
||||||
|
| empty
|
||||||
|
: bool
|
||||||
|
: True if the workspace contains no window.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
No additional attributes supported, only the generic ones (see
|
||||||
|
*GENERIC CONFIGURATION* in *yambar-modules*(5))
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- niri-workspaces:
|
||||||
|
content:
|
||||||
|
map:
|
||||||
|
default: {string: {text: "| {id}"}}
|
||||||
|
conditions:
|
||||||
|
active: {string: {text: "-> {id}"}}
|
||||||
|
~empty: {string: {text: "@ {id}"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
|
@ -19,10 +19,10 @@ pipewire - Monitors pipewire for volume, mute/unmute, device change
|
||||||
: Current device description
|
: Current device description
|
||||||
| form_factor
|
| form_factor
|
||||||
: string
|
: string
|
||||||
: Current device form factor (headset, speaker, mic, etc)
|
: Current device form factor (headset, speaker, mic, etc.)
|
||||||
| bus
|
| bus
|
||||||
: string
|
: string
|
||||||
: Current device bus (bluetooth, alsa, etc)
|
: Current device bus (bluetooth, alsa, etc.)
|
||||||
| icon
|
| icon
|
||||||
: string
|
: string
|
||||||
: Current device icon name
|
: Current device icon name
|
||||||
|
@ -43,6 +43,18 @@ pipewire - Monitors pipewire for volume, mute/unmute, device change
|
||||||
:[ *Type*
|
:[ *Type*
|
||||||
:[ *Req*
|
:[ *Req*
|
||||||
:< *Description*
|
:< *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
|
| content
|
||||||
: particle
|
: particle
|
||||||
: yes
|
: yes
|
||||||
|
|
|
@ -16,7 +16,7 @@ configurable amount of time.
|
||||||
In continuous mode, the script is executed once. It will typically run
|
In continuous mode, the script is executed once. It will typically run
|
||||||
in a loop, sending an updated tag set whenever it needs, or wants
|
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
|
to. The last tag set is used (displayed) by yambar until a new tag set
|
||||||
is received. This mode is intended to be used by scripts that depends
|
is received. This mode is intended to be used by scripts that depend
|
||||||
on non-polling methods to update their state.
|
on non-polling methods to update their state.
|
||||||
|
|
||||||
Tag sets, or _transactions_, are separated by an empty line
|
Tag sets, or _transactions_, are separated by an empty line
|
||||||
|
|
|
@ -174,6 +174,10 @@ Available modules have their own pages:
|
||||||
|
|
||||||
*yambar-modules-sway*(5)
|
*yambar-modules-sway*(5)
|
||||||
|
|
||||||
|
*yambar-modules-niri-language*(5)
|
||||||
|
|
||||||
|
*yambar-modules-niri-workspaces*(5)
|
||||||
|
|
||||||
*yambar-modules-xkb*(5)
|
*yambar-modules-xkb*(5)
|
||||||
|
|
||||||
*yambar-modules-xwindow*(5)
|
*yambar-modules-xwindow*(5)
|
||||||
|
|
|
@ -155,7 +155,7 @@ content:
|
||||||
|
|
||||||
This particle is a list (or sequence, if you like) of other
|
This particle is a list (or sequence, if you like) of other
|
||||||
particles. It can be used to render e.g. _string_ particles with
|
particles. It can be used to render e.g. _string_ particles with
|
||||||
different font and/or color formatting. Or ay other particle
|
different font and/or color formatting. Or any other particle
|
||||||
combinations.
|
combinations.
|
||||||
|
|
||||||
But note that this means you *cannot* set any attributes on the _list_
|
But note that this means you *cannot* set any attributes on the _list_
|
||||||
|
@ -214,6 +214,11 @@ content:
|
||||||
- string: ...
|
- 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
|
# MAP
|
||||||
|
|
||||||
This particle maps the values of a specific tag to different
|
This particle maps the values of a specific tag to different
|
||||||
|
@ -260,6 +265,26 @@ To match for empty strings, use ' "" ':
|
||||||
<tag> == ""
|
<tag> == ""
|
||||||
```
|
```
|
||||||
|
|
||||||
|
String glob matching
|
||||||
|
|
||||||
|
To perform string matching using globbing with "\*" & "?" characters:
|
||||||
|
\* Match any zero or more characters. ? Match exactly any one
|
||||||
|
character.
|
||||||
|
|
||||||
|
```
|
||||||
|
<tag> ~~ "hello*"
|
||||||
|
```
|
||||||
|
|
||||||
|
Will match any string starting with "hello", including "hello",
|
||||||
|
"hello1", "hello123", etc.
|
||||||
|
|
||||||
|
```
|
||||||
|
<tag> ~~ "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:
|
Furthermore, you may use the boolean operators:
|
||||||
|
|
||||||
[- &&
|
[- &&
|
||||||
|
@ -451,7 +476,7 @@ itself when needed.
|
||||||
|
|
||||||
```
|
```
|
||||||
content:
|
content:
|
||||||
progres-bar:
|
progress-bar:
|
||||||
tag: tag_name
|
tag: tag_name
|
||||||
length: 20
|
length: 20
|
||||||
start: {string: {text: ├}}
|
start: {string: {text: ├}}
|
||||||
|
|
|
@ -86,11 +86,15 @@ be used.
|
||||||
: format
|
: format
|
||||||
: Range tags
|
: Range tags
|
||||||
: Renders a range tag's value as a percentage value
|
: Renders a range tag's value as a percentage value
|
||||||
|
| /N
|
||||||
|
: format
|
||||||
|
: All tag types
|
||||||
|
: Renders a tag's value (in decimal) divided by N
|
||||||
| kb, mb, gb
|
| kb, mb, gb
|
||||||
: format
|
: format
|
||||||
: All tag types
|
: All tag types
|
||||||
: Renders a tag's value (in decimal) divided by 1000, 1000^2 or
|
: Renders a tag's value (in decimal) divided by 1000, 1000^2 or
|
||||||
1000^3. Note: no unit suffix is appended)
|
1000^3. Note: no unit suffix is appended
|
||||||
| kib, mib, gib
|
| kib, mib, gib
|
||||||
: format
|
: format
|
||||||
: All tag types
|
: All tag types
|
||||||
|
|
|
@ -25,7 +25,7 @@ yambar - modular status panel for X11 and Wayland
|
||||||
*-p*,*--print-pid*=_FILE_|_FD_
|
*-p*,*--print-pid*=_FILE_|_FD_
|
||||||
Print PID to this file, or FD, when successfully started. The file
|
Print PID to this file, or FD, when successfully started. The file
|
||||||
(or FD) is closed immediately after writing the PID. When a _FILE_
|
(or FD) is closed immediately after writing the PID. When a _FILE_
|
||||||
as been specified, the file is unlinked exit.
|
as been specified, the file is unlinked upon exiting.
|
||||||
|
|
||||||
*-d*,*--log-level*={*info*,*warning*,*error*,*none*}
|
*-d*,*--log-level*={*info*,*warning*,*error*,*none*}
|
||||||
Log level, used both for log output on stderr as well as
|
Log level, used both for log output on stderr as well as
|
||||||
|
|
|
@ -23,6 +23,11 @@ types that are frequently used:
|
||||||
- 000000ff: black, no transparency
|
- 000000ff: black, no transparency
|
||||||
- 00ff00ff: green, no transparency
|
- 00ff00ff: green, no transparency
|
||||||
- ff000099: red, semi-transparent
|
- 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
|
# FORMAT
|
||||||
[[ *Name*
|
[[ *Name*
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# For X11/i3, you'll want to replace calls to swaymsg with i3-msg, and
|
# For X11/i3, you'll want to replace calls to swaymsg with i3-msg, and
|
||||||
# the sway-xkb module with the xkb module.
|
# the sway-xkb module with the xkb module.
|
||||||
|
|
||||||
# fonts we'll be re-using here and there
|
# fonts we'll be reusing here and there
|
||||||
awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14
|
awesome: &awesome Font Awesome 6 Free:style=solid:pixelsize=14
|
||||||
awesome_brands: &awesome_brands Font Awesome 6 Brands:pixelsize=16
|
awesome_brands: &awesome_brands Font Awesome 6 Brands:pixelsize=16
|
||||||
|
|
||||||
|
|
69
examples/river-minimal.yml
Normal file
69
examples/river-minimal.yml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
bg_default: &bg_default {stack: [{background: {color: 81A1C1ff}}, {underline: {size: 4, color: D8DEE9ff}}]}
|
||||||
|
bar:
|
||||||
|
height: 32
|
||||||
|
location: top
|
||||||
|
background: 000000ff
|
||||||
|
font: NotoSans:pixelsize=16
|
||||||
|
|
||||||
|
right:
|
||||||
|
- clock:
|
||||||
|
content:
|
||||||
|
- string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"}
|
||||||
|
- string: {text: "{date}", right-margin: 5}
|
||||||
|
- string: {text: , font: "Font Awesome 6 Free:style=solid:size=12"}
|
||||||
|
- string: {text: "{time} "}
|
||||||
|
left:
|
||||||
|
- river:
|
||||||
|
anchors:
|
||||||
|
- base: &river_base
|
||||||
|
left-margin: 10
|
||||||
|
right-margin: 13
|
||||||
|
default: {string: {text: }}
|
||||||
|
conditions:
|
||||||
|
id == 1: {string: {text: 1}}
|
||||||
|
id == 2: {string: {text: 2}}
|
||||||
|
id == 3: {string: {text: 3}}
|
||||||
|
id == 4: {string: {text: 4}}
|
||||||
|
id == 5: {string: {text: 5}}
|
||||||
|
|
||||||
|
content:
|
||||||
|
map:
|
||||||
|
on-click:
|
||||||
|
left: sh -c "riverctl set-focused-tags $((1 << ({id} - 1)))"
|
||||||
|
right: sh -c "riverctl toggle-focused-tags $((1 << ({id} -1)))"
|
||||||
|
middle: sh -c "riverctl toggle-view-tags $((1 << ({id} -1)))"
|
||||||
|
conditions:
|
||||||
|
state == urgent:
|
||||||
|
map:
|
||||||
|
<<: *river_base
|
||||||
|
deco: {background: {color: D08770ff}}
|
||||||
|
state == focused:
|
||||||
|
map:
|
||||||
|
<<: *river_base
|
||||||
|
deco: *bg_default
|
||||||
|
state == visible && ~occupied:
|
||||||
|
map:
|
||||||
|
<<: *river_base
|
||||||
|
state == visible && occupied:
|
||||||
|
map:
|
||||||
|
<<: *river_base
|
||||||
|
deco: *bg_default
|
||||||
|
state == unfocused:
|
||||||
|
map:
|
||||||
|
<<: *river_base
|
||||||
|
state == invisible && ~occupied: {empty: {}}
|
||||||
|
state == invisible && occupied:
|
||||||
|
map:
|
||||||
|
<<: *river_base
|
||||||
|
deco: {underline: {size: 3, color: ea6962ff}}
|
||||||
|
|
||||||
|
|
||||||
|
center:
|
||||||
|
- foreign-toplevel:
|
||||||
|
content:
|
||||||
|
map:
|
||||||
|
conditions:
|
||||||
|
~activated: {empty: {}}
|
||||||
|
activated:
|
||||||
|
- string: {text: " {app-id}", foreground: ffa0a0ff}
|
||||||
|
- string: {text: ": {title}"}
|
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
# Now the fun part
|
# Now the fun part
|
||||||
#
|
#
|
||||||
# Exemple configuration:
|
# Example configuration:
|
||||||
#
|
#
|
||||||
# - script:
|
# - script:
|
||||||
# path: /absolute/path/to/dwl-tags.sh
|
# path: /absolute/path/to/dwl-tags.sh
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
# {aur} int number of aur packages
|
# {aur} int number of aur packages
|
||||||
# {pkg} int sum of both
|
# {pkg} int sum of both
|
||||||
#
|
#
|
||||||
# Exemples configuration:
|
# Examples configuration:
|
||||||
# - script:
|
# - script:
|
||||||
# path: /absolute/path/to/pacman.sh
|
# path: /absolute/path/to/pacman.sh
|
||||||
# args: []
|
# args: []
|
||||||
|
|
12
log.c
12
log.c
|
@ -39,9 +39,15 @@ log_init(enum log_colorize _colorize, bool _do_syslog, enum log_facility syslog_
|
||||||
[LOG_FACILITY_DAEMON] = LOG_DAEMON,
|
[LOG_FACILITY_DAEMON] = LOG_DAEMON,
|
||||||
};
|
};
|
||||||
|
|
||||||
colorize = _colorize == LOG_COLORIZE_NEVER ? false
|
/* Don't use colors if NO_COLOR is defined and not empty */
|
||||||
: _colorize == LOG_COLORIZE_ALWAYS ? true
|
const char *no_color_str = getenv("NO_COLOR");
|
||||||
: isatty(STDERR_FILENO);
|
const bool no_color = no_color_str != NULL && no_color_str[0] != '\0';
|
||||||
|
|
||||||
|
colorize = _colorize == LOG_COLORIZE_NEVER
|
||||||
|
? false
|
||||||
|
: _colorize == LOG_COLORIZE_ALWAYS
|
||||||
|
? true
|
||||||
|
: !no_color && isatty(STDERR_FILENO);
|
||||||
do_syslog = _do_syslog;
|
do_syslog = _do_syslog;
|
||||||
log_level = _log_level;
|
log_level = _log_level;
|
||||||
|
|
||||||
|
|
2
main.c
2
main.c
|
@ -87,7 +87,7 @@ get_config_path(void)
|
||||||
static struct bar *
|
static struct bar *
|
||||||
load_bar(const char *config_path, enum bar_backend backend)
|
load_bar(const char *config_path, enum bar_backend backend)
|
||||||
{
|
{
|
||||||
FILE *conf_file = fopen(config_path, "r");
|
FILE *conf_file = fopen(config_path, "re");
|
||||||
if (conf_file == NULL) {
|
if (conf_file == NULL) {
|
||||||
LOG_ERRNO("%s: failed to open", config_path);
|
LOG_ERRNO("%s: failed to open", config_path);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
14
meson.build
14
meson.build
|
@ -1,18 +1,22 @@
|
||||||
project('yambar', 'c',
|
project('yambar', 'c',
|
||||||
version: '1.11.0',
|
version: '1.11.0',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>=0.59.0',
|
meson_version: '>=0.60.0',
|
||||||
default_options: ['c_std=c18',
|
default_options: ['c_std=c18',
|
||||||
'warning_level=1',
|
'warning_level=1',
|
||||||
'werror=true',
|
|
||||||
'b_ndebug=if-release'])
|
'b_ndebug=if-release'])
|
||||||
|
|
||||||
is_debug_build = get_option('buildtype').startswith('debug')
|
is_debug_build = get_option('buildtype').startswith('debug')
|
||||||
plugs_as_libs = get_option('core-plugins-as-shared-libraries')
|
plugs_as_libs = get_option('core-plugins-as-shared-libraries')
|
||||||
|
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
|
cc_flags = [
|
||||||
|
'-Werror=all'
|
||||||
|
]
|
||||||
|
|
||||||
if cc.has_function('memfd_create')
|
if cc.has_function('memfd_create',
|
||||||
|
args: ['-D_GNU_SOURCE=200809L'],
|
||||||
|
prefix: '#include <sys/mman.h>')
|
||||||
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -74,6 +78,7 @@ 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: ['>=3.0.0', '<4.0.0'], fallback: 'fcft')
|
||||||
|
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
|
cc_flags +
|
||||||
['-D_GNU_SOURCE'] +
|
['-D_GNU_SOURCE'] +
|
||||||
(is_debug_build ? ['-D_DEBUG'] : []) +
|
(is_debug_build ? ['-D_DEBUG'] : []) +
|
||||||
(backend_x11 ? ['-DENABLE_X11'] : []) +
|
(backend_x11 ? ['-DENABLE_X11'] : []) +
|
||||||
|
@ -178,6 +183,7 @@ summary(
|
||||||
'Foreign toplevel (window tracking for Wayland)': plugin_foreign_toplevel_enabled,
|
'Foreign toplevel (window tracking for Wayland)': plugin_foreign_toplevel_enabled,
|
||||||
'Memory monitoring': plugin_mem_enabled,
|
'Memory monitoring': plugin_mem_enabled,
|
||||||
'Music Player Daemon (MPD)': plugin_mpd_enabled,
|
'Music Player Daemon (MPD)': plugin_mpd_enabled,
|
||||||
|
'Media Player Remote Interface Specificaion (MPRIS)': plugin_mpris_enabled,
|
||||||
'i3+Sway': plugin_i3_enabled,
|
'i3+Sway': plugin_i3_enabled,
|
||||||
'Label': plugin_label_enabled,
|
'Label': plugin_label_enabled,
|
||||||
'Network monitoring': plugin_network_enabled,
|
'Network monitoring': plugin_network_enabled,
|
||||||
|
@ -187,6 +193,8 @@ summary(
|
||||||
'River': plugin_river_enabled,
|
'River': plugin_river_enabled,
|
||||||
'Script': plugin_script_enabled,
|
'Script': plugin_script_enabled,
|
||||||
'Sway XKB keyboard': plugin_sway_xkb_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,
|
'XKB keyboard (for X11)': plugin_xkb_enabled,
|
||||||
'XWindow (window tracking for X11)': plugin_xwindow_enabled,
|
'XWindow (window tracking for X11)': plugin_xwindow_enabled,
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,6 +26,8 @@ option('plugin-mem', type: 'feature', value: 'auto',
|
||||||
description: 'Memory monitoring support')
|
description: 'Memory monitoring support')
|
||||||
option('plugin-mpd', type: 'feature', value: 'auto',
|
option('plugin-mpd', type: 'feature', value: 'auto',
|
||||||
description: 'Music Player Daemon (MPD) support')
|
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',
|
option('plugin-i3', type: 'feature', value: 'auto',
|
||||||
description: 'i3+Sway support')
|
description: 'i3+Sway support')
|
||||||
option('plugin-label', type: 'feature', value: 'auto',
|
option('plugin-label', type: 'feature', value: 'auto',
|
||||||
|
@ -44,6 +46,10 @@ option('plugin-script', type: 'feature', value: 'auto',
|
||||||
description: 'Script support')
|
description: 'Script support')
|
||||||
option('plugin-sway-xkb', type: 'feature', value: 'auto',
|
option('plugin-sway-xkb', type: 'feature', value: 'auto',
|
||||||
description: 'keyboard support for Sway')
|
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',
|
option('plugin-xkb', type: 'feature', value: 'auto',
|
||||||
description: 'keyboard support for X11')
|
description: 'keyboard support for X11')
|
||||||
option('plugin-xwindow', type: 'feature', value: 'auto',
|
option('plugin-xwindow', type: 'feature', value: 'auto',
|
||||||
|
|
|
@ -112,13 +112,13 @@ readint_from_fd(int fd)
|
||||||
static int
|
static int
|
||||||
initialize(struct private *m)
|
initialize(struct private *m)
|
||||||
{
|
{
|
||||||
int backlight_fd = open("/sys/class/backlight", O_RDONLY);
|
int backlight_fd = open("/sys/class/backlight", O_RDONLY | O_CLOEXEC);
|
||||||
if (backlight_fd == -1) {
|
if (backlight_fd == -1) {
|
||||||
LOG_ERRNO("/sys/class/backlight");
|
LOG_ERRNO("/sys/class/backlight");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY);
|
int base_dir_fd = openat(backlight_fd, m->device, O_RDONLY | O_CLOEXEC);
|
||||||
close(backlight_fd);
|
close(backlight_fd);
|
||||||
|
|
||||||
if (base_dir_fd == -1) {
|
if (base_dir_fd == -1) {
|
||||||
|
@ -126,7 +126,7 @@ initialize(struct private *m)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY);
|
int max_fd = openat(base_dir_fd, "max_brightness", O_RDONLY | O_CLOEXEC);
|
||||||
if (max_fd == -1) {
|
if (max_fd == -1) {
|
||||||
LOG_ERRNO("/sys/class/backlight/%s/max_brightness", m->device);
|
LOG_ERRNO("/sys/class/backlight/%s/max_brightness", m->device);
|
||||||
close(base_dir_fd);
|
close(base_dir_fd);
|
||||||
|
@ -136,7 +136,7 @@ initialize(struct private *m)
|
||||||
m->max_brightness = readint_from_fd(max_fd);
|
m->max_brightness = readint_from_fd(max_fd);
|
||||||
close(max_fd);
|
close(max_fd);
|
||||||
|
|
||||||
int current_fd = openat(base_dir_fd, "brightness", O_RDONLY);
|
int current_fd = openat(base_dir_fd, "brightness", O_RDONLY | O_CLOEXEC);
|
||||||
close(base_dir_fd);
|
close(base_dir_fd);
|
||||||
|
|
||||||
if (current_fd == -1) {
|
if (current_fd == -1) {
|
||||||
|
|
|
@ -259,13 +259,13 @@ initialize(struct private *m)
|
||||||
{
|
{
|
||||||
char line_buf[512];
|
char line_buf[512];
|
||||||
|
|
||||||
int pw_fd = open("/sys/class/power_supply", O_RDONLY);
|
int pw_fd = open("/sys/class/power_supply", O_RDONLY | O_CLOEXEC);
|
||||||
if (pw_fd < 0) {
|
if (pw_fd < 0) {
|
||||||
LOG_ERRNO("/sys/class/power_supply");
|
LOG_ERRNO("/sys/class/power_supply");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY);
|
int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY | O_CLOEXEC);
|
||||||
close(pw_fd);
|
close(pw_fd);
|
||||||
|
|
||||||
if (base_dir_fd < 0) {
|
if (base_dir_fd < 0) {
|
||||||
|
@ -274,7 +274,7 @@ initialize(struct private *m)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int fd = openat(base_dir_fd, "manufacturer", O_RDONLY);
|
int fd = openat(base_dir_fd, "manufacturer", O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
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;
|
m->manufacturer = NULL;
|
||||||
|
@ -285,7 +285,7 @@ initialize(struct private *m)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int fd = openat(base_dir_fd, "model_name", O_RDONLY);
|
int fd = openat(base_dir_fd, "model_name", O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
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;
|
m->model = NULL;
|
||||||
|
@ -298,7 +298,7 @@ initialize(struct private *m)
|
||||||
if (faccessat(base_dir_fd, "energy_full_design", 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) {
|
&& faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) {
|
||||||
{
|
{
|
||||||
int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY);
|
int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
LOG_ERRNO("/sys/class/power_supply/%s/energy_full_design", m->battery);
|
LOG_ERRNO("/sys/class/power_supply/%s/energy_full_design", m->battery);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -309,7 +309,7 @@ initialize(struct private *m)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int fd = openat(base_dir_fd, "energy_full", O_RDONLY);
|
int fd = openat(base_dir_fd, "energy_full", O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
LOG_ERRNO("/sys/class/power_supply/%s/energy_full", m->battery);
|
LOG_ERRNO("/sys/class/power_supply/%s/energy_full", m->battery);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -325,7 +325,7 @@ initialize(struct private *m)
|
||||||
if (faccessat(base_dir_fd, "charge_full_design", 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) {
|
&& faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) {
|
||||||
{
|
{
|
||||||
int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY);
|
int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
LOG_ERRNO("/sys/class/power_supply/%s/charge_full_design", m->battery);
|
LOG_ERRNO("/sys/class/power_supply/%s/charge_full_design", m->battery);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -336,7 +336,7 @@ initialize(struct private *m)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int fd = openat(base_dir_fd, "charge_full", O_RDONLY);
|
int fd = openat(base_dir_fd, "charge_full", O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
LOG_ERRNO("/sys/class/power_supply/%s/charge_full", m->battery);
|
LOG_ERRNO("/sys/class/power_supply/%s/charge_full", m->battery);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -362,13 +362,13 @@ update_status(struct module *mod)
|
||||||
{
|
{
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
|
|
||||||
int pw_fd = open("/sys/class/power_supply", O_RDONLY);
|
int pw_fd = open("/sys/class/power_supply", O_RDONLY | O_CLOEXEC);
|
||||||
if (pw_fd < 0) {
|
if (pw_fd < 0) {
|
||||||
LOG_ERRNO("/sys/class/power_supply");
|
LOG_ERRNO("/sys/class/power_supply");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY);
|
int base_dir_fd = openat(pw_fd, m->battery, O_RDONLY | O_CLOEXEC);
|
||||||
close(pw_fd);
|
close(pw_fd);
|
||||||
|
|
||||||
if (base_dir_fd < 0) {
|
if (base_dir_fd < 0) {
|
||||||
|
@ -376,14 +376,14 @@ update_status(struct module *mod)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int status_fd = openat(base_dir_fd, "status", O_RDONLY);
|
int status_fd = openat(base_dir_fd, "status", O_RDONLY | O_CLOEXEC);
|
||||||
if (status_fd < 0) {
|
if (status_fd < 0) {
|
||||||
LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery);
|
LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery);
|
||||||
close(base_dir_fd);
|
close(base_dir_fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY);
|
int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY | O_CLOEXEC);
|
||||||
if (capacity_fd < 0) {
|
if (capacity_fd < 0) {
|
||||||
LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery);
|
LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery);
|
||||||
close(status_fd);
|
close(status_fd);
|
||||||
|
@ -391,12 +391,12 @@ update_status(struct module *mod)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY);
|
int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY | O_CLOEXEC);
|
||||||
int power_fd = openat(base_dir_fd, "power_now", O_RDONLY);
|
int power_fd = openat(base_dir_fd, "power_now", O_RDONLY | O_CLOEXEC);
|
||||||
int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY);
|
int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY | O_CLOEXEC);
|
||||||
int current_fd = openat(base_dir_fd, "current_now", O_RDONLY);
|
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);
|
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);
|
int time_to_full_fd = openat(base_dir_fd, "time_to_full_now", O_RDONLY | O_CLOEXEC);
|
||||||
|
|
||||||
long capacity = readint_from_fd(capacity_fd);
|
long capacity = readint_from_fd(capacity_fd);
|
||||||
long energy = energy_fd >= 0 ? readint_from_fd(energy_fd) : -1;
|
long energy = energy_fd >= 0 ? readint_from_fd(energy_fd) : -1;
|
||||||
|
|
|
@ -124,7 +124,7 @@ refresh_cpu_stats(struct cpu_stats *cpu_stats, size_t core_count)
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
ssize_t read;
|
ssize_t read;
|
||||||
|
|
||||||
fp = fopen("/proc/stat", "r");
|
fp = fopen("/proc/stat", "re");
|
||||||
if (NULL == fp) {
|
if (NULL == fp) {
|
||||||
LOG_ERRNO("unable to open /proc/stat");
|
LOG_ERRNO("unable to open /proc/stat");
|
||||||
return;
|
return;
|
||||||
|
|
13
modules/dbus.h
Normal file
13
modules/dbus.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This header provides an generic interface for different versions of
|
||||||
|
// systemd-sdbus.
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBSYSTEMD)
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#elif defined(HAVE_LIBELOGIND)
|
||||||
|
#include <elogind/sd-bus.h>
|
||||||
|
#elif defined(HAVE_BASU)
|
||||||
|
#include <basu/sd-bus.h>
|
||||||
|
#endif
|
||||||
|
|
|
@ -105,7 +105,7 @@ refresh_device_stats(struct private *m)
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
ssize_t read;
|
ssize_t read;
|
||||||
|
|
||||||
fp = fopen("/proc/diskstats", "r");
|
fp = fopen("/proc/diskstats", "re");
|
||||||
if (NULL == fp) {
|
if (NULL == fp) {
|
||||||
LOG_ERRNO("unable to open /proc/diskstats");
|
LOG_ERRNO("unable to open /proc/diskstats");
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +129,7 @@ refresh_device_stats(struct private *m)
|
||||||
|
|
||||||
while ((read = getline(&line, &len, fp)) != -1) {
|
while ((read = getline(&line, &len, fp)) != -1) {
|
||||||
/*
|
/*
|
||||||
* For an explanation of the fields bellow, see
|
* For an explanation of the fields below, see
|
||||||
* https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
|
* https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
|
||||||
*/
|
*/
|
||||||
uint8_t major_number = 0;
|
uint8_t major_number = 0;
|
||||||
|
|
|
@ -231,7 +231,7 @@ process_line(char *line, struct module *module)
|
||||||
/* No need to check error IMHO */
|
/* No need to check error IMHO */
|
||||||
*target = strtoul(string, NULL, 10);
|
*target = strtoul(string, NULL, 10);
|
||||||
|
|
||||||
/* Populate informations */
|
/* Populate information */
|
||||||
if (index == 6) {
|
if (index == 6) {
|
||||||
for (size_t id = 1; id <= private->number_of_tags; ++id) {
|
for (size_t id = 1; id <= private->number_of_tags; ++id) {
|
||||||
uint32_t mask = 1 << (id - 1);
|
uint32_t mask = 1 << (id - 1);
|
||||||
|
@ -330,7 +330,7 @@ run_init(int *inotify_fd, int *inotify_wd, FILE **file, char *dwl_info_filename)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*file = fopen(dwl_info_filename, "r");
|
*file = fopen(dwl_info_filename, "re");
|
||||||
if (*file == NULL) {
|
if (*file == NULL) {
|
||||||
inotify_rm_watch(*inotify_fd, *inotify_wd);
|
inotify_rm_watch(*inotify_fd, *inotify_wd);
|
||||||
close(*inotify_fd);
|
close(*inotify_fd);
|
||||||
|
|
30
modules/i3.c
30
modules/i3.c
|
@ -514,7 +514,6 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void
|
||||||
|
|
||||||
else if (is_move) {
|
else if (is_move) {
|
||||||
struct workspace *w = workspace_lookup(m, current_id);
|
struct workspace *w = workspace_lookup(m, current_id);
|
||||||
assert(w != NULL);
|
|
||||||
|
|
||||||
struct json_object *_current_output;
|
struct json_object *_current_output;
|
||||||
if (!json_object_object_get_ex(current, "output", &_current_output)) {
|
if (!json_object_object_get_ex(current, "output", &_current_output)) {
|
||||||
|
@ -522,16 +521,22 @@ handle_workspace_event(int sock, int type, const struct json_object *json, void
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const char *current_output_string = json_object_get_string(_current_output);
|
||||||
|
|
||||||
free(w->output);
|
/* Ignore fallback_output ("For when there's no connected outputs") */
|
||||||
w->output = strdup(json_object_get_string(_current_output));
|
if (strcmp(current_output_string, "FALLBACK") != 0) {
|
||||||
|
|
||||||
/*
|
assert(w != NULL);
|
||||||
* If the moved workspace was focused, schedule a full update because
|
free(w->output);
|
||||||
* visibility for other workspaces may have changed.
|
w->output = strdup(current_output_string);
|
||||||
*/
|
|
||||||
if (w->focused) {
|
/*
|
||||||
i3_send_pkg(sock, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
|
* 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,7 +594,7 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
|
|
||||||
struct workspace *ws = NULL;
|
struct workspace *ws = NULL;
|
||||||
size_t focused = 0;
|
__attribute__((unused)) size_t focused = 0;
|
||||||
tll_foreach(m->workspaces, it)
|
tll_foreach(m->workspaces, it)
|
||||||
{
|
{
|
||||||
if (it->item.focused) {
|
if (it->item.focused) {
|
||||||
|
@ -659,7 +664,7 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m
|
||||||
char path[64];
|
char path[64];
|
||||||
snprintf(path, sizeof(path), "/proc/%u/comm", ws->window.pid);
|
snprintf(path, sizeof(path), "/proc/%u/comm", ws->window.pid);
|
||||||
|
|
||||||
int fd = open(path, O_RDONLY);
|
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
/* Application may simply have terminated */
|
/* Application may simply have terminated */
|
||||||
free(ws->window.application);
|
free(ws->window.application);
|
||||||
|
@ -871,6 +876,7 @@ content(struct module *mod)
|
||||||
struct tag_set tags = {
|
struct tag_set tags = {
|
||||||
.tags = (struct tag *[]){
|
.tags = (struct tag *[]){
|
||||||
tag_new_string(mod, "name", name),
|
tag_new_string(mod, "name", name),
|
||||||
|
tag_new_string(mod, "output", ws->output),
|
||||||
tag_new_bool(mod, "visible", ws->visible),
|
tag_new_bool(mod, "visible", ws->visible),
|
||||||
tag_new_bool(mod, "focused", ws->focused),
|
tag_new_bool(mod, "focused", ws->focused),
|
||||||
tag_new_bool(mod, "urgent", ws->urgent),
|
tag_new_bool(mod, "urgent", ws->urgent),
|
||||||
|
@ -882,7 +888,7 @@ content(struct module *mod)
|
||||||
|
|
||||||
tag_new_string(mod, "mode", m->mode),
|
tag_new_string(mod, "mode", m->mode),
|
||||||
},
|
},
|
||||||
.count = 9,
|
.count = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ws->focused) {
|
if (ws->focused) {
|
||||||
|
|
|
@ -54,7 +54,7 @@ get_mem_stats(uint64_t *mem_free, uint64_t *mem_total)
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
ssize_t read = 0;
|
ssize_t read = 0;
|
||||||
|
|
||||||
fp = fopen("/proc/meminfo", "r");
|
fp = fopen("/proc/meminfo", "re");
|
||||||
if (NULL == fp) {
|
if (NULL == fp) {
|
||||||
LOG_ERRNO("unable to open /proc/meminfo");
|
LOG_ERRNO("unable to open /proc/meminfo");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -22,6 +22,10 @@ plugin_mem_enabled = get_option('plugin-mem').allowed()
|
||||||
mpd = dependency('libmpdclient', required: get_option('plugin-mpd'))
|
mpd = dependency('libmpdclient', required: get_option('plugin-mpd'))
|
||||||
plugin_mpd_enabled = mpd.found()
|
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'))
|
json_i3 = dependency('json-c', required: get_option('plugin-i3'))
|
||||||
plugin_i3_enabled = json_i3.found()
|
plugin_i3_enabled = json_i3.found()
|
||||||
|
|
||||||
|
@ -45,6 +49,12 @@ plugin_script_enabled = get_option('plugin-script').allowed()
|
||||||
json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb'))
|
json_sway_xkb = dependency('json-c', required: get_option('plugin-sway-xkb'))
|
||||||
plugin_sway_xkb_enabled = json_sway_xkb.found()
|
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'))
|
xcb_xkb = dependency('xcb-xkb', required: get_option('plugin-xkb'))
|
||||||
plugin_xkb_enabled = backend_x11 and xcb_xkb.found()
|
plugin_xkb_enabled = backend_x11 and xcb_xkb.found()
|
||||||
|
|
||||||
|
@ -89,6 +99,11 @@ if plugin_mpd_enabled
|
||||||
mod_data += {'mpd': [[], [mpd]]}
|
mod_data += {'mpd': [[], [mpd]]}
|
||||||
endif
|
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
|
if plugin_i3_enabled
|
||||||
mod_data += {'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json_i3]]}
|
mod_data += {'i3': [['i3-common.c', 'i3-common.h'], [dynlist, json_i3]]}
|
||||||
endif
|
endif
|
||||||
|
@ -121,6 +136,14 @@ if plugin_sway_xkb_enabled
|
||||||
mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]}
|
mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]}
|
||||||
endif
|
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
|
if plugin_xkb_enabled
|
||||||
mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]}
|
mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]}
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -39,6 +39,7 @@ struct private
|
||||||
bool repeat;
|
bool repeat;
|
||||||
bool random;
|
bool random;
|
||||||
bool consume;
|
bool consume;
|
||||||
|
bool single;
|
||||||
int volume;
|
int volume;
|
||||||
char *album;
|
char *album;
|
||||||
char *artist;
|
char *artist;
|
||||||
|
@ -176,6 +177,7 @@ content(struct module *mod)
|
||||||
tag_new_bool(mod, "repeat", m->repeat),
|
tag_new_bool(mod, "repeat", m->repeat),
|
||||||
tag_new_bool(mod, "random", m->random),
|
tag_new_bool(mod, "random", m->random),
|
||||||
tag_new_bool(mod, "consume", m->consume),
|
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_int_range(mod, "volume", m->volume, 0, 100),
|
||||||
tag_new_string(mod, "album", m->album),
|
tag_new_string(mod, "album", m->album),
|
||||||
tag_new_string(mod, "artist", m->artist),
|
tag_new_string(mod, "artist", m->artist),
|
||||||
|
@ -187,7 +189,7 @@ content(struct module *mod)
|
||||||
tag_new_int_realtime(
|
tag_new_int_realtime(
|
||||||
mod, "elapsed", elapsed, 0, m->duration, realtime),
|
mod, "elapsed", elapsed, 0, m->duration, realtime),
|
||||||
},
|
},
|
||||||
.count = 13,
|
.count = 14,
|
||||||
};
|
};
|
||||||
|
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
|
@ -336,6 +338,7 @@ update_status(struct module *mod)
|
||||||
m->repeat = mpd_status_get_repeat(status);
|
m->repeat = mpd_status_get_repeat(status);
|
||||||
m->random = mpd_status_get_random(status);
|
m->random = mpd_status_get_random(status);
|
||||||
m->consume = mpd_status_get_consume(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->volume = mpd_status_get_volume(status);
|
||||||
m->duration = mpd_status_get_total_time(status) * 1000;
|
m->duration = mpd_status_get_total_time(status) * 1000;
|
||||||
m->elapsed.value = mpd_status_get_elapsed_ms(status);
|
m->elapsed.value = mpd_status_get_elapsed_ms(status);
|
||||||
|
@ -437,7 +440,7 @@ run(struct module *mod)
|
||||||
*/
|
*/
|
||||||
while (!aborted) {
|
while (!aborted) {
|
||||||
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
||||||
int res = poll(fds, sizeof(fds) / sizeof(fds[0]), 10 * 1000);
|
int res = poll(fds, sizeof(fds) / sizeof(fds[0]), 2 * 1000);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
|
@ -448,10 +451,16 @@ run(struct module *mod)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res == 1) {
|
if (res == 0) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (res == 1) {
|
||||||
assert(fds[0].revents & POLLIN);
|
assert(fds[0].revents & POLLIN);
|
||||||
aborted = true;
|
aborted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1100
modules/mpris.c
Normal file
1100
modules/mpris.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_arp.h>
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
#include <linux/nl80211.h>
|
#include <linux/nl80211.h>
|
||||||
#include <linux/rtnetlink.h>
|
#include <linux/rtnetlink.h>
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
#include <tllist.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
#define LOG_MODULE "network"
|
#define LOG_MODULE "network"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 1
|
||||||
#include "../bar/bar.h"
|
#include "../bar/bar.h"
|
||||||
#include "../config-verify.h"
|
#include "../config-verify.h"
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
|
@ -52,6 +53,8 @@ struct af_addr {
|
||||||
|
|
||||||
struct iface {
|
struct iface {
|
||||||
char *name;
|
char *name;
|
||||||
|
char *type; /* ARPHRD_NNN */
|
||||||
|
char *kind; /* IFLA_LINKINFO::IFLA_INFO_KIND */
|
||||||
|
|
||||||
uint32_t get_stats_seq_nr;
|
uint32_t get_stats_seq_nr;
|
||||||
|
|
||||||
|
@ -104,6 +107,8 @@ free_iface(struct iface iface)
|
||||||
{
|
{
|
||||||
tll_free(iface.addrs);
|
tll_free(iface.addrs);
|
||||||
free(iface.ssid);
|
free(iface.ssid);
|
||||||
|
free(iface.kind);
|
||||||
|
free(iface.type);
|
||||||
free(iface.name);
|
free(iface.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +124,10 @@ destroy(struct module *mod)
|
||||||
if (m->urandom_fd >= 0)
|
if (m->urandom_fd >= 0)
|
||||||
close(m->urandom_fd);
|
close(m->urandom_fd);
|
||||||
|
|
||||||
tll_foreach(m->ifaces, it) free_iface(it->item);
|
tll_foreach(m->ifaces, it) {
|
||||||
|
free_iface(it->item);
|
||||||
|
tll_remove(m->ifaces, it);
|
||||||
|
}
|
||||||
|
|
||||||
free(m);
|
free(m);
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
|
@ -204,6 +212,8 @@ content(struct module *mod)
|
||||||
struct tag_set tags = {
|
struct tag_set tags = {
|
||||||
.tags = (struct tag *[]){
|
.tags = (struct tag *[]){
|
||||||
tag_new_string(mod, "name", iface->name),
|
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_int(mod, "index", iface->index),
|
||||||
tag_new_bool(mod, "carrier", iface->carrier),
|
tag_new_bool(mod, "carrier", iface->carrier),
|
||||||
tag_new_string(mod, "state", state),
|
tag_new_string(mod, "state", state),
|
||||||
|
@ -218,7 +228,7 @@ content(struct module *mod)
|
||||||
tag_new_float(mod, "dl-speed", iface->dl_speed),
|
tag_new_float(mod, "dl-speed", iface->dl_speed),
|
||||||
tag_new_float(mod, "ul-speed", iface->ul_speed),
|
tag_new_float(mod, "ul-speed", iface->ul_speed),
|
||||||
},
|
},
|
||||||
.count = 14,
|
.count = 16,
|
||||||
};
|
};
|
||||||
exposables[idx++] = m->label->instantiate(m->label, &tags);
|
exposables[idx++] = m->label->instantiate(m->label, &tags);
|
||||||
tag_set_destroy(&tags);
|
tag_set_destroy(&tags);
|
||||||
|
@ -546,6 +556,79 @@ send_nl80211_get_scan(struct private *m)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len,
|
||||||
|
bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload,
|
||||||
|
size_t len, void *ctx),
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN;
|
||||||
|
const uint8_t *end = (const uint8_t *)genl + len;
|
||||||
|
|
||||||
|
for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end;
|
||||||
|
raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) {
|
||||||
|
uint16_t type = attr->nla_type & NLA_TYPE_MASK;
|
||||||
|
bool nested = (attr->nla_type & NLA_F_NESTED) != 0;
|
||||||
|
;
|
||||||
|
const void *payload = raw + NLA_HDRLEN;
|
||||||
|
|
||||||
|
if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len,
|
||||||
|
bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested,
|
||||||
|
const void *payload, size_t len, void *ctx),
|
||||||
|
void *ctx)
|
||||||
|
{
|
||||||
|
const uint8_t *raw = parent_payload;
|
||||||
|
const uint8_t *end = parent_payload + len;
|
||||||
|
|
||||||
|
for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end;
|
||||||
|
raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) {
|
||||||
|
uint16_t type = attr->nla_type & NLA_TYPE_MASK;
|
||||||
|
bool nested = (attr->nla_type & NLA_F_NESTED) != 0;
|
||||||
|
const void *payload = raw + NLA_HDRLEN;
|
||||||
|
|
||||||
|
if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_linkinfo(struct module *mod, struct iface *iface, uint16_t type,
|
||||||
|
bool nested, const void *payload, size_t len, void *_void)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case IFLA_INFO_KIND: {
|
||||||
|
const char *kind = payload;
|
||||||
|
free(iface->kind);
|
||||||
|
iface->kind = strndup(kind, len);
|
||||||
|
|
||||||
|
LOG_DBG("%s: IFLA_INFO_KIND: %s", iface->name, iface->kind);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IFLA_INFO_DATA:
|
||||||
|
//LOG_DBG("%s: IFLA_INFO_DATA", iface->name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_WARN("unrecognized IFLA_LINKINFO attribute: "
|
||||||
|
"type=%hu, nested=%d, len=%zu",
|
||||||
|
type, nested, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
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)
|
||||||
{
|
{
|
||||||
|
@ -578,9 +661,31 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iface == NULL) {
|
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);
|
mtx_lock(&mod->lock);
|
||||||
tll_push_back(m->ifaces, ((struct iface){
|
tll_push_back(m->ifaces, ((struct iface){
|
||||||
.index = msg->ifi_index,
|
.index = msg->ifi_index,
|
||||||
|
.type = type,
|
||||||
.state = IF_OPER_DOWN,
|
.state = IF_OPER_DOWN,
|
||||||
.addrs = tll_init(),
|
.addrs = tll_init(),
|
||||||
}));
|
}));
|
||||||
|
@ -593,8 +698,10 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size
|
||||||
case IFLA_IFNAME:
|
case IFLA_IFNAME:
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
iface->name = strdup((const char *)RTA_DATA(attr));
|
iface->name = strdup((const char *)RTA_DATA(attr));
|
||||||
LOG_DBG("%s: index=%d", iface->name, iface->index);
|
LOG_DBG("%s: index=%d, type=%s", iface->name, iface->index, iface->type);
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
|
break;
|
||||||
|
|
||||||
case IFLA_OPERSTATE: {
|
case IFLA_OPERSTATE: {
|
||||||
uint8_t operstate = *(const uint8_t *)RTA_DATA(attr);
|
uint8_t operstate = *(const uint8_t *)RTA_DATA(attr);
|
||||||
if (iface->state == operstate)
|
if (iface->state == operstate)
|
||||||
|
@ -629,7 +736,8 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size
|
||||||
if (memcmp(iface->mac, mac, sizeof(iface->mac)) == 0)
|
if (memcmp(iface->mac, mac, sizeof(iface->mac)) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x", iface->name, mac[0], mac[1], mac[2], mac[3],
|
LOG_DBG("%s: IFLA_ADDRESS: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
iface->name, mac[0], mac[1], mac[2], mac[3],
|
||||||
mac[4], mac[5]);
|
mac[4], mac[5]);
|
||||||
|
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
|
@ -637,6 +745,13 @@ handle_link(struct module *mod, uint16_t type, const struct ifinfomsg *msg, size
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IFLA_LINKINFO: {
|
||||||
|
foreach_nlattr_nested(
|
||||||
|
mod, iface, RTA_DATA(attr), RTA_PAYLOAD(attr),
|
||||||
|
&parse_linkinfo, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,7 +784,7 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iface == NULL) {
|
if (iface == NULL) {
|
||||||
LOG_ERR("failed to find network interface with index %d. Probaly a yambar bug", msg->ifa_index);
|
LOG_ERR("failed to find network interface with index %d. Probably a yambar bug", msg->ifa_index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,51 +834,6 @@ handle_address(struct module *mod, uint16_t type, const struct ifaddrmsg *msg, s
|
||||||
mod->bar->refresh(mod->bar);
|
mod->bar->refresh(mod->bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
foreach_nlattr(struct module *mod, struct iface *iface, const struct genlmsghdr *genl, size_t len,
|
|
||||||
bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested, const void *payload,
|
|
||||||
size_t len, void *ctx),
|
|
||||||
void *ctx)
|
|
||||||
{
|
|
||||||
const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN;
|
|
||||||
const uint8_t *end = (const uint8_t *)genl + len;
|
|
||||||
|
|
||||||
for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end;
|
|
||||||
raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) {
|
|
||||||
uint16_t type = attr->nla_type & NLA_TYPE_MASK;
|
|
||||||
bool nested = (attr->nla_type & NLA_F_NESTED) != 0;
|
|
||||||
;
|
|
||||||
const void *payload = raw + NLA_HDRLEN;
|
|
||||||
|
|
||||||
if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
foreach_nlattr_nested(struct module *mod, struct iface *iface, const void *parent_payload, size_t len,
|
|
||||||
bool (*cb)(struct module *mod, struct iface *iface, uint16_t type, bool nested,
|
|
||||||
const void *payload, size_t len, void *ctx),
|
|
||||||
void *ctx)
|
|
||||||
{
|
|
||||||
const uint8_t *raw = parent_payload;
|
|
||||||
const uint8_t *end = parent_payload + len;
|
|
||||||
|
|
||||||
for (const struct nlattr *attr = (const struct nlattr *)raw; raw < end;
|
|
||||||
raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw) {
|
|
||||||
uint16_t type = attr->nla_type & NLA_TYPE_MASK;
|
|
||||||
bool nested = (attr->nla_type & NLA_F_NESTED) != 0;
|
|
||||||
const void *payload = raw + NLA_HDRLEN;
|
|
||||||
|
|
||||||
if (!cb(mod, iface, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mcast_group {
|
struct mcast_group {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -1300,6 +1370,8 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
LOG_DBG("%s: got interface information", iface->name);
|
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);
|
foreach_nlattr(mod, iface, genl, msg_size, &handle_nl80211_new_interface, NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1504,7 +1576,7 @@ out:
|
||||||
static struct module *
|
static struct module *
|
||||||
network_new(struct particle *label, int poll_interval, int left_spacing, int right_spacing)
|
network_new(struct particle *label, int poll_interval, int left_spacing, int right_spacing)
|
||||||
{
|
{
|
||||||
int urandom_fd = open("/dev/urandom", O_RDONLY);
|
int urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
||||||
if (urandom_fd < 0) {
|
if (urandom_fd < 0) {
|
||||||
LOG_ERRNO("failed to open /dev/urandom");
|
LOG_ERRNO("failed to open /dev/urandom");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
377
modules/niri-common.c
Normal file
377
modules/niri-common.c
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/eventfd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <threads.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
45
modules/niri-common.h
Normal file
45
modules/niri-common.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <threads.h>
|
||||||
|
#include <tllist.h>
|
||||||
|
|
||||||
|
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);
|
160
modules/niri-language.c
Normal file
160
modules/niri-language.c
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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
|
163
modules/niri-workspaces.c
Normal file
163
modules/niri-workspaces.c
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -33,7 +33,7 @@ struct output_informations {
|
||||||
uint32_t device_id;
|
uint32_t device_id;
|
||||||
uint32_t card_profile_device_id;
|
uint32_t card_profile_device_id;
|
||||||
|
|
||||||
/* informations */
|
/* information */
|
||||||
bool muted;
|
bool muted;
|
||||||
uint16_t linear_volume; /* classic volume */
|
uint16_t linear_volume; /* classic volume */
|
||||||
uint16_t cubic_volume; /* volume a la pulseaudio */
|
uint16_t cubic_volume; /* volume a la pulseaudio */
|
||||||
|
@ -45,11 +45,23 @@ struct output_informations {
|
||||||
};
|
};
|
||||||
static struct output_informations const output_informations_null;
|
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 data;
|
||||||
struct private
|
struct private
|
||||||
{
|
{
|
||||||
struct particle *label;
|
struct particle *label;
|
||||||
struct data *data;
|
struct data *data;
|
||||||
|
int left_spacing;
|
||||||
|
int right_spacing;
|
||||||
|
|
||||||
/* pipewire related */
|
/* pipewire related */
|
||||||
struct output_informations sink_informations;
|
struct output_informations sink_informations;
|
||||||
|
@ -213,18 +225,23 @@ node_find_route(struct data *data, bool is_sink)
|
||||||
static void
|
static void
|
||||||
node_unhook_binded_node(struct data *data, bool is_sink)
|
node_unhook_binded_node(struct data *data, bool is_sink)
|
||||||
{
|
{
|
||||||
|
struct private *private = data->module->private;
|
||||||
|
|
||||||
struct node **target_node = NULL;
|
struct node **target_node = NULL;
|
||||||
struct spa_hook *target_listener = NULL;
|
struct spa_hook *target_listener = NULL;
|
||||||
void **target_proxy = NULL;
|
void **target_proxy = NULL;
|
||||||
|
struct output_informations *output_informations = NULL;
|
||||||
|
|
||||||
if (is_sink) {
|
if (is_sink) {
|
||||||
target_node = &data->binded_sink;
|
target_node = &data->binded_sink;
|
||||||
target_listener = &data->node_sink_listener;
|
target_listener = &data->node_sink_listener;
|
||||||
target_proxy = &data->node_sink;
|
target_proxy = &data->node_sink;
|
||||||
|
output_informations = &private->sink_informations;
|
||||||
} else {
|
} else {
|
||||||
target_node = &data->binded_source;
|
target_node = &data->binded_source;
|
||||||
target_listener = &data->node_source_listener;
|
target_listener = &data->node_source_listener;
|
||||||
target_proxy = &data->node_source;
|
target_proxy = &data->node_source;
|
||||||
|
output_informations = &private->source_informations;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*target_node == NULL)
|
if (*target_node == NULL)
|
||||||
|
@ -235,6 +252,9 @@ node_unhook_binded_node(struct data *data, bool is_sink)
|
||||||
|
|
||||||
*target_node = NULL;
|
*target_node = NULL;
|
||||||
*target_proxy = NULL;
|
*target_proxy = NULL;
|
||||||
|
|
||||||
|
output_informations_destroy(output_informations);
|
||||||
|
*output_informations = output_informations_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -333,7 +353,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32
|
||||||
X_FREE_SET(route->icon_name, X_STRDUP(data.icon_name));
|
X_FREE_SET(route->icon_name, X_STRDUP(data.icon_name));
|
||||||
route->direction = data.direction;
|
route->direction = data.direction;
|
||||||
|
|
||||||
/* set missing informations if possible */
|
/* set missing information if possible */
|
||||||
struct private *private = device->data->module->private;
|
struct private *private = device->data->module->private;
|
||||||
struct node *binded_node = NULL;
|
struct node *binded_node = NULL;
|
||||||
struct output_informations *output_informations = NULL;
|
struct output_informations *output_informations = NULL;
|
||||||
|
@ -350,7 +370,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32
|
||||||
if (binded_node == NULL)
|
if (binded_node == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Node's device is the the same as route's device */
|
/* Node's device is the same as route's device */
|
||||||
if (output_informations->device_id != route->device->id)
|
if (output_informations->device_id != route->device->id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -358,7 +378,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32
|
||||||
if (output_informations->card_profile_device_id != route->profile_device_id)
|
if (output_informations->card_profile_device_id != route->profile_device_id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Update missing informations */
|
/* Update missing information */
|
||||||
X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor));
|
X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor));
|
||||||
X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name));
|
X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name));
|
||||||
|
|
||||||
|
@ -384,7 +404,7 @@ node_events_info(void *userdata, struct pw_node_info const *info)
|
||||||
for (size_t i = 0; i < info->n_params; ++i) {
|
for (size_t i = 0; i < info->n_params; ++i) {
|
||||||
if (info->params[i].id == SPA_PARAM_Props) {
|
if (info->params[i].id == SPA_PARAM_Props) {
|
||||||
void *target_node = (node_data->is_sink ? data->node_sink : data->node_source);
|
void *target_node = (node_data->is_sink ? data->node_sink : data->node_source);
|
||||||
/* Found it, will emit a param event, the parem will then be handled
|
/* Found it, will emit a param event, the param will then be handled
|
||||||
* in node_events_param */
|
* in node_events_param */
|
||||||
pw_node_enum_params(target_node, 0, info->params[i].id, 0, -1, NULL);
|
pw_node_enum_params(target_node, 0, info->params[i].id, 0, -1, NULL);
|
||||||
break;
|
break;
|
||||||
|
@ -398,18 +418,18 @@ node_events_info(void *userdata, struct pw_node_info const *info)
|
||||||
struct spa_dict_item const *item = NULL;
|
struct spa_dict_item const *item = NULL;
|
||||||
|
|
||||||
item = spa_dict_lookup_item(info->props, "node.name");
|
item = spa_dict_lookup_item(info->props, "node.name");
|
||||||
if (item != NULL)
|
X_FREE_SET(output_informations->name, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||||
X_FREE_SET(output_informations->name, X_STRDUP(item->value));
|
|
||||||
|
|
||||||
item = spa_dict_lookup_item(info->props, "node.description");
|
item = spa_dict_lookup_item(info->props, "node.description");
|
||||||
if (item != NULL)
|
X_FREE_SET(output_informations->description, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||||
X_FREE_SET(output_informations->description, X_STRDUP(item->value));
|
|
||||||
|
|
||||||
item = spa_dict_lookup_item(info->props, "device.id");
|
item = spa_dict_lookup_item(info->props, "device.id");
|
||||||
if (item != NULL) {
|
if (item != NULL) {
|
||||||
uint32_t value = 0;
|
uint32_t value = 0;
|
||||||
spa_atou32(item->value, &value, 10);
|
spa_atou32(item->value, &value, 10);
|
||||||
output_informations->device_id = value;
|
output_informations->device_id = value;
|
||||||
|
} else {
|
||||||
|
output_informations->device_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
item = spa_dict_lookup_item(info->props, "card.profile.device");
|
item = spa_dict_lookup_item(info->props, "card.profile.device");
|
||||||
|
@ -417,30 +437,29 @@ node_events_info(void *userdata, struct pw_node_info const *info)
|
||||||
uint32_t value = 0;
|
uint32_t value = 0;
|
||||||
spa_atou32(item->value, &value, 10);
|
spa_atou32(item->value, &value, 10);
|
||||||
output_informations->card_profile_device_id = value;
|
output_informations->card_profile_device_id = value;
|
||||||
|
} else {
|
||||||
|
output_informations->card_profile_device_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Device's informations has an more important priority than node's informations */
|
/* Device's information has an more important priority than node's information */
|
||||||
/* icon_name */
|
/* icon_name */
|
||||||
struct route *route = node_find_route(data, node_data->is_sink);
|
struct route *route = node_find_route(data, node_data->is_sink);
|
||||||
if (route != NULL && route->icon_name != NULL)
|
if (route != NULL && route->icon_name != NULL)
|
||||||
output_informations->icon = X_STRDUP(route->icon_name);
|
X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name));
|
||||||
else {
|
else {
|
||||||
item = spa_dict_lookup_item(info->props, "device.icon-name");
|
item = spa_dict_lookup_item(info->props, "device.icon-name");
|
||||||
if (item != NULL)
|
X_FREE_SET(output_informations->icon, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||||
X_FREE_SET(output_informations->icon, X_STRDUP(item->value));
|
|
||||||
}
|
}
|
||||||
/* form_factor */
|
/* form_factor */
|
||||||
if (route != NULL && route->form_factor != NULL)
|
if (route != NULL && route->form_factor != NULL)
|
||||||
output_informations->form_factor = X_STRDUP(route->form_factor);
|
X_FREE_SET(output_informations->form_factor, X_STRDUP(route->form_factor));
|
||||||
else {
|
else {
|
||||||
item = spa_dict_lookup_item(info->props, "device.form-factor");
|
item = spa_dict_lookup_item(info->props, "device.form-factor");
|
||||||
if (item != NULL)
|
X_FREE_SET(output_informations->form_factor, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||||
X_FREE_SET(output_informations->form_factor, X_STRDUP(item->value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item = spa_dict_lookup_item(info->props, "device.bus");
|
item = spa_dict_lookup_item(info->props, "device.bus");
|
||||||
if (item != NULL)
|
X_FREE_SET(output_informations->bus, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||||
X_FREE_SET(output_informations->bus, X_STRDUP(item->value));
|
|
||||||
|
|
||||||
data->module->bar->refresh(data->module->bar);
|
data->module->bar->refresh(data->module->bar);
|
||||||
}
|
}
|
||||||
|
@ -659,7 +678,7 @@ static void
|
||||||
try_to_bind_node(struct node_data *node_data, char const *target_name, struct node **target_node, void **target_proxy,
|
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)
|
struct spa_hook *target_listener)
|
||||||
{
|
{
|
||||||
/* profile deactived */
|
/* profile deactivated */
|
||||||
if (target_name == NULL)
|
if (target_name == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -827,18 +846,8 @@ destroy(struct module *module)
|
||||||
pipewire_deinit(private->data);
|
pipewire_deinit(private->data);
|
||||||
private->label->destroy(private->label);
|
private->label->destroy(private->label);
|
||||||
|
|
||||||
/* sink */
|
output_informations_destroy(&private->sink_informations);
|
||||||
free(private->sink_informations.name);
|
output_informations_destroy(&private->source_informations);
|
||||||
free(private->sink_informations.description);
|
|
||||||
free(private->sink_informations.icon);
|
|
||||||
free(private->sink_informations.form_factor);
|
|
||||||
free(private->sink_informations.bus);
|
|
||||||
/* source */
|
|
||||||
free(private->source_informations.name);
|
|
||||||
free(private->source_informations.description);
|
|
||||||
free(private->source_informations.icon);
|
|
||||||
free(private->source_informations.form_factor);
|
|
||||||
free(private->source_informations.bus);
|
|
||||||
|
|
||||||
free(private);
|
free(private);
|
||||||
module_default_destroy(module);
|
module_default_destroy(module);
|
||||||
|
@ -911,7 +920,7 @@ content(struct module *module)
|
||||||
|
|
||||||
mtx_unlock(&module->lock);
|
mtx_unlock(&module->lock);
|
||||||
|
|
||||||
return dynlist_exposable_new(exposables, exposables_length, 0, 0);
|
return dynlist_exposable_new(exposables, exposables_length, private->left_spacing, private->right_spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -958,11 +967,13 @@ run(struct module *module)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct module *
|
static struct module *
|
||||||
pipewire_new(struct particle *label)
|
pipewire_new(struct particle *label, int left_spacing, int right_spacing)
|
||||||
{
|
{
|
||||||
struct private *private = calloc(1, sizeof(struct private));
|
struct private *private = calloc(1, sizeof(struct private));
|
||||||
assert(private != NULL);
|
assert(private != NULL);
|
||||||
private->label = label;
|
private->label = label;
|
||||||
|
private->left_spacing = left_spacing;
|
||||||
|
private->right_spacing = right_spacing;
|
||||||
|
|
||||||
struct module *module = module_common_new();
|
struct module *module = module_common_new();
|
||||||
module->private = private;
|
module->private = private;
|
||||||
|
@ -980,13 +991,25 @@ static struct module *
|
||||||
from_conf(struct yml_node const *node, struct conf_inherit inherited)
|
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 *content = yml_get_value(node, "content");
|
||||||
return pipewire_new(conf_to_particle(content, inherited));
|
struct yml_node const *spacing = yml_get_value(node, "spacing");
|
||||||
|
struct yml_node const *left_spacing = yml_get_value(node, "left-spacing");
|
||||||
|
struct yml_node const *right_spacing = yml_get_value(node, "right-spacing");
|
||||||
|
|
||||||
|
int left = spacing != NULL ? yml_value_as_int(spacing) : left_spacing != NULL ? yml_value_as_int(left_spacing) : 0;
|
||||||
|
int right = spacing != NULL ? yml_value_as_int(spacing)
|
||||||
|
: right_spacing != NULL ? yml_value_as_int(right_spacing)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return pipewire_new(conf_to_particle(content, inherited), left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
verify_conf(keychain_t *keychain, struct yml_node const *node)
|
verify_conf(keychain_t *keychain, struct yml_node const *node)
|
||||||
{
|
{
|
||||||
static struct attr_info const attrs[] = {
|
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,
|
MODULE_COMMON_ATTRS,
|
||||||
};
|
};
|
||||||
return conf_verify_dict(keychain, node, attrs);
|
return conf_verify_dict(keychain, node, attrs);
|
||||||
|
|
|
@ -438,7 +438,7 @@ run(struct module *mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create refresh timer.
|
// Create refresh timer.
|
||||||
priv->refresh_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
priv->refresh_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||||
if (priv->refresh_timer_fd < 0) {
|
if (priv->refresh_timer_fd < 0) {
|
||||||
LOG_ERRNO("failed to create timerfd");
|
LOG_ERRNO("failed to create timerfd");
|
||||||
pa_mainloop_free(priv->mainloop);
|
pa_mainloop_free(priv->mainloop);
|
||||||
|
|
|
@ -161,13 +161,10 @@ content(struct module *mod)
|
||||||
static void
|
static void
|
||||||
find_mount_points(const char *dev_path, mount_point_list_t *mount_points)
|
find_mount_points(const char *dev_path, mount_point_list_t *mount_points)
|
||||||
{
|
{
|
||||||
int fd = open("/proc/self/mountinfo", O_RDONLY | O_CLOEXEC);
|
FILE *f = fopen("/proc/self/mountinfo", "re");
|
||||||
FILE *f = fd >= 0 ? fdopen(fd, "r") : NULL;
|
|
||||||
|
|
||||||
if (fd < 0 || f == NULL) {
|
if (f == NULL) {
|
||||||
LOG_ERRNO("failed to open /proc/self/mountinfo");
|
LOG_ERRNO("failed to open /proc/self/mountinfo");
|
||||||
if (fd >= 0)
|
|
||||||
close(fd);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ struct seat {
|
||||||
struct private
|
struct private
|
||||||
{
|
{
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
|
bool is_running;
|
||||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||||
struct zriver_status_manager_v1 *status_manager;
|
struct zriver_status_manager_v1 *status_manager;
|
||||||
struct particle *template;
|
struct particle *template;
|
||||||
|
@ -88,6 +89,11 @@ content(struct module *mod)
|
||||||
|
|
||||||
mtx_lock(&m->mod->lock);
|
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 urgent = 0;
|
||||||
uint32_t occupied = 0;
|
uint32_t occupied = 0;
|
||||||
uint32_t output_focused = 0;
|
uint32_t output_focused = 0;
|
||||||
|
@ -685,6 +691,8 @@ run(struct module *mod)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m->is_running = true;
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -130,7 +130,7 @@ update_application(struct module *mod)
|
||||||
char path[1024];
|
char path[1024];
|
||||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||||
|
|
||||||
int fd = open(path, O_RDONLY);
|
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,61 @@
|
||||||
|
|
||||||
#include "map.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
|
static bool
|
||||||
int_condition(const long tag_value, const long cond_value, enum map_op op)
|
int_condition(const long tag_value, const long cond_value, enum map_op op)
|
||||||
{
|
{
|
||||||
|
@ -75,6 +130,8 @@ str_condition(const char *tag_value, const char *cond_value, enum map_op op)
|
||||||
return strcmp(tag_value, cond_value) >= 0;
|
return strcmp(tag_value, cond_value) >= 0;
|
||||||
case MAP_OP_GT:
|
case MAP_OP_GT:
|
||||||
return strcmp(tag_value, cond_value) > 0;
|
return strcmp(tag_value, cond_value) > 0;
|
||||||
|
case MAP_OP_LIKE:
|
||||||
|
return string_like(tag_value, cond_value) != 0;
|
||||||
case MAP_OP_SELF:
|
case MAP_OP_SELF:
|
||||||
LOG_WARN("using String tag as bool");
|
LOG_WARN("using String tag as bool");
|
||||||
default:
|
default:
|
||||||
|
@ -166,6 +223,7 @@ free_map_condition(struct map_condition *c)
|
||||||
case MAP_OP_LE:
|
case MAP_OP_LE:
|
||||||
case MAP_OP_LT:
|
case MAP_OP_LT:
|
||||||
case MAP_OP_GE:
|
case MAP_OP_GE:
|
||||||
|
case MAP_OP_LIKE:
|
||||||
case MAP_OP_GT:
|
case MAP_OP_GT:
|
||||||
free(c->value);
|
free(c->value);
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
|
|
|
@ -9,6 +9,7 @@ enum map_op {
|
||||||
MAP_OP_GT,
|
MAP_OP_GT,
|
||||||
MAP_OP_SELF,
|
MAP_OP_SELF,
|
||||||
MAP_OP_NOT,
|
MAP_OP_NOT,
|
||||||
|
MAP_OP_LIKE,
|
||||||
|
|
||||||
MAP_OP_AND,
|
MAP_OP_AND,
|
||||||
MAP_OP_OR,
|
MAP_OP_OR,
|
||||||
|
|
|
@ -69,6 +69,7 @@ void yyerror(const char *s);
|
||||||
\< yylval.op = MAP_OP_LT; return CMP_OP;
|
\< yylval.op = MAP_OP_LT; return CMP_OP;
|
||||||
>= yylval.op = MAP_OP_GE; return CMP_OP;
|
>= yylval.op = MAP_OP_GE; return CMP_OP;
|
||||||
> yylval.op = MAP_OP_GT; 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_AND; return BOOL_OP;
|
||||||
\|\| yylval.op = MAP_OP_OR; return BOOL_OP;
|
\|\| yylval.op = MAP_OP_OR; return BOOL_OP;
|
||||||
~ return NOT;
|
~ return NOT;
|
||||||
|
|
|
@ -35,27 +35,27 @@ result: condition { MAP_CONDITION_PARSE_RESULT = $<condition>1; };
|
||||||
condition:
|
condition:
|
||||||
WORD {
|
WORD {
|
||||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||||
$<condition>$->tag = $<str>1;
|
$<condition>$->tag = $<str>1;
|
||||||
$<condition>$->op = MAP_OP_SELF;
|
$<condition>$->op = MAP_OP_SELF;
|
||||||
}
|
}
|
||||||
|
|
|
|
||||||
WORD CMP_OP WORD {
|
WORD CMP_OP WORD {
|
||||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||||
$<condition>$->tag = $<str>1;
|
$<condition>$->tag = $<str>1;
|
||||||
$<condition>$->op = $<op>2;
|
$<condition>$->op = $<op>2;
|
||||||
$<condition>$->value = $<str>3;
|
$<condition>$->value = $<str>3;
|
||||||
}
|
}
|
||||||
|
|
|
|
||||||
WORD CMP_OP STRING {
|
WORD CMP_OP STRING {
|
||||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||||
$<condition>$->tag = $<str>1;
|
$<condition>$->tag = $<str>1;
|
||||||
$<condition>$->op = $<op>2;
|
$<condition>$->op = $<op>2;
|
||||||
$<condition>$->value = $<str>3;
|
$<condition>$->value = $<str>3;
|
||||||
}
|
}
|
||||||
|
|
|
|
||||||
L_PAR condition R_PAR { $<condition>$ = $<condition>2; }
|
L_PAR condition R_PAR { $<condition>$ = $<condition>2; }
|
||||||
|
|
|
|
||||||
NOT condition {
|
NOT condition {
|
||||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||||
$<condition>$->cond1 = $<condition>2;
|
$<condition>$->cond1 = $<condition>2;
|
||||||
$<condition>$->op = MAP_OP_NOT;
|
$<condition>$->op = MAP_OP_NOT;
|
||||||
|
@ -79,7 +79,7 @@ static char const*
|
||||||
token_to_str(yysymbol_kind_t tkn)
|
token_to_str(yysymbol_kind_t tkn)
|
||||||
{
|
{
|
||||||
switch (tkn) {
|
switch (tkn) {
|
||||||
case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >";
|
case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >, ~~";
|
||||||
case YYSYMBOL_BOOL_OP: return "||, &&";
|
case YYSYMBOL_BOOL_OP: return "||, &&";
|
||||||
case YYSYMBOL_L_PAR: return "(";
|
case YYSYMBOL_L_PAR: return "(";
|
||||||
case YYSYMBOL_R_PAR: return ")";
|
case YYSYMBOL_R_PAR: return ")";
|
||||||
|
|
18
plugin.c
18
plugin.c
|
@ -57,6 +57,9 @@ EXTERN_MODULE(mem);
|
||||||
#if defined(HAVE_PLUGIN_mpd)
|
#if defined(HAVE_PLUGIN_mpd)
|
||||||
EXTERN_MODULE(mpd);
|
EXTERN_MODULE(mpd);
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_PLUGIN_mpris)
|
||||||
|
EXTERN_MODULE(mpris);
|
||||||
|
#endif
|
||||||
#if defined(HAVE_PLUGIN_i3)
|
#if defined(HAVE_PLUGIN_i3)
|
||||||
EXTERN_MODULE(i3);
|
EXTERN_MODULE(i3);
|
||||||
#endif
|
#endif
|
||||||
|
@ -84,6 +87,12 @@ EXTERN_MODULE(script);
|
||||||
#if defined(HAVE_PLUGIN_sway_xkb)
|
#if defined(HAVE_PLUGIN_sway_xkb)
|
||||||
EXTERN_MODULE(sway_xkb);
|
EXTERN_MODULE(sway_xkb);
|
||||||
#endif
|
#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)
|
#if defined(HAVE_PLUGIN_xkb)
|
||||||
EXTERN_MODULE(xkb);
|
EXTERN_MODULE(xkb);
|
||||||
#endif
|
#endif
|
||||||
|
@ -187,6 +196,9 @@ static void __attribute__((constructor)) init(void)
|
||||||
#if defined(HAVE_PLUGIN_mpd)
|
#if defined(HAVE_PLUGIN_mpd)
|
||||||
REGISTER_CORE_MODULE(mpd, mpd);
|
REGISTER_CORE_MODULE(mpd, mpd);
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_PLUGIN_mpris)
|
||||||
|
REGISTER_CORE_MODULE(mpris, mpris);
|
||||||
|
#endif
|
||||||
#if defined(HAVE_PLUGIN_i3)
|
#if defined(HAVE_PLUGIN_i3)
|
||||||
REGISTER_CORE_MODULE(i3, i3);
|
REGISTER_CORE_MODULE(i3, i3);
|
||||||
#endif
|
#endif
|
||||||
|
@ -214,6 +226,12 @@ static void __attribute__((constructor)) init(void)
|
||||||
#if defined(HAVE_PLUGIN_sway_xkb)
|
#if defined(HAVE_PLUGIN_sway_xkb)
|
||||||
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_PLUGIN_niri_language)
|
||||||
|
REGISTER_CORE_MODULE(niri-language, niri_language);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_PLUGIN_niri_workspaces)
|
||||||
|
REGISTER_CORE_MODULE(niri-workspaces, niri_workspaces);
|
||||||
|
#endif
|
||||||
#if defined(HAVE_PLUGIN_xkb)
|
#if defined(HAVE_PLUGIN_xkb)
|
||||||
REGISTER_CORE_MODULE(xkb, xkb);
|
REGISTER_CORE_MODULE(xkb, xkb);
|
||||||
#endif
|
#endif
|
||||||
|
|
89
tag.c
89
tag.c
|
@ -430,12 +430,12 @@ sbuf_append(struct sbuf *s1, const char *s2)
|
||||||
|
|
||||||
// stores the number in "*value" on success
|
// stores the number in "*value" on success
|
||||||
static bool
|
static bool
|
||||||
is_number(const char *str, int *value)
|
is_number(const char *str, long *value)
|
||||||
{
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
char *end;
|
char *end;
|
||||||
int v = strtol(str, &end, 10);
|
long v = strtol(str, &end, 10);
|
||||||
if (errno != 0 || *end != '\0')
|
if (errno != 0 || *end != '\0')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -510,12 +510,7 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
FMT_HEX,
|
FMT_HEX,
|
||||||
FMT_OCT,
|
FMT_OCT,
|
||||||
FMT_PERCENT,
|
FMT_PERCENT,
|
||||||
FMT_KBYTE,
|
FMT_DIVIDE,
|
||||||
FMT_MBYTE,
|
|
||||||
FMT_GBYTE,
|
|
||||||
FMT_KIBYTE,
|
|
||||||
FMT_MIBYTE,
|
|
||||||
FMT_GIBYTE,
|
|
||||||
} format
|
} format
|
||||||
= FMT_DEFAULT;
|
= FMT_DEFAULT;
|
||||||
|
|
||||||
|
@ -527,8 +522,9 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
} kind
|
} kind
|
||||||
= VALUE_VALUE;
|
= VALUE_VALUE;
|
||||||
|
|
||||||
int digits = 0;
|
long digits = 0;
|
||||||
int decimals = 2;
|
long decimals = 2;
|
||||||
|
long divider = 1;
|
||||||
bool zero_pad = false;
|
bool zero_pad = false;
|
||||||
char *point = NULL;
|
char *point = NULL;
|
||||||
|
|
||||||
|
@ -541,18 +537,38 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
format = FMT_OCT;
|
format = FMT_OCT;
|
||||||
else if (strcmp(tag_args[i], "%") == 0)
|
else if (strcmp(tag_args[i], "%") == 0)
|
||||||
format = FMT_PERCENT;
|
format = FMT_PERCENT;
|
||||||
else if (strcmp(tag_args[i], "kb") == 0)
|
else if (*tag_args[i] == '/') {
|
||||||
format = FMT_KBYTE;
|
format = FMT_DIVIDE;
|
||||||
else if (strcmp(tag_args[i], "mb") == 0)
|
const char *divider_str = tag_args[i] + 1;
|
||||||
format = FMT_MBYTE;
|
if (!is_number(divider_str, ÷r) || divider == 0) {
|
||||||
else if (strcmp(tag_args[i], "gb") == 0)
|
divider = 1;
|
||||||
format = FMT_GBYTE;
|
LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str);
|
||||||
else if (strcmp(tag_args[i], "kib") == 0)
|
}
|
||||||
format = FMT_KIBYTE;
|
}
|
||||||
else if (strcmp(tag_args[i], "mib") == 0)
|
else if (strcmp(tag_args[i], "kb") == 0) {
|
||||||
format = FMT_MIBYTE;
|
format = FMT_DIVIDE;
|
||||||
else if (strcmp(tag_args[i], "gib") == 0)
|
divider = 1000;
|
||||||
format = FMT_GIBYTE;
|
}
|
||||||
|
else if (strcmp(tag_args[i], "mb") == 0) {
|
||||||
|
format = FMT_DIVIDE;
|
||||||
|
divider = 1000 * 1000;
|
||||||
|
}
|
||||||
|
else if (strcmp(tag_args[i], "gb") == 0) {
|
||||||
|
format = FMT_DIVIDE;
|
||||||
|
divider = 1000 * 1000 * 1000;
|
||||||
|
}
|
||||||
|
else if (strcmp(tag_args[i], "kib") == 0) {
|
||||||
|
format = FMT_DIVIDE;
|
||||||
|
divider = 1024;
|
||||||
|
}
|
||||||
|
else if (strcmp(tag_args[i], "mib") == 0) {
|
||||||
|
format = FMT_DIVIDE;
|
||||||
|
divider = 1024 * 1024;
|
||||||
|
}
|
||||||
|
else if (strcmp(tag_args[i], "gib") == 0) {
|
||||||
|
format = FMT_DIVIDE;
|
||||||
|
divider = 1024 * 1024 * 1024;
|
||||||
|
}
|
||||||
else if (strcmp(tag_args[i], "min") == 0)
|
else if (strcmp(tag_args[i], "min") == 0)
|
||||||
kind = VALUE_MIN;
|
kind = VALUE_MIN;
|
||||||
else if (strcmp(tag_args[i], "max") == 0)
|
else if (strcmp(tag_args[i], "max") == 0)
|
||||||
|
@ -634,20 +650,7 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case FMT_KBYTE:
|
case FMT_DIVIDE: {
|
||||||
case FMT_MBYTE:
|
|
||||||
case FMT_GBYTE:
|
|
||||||
case FMT_KIBYTE:
|
|
||||||
case FMT_MIBYTE:
|
|
||||||
case FMT_GIBYTE: {
|
|
||||||
const long divider = format == FMT_KBYTE ? 1000
|
|
||||||
: format == FMT_MBYTE ? 1000 * 1000
|
|
||||||
: format == FMT_GBYTE ? 1000 * 1000 * 1000
|
|
||||||
: format == FMT_KIBYTE ? 1024
|
|
||||||
: format == FMT_MIBYTE ? 1024 * 1024
|
|
||||||
: format == FMT_GIBYTE ? 1024 * 1024 * 1024
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
char str[24];
|
char str[24];
|
||||||
if (tag->type(tag) == TAG_TYPE_FLOAT) {
|
if (tag->type(tag) == TAG_TYPE_FLOAT) {
|
||||||
const char *fmt = zero_pad ? "%0*.*f" : "%*.*f";
|
const char *fmt = zero_pad ? "%0*.*f" : "%*.*f";
|
||||||
|
@ -684,19 +687,7 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
fmt = zero_pad ? "%0*lu" : "%*lu";
|
fmt = zero_pad ? "%0*lu" : "%*lu";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FMT_KBYTE:
|
case FMT_DIVIDE: {
|
||||||
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;
|
value /= divider;
|
||||||
fmt = zero_pad ? "%0*lu" : "%*lu";
|
fmt = zero_pad ? "%0*lu" : "%*lu";
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -7,4 +7,9 @@ test('no-config', yambar, args: ['-C', '-c', 'xyz'], should_fail: true)
|
||||||
test('config-isnt-file', yambar, args: ['-C', '-c', '.'], should_fail: true)
|
test('config-isnt-file', yambar, args: ['-C', '-c', '.'], should_fail: true)
|
||||||
test('config-no-bar', yambar, args: ['-C', '-c', join_paths(pwd, 'no-bar.yml')],
|
test('config-no-bar', yambar, args: ['-C', '-c', join_paths(pwd, 'no-bar.yml')],
|
||||||
should_fail: true)
|
should_fail: true)
|
||||||
test('full-conf-good', yambar, args: ['-C', '-c', join_paths(pwd, 'full-conf-good.yml')])
|
if plugin_alsa_enabled and plugin_backlight_enabled and \
|
||||||
|
plugin_battery_enabled and plugin_clock_enabled and \
|
||||||
|
plugin_i3_enabled and plugin_mpd_enabled and plugin_network_enabled \
|
||||||
|
and plugin_removables_enabled
|
||||||
|
test('full-conf-good', yambar, args: ['-C', '-c', join_paths(pwd, 'full-conf-good.yml')])
|
||||||
|
endif
|
||||||
|
|
43
yml.c
43
yml.c
|
@ -366,6 +366,47 @@ format_error(enum yml_error err, const struct yml_node *parent, const struct yml
|
||||||
return err_str;
|
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 *
|
struct yml_node *
|
||||||
yml_load(FILE *yml, char **error)
|
yml_load(FILE *yml, char **error)
|
||||||
{
|
{
|
||||||
|
@ -456,7 +497,7 @@ yml_load(FILE *yml, char **error)
|
||||||
case YAML_SCALAR_EVENT: {
|
case YAML_SCALAR_EVENT: {
|
||||||
struct yml_node *new_scalar = calloc(1, sizeof(*new_scalar));
|
struct yml_node *new_scalar = calloc(1, sizeof(*new_scalar));
|
||||||
new_scalar->type = SCALAR;
|
new_scalar->type = SCALAR;
|
||||||
new_scalar->scalar.value = strndup((const char *)event.data.scalar.value, event.data.scalar.length);
|
new_scalar->scalar.value = replace_env_variables((const char *)event.data.scalar.value, event.data.scalar.length);
|
||||||
|
|
||||||
enum yml_error err = add_node(n, new_scalar, event.start_mark);
|
enum yml_error err = add_node(n, new_scalar, event.start_mark);
|
||||||
if (err != YML_ERR_NONE) {
|
if (err != YML_ERR_NONE) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue