mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-19 19:25:41 +02:00
Merge remote-tracking branch 'origin/master' into module_mpris
This commit is contained in:
commit
6fea536354
42 changed files with 1112 additions and 157 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -20,12 +20,22 @@
|
||||||
environment variable.
|
environment variable.
|
||||||
* network: `type` tag ([#380][380]).
|
* network: `type` tag ([#380][380]).
|
||||||
* network: `type` and `kind` tags ([#380][380]).
|
* network: `type` and `kind` tags ([#380][380]).
|
||||||
* tags: `b` tag formatter; divides the tag's decimal value with `8`
|
* tags: `/<N>` tag formatter: divides the tag's decimal value with `N`
|
||||||
([#392][392]).
|
([#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]).
|
||||||
|
|
||||||
[96]: https://codeberg.org/dnkl/yambar/issues/96
|
[96]: https://codeberg.org/dnkl/yambar/issues/96
|
||||||
[380]: https://codeberg.org/dnkl/yambar/issues/380
|
[380]: https://codeberg.org/dnkl/yambar/issues/380
|
||||||
[392]: https://codeberg.org/dnkl/yambar/issues/392
|
[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
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -44,9 +54,12 @@
|
||||||
* i3/sway: crash when output is turned off an on ([#300][300]).
|
* i3/sway: crash when output is turned off an on ([#300][300]).
|
||||||
* mpd: yambar never attempting to reconnect after MPD closed the
|
* mpd: yambar never attempting to reconnect after MPD closed the
|
||||||
connection (for example, when MPD is restarted).
|
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
|
[377]: https://codeberg.org/dnkl/yambar/issues/377
|
||||||
[300]: https://codeberg.org/dnkl/yambar/issues/300
|
[300]: https://codeberg.org/dnkl/yambar/issues/300
|
||||||
|
[424]: https://codeberg.org/dnkl/yambar/pulls/424
|
||||||
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -47,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
|
||||||
|
|
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
|
||||||
|
|
|
@ -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_
|
||||||
|
@ -265,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:
|
||||||
|
|
||||||
[- &&
|
[- &&
|
||||||
|
@ -456,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,10 @@ 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
|
||||||
| b
|
| /N
|
||||||
: format
|
: format
|
||||||
: All tag types
|
: All tag types
|
||||||
: Renders a tag's value (in decimal) divided by 8. Note: no unit
|
: Renders a tag's value (in decimal) divided by N
|
||||||
suffix is appended
|
|
||||||
| kb, mb, gb
|
| kb, mb, gb
|
||||||
: 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
|
||||||
|
|
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;
|
||||||
|
|
|
@ -197,6 +197,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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -664,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);
|
||||||
|
@ -876,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),
|
||||||
|
@ -887,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;
|
||||||
|
|
|
@ -48,6 +48,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()
|
||||||
|
|
||||||
|
@ -128,6 +134,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);
|
||||||
|
|
|
@ -1576,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
|
|
@ -45,6 +45,16 @@ 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
|
||||||
{
|
{
|
||||||
|
@ -213,18 +223,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 +250,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
|
||||||
|
@ -350,7 +368,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;
|
||||||
|
|
||||||
|
@ -398,18 +416,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 +435,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 information has an more important priority than node's information */
|
/* 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);
|
||||||
}
|
}
|
||||||
|
@ -827,18 +844,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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 ")";
|
||||||
|
|
12
plugin.c
12
plugin.c
|
@ -87,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
|
||||||
|
@ -220,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
|
||||||
|
|
112
tag.c
112
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,13 +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_BYTE,
|
FMT_DIVIDE,
|
||||||
FMT_KBYTE,
|
|
||||||
FMT_MBYTE,
|
|
||||||
FMT_GBYTE,
|
|
||||||
FMT_KIBYTE,
|
|
||||||
FMT_MIBYTE,
|
|
||||||
FMT_GIBYTE,
|
|
||||||
} format
|
} format
|
||||||
= FMT_DEFAULT;
|
= FMT_DEFAULT;
|
||||||
|
|
||||||
|
@ -528,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;
|
||||||
|
|
||||||
|
@ -542,20 +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], "b") == 0)
|
else if (*tag_args[i] == '/') {
|
||||||
format = FMT_BYTE;
|
format = FMT_DIVIDE;
|
||||||
else if (strcmp(tag_args[i], "kb") == 0)
|
const char *divider_str = tag_args[i] + 1;
|
||||||
format = FMT_KBYTE;
|
if (!is_number(divider_str, ÷r) || divider == 0) {
|
||||||
else if (strcmp(tag_args[i], "mb") == 0)
|
divider = 1;
|
||||||
format = FMT_MBYTE;
|
LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str);
|
||||||
else if (strcmp(tag_args[i], "gb") == 0)
|
}
|
||||||
format = FMT_GBYTE;
|
}
|
||||||
else if (strcmp(tag_args[i], "kib") == 0)
|
else if (strcmp(tag_args[i], "kb") == 0) {
|
||||||
format = FMT_KIBYTE;
|
format = FMT_DIVIDE;
|
||||||
else if (strcmp(tag_args[i], "mib") == 0)
|
divider = 1000;
|
||||||
format = FMT_MIBYTE;
|
}
|
||||||
else if (strcmp(tag_args[i], "gib") == 0)
|
else if (strcmp(tag_args[i], "mb") == 0) {
|
||||||
format = FMT_GIBYTE;
|
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)
|
||||||
|
@ -637,30 +650,7 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case FMT_BYTE:
|
case FMT_DIVIDE: {
|
||||||
case FMT_KBYTE:
|
|
||||||
case FMT_MBYTE:
|
|
||||||
case FMT_GBYTE:
|
|
||||||
case FMT_KIBYTE:
|
|
||||||
case FMT_MIBYTE:
|
|
||||||
case FMT_GIBYTE: {
|
|
||||||
const long divider =
|
|
||||||
format == FMT_BYTE
|
|
||||||
? 8
|
|
||||||
: format == FMT_KBYTE
|
|
||||||
? 1000
|
|
||||||
: format == FMT_MBYTE
|
|
||||||
? 1000 * 1000
|
|
||||||
: format == FMT_GBYTE
|
|
||||||
? 1000 * 1000 * 1000
|
|
||||||
: format == FMT_KIBYTE
|
|
||||||
? 1024
|
|
||||||
: format == FMT_MIBYTE
|
|
||||||
? 1024 * 1024
|
|
||||||
: format == FMT_GIBYTE
|
|
||||||
? 1024 * 1024 * 1024
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
char str[24];
|
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";
|
||||||
|
@ -697,29 +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_BYTE:
|
case FMT_DIVIDE: {
|
||||||
case FMT_KBYTE:
|
|
||||||
case FMT_MBYTE:
|
|
||||||
case FMT_GBYTE:
|
|
||||||
case FMT_KIBYTE:
|
|
||||||
case FMT_MIBYTE:
|
|
||||||
case FMT_GIBYTE: {
|
|
||||||
const long divider =
|
|
||||||
format == FMT_BYTE
|
|
||||||
? 8
|
|
||||||
: format == FMT_KBYTE
|
|
||||||
? 1024
|
|
||||||
: format == FMT_MBYTE
|
|
||||||
? 1024 * 1024
|
|
||||||
: format == FMT_GBYTE
|
|
||||||
? 1024 * 1024 * 1024
|
|
||||||
: format == FMT_KIBYTE
|
|
||||||
? 1000
|
|
||||||
: format == FMT_MIBYTE
|
|
||||||
? 1000 * 1000
|
|
||||||
: format == FMT_GIBYTE
|
|
||||||
? 1000 * 1000 * 1000
|
|
||||||
: 1;
|
|
||||||
value /= divider;
|
value /= divider;
|
||||||
fmt = zero_pad ? "%0*lu" : "%*lu";
|
fmt = zero_pad ? "%0*lu" : "%*lu";
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue