mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-20 03:35: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.
|
||||
* network: `type` tag ([#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]).
|
||||
* 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
|
||||
[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
|
||||
|
||||
|
||||
### Changed
|
||||
|
@ -44,9 +54,12 @@
|
|||
* 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
|
||||
|
|
|
@ -101,7 +101,7 @@ setup(struct bar *_bar)
|
|||
backend->x = mon->x;
|
||||
backend->y = mon->y;
|
||||
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;
|
||||
|
||||
|
@ -369,7 +369,7 @@ refresh(const struct bar *_bar)
|
|||
|
||||
/* 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 */
|
||||
xcb_expose_event_t *evt = calloc(32, 1);
|
||||
|
||||
|
|
|
@ -47,6 +47,12 @@ endif
|
|||
if plugin_network_enabled
|
||||
plugin_pages += ['yambar-modules-network.5.scd']
|
||||
endif
|
||||
if plugin_niri_language_enabled
|
||||
plugin_pages += ['yambar-modules-niri-language.5.scd']
|
||||
endif
|
||||
if plugin_niri_workspaces_enabled
|
||||
plugin_pages += ['yambar-modules-niri-workspaces.5.scd']
|
||||
endif
|
||||
if plugin_pipewire_enabled
|
||||
plugin_pages += ['yambar-modules-pipewire.5.scd']
|
||||
endif
|
||||
|
|
|
@ -137,7 +137,7 @@ content:
|
|||
|
||||
# STACK
|
||||
|
||||
This particles combines multiple decorations.
|
||||
This particle combines multiple decorations.
|
||||
|
||||
## CONFIGURATION
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ currently present in the machine.
|
|||
for the machine
|
||||
| is_disk
|
||||
: boolean
|
||||
: whether or not the device is a disk (e.g. sda, sdb) or a partition
|
||||
(e.g. sda1, sda2, ...). "Total" is advertised as a disk.
|
||||
: whether or not the device is a disk (e.g., sda, sdb) or a partition
|
||||
(e.g., sda1, sda2, ...). "Total" is advertised as a disk.
|
||||
| read_speed
|
||||
: int
|
||||
: bytes read, in bytes/s
|
||||
|
|
|
@ -26,6 +26,9 @@ with the _application_ and _title_ tags to replace the X11-only
|
|||
| name
|
||||
: string
|
||||
: The workspace name
|
||||
| output
|
||||
: string
|
||||
: The output (monitor) the workspace is on
|
||||
| visible
|
||||
: bool
|
||||
: True if the workspace is currently visible (on any output)
|
||||
|
|
|
@ -20,6 +20,9 @@ mpd - This module provides MPD status such as currently playing artist/album/son
|
|||
| consume
|
||||
: bool
|
||||
: True if the *consume* flag is set
|
||||
| single
|
||||
: bool
|
||||
: True if the *single* flag is set
|
||||
| volume
|
||||
: range
|
||||
: Volume of MPD in percentage
|
||||
|
|
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
|
||||
| form_factor
|
||||
: string
|
||||
: Current device form factor (headset, speaker, mic, etc)
|
||||
: Current device form factor (headset, speaker, mic, etc.)
|
||||
| bus
|
||||
: string
|
||||
: Current device bus (bluetooth, alsa, etc)
|
||||
: Current device bus (bluetooth, alsa, etc.)
|
||||
| icon
|
||||
: string
|
||||
: 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 a loop, sending an updated tag set whenever it needs, or wants
|
||||
to. The last tag set is used (displayed) by yambar until a new tag set
|
||||
is received. This mode is intended to be used by scripts that depends
|
||||
is received. This mode is intended to be used by scripts that depend
|
||||
on non-polling methods to update their state.
|
||||
|
||||
Tag sets, or _transactions_, are separated by an empty line
|
||||
|
|
|
@ -174,6 +174,10 @@ Available modules have their own pages:
|
|||
|
||||
*yambar-modules-sway*(5)
|
||||
|
||||
*yambar-modules-niri-language*(5)
|
||||
|
||||
*yambar-modules-niri-workspaces*(5)
|
||||
|
||||
*yambar-modules-xkb*(5)
|
||||
|
||||
*yambar-modules-xwindow*(5)
|
||||
|
|
|
@ -155,7 +155,7 @@ content:
|
|||
|
||||
This particle is a list (or sequence, if you like) of other
|
||||
particles. It can be used to render e.g. _string_ particles with
|
||||
different font and/or color formatting. Or ay other particle
|
||||
different font and/or color formatting. Or any other particle
|
||||
combinations.
|
||||
|
||||
But note that this means you *cannot* set any attributes on the _list_
|
||||
|
@ -265,6 +265,26 @@ To match for empty strings, use ' "" ':
|
|||
<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:
|
||||
|
||||
[- &&
|
||||
|
@ -456,7 +476,7 @@ itself when needed.
|
|||
|
||||
```
|
||||
content:
|
||||
progres-bar:
|
||||
progress-bar:
|
||||
tag: tag_name
|
||||
length: 20
|
||||
start: {string: {text: ├}}
|
||||
|
|
|
@ -86,11 +86,10 @@ be used.
|
|||
: format
|
||||
: Range tags
|
||||
: Renders a range tag's value as a percentage value
|
||||
| b
|
||||
| /N
|
||||
: format
|
||||
: All tag types
|
||||
: Renders a tag's value (in decimal) divided by 8. Note: no unit
|
||||
suffix is appended
|
||||
: Renders a tag's value (in decimal) divided by N
|
||||
| kb, mb, gb
|
||||
: format
|
||||
: All tag types
|
||||
|
|
|
@ -25,7 +25,7 @@ yambar - modular status panel for X11 and Wayland
|
|||
*-p*,*--print-pid*=_FILE_|_FD_
|
||||
Print PID to this file, or FD, when successfully started. The file
|
||||
(or FD) is closed immediately after writing the PID. When a _FILE_
|
||||
as been specified, the file is unlinked exit.
|
||||
as been specified, the file is unlinked upon exiting.
|
||||
|
||||
*-d*,*--log-level*={*info*,*warning*,*error*,*none*}
|
||||
Log level, used both for log output on stderr as well as
|
||||
|
|
2
main.c
2
main.c
|
@ -87,7 +87,7 @@ get_config_path(void)
|
|||
static struct bar *
|
||||
load_bar(const char *config_path, enum bar_backend backend)
|
||||
{
|
||||
FILE *conf_file = fopen(config_path, "r");
|
||||
FILE *conf_file = fopen(config_path, "re");
|
||||
if (conf_file == NULL) {
|
||||
LOG_ERRNO("%s: failed to open", config_path);
|
||||
return NULL;
|
||||
|
|
|
@ -197,6 +197,8 @@ summary(
|
|||
'River': plugin_river_enabled,
|
||||
'Script': plugin_script_enabled,
|
||||
'Sway XKB keyboard': plugin_sway_xkb_enabled,
|
||||
'Niri language': plugin_niri_language_enabled,
|
||||
'Niri workspaces': plugin_niri_workspaces_enabled,
|
||||
'XKB keyboard (for X11)': plugin_xkb_enabled,
|
||||
'XWindow (window tracking for X11)': plugin_xwindow_enabled,
|
||||
},
|
||||
|
|
|
@ -46,6 +46,10 @@ option('plugin-script', type: 'feature', value: 'auto',
|
|||
description: 'Script support')
|
||||
option('plugin-sway-xkb', type: 'feature', value: 'auto',
|
||||
description: 'keyboard support for Sway')
|
||||
option('plugin-niri-language', type: 'feature', value: 'auto',
|
||||
description: 'language support for Niri')
|
||||
option('plugin-niri-workspaces', type: 'feature', value: 'auto',
|
||||
description: 'workspaces support for Niri')
|
||||
option('plugin-xkb', type: 'feature', value: 'auto',
|
||||
description: 'keyboard support for X11')
|
||||
option('plugin-xwindow', type: 'feature', value: 'auto',
|
||||
|
|
|
@ -112,13 +112,13 @@ readint_from_fd(int fd)
|
|||
static int
|
||||
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) {
|
||||
LOG_ERRNO("/sys/class/backlight");
|
||||
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);
|
||||
|
||||
if (base_dir_fd == -1) {
|
||||
|
@ -126,7 +126,7 @@ initialize(struct private *m)
|
|||
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) {
|
||||
LOG_ERRNO("/sys/class/backlight/%s/max_brightness", m->device);
|
||||
close(base_dir_fd);
|
||||
|
@ -136,7 +136,7 @@ initialize(struct private *m)
|
|||
m->max_brightness = readint_from_fd(max_fd);
|
||||
close(max_fd);
|
||||
|
||||
int current_fd = openat(base_dir_fd, "brightness", O_RDONLY);
|
||||
int current_fd = openat(base_dir_fd, "brightness", O_RDONLY | O_CLOEXEC);
|
||||
close(base_dir_fd);
|
||||
|
||||
if (current_fd == -1) {
|
||||
|
|
|
@ -259,13 +259,13 @@ initialize(struct private *m)
|
|||
{
|
||||
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) {
|
||||
LOG_ERRNO("/sys/class/power_supply");
|
||||
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);
|
||||
|
||||
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) {
|
||||
LOG_WARN("/sys/class/power_supply/%s/manufacturer: %s", m->battery, strerror(errno));
|
||||
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) {
|
||||
LOG_WARN("/sys/class/power_supply/%s/model_name: %s", m->battery, strerror(errno));
|
||||
m->model = NULL;
|
||||
|
@ -298,7 +298,7 @@ initialize(struct private *m)
|
|||
if (faccessat(base_dir_fd, "energy_full_design", O_RDONLY, 0) == 0
|
||||
&& faccessat(base_dir_fd, "energy_full", O_RDONLY, 0) == 0) {
|
||||
{
|
||||
int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY);
|
||||
int fd = openat(base_dir_fd, "energy_full_design", O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
LOG_ERRNO("/sys/class/power_supply/%s/energy_full_design", m->battery);
|
||||
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) {
|
||||
LOG_ERRNO("/sys/class/power_supply/%s/energy_full", m->battery);
|
||||
goto err;
|
||||
|
@ -325,7 +325,7 @@ initialize(struct private *m)
|
|||
if (faccessat(base_dir_fd, "charge_full_design", O_RDONLY, 0) == 0
|
||||
&& faccessat(base_dir_fd, "charge_full", O_RDONLY, 0) == 0) {
|
||||
{
|
||||
int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY);
|
||||
int fd = openat(base_dir_fd, "charge_full_design", O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
LOG_ERRNO("/sys/class/power_supply/%s/charge_full_design", m->battery);
|
||||
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) {
|
||||
LOG_ERRNO("/sys/class/power_supply/%s/charge_full", m->battery);
|
||||
goto err;
|
||||
|
@ -362,13 +362,13 @@ update_status(struct module *mod)
|
|||
{
|
||||
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) {
|
||||
LOG_ERRNO("/sys/class/power_supply");
|
||||
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);
|
||||
|
||||
if (base_dir_fd < 0) {
|
||||
|
@ -376,14 +376,14 @@ update_status(struct module *mod)
|
|||
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) {
|
||||
LOG_ERRNO("/sys/class/power_supply/%s/status", m->battery);
|
||||
close(base_dir_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY);
|
||||
int capacity_fd = openat(base_dir_fd, "capacity", O_RDONLY | O_CLOEXEC);
|
||||
if (capacity_fd < 0) {
|
||||
LOG_ERRNO("/sys/class/power_supply/%s/capacity", m->battery);
|
||||
close(status_fd);
|
||||
|
@ -391,12 +391,12 @@ update_status(struct module *mod)
|
|||
return false;
|
||||
}
|
||||
|
||||
int energy_fd = openat(base_dir_fd, "energy_now", O_RDONLY);
|
||||
int power_fd = openat(base_dir_fd, "power_now", O_RDONLY);
|
||||
int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY);
|
||||
int current_fd = openat(base_dir_fd, "current_now", O_RDONLY);
|
||||
int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY);
|
||||
int time_to_full_fd = openat(base_dir_fd, "time_to_full_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 | O_CLOEXEC);
|
||||
int charge_fd = openat(base_dir_fd, "charge_now", O_RDONLY | O_CLOEXEC);
|
||||
int current_fd = openat(base_dir_fd, "current_now", O_RDONLY | O_CLOEXEC);
|
||||
int time_to_empty_fd = openat(base_dir_fd, "time_to_empty_now", O_RDONLY | O_CLOEXEC);
|
||||
int time_to_full_fd = openat(base_dir_fd, "time_to_full_now", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
long capacity = readint_from_fd(capacity_fd);
|
||||
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;
|
||||
ssize_t read;
|
||||
|
||||
fp = fopen("/proc/stat", "r");
|
||||
fp = fopen("/proc/stat", "re");
|
||||
if (NULL == fp) {
|
||||
LOG_ERRNO("unable to open /proc/stat");
|
||||
return;
|
||||
|
|
|
@ -105,7 +105,7 @@ refresh_device_stats(struct private *m)
|
|||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
fp = fopen("/proc/diskstats", "r");
|
||||
fp = fopen("/proc/diskstats", "re");
|
||||
if (NULL == fp) {
|
||||
LOG_ERRNO("unable to open /proc/diskstats");
|
||||
return;
|
||||
|
|
|
@ -330,7 +330,7 @@ run_init(int *inotify_fd, int *inotify_wd, FILE **file, char *dwl_info_filename)
|
|||
return 1;
|
||||
}
|
||||
|
||||
*file = fopen(dwl_info_filename, "r");
|
||||
*file = fopen(dwl_info_filename, "re");
|
||||
if (*file == NULL) {
|
||||
inotify_rm_watch(*inotify_fd, *inotify_wd);
|
||||
close(*inotify_fd);
|
||||
|
|
|
@ -664,7 +664,7 @@ handle_window_event(int sock, int type, const struct json_object *json, void *_m
|
|||
char path[64];
|
||||
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) {
|
||||
/* Application may simply have terminated */
|
||||
free(ws->window.application);
|
||||
|
@ -876,6 +876,7 @@ content(struct module *mod)
|
|||
struct tag_set tags = {
|
||||
.tags = (struct tag *[]){
|
||||
tag_new_string(mod, "name", name),
|
||||
tag_new_string(mod, "output", ws->output),
|
||||
tag_new_bool(mod, "visible", ws->visible),
|
||||
tag_new_bool(mod, "focused", ws->focused),
|
||||
tag_new_bool(mod, "urgent", ws->urgent),
|
||||
|
@ -887,7 +888,7 @@ content(struct module *mod)
|
|||
|
||||
tag_new_string(mod, "mode", m->mode),
|
||||
},
|
||||
.count = 9,
|
||||
.count = 10,
|
||||
};
|
||||
|
||||
if (ws->focused) {
|
||||
|
|
|
@ -54,7 +54,7 @@ get_mem_stats(uint64_t *mem_free, uint64_t *mem_total)
|
|||
size_t len = 0;
|
||||
ssize_t read = 0;
|
||||
|
||||
fp = fopen("/proc/meminfo", "r");
|
||||
fp = fopen("/proc/meminfo", "re");
|
||||
if (NULL == fp) {
|
||||
LOG_ERRNO("unable to open /proc/meminfo");
|
||||
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'))
|
||||
plugin_sway_xkb_enabled = json_sway_xkb.found()
|
||||
|
||||
json_niri_language = dependency('json-c', required: get_option('plugin-niri-language'))
|
||||
plugin_niri_language_enabled = json_niri_language.found()
|
||||
|
||||
json_niri_workspaces = dependency('json-c', required: get_option('plugin-niri-workspaces'))
|
||||
plugin_niri_workspaces_enabled = json_niri_workspaces.found()
|
||||
|
||||
xcb_xkb = dependency('xcb-xkb', required: get_option('plugin-xkb'))
|
||||
plugin_xkb_enabled = backend_x11 and xcb_xkb.found()
|
||||
|
||||
|
@ -128,6 +134,14 @@ if plugin_sway_xkb_enabled
|
|||
mod_data += {'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json_sway_xkb]]}
|
||||
endif
|
||||
|
||||
if plugin_niri_language_enabled
|
||||
mod_data += {'niri-language': [['niri-common.c', 'niri-common.h'], [dynlist, json_niri_language]]}
|
||||
endif
|
||||
|
||||
if plugin_niri_workspaces_enabled
|
||||
mod_data += {'niri-workspaces': [['niri-common.c', 'niri-common.h'], [dynlist, json_niri_workspaces]]}
|
||||
endif
|
||||
|
||||
if plugin_xkb_enabled
|
||||
mod_data += {'xkb': [[], [xcb_stuff, xcb_xkb]]}
|
||||
endif
|
||||
|
|
|
@ -39,6 +39,7 @@ struct private
|
|||
bool repeat;
|
||||
bool random;
|
||||
bool consume;
|
||||
bool single;
|
||||
int volume;
|
||||
char *album;
|
||||
char *artist;
|
||||
|
@ -176,6 +177,7 @@ content(struct module *mod)
|
|||
tag_new_bool(mod, "repeat", m->repeat),
|
||||
tag_new_bool(mod, "random", m->random),
|
||||
tag_new_bool(mod, "consume", m->consume),
|
||||
tag_new_bool(mod, "single", m->single),
|
||||
tag_new_int_range(mod, "volume", m->volume, 0, 100),
|
||||
tag_new_string(mod, "album", m->album),
|
||||
tag_new_string(mod, "artist", m->artist),
|
||||
|
@ -187,7 +189,7 @@ content(struct module *mod)
|
|||
tag_new_int_realtime(
|
||||
mod, "elapsed", elapsed, 0, m->duration, realtime),
|
||||
},
|
||||
.count = 13,
|
||||
.count = 14,
|
||||
};
|
||||
|
||||
mtx_unlock(&mod->lock);
|
||||
|
@ -336,6 +338,7 @@ update_status(struct module *mod)
|
|||
m->repeat = mpd_status_get_repeat(status);
|
||||
m->random = mpd_status_get_random(status);
|
||||
m->consume = mpd_status_get_consume(status);
|
||||
m->single = mpd_status_get_single_state(status) == MPD_SINGLE_ONESHOT;
|
||||
m->volume = mpd_status_get_volume(status);
|
||||
m->duration = mpd_status_get_total_time(status) * 1000;
|
||||
m->elapsed.value = mpd_status_get_elapsed_ms(status);
|
||||
|
|
|
@ -1576,7 +1576,7 @@ out:
|
|||
static struct module *
|
||||
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) {
|
||||
LOG_ERRNO("failed to open /dev/urandom");
|
||||
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 void
|
||||
output_informations_destroy(struct output_informations *output_informations)
|
||||
{
|
||||
free(output_informations->name);
|
||||
free(output_informations->description);
|
||||
free(output_informations->icon);
|
||||
free(output_informations->form_factor);
|
||||
free(output_informations->bus);
|
||||
}
|
||||
|
||||
struct data;
|
||||
struct private
|
||||
{
|
||||
|
@ -213,18 +223,23 @@ node_find_route(struct data *data, bool is_sink)
|
|||
static void
|
||||
node_unhook_binded_node(struct data *data, bool is_sink)
|
||||
{
|
||||
struct private *private = data->module->private;
|
||||
|
||||
struct node **target_node = NULL;
|
||||
struct spa_hook *target_listener = NULL;
|
||||
void **target_proxy = NULL;
|
||||
struct output_informations *output_informations = NULL;
|
||||
|
||||
if (is_sink) {
|
||||
target_node = &data->binded_sink;
|
||||
target_listener = &data->node_sink_listener;
|
||||
target_proxy = &data->node_sink;
|
||||
output_informations = &private->sink_informations;
|
||||
} else {
|
||||
target_node = &data->binded_source;
|
||||
target_listener = &data->node_source_listener;
|
||||
target_proxy = &data->node_source;
|
||||
output_informations = &private->source_informations;
|
||||
}
|
||||
|
||||
if (*target_node == NULL)
|
||||
|
@ -235,6 +250,9 @@ node_unhook_binded_node(struct data *data, bool is_sink)
|
|||
|
||||
*target_node = NULL;
|
||||
*target_proxy = NULL;
|
||||
|
||||
output_informations_destroy(output_informations);
|
||||
*output_informations = output_informations_null;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -350,7 +368,7 @@ device_events_param(void *userdata, int seq, uint32_t id, uint32_t index, uint32
|
|||
if (binded_node == NULL)
|
||||
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)
|
||||
return;
|
||||
|
||||
|
@ -398,18 +416,18 @@ node_events_info(void *userdata, struct pw_node_info const *info)
|
|||
struct spa_dict_item const *item = NULL;
|
||||
|
||||
item = spa_dict_lookup_item(info->props, "node.name");
|
||||
if (item != NULL)
|
||||
X_FREE_SET(output_informations->name, X_STRDUP(item->value));
|
||||
X_FREE_SET(output_informations->name, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||
|
||||
item = spa_dict_lookup_item(info->props, "node.description");
|
||||
if (item != NULL)
|
||||
X_FREE_SET(output_informations->description, X_STRDUP(item->value));
|
||||
X_FREE_SET(output_informations->description, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||
|
||||
item = spa_dict_lookup_item(info->props, "device.id");
|
||||
if (item != NULL) {
|
||||
uint32_t value = 0;
|
||||
spa_atou32(item->value, &value, 10);
|
||||
output_informations->device_id = value;
|
||||
} else {
|
||||
output_informations->device_id = 0;
|
||||
}
|
||||
|
||||
item = spa_dict_lookup_item(info->props, "card.profile.device");
|
||||
|
@ -417,30 +435,29 @@ node_events_info(void *userdata, struct pw_node_info const *info)
|
|||
uint32_t value = 0;
|
||||
spa_atou32(item->value, &value, 10);
|
||||
output_informations->card_profile_device_id = value;
|
||||
} else {
|
||||
output_informations->card_profile_device_id = 0;
|
||||
}
|
||||
|
||||
/* Device's information has an more important priority than node's information */
|
||||
/* icon_name */
|
||||
struct route *route = node_find_route(data, node_data->is_sink);
|
||||
if (route != NULL && route->icon_name != NULL)
|
||||
output_informations->icon = X_STRDUP(route->icon_name);
|
||||
X_FREE_SET(output_informations->icon, X_STRDUP(route->icon_name));
|
||||
else {
|
||||
item = spa_dict_lookup_item(info->props, "device.icon-name");
|
||||
if (item != NULL)
|
||||
X_FREE_SET(output_informations->icon, X_STRDUP(item->value));
|
||||
X_FREE_SET(output_informations->icon, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||
}
|
||||
/* form_factor */
|
||||
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 {
|
||||
item = spa_dict_lookup_item(info->props, "device.form-factor");
|
||||
if (item != NULL)
|
||||
X_FREE_SET(output_informations->form_factor, X_STRDUP(item->value));
|
||||
X_FREE_SET(output_informations->form_factor, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||
}
|
||||
|
||||
item = spa_dict_lookup_item(info->props, "device.bus");
|
||||
if (item != NULL)
|
||||
X_FREE_SET(output_informations->bus, X_STRDUP(item->value));
|
||||
X_FREE_SET(output_informations->bus, item != NULL ? X_STRDUP(item->value) : NULL);
|
||||
|
||||
data->module->bar->refresh(data->module->bar);
|
||||
}
|
||||
|
@ -827,18 +844,8 @@ destroy(struct module *module)
|
|||
pipewire_deinit(private->data);
|
||||
private->label->destroy(private->label);
|
||||
|
||||
/* sink */
|
||||
free(private->sink_informations.name);
|
||||
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);
|
||||
output_informations_destroy(&private->sink_informations);
|
||||
output_informations_destroy(&private->source_informations);
|
||||
|
||||
free(private);
|
||||
module_default_destroy(module);
|
||||
|
|
|
@ -438,7 +438,7 @@ run(struct module *mod)
|
|||
}
|
||||
|
||||
// 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) {
|
||||
LOG_ERRNO("failed to create timerfd");
|
||||
pa_mainloop_free(priv->mainloop);
|
||||
|
|
|
@ -161,13 +161,10 @@ content(struct module *mod)
|
|||
static void
|
||||
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 = fd >= 0 ? fdopen(fd, "r") : NULL;
|
||||
FILE *f = fopen("/proc/self/mountinfo", "re");
|
||||
|
||||
if (fd < 0 || f == NULL) {
|
||||
if (f == NULL) {
|
||||
LOG_ERRNO("failed to open /proc/self/mountinfo");
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ update_application(struct module *mod)
|
|||
char path[1024];
|
||||
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)
|
||||
return;
|
||||
|
||||
|
|
|
@ -13,6 +13,61 @@
|
|||
|
||||
#include "map.h"
|
||||
|
||||
// String globbing match.
|
||||
// Note: Uses "non-greedy" implementation for "*" wildcard matching
|
||||
static bool
|
||||
string_like(const char* name, const char* pattern)
|
||||
{
|
||||
LOG_DBG("pattern:%s name:%s", pattern, name);
|
||||
int px = 0, nx = 0;
|
||||
int nextpx = 0, nextnx = 0;
|
||||
|
||||
while (px < strlen(pattern) || nx < strlen(name)) {
|
||||
if (px < strlen(pattern)) {
|
||||
char c = pattern[px];
|
||||
switch (c) {
|
||||
case '?': {
|
||||
// single character
|
||||
px++;
|
||||
nx++;
|
||||
continue;
|
||||
}
|
||||
case '*': {
|
||||
// zero or more glob
|
||||
nextpx=px;
|
||||
nextnx=nx+1;
|
||||
px++;
|
||||
continue;
|
||||
}
|
||||
default: {
|
||||
// normal character
|
||||
if (nx < strlen(name) && name[nx] == c)
|
||||
{
|
||||
px++;
|
||||
nx++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// mismatch
|
||||
if (0 < nextnx && nextnx <= strlen(name)) {
|
||||
px = nextpx;
|
||||
nx = nextnx;
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
LOG_DBG("map: name %s matched all the pattern %s", name, pattern);
|
||||
// Matched all of pattern to all of name. Success.
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
int_condition(const long tag_value, const long cond_value, enum map_op op)
|
||||
{
|
||||
|
@ -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;
|
||||
case MAP_OP_GT:
|
||||
return strcmp(tag_value, cond_value) > 0;
|
||||
case MAP_OP_LIKE:
|
||||
return string_like(tag_value, cond_value) != 0;
|
||||
case MAP_OP_SELF:
|
||||
LOG_WARN("using String tag as bool");
|
||||
default:
|
||||
|
@ -166,6 +223,7 @@ free_map_condition(struct map_condition *c)
|
|||
case MAP_OP_LE:
|
||||
case MAP_OP_LT:
|
||||
case MAP_OP_GE:
|
||||
case MAP_OP_LIKE:
|
||||
case MAP_OP_GT:
|
||||
free(c->value);
|
||||
/* FALLTHROUGH */
|
||||
|
|
|
@ -9,6 +9,7 @@ enum map_op {
|
|||
MAP_OP_GT,
|
||||
MAP_OP_SELF,
|
||||
MAP_OP_NOT,
|
||||
MAP_OP_LIKE,
|
||||
|
||||
MAP_OP_AND,
|
||||
MAP_OP_OR,
|
||||
|
|
|
@ -69,6 +69,7 @@ void yyerror(const char *s);
|
|||
\< yylval.op = MAP_OP_LT; return CMP_OP;
|
||||
>= yylval.op = MAP_OP_GE; return CMP_OP;
|
||||
> yylval.op = MAP_OP_GT; return CMP_OP;
|
||||
~~ yylval.op = MAP_OP_LIKE; return CMP_OP;
|
||||
&& yylval.op = MAP_OP_AND; return BOOL_OP;
|
||||
\|\| yylval.op = MAP_OP_OR; return BOOL_OP;
|
||||
~ return NOT;
|
||||
|
|
|
@ -35,27 +35,27 @@ result: condition { MAP_CONDITION_PARSE_RESULT = $<condition>1; };
|
|||
condition:
|
||||
WORD {
|
||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||
$<condition>$->tag = $<str>1;
|
||||
$<condition>$->tag = $<str>1;
|
||||
$<condition>$->op = MAP_OP_SELF;
|
||||
}
|
||||
|
|
||||
WORD CMP_OP WORD {
|
||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||
$<condition>$->tag = $<str>1;
|
||||
$<condition>$->tag = $<str>1;
|
||||
$<condition>$->op = $<op>2;
|
||||
$<condition>$->value = $<str>3;
|
||||
$<condition>$->value = $<str>3;
|
||||
}
|
||||
|
|
||||
WORD CMP_OP STRING {
|
||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||
$<condition>$->tag = $<str>1;
|
||||
$<condition>$->tag = $<str>1;
|
||||
$<condition>$->op = $<op>2;
|
||||
$<condition>$->value = $<str>3;
|
||||
$<condition>$->value = $<str>3;
|
||||
}
|
||||
|
|
||||
L_PAR condition R_PAR { $<condition>$ = $<condition>2; }
|
||||
|
|
||||
NOT condition {
|
||||
NOT condition {
|
||||
$<condition>$ = malloc(sizeof(struct map_condition));
|
||||
$<condition>$->cond1 = $<condition>2;
|
||||
$<condition>$->op = MAP_OP_NOT;
|
||||
|
@ -79,7 +79,7 @@ static char const*
|
|||
token_to_str(yysymbol_kind_t tkn)
|
||||
{
|
||||
switch (tkn) {
|
||||
case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >";
|
||||
case YYSYMBOL_CMP_OP: return "==, !=, <=, <, >=, >, ~~";
|
||||
case YYSYMBOL_BOOL_OP: return "||, &&";
|
||||
case YYSYMBOL_L_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)
|
||||
EXTERN_MODULE(sway_xkb);
|
||||
#endif
|
||||
#if defined(HAVE_PLUGIN_niri_language)
|
||||
EXTERN_MODULE(niri_language);
|
||||
#endif
|
||||
#if defined(HAVE_PLUGIN_niri_workspaces)
|
||||
EXTERN_MODULE(niri_workspaces);
|
||||
#endif
|
||||
#if defined(HAVE_PLUGIN_xkb)
|
||||
EXTERN_MODULE(xkb);
|
||||
#endif
|
||||
|
@ -220,6 +226,12 @@ static void __attribute__((constructor)) init(void)
|
|||
#if defined(HAVE_PLUGIN_sway_xkb)
|
||||
REGISTER_CORE_MODULE(sway-xkb, sway_xkb);
|
||||
#endif
|
||||
#if defined(HAVE_PLUGIN_niri_language)
|
||||
REGISTER_CORE_MODULE(niri-language, niri_language);
|
||||
#endif
|
||||
#if defined(HAVE_PLUGIN_niri_workspaces)
|
||||
REGISTER_CORE_MODULE(niri-workspaces, niri_workspaces);
|
||||
#endif
|
||||
#if defined(HAVE_PLUGIN_xkb)
|
||||
REGISTER_CORE_MODULE(xkb, xkb);
|
||||
#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
|
||||
static bool
|
||||
is_number(const char *str, int *value)
|
||||
is_number(const char *str, long *value)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
char *end;
|
||||
int v = strtol(str, &end, 10);
|
||||
long v = strtol(str, &end, 10);
|
||||
if (errno != 0 || *end != '\0')
|
||||
return false;
|
||||
|
||||
|
@ -510,13 +510,7 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
|||
FMT_HEX,
|
||||
FMT_OCT,
|
||||
FMT_PERCENT,
|
||||
FMT_BYTE,
|
||||
FMT_KBYTE,
|
||||
FMT_MBYTE,
|
||||
FMT_GBYTE,
|
||||
FMT_KIBYTE,
|
||||
FMT_MIBYTE,
|
||||
FMT_GIBYTE,
|
||||
FMT_DIVIDE,
|
||||
} format
|
||||
= FMT_DEFAULT;
|
||||
|
||||
|
@ -528,8 +522,9 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
|||
} kind
|
||||
= VALUE_VALUE;
|
||||
|
||||
int digits = 0;
|
||||
int decimals = 2;
|
||||
long digits = 0;
|
||||
long decimals = 2;
|
||||
long divider = 1;
|
||||
bool zero_pad = false;
|
||||
char *point = NULL;
|
||||
|
||||
|
@ -542,20 +537,38 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
|||
format = FMT_OCT;
|
||||
else if (strcmp(tag_args[i], "%") == 0)
|
||||
format = FMT_PERCENT;
|
||||
else if (strcmp(tag_args[i], "b") == 0)
|
||||
format = FMT_BYTE;
|
||||
else if (strcmp(tag_args[i], "kb") == 0)
|
||||
format = FMT_KBYTE;
|
||||
else if (strcmp(tag_args[i], "mb") == 0)
|
||||
format = FMT_MBYTE;
|
||||
else if (strcmp(tag_args[i], "gb") == 0)
|
||||
format = FMT_GBYTE;
|
||||
else if (strcmp(tag_args[i], "kib") == 0)
|
||||
format = FMT_KIBYTE;
|
||||
else if (strcmp(tag_args[i], "mib") == 0)
|
||||
format = FMT_MIBYTE;
|
||||
else if (strcmp(tag_args[i], "gib") == 0)
|
||||
format = FMT_GIBYTE;
|
||||
else if (*tag_args[i] == '/') {
|
||||
format = FMT_DIVIDE;
|
||||
const char *divider_str = tag_args[i] + 1;
|
||||
if (!is_number(divider_str, ÷r) || divider == 0) {
|
||||
divider = 1;
|
||||
LOG_WARN("tag `%s`: invalid divider %s, reset to 1", tag_name, divider_str);
|
||||
}
|
||||
}
|
||||
else if (strcmp(tag_args[i], "kb") == 0) {
|
||||
format = FMT_DIVIDE;
|
||||
divider = 1000;
|
||||
}
|
||||
else if (strcmp(tag_args[i], "mb") == 0) {
|
||||
format = FMT_DIVIDE;
|
||||
divider = 1000 * 1000;
|
||||
}
|
||||
else if (strcmp(tag_args[i], "gb") == 0) {
|
||||
format = FMT_DIVIDE;
|
||||
divider = 1000 * 1000 * 1000;
|
||||
}
|
||||
else if (strcmp(tag_args[i], "kib") == 0) {
|
||||
format = FMT_DIVIDE;
|
||||
divider = 1024;
|
||||
}
|
||||
else if (strcmp(tag_args[i], "mib") == 0) {
|
||||
format = FMT_DIVIDE;
|
||||
divider = 1024 * 1024;
|
||||
}
|
||||
else if (strcmp(tag_args[i], "gib") == 0) {
|
||||
format = FMT_DIVIDE;
|
||||
divider = 1024 * 1024 * 1024;
|
||||
}
|
||||
else if (strcmp(tag_args[i], "min") == 0)
|
||||
kind = VALUE_MIN;
|
||||
else if (strcmp(tag_args[i], "max") == 0)
|
||||
|
@ -637,30 +650,7 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
|||
break;
|
||||
}
|
||||
|
||||
case FMT_BYTE:
|
||||
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;
|
||||
|
||||
case FMT_DIVIDE: {
|
||||
char str[24];
|
||||
if (tag->type(tag) == TAG_TYPE_FLOAT) {
|
||||
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";
|
||||
break;
|
||||
|
||||
case FMT_BYTE:
|
||||
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;
|
||||
case FMT_DIVIDE: {
|
||||
value /= divider;
|
||||
fmt = zero_pad ? "%0*lu" : "%*lu";
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue