forked from external/yambar
modules/dwl: new module
This commit is contained in:
parent
6027b2728b
commit
f5cfc103d0
10 changed files with 625 additions and 5 deletions
|
@ -32,11 +32,13 @@
|
||||||
* mpd: `file` tag ([#219][219]).
|
* mpd: `file` tag ([#219][219]).
|
||||||
* pipewire: add a new module for pipewire ([#224][224])
|
* pipewire: add a new module for pipewire ([#224][224])
|
||||||
* on-click: support `next`/`previous` mouse buttons ([#228][228]).
|
* on-click: support `next`/`previous` mouse buttons ([#228][228]).
|
||||||
|
* dwl: add a new module for DWL ([#218][218])
|
||||||
|
|
||||||
[153]: https://codeberg.org/dnkl/yambar/issues/153
|
[153]: https://codeberg.org/dnkl/yambar/issues/153
|
||||||
[159]: https://codeberg.org/dnkl/yambar/issues/159
|
[159]: https://codeberg.org/dnkl/yambar/issues/159
|
||||||
[200]: https://codeberg.org/dnkl/yambar/issues/200
|
[200]: https://codeberg.org/dnkl/yambar/issues/200
|
||||||
[202]: https://codeberg.org/dnkl/yambar/issues/202
|
[202]: https://codeberg.org/dnkl/yambar/issues/202
|
||||||
|
[218]: https://codeberg.org/dnkl/yambar/pulls/218
|
||||||
[219]: https://codeberg.org/dnkl/yambar/pulls/219
|
[219]: https://codeberg.org/dnkl/yambar/pulls/219
|
||||||
[223]: https://codeberg.org/dnkl/yambar/pulls/223
|
[223]: https://codeberg.org/dnkl/yambar/pulls/223
|
||||||
[224]: https://codeberg.org/dnkl/yambar/pulls/224
|
[224]: https://codeberg.org/dnkl/yambar/pulls/224
|
||||||
|
|
|
@ -6,7 +6,7 @@ scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
||||||
foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd',
|
foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd',
|
||||||
'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd',
|
'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd',
|
||||||
'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd',
|
'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd',
|
||||||
'yambar-modules-disk-io.5.scd',
|
'yambar-modules-disk-io.5.scd', 'yambar-modules-dwl.5.scd',
|
||||||
'yambar-modules-foreign-toplevel.5.scd',
|
'yambar-modules-foreign-toplevel.5.scd',
|
||||||
'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd',
|
'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd',
|
||||||
'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd',
|
'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd',
|
||||||
|
|
95
doc/yambar-modules-dwl.5.scd
Normal file
95
doc/yambar-modules-dwl.5.scd
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
yambar-modules-dwl(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
dwl - This module provides information about dwl tags, and information.
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module provides a map of each tags present in dwl.
|
||||||
|
|
||||||
|
Each tags has its _id_, its status (_selected_, _empty_, _urgent_)
|
||||||
|
and the global data like _title_, _fullscreen_, _floating_,
|
||||||
|
_selmon_, and _layout_). The tags start a 1. For needs where
|
||||||
|
you only want information about the global data and not the _tags_,
|
||||||
|
there is a tag with the id _0_ that contains only the global data.
|
||||||
|
|
||||||
|
This module will track *only* the monitor where yambar was launched on.
|
||||||
|
If you have a multi monitor setup, please launch yambar on each of your
|
||||||
|
monitors.
|
||||||
|
|
||||||
|
Please, be aware that only *one instance* of this module is supported.
|
||||||
|
Running multiple instances at the same time may result in
|
||||||
|
*undefined behavior*.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| id
|
||||||
|
: int
|
||||||
|
: Dwl tag id.
|
||||||
|
| selected
|
||||||
|
: bool
|
||||||
|
: True if the tag is currently selected.
|
||||||
|
| empty
|
||||||
|
: bool
|
||||||
|
: True if there are no windows in the tag.
|
||||||
|
| urgent
|
||||||
|
: bool
|
||||||
|
: True if the tag has the urgent flag set.
|
||||||
|
| title
|
||||||
|
: string
|
||||||
|
: The currently focused window's title.
|
||||||
|
| fullscreen
|
||||||
|
: bool
|
||||||
|
: True if there is a fullscreen window in the current tag.
|
||||||
|
| floating
|
||||||
|
: bool
|
||||||
|
: True if there is a floating window in the current tag.
|
||||||
|
| selmon
|
||||||
|
: bool
|
||||||
|
: True if the monitor is actually focused.
|
||||||
|
| layout
|
||||||
|
: string
|
||||||
|
: The actual layout name of the tag.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| number-of-tags
|
||||||
|
: int
|
||||||
|
: yes
|
||||||
|
: The number of defined tags in the dwl `config.def.h`.
|
||||||
|
| dwl-info-filename
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: The filepath to the log emitted by dwl when running.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- dwl:
|
||||||
|
number-of-tags: 9
|
||||||
|
dwl-info-filename: "/home/ogromny/dwl_info"
|
||||||
|
content:
|
||||||
|
list:
|
||||||
|
items:
|
||||||
|
- map:
|
||||||
|
conditions:
|
||||||
|
selected: {string: {text: "-> {id}"}}
|
||||||
|
~empty: {string: {text: "{id}"}}
|
||||||
|
urgent: {string: {text: "=> {id} <="}}
|
||||||
|
# default tag
|
||||||
|
id == 0: {string: {text: "{layout} {title}"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
|
@ -135,6 +135,7 @@ yambar = executable(
|
||||||
plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[],
|
plugin_mpd_enabled? '-DPLUGIN_ENABLED_MPD':[],
|
||||||
plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[],
|
plugin_pulse_enabled? '-DPLUGIN_ENABLED_PULSE':[],
|
||||||
plugin_pipewire_enabled? '-DPLUGIN_ENABLED_PIPEWIRE':[],
|
plugin_pipewire_enabled? '-DPLUGIN_ENABLED_PIPEWIRE':[],
|
||||||
|
plugin_dwl_enabled? '-DPLUGIN_ENABLED_DWL':[],
|
||||||
],
|
],
|
||||||
build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles',
|
build_rpath: '$ORIGIN/modules:$ORIGIN/decorations:$ORIGIN/particles',
|
||||||
export_dynamic: true,
|
export_dynamic: true,
|
||||||
|
@ -175,7 +176,8 @@ summary(
|
||||||
{
|
{
|
||||||
'Music Player Daemon (MPD)': plugin_mpd_enabled,
|
'Music Player Daemon (MPD)': plugin_mpd_enabled,
|
||||||
'PulseAudio': plugin_pulse_enabled,
|
'PulseAudio': plugin_pulse_enabled,
|
||||||
'Pipewire': plugin_pipewire_enabled
|
'Pipewire': plugin_pipewire_enabled,
|
||||||
|
'DWL (dwm for wayland)': plugin_dwl_enabled,
|
||||||
},
|
},
|
||||||
section: 'Optional modules',
|
section: 'Optional modules',
|
||||||
bool_yn: true
|
bool_yn: true
|
||||||
|
|
|
@ -14,3 +14,6 @@ option(
|
||||||
option(
|
option(
|
||||||
'plugin-pipewire', type: 'feature', value: 'auto',
|
'plugin-pipewire', type: 'feature', value: 'auto',
|
||||||
description: 'Pipewire support')
|
description: 'Pipewire support')
|
||||||
|
option(
|
||||||
|
'plugin-dwl', type: 'feature', value: 'auto',
|
||||||
|
description: 'DWL (dwm for wayland) support')
|
||||||
|
|
487
modules/dwl.c
Normal file
487
modules/dwl.c
Normal file
|
@ -0,0 +1,487 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define ARR_LEN(x) (sizeof((x)) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
#include "../config-verify.h"
|
||||||
|
#include "../config.h"
|
||||||
|
#include "../log.h"
|
||||||
|
#include "../module.h"
|
||||||
|
#include "../particles/dynlist.h"
|
||||||
|
#include "../plugin.h"
|
||||||
|
|
||||||
|
#define LOG_MODULE "dwl"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
|
||||||
|
struct dwl_tag {
|
||||||
|
int id;
|
||||||
|
bool selected;
|
||||||
|
bool empty;
|
||||||
|
bool urgent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct private
|
||||||
|
{
|
||||||
|
struct particle *label;
|
||||||
|
|
||||||
|
char const *monitor;
|
||||||
|
|
||||||
|
unsigned int number_of_tags;
|
||||||
|
char *dwl_info_filename;
|
||||||
|
|
||||||
|
/* dwl data */
|
||||||
|
char *title;
|
||||||
|
bool fullscreen;
|
||||||
|
bool floating;
|
||||||
|
bool selmon;
|
||||||
|
tll(struct dwl_tag *) tags;
|
||||||
|
char *layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LINE_MODE {
|
||||||
|
LINE_MODE_0,
|
||||||
|
LINE_MODE_TITLE,
|
||||||
|
LINE_MODE_FULLSCREEN,
|
||||||
|
LINE_MODE_FLOATING,
|
||||||
|
LINE_MODE_SELMON,
|
||||||
|
LINE_MODE_TAGS,
|
||||||
|
LINE_MODE_LAYOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy(struct module *module)
|
||||||
|
{
|
||||||
|
struct private *private = module->private;
|
||||||
|
private->label->destroy(private->label);
|
||||||
|
|
||||||
|
tll_free_and_free(private->tags, free);
|
||||||
|
free(private->dwl_info_filename);
|
||||||
|
free(private->title);
|
||||||
|
free(private->layout);
|
||||||
|
free(private);
|
||||||
|
|
||||||
|
module_default_destroy(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char const *
|
||||||
|
description(struct module *module)
|
||||||
|
{
|
||||||
|
return "dwl";
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct exposable *
|
||||||
|
content(struct module *module)
|
||||||
|
{
|
||||||
|
struct private const *private = module->private;
|
||||||
|
mtx_lock(&module->lock);
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
/* + 1 for `default` tag */
|
||||||
|
struct exposable *exposable[tll_length(private->tags) + 1];
|
||||||
|
tll_foreach(private->tags, it)
|
||||||
|
{
|
||||||
|
struct tag_set tags = {
|
||||||
|
.tags = (struct tag*[]){
|
||||||
|
tag_new_string(module, "title", private->title),
|
||||||
|
tag_new_bool(module, "fullscreen", private->fullscreen),
|
||||||
|
tag_new_bool(module, "floating", private->floating),
|
||||||
|
tag_new_bool(module, "selmon", private->selmon),
|
||||||
|
tag_new_string(module, "layout", private->layout),
|
||||||
|
tag_new_int(module, "id", it->item->id),
|
||||||
|
tag_new_bool(module, "selected", it->item->selected),
|
||||||
|
tag_new_bool(module, "empty", it->item->empty),
|
||||||
|
tag_new_bool(module, "urgent", it->item->urgent),
|
||||||
|
},
|
||||||
|
.count = 9,
|
||||||
|
};
|
||||||
|
exposable[i++] = private->label->instantiate(private->label, &tags);
|
||||||
|
tag_set_destroy(&tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default tag (used for title, layout, etc) */
|
||||||
|
struct tag_set tags = {
|
||||||
|
.tags = (struct tag*[]){
|
||||||
|
tag_new_string(module, "title", private->title),
|
||||||
|
tag_new_bool(module, "fullscreen", private->fullscreen),
|
||||||
|
tag_new_bool(module, "floating", private->floating),
|
||||||
|
tag_new_bool(module, "selmon", private->selmon),
|
||||||
|
tag_new_string(module, "layout", private->layout),
|
||||||
|
tag_new_int(module, "id", 0),
|
||||||
|
tag_new_bool(module, "selected", false),
|
||||||
|
tag_new_bool(module, "empty", true),
|
||||||
|
tag_new_bool(module, "urgent", false),
|
||||||
|
},
|
||||||
|
.count = 9,
|
||||||
|
};
|
||||||
|
exposable[i++] = private->label->instantiate(private->label, &tags);
|
||||||
|
tag_set_destroy(&tags);
|
||||||
|
|
||||||
|
mtx_unlock(&module->lock);
|
||||||
|
return dynlist_exposable_new(exposable, i, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dwl_tag *
|
||||||
|
dwl_tag_find_or_create(struct private *private, uint32_t id)
|
||||||
|
{
|
||||||
|
tll_foreach(private->tags, it)
|
||||||
|
{
|
||||||
|
if (it->item->id == id)
|
||||||
|
return it->item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No need to order the tag, `print_status` from dwl already orders tags */
|
||||||
|
struct dwl_tag *dwl_tag = calloc(1, sizeof(struct dwl_tag));
|
||||||
|
dwl_tag->id = id;
|
||||||
|
tll_push_back(private->tags, dwl_tag);
|
||||||
|
return dwl_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_line(char *line, struct module *module)
|
||||||
|
{
|
||||||
|
struct private *private = module->private;
|
||||||
|
enum LINE_MODE line_mode = LINE_MODE_0;
|
||||||
|
|
||||||
|
/* Remove \n */
|
||||||
|
line[strcspn(line, "\n")] = '\0';
|
||||||
|
|
||||||
|
/* Split line by space */
|
||||||
|
size_t index = 1;
|
||||||
|
char *save_pointer = NULL;
|
||||||
|
char *string = strtok_r(line, " ", &save_pointer);
|
||||||
|
while (string != NULL) {
|
||||||
|
/* dwl logs are formatted like this
|
||||||
|
* $1 -> monitor
|
||||||
|
* $2 -> action
|
||||||
|
* $3 -> arg1
|
||||||
|
* $4 -> arg2
|
||||||
|
* ... */
|
||||||
|
|
||||||
|
/* monitor */
|
||||||
|
if (index == 1) {
|
||||||
|
/* Not our monitor */
|
||||||
|
if (strcmp(string, private->monitor) != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* action */
|
||||||
|
else if (index == 2) {
|
||||||
|
if (strcmp(string, "title") == 0)
|
||||||
|
line_mode = LINE_MODE_TITLE;
|
||||||
|
else if (strcmp(string, "fullscreen") == 0)
|
||||||
|
line_mode = LINE_MODE_FULLSCREEN;
|
||||||
|
else if (strcmp(string, "floating") == 0)
|
||||||
|
line_mode = LINE_MODE_FLOATING;
|
||||||
|
else if (strcmp(string, "selmon") == 0)
|
||||||
|
line_mode = LINE_MODE_SELMON;
|
||||||
|
else if (strcmp(string, "tags") == 0)
|
||||||
|
line_mode = LINE_MODE_TAGS;
|
||||||
|
else if (strcmp(string, "layout") == 0)
|
||||||
|
line_mode = LINE_MODE_LAYOUT;
|
||||||
|
else {
|
||||||
|
LOG_WARN("UNKNOWN action, please open an issue on https://codeberg.org/dnkl/yambar");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* args */
|
||||||
|
else {
|
||||||
|
if (line_mode == LINE_MODE_TAGS) {
|
||||||
|
static uint32_t occupied, selected, client_tags, urgent;
|
||||||
|
static uint32_t *target = NULL;
|
||||||
|
|
||||||
|
/* dwl tags action log are formatted like this
|
||||||
|
* $3 -> occupied
|
||||||
|
* $4 -> tags
|
||||||
|
* $5 -> clientTags (not needed)
|
||||||
|
* $6 -> urgent */
|
||||||
|
if (index == 3)
|
||||||
|
target = &occupied;
|
||||||
|
else if (index == 4)
|
||||||
|
target = &selected;
|
||||||
|
else if (index == 5)
|
||||||
|
target = &client_tags;
|
||||||
|
else if (index == 6)
|
||||||
|
target = &urgent;
|
||||||
|
|
||||||
|
/* No need to check error IMHO */
|
||||||
|
*target = strtoul(string, NULL, 10);
|
||||||
|
|
||||||
|
/* Populate informations */
|
||||||
|
if (index == 6) {
|
||||||
|
for (size_t id = 1; id <= private->number_of_tags; ++id) {
|
||||||
|
uint32_t mask = 1 << (id - 1);
|
||||||
|
|
||||||
|
struct dwl_tag *dwl_tag = dwl_tag_find_or_create(private, id);
|
||||||
|
dwl_tag->selected = mask & selected;
|
||||||
|
dwl_tag->empty = !(mask & occupied);
|
||||||
|
dwl_tag->urgent = mask & urgent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
switch (line_mode) {
|
||||||
|
case LINE_MODE_TITLE:
|
||||||
|
free(private->title);
|
||||||
|
private->title = strdup(string);
|
||||||
|
break;
|
||||||
|
case LINE_MODE_FULLSCREEN:
|
||||||
|
private->fullscreen = (strcmp(string, "0") != 0);
|
||||||
|
break;
|
||||||
|
case LINE_MODE_FLOATING:
|
||||||
|
private->floating = (strcmp(string, "0") != 0);
|
||||||
|
break;
|
||||||
|
case LINE_MODE_SELMON:
|
||||||
|
private->selmon = (strcmp(string, "0") != 0);
|
||||||
|
break;
|
||||||
|
case LINE_MODE_LAYOUT:
|
||||||
|
free(private->layout);
|
||||||
|
private->layout = strdup(string);
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
assert(false); /* unreachable */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string = strtok_r(NULL, " ", &save_pointer);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
file_read_content(FILE *file, struct module *module)
|
||||||
|
{
|
||||||
|
static char buffer[1024];
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
while (fgets(buffer, ARR_LEN(buffer), file) != NULL)
|
||||||
|
process_line(buffer, module);
|
||||||
|
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
|
||||||
|
/* Check whether error has been */
|
||||||
|
if (ferror(file) != 0) {
|
||||||
|
LOG_ERRNO("unable to read file's content.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
file_seek_to_last_n_lines(FILE *file, int number_of_lines)
|
||||||
|
{
|
||||||
|
if (number_of_lines == 0 || file == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
|
||||||
|
long position = ftell(file);
|
||||||
|
while (position > 0) {
|
||||||
|
/* Cannot go less than position 0 */
|
||||||
|
if (fseek(file, --position, SEEK_SET) == EINVAL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (fgetc(file) == '\n')
|
||||||
|
if (number_of_lines-- == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_init(int *inotify_fd, int *inotify_wd, FILE **file, char *dwl_info_filename)
|
||||||
|
{
|
||||||
|
*inotify_fd = inotify_init();
|
||||||
|
if (*inotify_fd == -1) {
|
||||||
|
LOG_ERRNO("unable to create inotify fd.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*inotify_wd = inotify_add_watch(*inotify_fd, dwl_info_filename, IN_MODIFY);
|
||||||
|
if (*inotify_wd == -1) {
|
||||||
|
close(*inotify_fd);
|
||||||
|
LOG_ERRNO("unable to add watch to inotify fd.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*file = fopen(dwl_info_filename, "r");
|
||||||
|
if (*file == NULL) {
|
||||||
|
inotify_rm_watch(*inotify_fd, *inotify_wd);
|
||||||
|
close(*inotify_fd);
|
||||||
|
LOG_ERRNO("unable to open file.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_clean(int inotify_fd, int inotify_wd, FILE *file)
|
||||||
|
{
|
||||||
|
if (inotify_fd != -1) {
|
||||||
|
if (inotify_wd != -1)
|
||||||
|
inotify_rm_watch(inotify_fd, inotify_wd);
|
||||||
|
close(inotify_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file != NULL) {
|
||||||
|
if (fclose(file) == EOF) {
|
||||||
|
LOG_ERRNO("unable to close file.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
run(struct module *module)
|
||||||
|
{
|
||||||
|
struct private *private = module->private;
|
||||||
|
|
||||||
|
/* Ugly, but I didn't find better way for waiting
|
||||||
|
* the monitor's name to be set */
|
||||||
|
do {
|
||||||
|
private->monitor = module->bar->output_name(module->bar);
|
||||||
|
usleep(50);
|
||||||
|
} while (private->monitor == NULL);
|
||||||
|
|
||||||
|
int inotify_fd = -1, inotify_wd = -1;
|
||||||
|
FILE *file = NULL;
|
||||||
|
if (run_init(&inotify_fd, &inotify_wd, &file, private->dwl_info_filename) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Dwl output is 6 lines per monitor, so let's assume that nobody has
|
||||||
|
* more than 5 monitors (6 * 5 = 30) */
|
||||||
|
mtx_lock(&module->lock);
|
||||||
|
file_seek_to_last_n_lines(file, 30);
|
||||||
|
if (file_read_content(file, module) != 0) {
|
||||||
|
mtx_unlock(&module->lock);
|
||||||
|
return run_clean(inotify_fd, inotify_wd, file);
|
||||||
|
}
|
||||||
|
mtx_unlock(&module->lock);
|
||||||
|
|
||||||
|
module->bar->refresh(module->bar);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct pollfd fds[] = {
|
||||||
|
(struct pollfd){.fd = module->abort_fd, .events = POLLIN},
|
||||||
|
(struct pollfd){.fd = inotify_fd, .events = POLLIN},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (poll(fds, ARR_LEN(fds), -1) == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOG_ERRNO("unable to poll.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds[0].revents & POLLIN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* fds[1] (inotify_fd) must be POLLIN otherwise issue happen'd */
|
||||||
|
if (!(fds[1].revents & POLLIN)) {
|
||||||
|
LOG_ERR("expected POLLIN revent");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block until event */
|
||||||
|
static char buffer[1024];
|
||||||
|
ssize_t length = read(inotify_fd, buffer, ARR_LEN(buffer));
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (length == -1) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOG_ERRNO("unable to read %s", private->dwl_info_filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx_lock(&module->lock);
|
||||||
|
if (file_read_content(file, module) != 0) {
|
||||||
|
mtx_unlock(&module->lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mtx_unlock(&module->lock);
|
||||||
|
|
||||||
|
module->bar->refresh(module->bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return run_clean(inotify_fd, inotify_wd, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct module *
|
||||||
|
dwl_new(struct particle *label, int number_of_tags, char const *dwl_info_filename)
|
||||||
|
{
|
||||||
|
struct private *private = calloc(1, sizeof(struct private));
|
||||||
|
private->label = label;
|
||||||
|
private->number_of_tags = number_of_tags;
|
||||||
|
private->dwl_info_filename = strdup(dwl_info_filename);
|
||||||
|
|
||||||
|
struct module *module = module_common_new();
|
||||||
|
module->private = private;
|
||||||
|
module->run = &run;
|
||||||
|
module->destroy = &destroy;
|
||||||
|
module->content = &content;
|
||||||
|
module->description = &description;
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct module *
|
||||||
|
from_conf(struct yml_node const *node, struct conf_inherit inherited)
|
||||||
|
{
|
||||||
|
struct yml_node const *content = yml_get_value(node, "content");
|
||||||
|
struct yml_node const *number_of_tags = yml_get_value(node, "number-of-tags");
|
||||||
|
struct yml_node const *dwl_info_filename = yml_get_value(node, "dwl-info-filename");
|
||||||
|
|
||||||
|
return dwl_new(conf_to_particle(content, inherited), yml_value_as_int(number_of_tags),
|
||||||
|
yml_value_as_string(dwl_info_filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
verify_conf(keychain_t *keychain, struct yml_node const *node)
|
||||||
|
{
|
||||||
|
|
||||||
|
static struct attr_info const attrs[] = {
|
||||||
|
{"number-of-tags", true, &conf_verify_unsigned},
|
||||||
|
{"dwl-info-filename", true, &conf_verify_string},
|
||||||
|
MODULE_COMMON_ATTRS,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!conf_verify_dict(keychain, node, attrs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* No need to check whether is `number_of_tags` is a int
|
||||||
|
* because `conf_verify_unsigned` already did it */
|
||||||
|
struct yml_node const *key = yml_get_key(node, "number-of-tags");
|
||||||
|
struct yml_node const *value = yml_get_value(node, "number-of-tags");
|
||||||
|
if (yml_value_as_int(value) == 0) {
|
||||||
|
LOG_ERR("%s: %s must not be 0", conf_err_prefix(keychain, key), yml_value_as_string(key));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No need to check whether is `dwl_info_filename` is a string
|
||||||
|
* because `conf_verify_string` already did it */
|
||||||
|
key = yml_get_key(node, "dwl-info-filename");
|
||||||
|
value = yml_get_value(node, "dwl-info-filename");
|
||||||
|
if (strlen(yml_value_as_string(value)) == 0) {
|
||||||
|
LOG_ERR("%s: %s must not be empty", conf_err_prefix(keychain, key), yml_value_as_string(key));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct module_iface const module_dwl_iface = {
|
||||||
|
.verify_conf = &verify_conf,
|
||||||
|
.from_conf = &from_conf,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES)
|
||||||
|
extern struct module_iface const iface __attribute__((weak, alias("module_dwl_iface")));
|
||||||
|
#endif
|
|
@ -16,6 +16,8 @@ plugin_pipewire_enabled = pipewire.found()
|
||||||
pulse = dependency('libpulse', required: get_option('plugin-pulse'))
|
pulse = dependency('libpulse', required: get_option('plugin-pulse'))
|
||||||
plugin_pulse_enabled = pulse.found()
|
plugin_pulse_enabled = pulse.found()
|
||||||
|
|
||||||
|
plugin_dwl_enabled = get_option('plugin-dwl').allowed()
|
||||||
|
|
||||||
# Module name -> (source-list, dep-list)
|
# Module name -> (source-list, dep-list)
|
||||||
mod_data = {
|
mod_data = {
|
||||||
'alsa': [[], [m, alsa]],
|
'alsa': [[], [m, alsa]],
|
||||||
|
@ -33,6 +35,10 @@ mod_data = {
|
||||||
'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]],
|
'sway-xkb': [['i3-common.c', 'i3-common.h'], [dynlist, json]],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if plugin_dwl_enabled
|
||||||
|
mod_data += {'dwl': [[], [dynlist]]}
|
||||||
|
endif
|
||||||
|
|
||||||
if plugin_mpd_enabled
|
if plugin_mpd_enabled
|
||||||
mod_data += {'mpd': [[], [mpd]]}
|
mod_data += {'mpd': [[], [mpd]]}
|
||||||
endif
|
endif
|
||||||
|
|
6
plugin.c
6
plugin.c
|
@ -37,6 +37,9 @@ EXTERN_MODULE(backlight);
|
||||||
EXTERN_MODULE(battery);
|
EXTERN_MODULE(battery);
|
||||||
EXTERN_MODULE(clock);
|
EXTERN_MODULE(clock);
|
||||||
EXTERN_MODULE(disk_io);
|
EXTERN_MODULE(disk_io);
|
||||||
|
#if defined(PLUGIN_ENABLED_DWL)
|
||||||
|
EXTERN_MODULE(dwl);
|
||||||
|
#endif
|
||||||
EXTERN_MODULE(foreign_toplevel);
|
EXTERN_MODULE(foreign_toplevel);
|
||||||
EXTERN_MODULE(i3);
|
EXTERN_MODULE(i3);
|
||||||
EXTERN_MODULE(label);
|
EXTERN_MODULE(label);
|
||||||
|
@ -126,6 +129,9 @@ init(void)
|
||||||
REGISTER_CORE_MODULE(battery, battery);
|
REGISTER_CORE_MODULE(battery, battery);
|
||||||
REGISTER_CORE_MODULE(clock, clock);
|
REGISTER_CORE_MODULE(clock, clock);
|
||||||
REGISTER_CORE_MODULE(disk-io, disk_io);
|
REGISTER_CORE_MODULE(disk-io, disk_io);
|
||||||
|
#if defined(PLUGIN_ENABLED_DWL)
|
||||||
|
REGISTER_CORE_MODULE(dwl, dwl);
|
||||||
|
#endif
|
||||||
#if defined(HAVE_PLUGIN_foreign_toplevel)
|
#if defined(HAVE_PLUGIN_foreign_toplevel)
|
||||||
REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel);
|
REGISTER_CORE_MODULE(foreign-toplevel, foreign_toplevel);
|
||||||
#endif
|
#endif
|
||||||
|
|
23
yml.c
23
yml.c
|
@ -640,9 +640,11 @@ yml_is_list(const struct yml_node *node)
|
||||||
return node->type == LIST;
|
return node->type == LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct yml_node *
|
static struct yml_node const *
|
||||||
yml_get_value(const struct yml_node *node, const char *_path)
|
yml_get_(struct yml_node const *node, char const *_path, bool value)
|
||||||
{
|
{
|
||||||
|
/* value: true for value, false for key */
|
||||||
|
|
||||||
if (node != NULL && node->type == ROOT)
|
if (node != NULL && node->type == ROOT)
|
||||||
node = node->root.root;
|
node = node->root.root;
|
||||||
|
|
||||||
|
@ -662,7 +664,11 @@ yml_get_value(const struct yml_node *node, const char *_path)
|
||||||
if (strcmp(it->item.key->scalar.value, part) == 0) {
|
if (strcmp(it->item.key->scalar.value, part) == 0) {
|
||||||
if (next_part == NULL) {
|
if (next_part == NULL) {
|
||||||
free(path);
|
free(path);
|
||||||
return it->item.value;
|
|
||||||
|
if (value)
|
||||||
|
return it->item.value;
|
||||||
|
else
|
||||||
|
return it->item.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = it->item.value;
|
node = it->item.value;
|
||||||
|
@ -675,6 +681,17 @@ yml_get_value(const struct yml_node *node, const char *_path)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct yml_node *
|
||||||
|
yml_get_value(const struct yml_node *node, const char *_path)
|
||||||
|
{
|
||||||
|
return yml_get_(node, _path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct yml_node const *
|
||||||
|
yml_get_key(struct yml_node const *node, char const *_path) {
|
||||||
|
return yml_get_(node, _path, false);
|
||||||
|
}
|
||||||
|
|
||||||
struct yml_list_iter
|
struct yml_list_iter
|
||||||
yml_list_iter(const struct yml_node *list)
|
yml_list_iter(const struct yml_node *list)
|
||||||
{
|
{
|
||||||
|
|
2
yml.h
2
yml.h
|
@ -13,6 +13,8 @@ bool yml_is_list(const struct yml_node *node);
|
||||||
|
|
||||||
const struct yml_node *yml_get_value(
|
const struct yml_node *yml_get_value(
|
||||||
const struct yml_node *node, const char *path);
|
const struct yml_node *node, const char *path);
|
||||||
|
const struct yml_node *yml_get_key(
|
||||||
|
struct yml_node const *node, char const *path);
|
||||||
|
|
||||||
struct yml_list_iter {
|
struct yml_list_iter {
|
||||||
const struct yml_node *node;
|
const struct yml_node *node;
|
||||||
|
|
Loading…
Add table
Reference in a new issue