mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-24 12:55:41 +02:00
Merge branch 'master' into releases/1.6
This commit is contained in:
commit
213974c796
69 changed files with 2205 additions and 1070 deletions
|
@ -1,4 +1,4 @@
|
||||||
image: alpine/edge
|
image: alpine/latest
|
||||||
packages:
|
packages:
|
||||||
- musl-dev
|
- musl-dev
|
||||||
- eudev-libs
|
- eudev-libs
|
||||||
|
@ -21,20 +21,22 @@ packages:
|
||||||
- alsa-lib-dev
|
- alsa-lib-dev
|
||||||
- ttf-dejavu
|
- ttf-dejavu
|
||||||
- gcovr
|
- gcovr
|
||||||
|
- python3
|
||||||
|
- py3-pip
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://git.sr.ht/~dnkl/yambar
|
- https://codeberg.org/dnkl/yambar
|
||||||
|
|
||||||
triggers:
|
# triggers:
|
||||||
- action: email
|
# - action: email
|
||||||
condition: failure
|
# condition: failure
|
||||||
to: daniel@ekloef.se
|
# to: <comitter>
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- install-gcovr: |
|
- codespell: |
|
||||||
python2 -m ensurepip --user --upgrade
|
pip install codespell
|
||||||
python2 -m pip install --user --upgrade pip
|
cd yambar
|
||||||
python2 -m pip install --user --upgrade setuptools
|
~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd
|
||||||
- setup: |
|
- setup: |
|
||||||
mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared
|
mkdir -p bld/debug bld/release bld/x11-only bld/wayland-only bld/plugs-are-shared
|
||||||
meson --buildtype=debug -Db_coverage=true yambar bld/debug
|
meson --buildtype=debug -Db_coverage=true yambar bld/debug
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
/bld/
|
/bld/
|
||||||
/pkg/
|
/pkg/
|
||||||
/src/
|
/src/
|
||||||
/subprojects/
|
/subprojects/*
|
||||||
|
!/subprojects/*.wrap
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
image: alpine:edge
|
image: alpine:latest
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- info
|
- info
|
||||||
|
@ -8,7 +8,6 @@ variables:
|
||||||
GIT_SUBMODULE_STRATEGY: normal
|
GIT_SUBMODULE_STRATEGY: normal
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
|
||||||
- apk update
|
- apk update
|
||||||
- apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc
|
- apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc
|
||||||
- apk add pixman-dev freetype-dev fontconfig-dev
|
- apk add pixman-dev freetype-dev fontconfig-dev
|
||||||
|
@ -17,10 +16,6 @@ before_script:
|
||||||
- apk add json-c-dev libmpdclient-dev alsa-lib-dev
|
- apk add json-c-dev libmpdclient-dev alsa-lib-dev
|
||||||
- apk add ttf-dejavu
|
- apk add ttf-dejavu
|
||||||
- apk add git
|
- apk add git
|
||||||
- mkdir -p subprojects && cd subprojects
|
|
||||||
- git clone https://codeberg.org/dnkl/tllist.git
|
|
||||||
- git clone https://codeberg.org/dnkl/fcft.git
|
|
||||||
- cd ..
|
|
||||||
|
|
||||||
versions:
|
versions:
|
||||||
stage: info
|
stage: info
|
||||||
|
@ -92,3 +87,12 @@ plugins_as_shared_modules:
|
||||||
- meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../
|
- meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../
|
||||||
- ninja -k0
|
- ninja -k0
|
||||||
- meson test --print-errorlogs
|
- meson test --print-errorlogs
|
||||||
|
|
||||||
|
codespell:
|
||||||
|
image: alpine:latest
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- apk add python3
|
||||||
|
- apk add py3-pip
|
||||||
|
- pip install codespell
|
||||||
|
- codespell README.md CHANGELOG.md *.c *.h doc/*.scd
|
||||||
|
|
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -9,18 +9,51 @@
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* Text shaping support.
|
||||||
|
* Support for middle and right mouse buttons, mouse wheel and trackpad
|
||||||
|
scrolling (https://codeberg.org/dnkl/yambar/issues/39).
|
||||||
|
* script: polling mode. See the new `poll-interval` option
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/67).
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
* doc: split up **yambar-modules**(5) into multiple man pages, one for
|
||||||
|
each module (https://codeberg.org/dnkl/yambar/issues/15).
|
||||||
|
* fcft >= 2.4.0 is now required.
|
||||||
|
* sway-xkb: non-keyboard inputs are now ignored
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/51).
|
||||||
|
* battery: don’t terminate (causing last status to “freeze”) when
|
||||||
|
failing to update; retry again later
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/44).
|
||||||
|
* battery: differentiate "Not Charging" and "Discharging" in state
|
||||||
|
tag of battery module.
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/57).
|
||||||
|
* string: use HORIZONTAL ELLIPSIS instead of three regular periods
|
||||||
|
when truncating a string
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/73).
|
||||||
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
### Removed
|
### Removed
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Crash when merging non-dictionary anchors in the YAML configuration
|
* Crash when merging non-dictionary anchors in the YAML configuration
|
||||||
(https://codeberg.org/dnkl/yambar/issues/32).
|
(https://codeberg.org/dnkl/yambar/issues/32).
|
||||||
|
* Crash in the `ramp` particle when the tag’s value was out-of-bounds
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/45).
|
||||||
|
* Crash when a string particle contained `{}`
|
||||||
|
(https://codeberg.org/dnkl/yambar/issues/48).
|
||||||
|
* `script` module rejecting range tag end values containing the digit
|
||||||
|
`9` (https://codeberg.org/dnkl/yambar/issues/60).
|
||||||
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
### Contributors
|
### Contributors
|
||||||
|
|
||||||
|
* [novakane](https://codeberg.org/novakane)
|
||||||
|
* [mz](https://codeberg.org/mz)
|
||||||
|
|
||||||
## 1.6.1
|
## 1.6.1
|
||||||
|
|
||||||
|
|
2
PKGBUILD
2
PKGBUILD
|
@ -15,7 +15,7 @@ depends=(
|
||||||
'libudev.so'
|
'libudev.so'
|
||||||
'json-c'
|
'json-c'
|
||||||
'libmpdclient'
|
'libmpdclient'
|
||||||
'fcft>=2.0.0')
|
'fcft>=2.4.0')
|
||||||
optdepends=('xcb-util-errors: better X error messages')
|
optdepends=('xcb-util-errors: better X error messages')
|
||||||
source=()
|
source=()
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ depends=(
|
||||||
'libudev.so'
|
'libudev.so'
|
||||||
'json-c'
|
'json-c'
|
||||||
'libmpdclient'
|
'libmpdclient'
|
||||||
'fcft>=2.0.0')
|
'fcft>=2.4.0')
|
||||||
source=()
|
source=()
|
||||||
|
|
||||||
pkgver() {
|
pkgver() {
|
||||||
|
|
15
README.md
15
README.md
|
@ -1,5 +1,8 @@
|
||||||
# Yambar
|
# Yambar
|
||||||
|
|
||||||
|
[](https://repology.org/project/yambar/versions)
|
||||||
|
|
||||||
|
|
||||||
## Index
|
## Index
|
||||||
|
|
||||||
1. [Introduction](#introduction)
|
1. [Introduction](#introduction)
|
||||||
|
@ -87,18 +90,6 @@ Available modules:
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
If you have not installed [tllist](https://codeberg.org/dnkl/tllist)
|
|
||||||
and [fcft](https://codeberg.org/dnkl/fcft) as system libraries, clone
|
|
||||||
them into the `subprojects` directory:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
mkdir -p subprojects
|
|
||||||
pushd subprojects
|
|
||||||
git clone https://codeberg.org/dnkl/tllist.git
|
|
||||||
git clone https://codeberg.org/dnkl/fcft.git
|
|
||||||
popd
|
|
||||||
```
|
|
||||||
|
|
||||||
To build, first, create a build directory, and switch to it:
|
To build, first, create a build directory, and switch to it:
|
||||||
```sh
|
```sh
|
||||||
mkdir -p bld/release && cd bld/release
|
mkdir -p bld/release && cd bld/release
|
||||||
|
|
|
@ -10,7 +10,7 @@ struct backend {
|
||||||
void (*loop)(struct bar *bar,
|
void (*loop)(struct bar *bar,
|
||||||
void (*expose)(const struct bar *bar),
|
void (*expose)(const struct bar *bar),
|
||||||
void (*on_mouse)(struct bar *bar, enum mouse_event event,
|
void (*on_mouse)(struct bar *bar, enum mouse_event event,
|
||||||
int x, int y));
|
enum mouse_button btn, int x, int y));
|
||||||
void (*commit)(const struct bar *bar);
|
void (*commit)(const struct bar *bar);
|
||||||
void (*refresh)(const struct bar *bar);
|
void (*refresh)(const struct bar *bar);
|
||||||
void (*set_cursor)(struct bar *bar, const char *cursor);
|
void (*set_cursor)(struct bar *bar, const char *cursor);
|
||||||
|
|
29
bar/bar.c
29
bar/bar.c
|
@ -2,12 +2,14 @@
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <threads.h>
|
#include <threads.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
|
|
||||||
|
@ -149,7 +151,8 @@ set_cursor(struct bar *bar, const char *cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
on_mouse(struct bar *_bar, enum mouse_event event, enum mouse_button btn,
|
||||||
|
int x, int y)
|
||||||
{
|
{
|
||||||
struct private *bar = _bar->private;
|
struct private *bar = _bar->private;
|
||||||
|
|
||||||
|
@ -171,7 +174,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
||||||
mx += bar->left_spacing;
|
mx += bar->left_spacing;
|
||||||
if (x >= mx && x < mx + e->width) {
|
if (x >= mx && x < mx + e->width) {
|
||||||
if (e->on_mouse != NULL)
|
if (e->on_mouse != NULL)
|
||||||
e->on_mouse(e, _bar, event, x - mx, y);
|
e->on_mouse(e, _bar, event, btn, x - mx, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +188,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
||||||
mx += bar->left_spacing;
|
mx += bar->left_spacing;
|
||||||
if (x >= mx && x < mx + e->width) {
|
if (x >= mx && x < mx + e->width) {
|
||||||
if (e->on_mouse != NULL)
|
if (e->on_mouse != NULL)
|
||||||
e->on_mouse(e, _bar, event, x - mx, y);
|
e->on_mouse(e, _bar, event, btn, x - mx, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +206,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
||||||
mx += bar->left_spacing;
|
mx += bar->left_spacing;
|
||||||
if (x >= mx && x < mx + e->width) {
|
if (x >= mx && x < mx + e->width) {
|
||||||
if (e->on_mouse != NULL)
|
if (e->on_mouse != NULL)
|
||||||
e->on_mouse(e, _bar, event, x - mx, y);
|
e->on_mouse(e, _bar, event, btn, x - mx, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +216,20 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
||||||
set_cursor(_bar, "left_ptr");
|
set_cursor(_bar, "left_ptr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_module_thread_name(thrd_t id, struct module *mod)
|
||||||
|
{
|
||||||
|
char title[16];
|
||||||
|
if (mod->description != NULL)
|
||||||
|
strncpy(title, mod->description(mod), sizeof(title));
|
||||||
|
else
|
||||||
|
strncpy(title, "mod:<unknown>", sizeof(title));
|
||||||
|
|
||||||
|
title[15] = '\0';
|
||||||
|
|
||||||
|
if (pthread_setname_np(id, title) < 0)
|
||||||
|
LOG_ERRNO("failed to set thread title");
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
run(struct bar *_bar)
|
run(struct bar *_bar)
|
||||||
|
@ -240,18 +257,21 @@ run(struct bar *_bar)
|
||||||
|
|
||||||
mod->abort_fd = _bar->abort_fd;
|
mod->abort_fd = _bar->abort_fd;
|
||||||
thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, mod);
|
thrd_create(&thrd_left[i], (int (*)(void *))bar->left.mods[i]->run, mod);
|
||||||
|
set_module_thread_name(thrd_left[i], mod);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < bar->center.count; i++) {
|
for (size_t i = 0; i < bar->center.count; i++) {
|
||||||
struct module *mod = bar->center.mods[i];
|
struct module *mod = bar->center.mods[i];
|
||||||
|
|
||||||
mod->abort_fd = _bar->abort_fd;
|
mod->abort_fd = _bar->abort_fd;
|
||||||
thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, mod);
|
thrd_create(&thrd_center[i], (int (*)(void *))bar->center.mods[i]->run, mod);
|
||||||
|
set_module_thread_name(thrd_center[i], mod);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < bar->right.count; i++) {
|
for (size_t i = 0; i < bar->right.count; i++) {
|
||||||
struct module *mod = bar->right.mods[i];
|
struct module *mod = bar->right.mods[i];
|
||||||
|
|
||||||
mod->abort_fd = _bar->abort_fd;
|
mod->abort_fd = _bar->abort_fd;
|
||||||
thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, mod);
|
thrd_create(&thrd_right[i], (int (*)(void *))bar->right.mods[i]->run, mod);
|
||||||
|
set_module_thread_name(thrd_right[i], mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("all modules started");
|
LOG_DBG("all modules started");
|
||||||
|
@ -388,6 +408,7 @@ bar_new(const struct bar_config *config)
|
||||||
priv->right_spacing = config->right_spacing;
|
priv->right_spacing = config->right_spacing;
|
||||||
priv->left_margin = config->left_margin;
|
priv->left_margin = config->left_margin;
|
||||||
priv->right_margin = config->right_margin;
|
priv->right_margin = config->right_margin;
|
||||||
|
priv->trackpad_sensitivity = config->trackpad_sensitivity;
|
||||||
priv->border.width = config->border.width;
|
priv->border.width = config->border.width;
|
||||||
priv->border.color = config->border.color;
|
priv->border.color = config->border.color;
|
||||||
priv->border.left_margin = config->border.left_margin;
|
priv->border.left_margin = config->border.left_margin;
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct bar_config {
|
||||||
int height;
|
int height;
|
||||||
int left_spacing, right_spacing;
|
int left_spacing, right_spacing;
|
||||||
int left_margin, right_margin;
|
int left_margin, right_margin;
|
||||||
|
int trackpad_sensitivity;
|
||||||
|
|
||||||
pixman_color_t background;
|
pixman_color_t background;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ struct private {
|
||||||
int height;
|
int height;
|
||||||
int left_spacing, right_spacing;
|
int left_spacing, right_spacing;
|
||||||
int left_margin, right_margin;
|
int left_margin, right_margin;
|
||||||
|
int trackpad_sensitivity;
|
||||||
|
|
||||||
pixman_color_t background;
|
pixman_color_t background;
|
||||||
|
|
||||||
|
|
100
bar/wayland.c
100
bar/wayland.c
|
@ -6,9 +6,11 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <linux/memfd.h>
|
#include <linux/memfd.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
|
||||||
#include <pixman.h>
|
#include <pixman.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
@ -111,8 +113,12 @@ struct wayland_backend {
|
||||||
struct buffer *next_buffer; /* Bar is rendering to this one */
|
struct buffer *next_buffer; /* Bar is rendering to this one */
|
||||||
struct buffer *pending_buffer; /* Finished, but not yet rendered */
|
struct buffer *pending_buffer; /* Finished, but not yet rendered */
|
||||||
|
|
||||||
|
double aggregated_scroll;
|
||||||
|
bool have_discrete;
|
||||||
|
|
||||||
void (*bar_expose)(const struct bar *bar);
|
void (*bar_expose)(const struct bar *bar);
|
||||||
void (*bar_on_mouse)(struct bar *bar, enum mouse_event event, int x, int y);
|
void (*bar_on_mouse)(struct bar *bar, enum mouse_event event,
|
||||||
|
enum mouse_button btn, int x, int y);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -245,6 +251,8 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
struct wayland_backend *backend = seat->backend;
|
struct wayland_backend *backend = seat->backend;
|
||||||
|
|
||||||
|
backend->have_discrete = false;
|
||||||
|
|
||||||
if (backend->active_seat == seat)
|
if (backend->active_seat == seat)
|
||||||
backend->active_seat = NULL;
|
backend->active_seat = NULL;
|
||||||
}
|
}
|
||||||
|
@ -261,33 +269,81 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
|
||||||
backend->active_seat = seat;
|
backend->active_seat = seat;
|
||||||
backend->bar_on_mouse(
|
backend->bar_on_mouse(
|
||||||
backend->bar, ON_MOUSE_MOTION, seat->pointer.x, seat->pointer.y);
|
backend->bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE,
|
||||||
|
seat->pointer.x, seat->pointer.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
||||||
{
|
{
|
||||||
if (state != WL_POINTER_BUTTON_STATE_PRESSED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
struct wayland_backend *backend = seat->backend;
|
struct wayland_backend *backend = seat->backend;
|
||||||
|
|
||||||
|
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||||
backend->active_seat = seat;
|
backend->active_seat = seat;
|
||||||
|
else {
|
||||||
|
enum mouse_button btn;
|
||||||
|
|
||||||
|
switch (button) {
|
||||||
|
case BTN_LEFT: btn = MOUSE_BTN_LEFT; break;
|
||||||
|
case BTN_MIDDLE: btn = MOUSE_BTN_MIDDLE; break;
|
||||||
|
case BTN_RIGHT: btn = MOUSE_BTN_RIGHT; break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
backend->bar_on_mouse(
|
backend->bar_on_mouse(
|
||||||
backend->bar, ON_MOUSE_CLICK, seat->pointer.x, seat->pointer.y);
|
backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value)
|
uint32_t time, uint32_t axis, wl_fixed_t value)
|
||||||
{
|
{
|
||||||
|
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct seat *seat = data;
|
||||||
|
struct wayland_backend *backend = seat->backend;
|
||||||
|
struct private *bar = backend->bar->private;
|
||||||
|
|
||||||
|
backend->active_seat = seat;
|
||||||
|
|
||||||
|
if (backend->have_discrete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const double amount = wl_fixed_to_double(value);
|
||||||
|
|
||||||
|
if ((backend->aggregated_scroll > 0 && amount < 0) ||
|
||||||
|
(backend->aggregated_scroll < 0 && amount > 0))
|
||||||
|
{
|
||||||
|
backend->aggregated_scroll = amount;
|
||||||
|
} else
|
||||||
|
backend->aggregated_scroll += amount;
|
||||||
|
|
||||||
|
enum mouse_button btn = backend->aggregated_scroll > 0
|
||||||
|
? MOUSE_BTN_WHEEL_DOWN
|
||||||
|
: MOUSE_BTN_WHEEL_UP;
|
||||||
|
|
||||||
|
const double step = bar->trackpad_sensitivity;
|
||||||
|
const double adjust = backend->aggregated_scroll > 0 ? -step : step;
|
||||||
|
|
||||||
|
while (fabs(backend->aggregated_scroll) >= step) {
|
||||||
|
backend->bar_on_mouse(
|
||||||
|
backend->bar, ON_MOUSE_CLICK, btn,
|
||||||
|
seat->pointer.x, seat->pointer.y);
|
||||||
|
backend->aggregated_scroll += adjust;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
||||||
{
|
{
|
||||||
|
struct seat *seat = data;
|
||||||
|
struct wayland_backend *backend = seat->backend;
|
||||||
|
backend->have_discrete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -300,12 +356,36 @@ static void
|
||||||
wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t time, uint32_t axis)
|
uint32_t time, uint32_t axis)
|
||||||
{
|
{
|
||||||
|
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct seat *seat = data;
|
||||||
|
struct wayland_backend *backend = seat->backend;
|
||||||
|
backend->aggregated_scroll = 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t axis, int32_t discrete)
|
uint32_t axis, int32_t discrete)
|
||||||
{
|
{
|
||||||
|
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct seat *seat = data;
|
||||||
|
struct wayland_backend *backend = seat->backend;
|
||||||
|
backend->have_discrete = true;
|
||||||
|
|
||||||
|
enum mouse_button btn = discrete > 0
|
||||||
|
? MOUSE_BTN_WHEEL_DOWN
|
||||||
|
: MOUSE_BTN_WHEEL_UP;
|
||||||
|
|
||||||
|
int count = abs(discrete);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < count; i++) {
|
||||||
|
backend->bar_on_mouse(
|
||||||
|
backend->bar, ON_MOUSE_CLICK, btn,
|
||||||
|
seat->pointer.x, seat->pointer.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
static const struct wl_pointer_listener pointer_listener = {
|
||||||
|
@ -465,6 +545,7 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct monitor *mon = data;
|
struct monitor *mon = data;
|
||||||
|
free(mon->name);
|
||||||
mon->name = strdup(name);
|
mon->name = strdup(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +640,7 @@ handle_global(void *data, struct wl_registry *registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
const uint32_t required = 3;
|
const uint32_t required = 5;
|
||||||
if (!verify_iface_version(interface, version, required))
|
if (!verify_iface_version(interface, version, required))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1009,11 +1090,14 @@ cleanup(struct bar *_bar)
|
||||||
static void
|
static void
|
||||||
loop(struct bar *_bar,
|
loop(struct bar *_bar,
|
||||||
void (*expose)(const struct bar *bar),
|
void (*expose)(const struct bar *bar),
|
||||||
void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y))
|
void (*on_mouse)(struct bar *bar, enum mouse_event event,
|
||||||
|
enum mouse_button btn, int x, int y))
|
||||||
{
|
{
|
||||||
struct private *bar = _bar->private;
|
struct private *bar = _bar->private;
|
||||||
struct wayland_backend *backend = bar->backend.data;
|
struct wayland_backend *backend = bar->backend.data;
|
||||||
|
|
||||||
|
pthread_setname_np(pthread_self(), "bar(wayland)");
|
||||||
|
|
||||||
backend->bar_expose = expose;
|
backend->bar_expose = expose;
|
||||||
backend->bar_on_mouse = on_mouse;
|
backend->bar_on_mouse = on_mouse;
|
||||||
|
|
||||||
|
|
16
bar/xcb.c
16
bar/xcb.c
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <pixman.h>
|
#include <pixman.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
@ -311,11 +312,14 @@ cleanup(struct bar *_bar)
|
||||||
static void
|
static void
|
||||||
loop(struct bar *_bar,
|
loop(struct bar *_bar,
|
||||||
void (*expose)(const struct bar *bar),
|
void (*expose)(const struct bar *bar),
|
||||||
void (*on_mouse)(struct bar *bar, enum mouse_event event, int x, int y))
|
void (*on_mouse)(struct bar *bar, enum mouse_event event,
|
||||||
|
enum mouse_button btn, int x, int y))
|
||||||
{
|
{
|
||||||
struct private *bar = _bar->private;
|
struct private *bar = _bar->private;
|
||||||
struct xcb_backend *backend = bar->backend.data;
|
struct xcb_backend *backend = bar->backend.data;
|
||||||
|
|
||||||
|
pthread_setname_np(pthread_self(), "bar(xcb)");
|
||||||
|
|
||||||
const int fd = xcb_get_file_descriptor(backend->conn);
|
const int fd = xcb_get_file_descriptor(backend->conn);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -354,7 +358,7 @@ loop(struct bar *_bar,
|
||||||
|
|
||||||
case XCB_MOTION_NOTIFY: {
|
case XCB_MOTION_NOTIFY: {
|
||||||
const xcb_motion_notify_event_t *evt = (void *)e;
|
const xcb_motion_notify_event_t *evt = (void *)e;
|
||||||
on_mouse(_bar, ON_MOUSE_MOTION, evt->event_x, evt->event_y);
|
on_mouse(_bar, ON_MOUSE_MOTION, MOUSE_BTN_NONE, evt->event_x, evt->event_y);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +367,13 @@ loop(struct bar *_bar,
|
||||||
|
|
||||||
case XCB_BUTTON_RELEASE: {
|
case XCB_BUTTON_RELEASE: {
|
||||||
const xcb_button_release_event_t *evt = (void *)e;
|
const xcb_button_release_event_t *evt = (void *)e;
|
||||||
on_mouse(_bar, ON_MOUSE_CLICK, evt->event_x, evt->event_y);
|
|
||||||
|
switch (evt->detail) {
|
||||||
|
case 1: case 2: case 3: case 4: case 5:
|
||||||
|
on_mouse(_bar, ON_MOUSE_CLICK,
|
||||||
|
evt->detail, evt->event_x, evt->event_y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,26 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
conf_verify_on_click(keychain_t *chain, const struct yml_node *node)
|
||||||
|
{
|
||||||
|
/* on-click: <command> */
|
||||||
|
const char *s = yml_value_as_string(node);
|
||||||
|
if (s != NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
static const struct attr_info info[] = {
|
||||||
|
{"left", false, &conf_verify_string},
|
||||||
|
{"middle", false, &conf_verify_string},
|
||||||
|
{"right", false, &conf_verify_string},
|
||||||
|
{"wheel-up", false, &conf_verify_string},
|
||||||
|
{"wheel-down", false, &conf_verify_string},
|
||||||
|
{NULL, false, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
return conf_verify_dict(chain, node, info);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
conf_verify_color(keychain_t *chain, const struct yml_node *node)
|
conf_verify_color(keychain_t *chain, const struct yml_node *node)
|
||||||
{
|
{
|
||||||
|
@ -403,6 +423,8 @@ conf_verify_bar(const struct yml_node *bar)
|
||||||
{"center", false, &verify_module_list},
|
{"center", false, &verify_module_list},
|
||||||
{"right", false, &verify_module_list},
|
{"right", false, &verify_module_list},
|
||||||
|
|
||||||
|
{"trackpad-sensitivity", false, &conf_verify_int},
|
||||||
|
|
||||||
{NULL, false, NULL},
|
{NULL, false, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ bool conf_verify_list(keychain_t *chain, const struct yml_node *node,
|
||||||
bool conf_verify_dict(keychain_t *chain, const struct yml_node *node,
|
bool conf_verify_dict(keychain_t *chain, const struct yml_node *node,
|
||||||
const struct attr_info info[]); /* NULL-terminated list */
|
const struct attr_info info[]); /* NULL-terminated list */
|
||||||
|
|
||||||
|
bool conf_verify_on_click(keychain_t *chain, const struct yml_node *node);
|
||||||
bool conf_verify_color(keychain_t *chain, const struct yml_node *node);
|
bool conf_verify_color(keychain_t *chain, const struct yml_node *node);
|
||||||
bool conf_verify_font(keychain_t *chain, const struct yml_node *node);
|
bool conf_verify_font(keychain_t *chain, const struct yml_node *node);
|
||||||
|
|
||||||
|
|
41
config.c
41
config.c
|
@ -139,8 +139,37 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
int right = margin != NULL ? yml_value_as_int(margin) :
|
int right = margin != NULL ? yml_value_as_int(margin) :
|
||||||
right_margin != NULL ? yml_value_as_int(right_margin) : 0;
|
right_margin != NULL ? yml_value_as_int(right_margin) : 0;
|
||||||
|
|
||||||
const char *on_click_template
|
const char *on_click_templates[MOUSE_BTN_COUNT] = {NULL};
|
||||||
= on_click != NULL ? yml_value_as_string(on_click) : NULL;
|
if (on_click != NULL) {
|
||||||
|
const char *legacy = yml_value_as_string(on_click);
|
||||||
|
|
||||||
|
if (legacy != NULL)
|
||||||
|
on_click_templates[MOUSE_BTN_LEFT] = legacy;
|
||||||
|
|
||||||
|
if (yml_is_dict(on_click)) {
|
||||||
|
for (struct yml_dict_iter it = yml_dict_iter(on_click);
|
||||||
|
it.key != NULL;
|
||||||
|
yml_dict_next(&it))
|
||||||
|
{
|
||||||
|
const char *key = yml_value_as_string(it.key);
|
||||||
|
const char *template = yml_value_as_string(it.value);
|
||||||
|
|
||||||
|
if (strcmp(key, "left") == 0)
|
||||||
|
on_click_templates[MOUSE_BTN_LEFT] = template;
|
||||||
|
else if (strcmp(key, "middle") == 0)
|
||||||
|
on_click_templates[MOUSE_BTN_MIDDLE] = template;
|
||||||
|
else if (strcmp(key, "right") == 0)
|
||||||
|
on_click_templates[MOUSE_BTN_RIGHT] = template;
|
||||||
|
else if (strcmp(key, "wheel-up") == 0)
|
||||||
|
on_click_templates[MOUSE_BTN_WHEEL_UP] = template;
|
||||||
|
else if (strcmp(key, "wheel-down") == 0)
|
||||||
|
on_click_templates[MOUSE_BTN_WHEEL_DOWN] = template;
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct deco *deco = deco_node != NULL ? conf_to_deco(deco_node) : NULL;
|
struct deco *deco = deco_node != NULL ? conf_to_deco(deco_node) : NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -159,7 +188,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
|
|
||||||
/* Instantiate base/common particle */
|
/* Instantiate base/common particle */
|
||||||
struct particle *common = particle_common_new(
|
struct particle *common = particle_common_new(
|
||||||
left, right, on_click_template, font, foreground, deco);
|
left, right, on_click_templates, font, foreground, deco);
|
||||||
|
|
||||||
const struct particle_iface *iface = plugin_load_particle(type);
|
const struct particle_iface *iface = plugin_load_particle(type);
|
||||||
|
|
||||||
|
@ -223,6 +252,12 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend)
|
||||||
if (right_margin != NULL)
|
if (right_margin != NULL)
|
||||||
conf.right_margin = yml_value_as_int(right_margin);
|
conf.right_margin = yml_value_as_int(right_margin);
|
||||||
|
|
||||||
|
const struct yml_node *trackpad_sensitivity =
|
||||||
|
yml_get_value(bar, "trackpad-sensitivity");
|
||||||
|
conf.trackpad_sensitivity = trackpad_sensitivity != NULL
|
||||||
|
? yml_value_as_int(trackpad_sensitivity)
|
||||||
|
: 30;
|
||||||
|
|
||||||
const struct yml_node *border = yml_get_value(bar, "border");
|
const struct yml_node *border = yml_get_value(bar, "border");
|
||||||
if (border != NULL) {
|
if (border != NULL) {
|
||||||
const struct yml_node *width = yml_get_value(border, "width");
|
const struct yml_node *width = yml_get_value(border, "width");
|
||||||
|
|
|
@ -4,8 +4,15 @@ scdoc = dependency('scdoc', native: true)
|
||||||
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
|
scdoc_prog = find_program(scdoc.get_pkgconfig_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.5.scd', 'yambar-particles.5.scd',
|
'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.5.scd',
|
||||||
'yambar-tags.5.scd']
|
'yambar-modules-battery.5.scd', 'yambar-modules-clock.5.scd',
|
||||||
|
'yambar-modules-i3.5.scd', 'yambar-modules-label.5.scd',
|
||||||
|
'yambar-modules-mpd.5.scd', 'yambar-modules-network.5.scd',
|
||||||
|
'yambar-modules-removables.5.scd', 'yambar-modules-river.5.scd',
|
||||||
|
'yambar-modules-script.5.scd', 'yambar-modules-sway-xkb.5.scd',
|
||||||
|
'yambar-modules-sway.5.scd', 'yambar-modules-xkb.5.scd',
|
||||||
|
'yambar-modules-xwindow.5.scd', 'yambar-modules.5.scd',
|
||||||
|
'yambar-particles.5.scd', 'yambar-tags.5.scd']
|
||||||
parts = man_src.split('.')
|
parts = man_src.split('.')
|
||||||
name = parts[-3]
|
name = parts[-3]
|
||||||
section = parts[-2]
|
section = parts[-2]
|
||||||
|
|
51
doc/yambar-modules-alsa.5.scd
Normal file
51
doc/yambar-modules-alsa.5.scd
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
yambar-modules-alsa(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
alsa - Monitors an alsa soundcard for volume and mute/unmute changes
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| volume
|
||||||
|
: range
|
||||||
|
: Volume level, with min and max as start and end range values
|
||||||
|
| percent
|
||||||
|
: range
|
||||||
|
: Volume level, as a percentage
|
||||||
|
| muted
|
||||||
|
: bool
|
||||||
|
: True if muted, otherwise false
|
||||||
|
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| card
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: The soundcard name. *default* might work.
|
||||||
|
| mixer
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: Mixer channel to monitor. _Master_ might work.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- alsa:
|
||||||
|
card: hw:PCH
|
||||||
|
mixer: Master
|
||||||
|
content: {string: {text: "{volume}"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
47
doc/yambar-modules-backlight.5.scd
Normal file
47
doc/yambar-modules-backlight.5.scd
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
yambar-modules-backlight(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
backlight - This module reads monitor backlight status
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
This module reads monitor backlight status from
|
||||||
|
_/sys/class/backlight_, and uses *udev* to monitor for changes.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| brightness
|
||||||
|
: range
|
||||||
|
: The current brightness level, in absolute value
|
||||||
|
| percent
|
||||||
|
: range
|
||||||
|
: The current brightness level, in percent
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: The backlight device's name (one of the names in */sys/class/backlight*)
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- backlight:
|
||||||
|
name: intel_backlight
|
||||||
|
content:
|
||||||
|
string: {text: "backlight: {percent}%"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
66
doc/yambar-modules-battery.5.scd
Normal file
66
doc/yambar-modules-battery.5.scd
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
yambar-modules-battery(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
battery - This module reads battery status
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module reads battery status from _/sys/class/power_supply_ and
|
||||||
|
uses *udev* to monitor for changes.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: Battery device name
|
||||||
|
| manufacturer
|
||||||
|
: string
|
||||||
|
: Name of the battery manufacturer
|
||||||
|
| model
|
||||||
|
: string
|
||||||
|
: Battery model name
|
||||||
|
| state
|
||||||
|
: string
|
||||||
|
: One of *full*, *not charging*, *charging*, *discharging* or *unknown*
|
||||||
|
| capacity
|
||||||
|
: range
|
||||||
|
: capacity left, in percent
|
||||||
|
| estimate
|
||||||
|
: string
|
||||||
|
: Estimated time left (to empty while discharging, or to full while
|
||||||
|
charging), formatted as HH:MM.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: Battery device name (one of the names in */sys/class/power_supply*)
|
||||||
|
| poll-interval
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: How often, in seconds, to poll for capacity changes (default=*60*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting).
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- battery:
|
||||||
|
name: BAT0
|
||||||
|
poll-interval: 30
|
||||||
|
content:
|
||||||
|
string: {text: "BAT: {capacity}% {estimate}"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
47
doc/yambar-modules-clock.5.scd
Normal file
47
doc/yambar-modules-clock.5.scd
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
yambar-modules-clock(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
clock - This module provides the current date and time
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| time
|
||||||
|
: string
|
||||||
|
: Current time, formatted using the _time-format_ attribute
|
||||||
|
| date
|
||||||
|
: string
|
||||||
|
: Current date, formatted using the _date-format_ attribute
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| time-format
|
||||||
|
: string
|
||||||
|
: no
|
||||||
|
: *strftime* formatter for the _time_ tag (default=*%H:%M*)
|
||||||
|
| date-format
|
||||||
|
: string
|
||||||
|
: no
|
||||||
|
: *strftime* formatter for the _date_ date (default=*%x*)
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- clock:
|
||||||
|
time-format: "%H:%M %Z"
|
||||||
|
content:
|
||||||
|
string: {text: "{date} {time}"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
104
doc/yambar-modules-i3.5.scd
Normal file
104
doc/yambar-modules-i3.5.scd
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
yambar-modules-i3(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
i3 - This module monitors i3 and sway workspaces
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Unlike other modules where the _content_ attribute is just a single
|
||||||
|
*particle*, the i3 module's _content_ is an associative array mapping
|
||||||
|
i3/sway workspace names to a particle.
|
||||||
|
|
||||||
|
You can add an empty workspace name, *""*, as a catch-all workspace
|
||||||
|
particle. The *i3* module will fallback to this entry if it cannot
|
||||||
|
find the workspace name in the _content_ map.
|
||||||
|
|
||||||
|
It also recognizes the special name *current*, which always represents
|
||||||
|
the currently focused workspace. On Sway, this can be used together
|
||||||
|
with the _application_ and _title_ tags to replace the X11-only
|
||||||
|
*xwindow* module.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: The workspace name
|
||||||
|
| visible
|
||||||
|
: bool
|
||||||
|
: True if the workspace is currently visible (on any output)
|
||||||
|
| focused
|
||||||
|
: bool
|
||||||
|
: True if the workspace is currently focused
|
||||||
|
| urgent
|
||||||
|
: bool
|
||||||
|
: True if the workspace has the urgent flag set
|
||||||
|
| state
|
||||||
|
: string
|
||||||
|
: One of *urgent*, *focused*, *unfocused* or *invisible* (note:
|
||||||
|
*unfocused* is when it is visible, but neither focused nor urgent).
|
||||||
|
| application
|
||||||
|
: string
|
||||||
|
: Name of application currently focused on this workspace (Sway only - use the *xwindow* module in i3)
|
||||||
|
| title
|
||||||
|
: string
|
||||||
|
: This workspace's focused window's title
|
||||||
|
| mode
|
||||||
|
: string
|
||||||
|
: The name of the current mode
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| content
|
||||||
|
: associative array
|
||||||
|
: yes
|
||||||
|
: Unlike other modules, _content_ is an associative array mapping
|
||||||
|
workspace names to particles. Use *""* to specify a default
|
||||||
|
fallback particle, or *current* for the currently active workspace.
|
||||||
|
| sort
|
||||||
|
: enum
|
||||||
|
: no
|
||||||
|
: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_.
|
||||||
|
| left-spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Space, in pixels, on the left-side of each rendered workspace particle
|
||||||
|
| right-spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Space, in pixels, on the right-side of each rendered workspace particle
|
||||||
|
| spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Short-hand for setting both _left-spacing_ and _right-spacing_
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
This renders all workspace names, with an *\** indicating the
|
||||||
|
currently focused one. It also renders the currently focused
|
||||||
|
application name and window title.
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- i3:
|
||||||
|
content:
|
||||||
|
"":
|
||||||
|
map:
|
||||||
|
tag: state
|
||||||
|
default: {string: {text: "{name}"}}
|
||||||
|
values:
|
||||||
|
focused: {string: {text: "{name}*"}}
|
||||||
|
current: { string: {text: "{application}: {title}"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
32
doc/yambar-modules-label.5.scd
Normal file
32
doc/yambar-modules-label.5.scd
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
yambar-modules-label(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
label - This module renders the provided _content_ particle
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module renders the provided _content_ particle, but provides no
|
||||||
|
additional data.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
No additional attributes supported, only the generic ones (see
|
||||||
|
*GENERIC CONFIGURATION* in *yambar-modules*(5))
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- label:
|
||||||
|
content: {string: {text: hello world}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
80
doc/yambar-modules-mpd.5.scd
Normal file
80
doc/yambar-modules-mpd.5.scd
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
yambar-modules-mpd(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
mpd - This module provides MPD status such as currently playing artist/album/song
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| state
|
||||||
|
: string
|
||||||
|
: One of *offline*, *stopped*, *paused* or *playing*
|
||||||
|
| repeat
|
||||||
|
: bool
|
||||||
|
: True if the *repeat* flag is set
|
||||||
|
| random
|
||||||
|
: bool
|
||||||
|
: True if the *random* flag is set
|
||||||
|
| consume
|
||||||
|
: bool
|
||||||
|
: True if the *consume* flag is set
|
||||||
|
| volume
|
||||||
|
: range
|
||||||
|
: Volume of MPD in percentage
|
||||||
|
| album
|
||||||
|
: string
|
||||||
|
: Currently playing album (also valid in *paused* state)
|
||||||
|
| artist
|
||||||
|
: string
|
||||||
|
: Artist of currently playing song (also valid in *paused* state)
|
||||||
|
| title
|
||||||
|
: string
|
||||||
|
: Title of currently playing song (also valid in *paused* state)
|
||||||
|
| pos
|
||||||
|
: string
|
||||||
|
: *%M:%S*-formatted string describing the song's current position
|
||||||
|
(also see _elapsed_)
|
||||||
|
| end
|
||||||
|
: string
|
||||||
|
: *%M:%S*-formatted string describing the song's total length (also
|
||||||
|
see _duration_)
|
||||||
|
| elapsed
|
||||||
|
: realtime
|
||||||
|
: Position in currently playing song, in milliseconds. Can be used
|
||||||
|
with a _progress-bar_ particle.
|
||||||
|
| duration
|
||||||
|
: int
|
||||||
|
: Length of currently playing song, in milliseconds
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| host
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: Hostname/IP/unix-socket to connect to
|
||||||
|
| port
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: TCP port to connect to
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- mpd:
|
||||||
|
host: /run/mpd/socket
|
||||||
|
content:
|
||||||
|
string: {text: "{artist} - {album} - {title} ({end})"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
67
doc/yambar-modules-network.5.scd
Normal file
67
doc/yambar-modules-network.5.scd
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
yambar-modules-network(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
network - This module monitors network connection state
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module monitors network connection state; disconnected/connected
|
||||||
|
state and MAC/IP addresses.
|
||||||
|
|
||||||
|
Note: while the module internally tracks all assigned IPv4/IPv6
|
||||||
|
addresses, it currently exposes only a single IPv4 and a single IPv6
|
||||||
|
address.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: Network interface name
|
||||||
|
| index
|
||||||
|
: int
|
||||||
|
: Network interface index
|
||||||
|
| carrier
|
||||||
|
: bool
|
||||||
|
: True if the interface has CARRIER. That is, if it is physically connected.
|
||||||
|
| state
|
||||||
|
: string
|
||||||
|
: One of *unknown*, *not present*, *down*, *lower layers down*,
|
||||||
|
*testing*, *dormant* or *up*. You are probably interested in *down* and *up*.
|
||||||
|
| mac
|
||||||
|
: string
|
||||||
|
: MAC address
|
||||||
|
| ipv4
|
||||||
|
: string
|
||||||
|
: IPv4 address assigned to the interface, or *""* if none
|
||||||
|
| ipv6
|
||||||
|
: string
|
||||||
|
: IPv6 address assigned to the interface, or *""* if none
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: Name of network interface to monitor
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- network:
|
||||||
|
name: wlp3s0
|
||||||
|
content:
|
||||||
|
string: {text: "{name}: {state} ({ipv4})"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
89
doc/yambar-modules-removables.5.scd
Normal file
89
doc/yambar-modules-removables.5.scd
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
yambar-modules-removables(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
removables - This module detects removable drives
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module detects removable drives (USB sticks, CD-ROMs) and
|
||||||
|
instantiates the provided _content_ particle for each detected drive.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| vendor
|
||||||
|
: string
|
||||||
|
: Name of the drive vendor
|
||||||
|
| model
|
||||||
|
: string
|
||||||
|
: Drive model name
|
||||||
|
| optical
|
||||||
|
: bool
|
||||||
|
: True if the drive is an optical drive (CD-ROM, DVD-ROM etc)
|
||||||
|
| device
|
||||||
|
: string
|
||||||
|
: Volume device name (typically */dev/sd?*)
|
||||||
|
| size
|
||||||
|
: range
|
||||||
|
: The volume's size, in bytes. The tag's maximum value is set to the
|
||||||
|
underlying block device's size
|
||||||
|
| label
|
||||||
|
: string
|
||||||
|
: The volume's label, or its size if it has no label
|
||||||
|
| mounted
|
||||||
|
: bool
|
||||||
|
: True if the volume is mounted
|
||||||
|
| mount_point
|
||||||
|
: string
|
||||||
|
: Path where the volume is mounted, or *""* if it is not mounted
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| left-spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Space, in pixels, in the left side of each rendered volume
|
||||||
|
| right-spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Space, in pixels, on the right side of each rendered volume
|
||||||
|
| spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Short-hand for setting both _left-spacing_ and _right-spacing_
|
||||||
|
| ignore
|
||||||
|
: list of strings
|
||||||
|
: no
|
||||||
|
: List of device paths that should be ignored (e.g. /dev/mmcblk0, or /dev/mmcblk0p1)
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
right:
|
||||||
|
- removables:
|
||||||
|
content:
|
||||||
|
map:
|
||||||
|
tag: mounted
|
||||||
|
values:
|
||||||
|
false:
|
||||||
|
string:
|
||||||
|
on-click: udisksctl mount -b {device}
|
||||||
|
text: "{label}"
|
||||||
|
true:
|
||||||
|
string:
|
||||||
|
on-click: udisksctl unmount -b {device}
|
||||||
|
text: "{label}"
|
||||||
|
deco: {underline: {size: 2, color: ffffffff}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
85
doc/yambar-modules-river.5.scd
Normal file
85
doc/yambar-modules-river.5.scd
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
yambar-modules-river(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
river - This module provide information about the river tags
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module uses river's (https://github.com/ifreund/river, a dynamic
|
||||||
|
tiling Wayland compositor) status protocol to provide information
|
||||||
|
about the river tags.
|
||||||
|
|
||||||
|
It has an interface similar to the i3/sway module.
|
||||||
|
|
||||||
|
The configuration for the river module specifies one _title_ particle,
|
||||||
|
which will be instantiated with tags representing the currently active
|
||||||
|
seat and the currently focused view's title.
|
||||||
|
|
||||||
|
It also specifies a _content_ template particle, which is instantiated
|
||||||
|
once for all 32 river tags. This means you probably want to use a
|
||||||
|
*map* particle to hide unused river tags.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| id
|
||||||
|
: int
|
||||||
|
: River tag number
|
||||||
|
| visible
|
||||||
|
: bool
|
||||||
|
: True if the river tag is focused by at least one output (i.e. visible on at least one monitor).
|
||||||
|
| focused
|
||||||
|
: bool
|
||||||
|
: True if the river tag is _visible_ and has keyboard focus.
|
||||||
|
| occupied
|
||||||
|
: bool
|
||||||
|
: True if the river tag has views (i.e. windows).
|
||||||
|
| state
|
||||||
|
: string
|
||||||
|
: Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors.
|
||||||
|
| seat
|
||||||
|
: string
|
||||||
|
: The name of the currently active seat (*title* particle only, see CONFIGURATION)
|
||||||
|
| title
|
||||||
|
: string
|
||||||
|
: The focused view's title (*title* particle only, see CONFIGURATION)
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| title
|
||||||
|
: particle
|
||||||
|
: no
|
||||||
|
: Particle that will be instantiated with the _seat_ and _title_ tags.
|
||||||
|
| content
|
||||||
|
: particle
|
||||||
|
: yes
|
||||||
|
: Template particle that will be instantiated once for all of the 32 river tags.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- river:
|
||||||
|
title: {string: { text: "{seat} - {title}" }}
|
||||||
|
content:
|
||||||
|
map:
|
||||||
|
tag: occupied
|
||||||
|
values:
|
||||||
|
false: {empty: {}}
|
||||||
|
true:
|
||||||
|
string:
|
||||||
|
margin: 5
|
||||||
|
text: "{id}: {state}"
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
119
doc/yambar-modules-script.5.scd
Normal file
119
doc/yambar-modules-script.5.scd
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
yambar-modules-script(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
script - This module executes a user-provided script (or binary!)
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module executes a user-provided script (or binary!) that writes
|
||||||
|
tags on its stdout.
|
||||||
|
|
||||||
|
Scripts can be run in two modes: yambar polled, or continuously. In the
|
||||||
|
yambar polled mode, the script is expected to write one set of tags
|
||||||
|
and then exit. Yambar will execute the script again after a
|
||||||
|
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
|
||||||
|
on non-polling methods to update their state.
|
||||||
|
|
||||||
|
Tag sets, or _transactions_, are separated by an empty line
|
||||||
|
(e.g. *echo ""*). The empty line is required to commit (update) the
|
||||||
|
tag even for only one transaction.
|
||||||
|
|
||||||
|
Each _tag_ is a single line on the format:
|
||||||
|
|
||||||
|
```
|
||||||
|
name|type|value
|
||||||
|
```
|
||||||
|
|
||||||
|
Where _name_ is what you also use to refer to the tag in the yambar
|
||||||
|
configuration, _type_ is one of the tag types defined in
|
||||||
|
*yambar-tags*(5), and _value_ is the tag’s value.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
var1|string|hello
|
||||||
|
var2|int|13
|
||||||
|
<empty>
|
||||||
|
var1|string|world
|
||||||
|
var2|int|37
|
||||||
|
<empty>
|
||||||
|
```
|
||||||
|
|
||||||
|
The example above consists of two transactions. Each transaction has
|
||||||
|
two tags: one string tag and one integer tag. The second transaction
|
||||||
|
replaces the tags from the first transaction. Note that **both**
|
||||||
|
transactions need to be terminated with an empty line.
|
||||||
|
|
||||||
|
Supported _types_ are:
|
||||||
|
|
||||||
|
- string
|
||||||
|
- int
|
||||||
|
- bool
|
||||||
|
- float
|
||||||
|
- range:n-m (e.g. *var|range:0-100|57*)
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
User defined.
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| path
|
||||||
|
: string
|
||||||
|
: yes
|
||||||
|
: Path to script/binary to execute. Must be an absolute path.
|
||||||
|
| args
|
||||||
|
: list of strings
|
||||||
|
: no
|
||||||
|
: Arguments to pass to the script/binary.
|
||||||
|
| poll-interval
|
||||||
|
: integer
|
||||||
|
: Number of seconds between each script run. If unset, continuous mode
|
||||||
|
is used.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
Here is an "hello world" example script:
|
||||||
|
|
||||||
|
```
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
echo "test|string|hello"
|
||||||
|
echo ""
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
echo "test|string|world"
|
||||||
|
echo ""
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
This script runs in continuous mode, and will emit a single string tag,
|
||||||
|
_test_, and alternate its value between *hello* and *world* every
|
||||||
|
three seconds.
|
||||||
|
|
||||||
|
A corresponding yambar configuration could look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- script:
|
||||||
|
path: /path/to/script.sh
|
||||||
|
args: []
|
||||||
|
content: {string: {text: "{test}"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
70
doc/yambar-modules-sway-xkb.5.scd
Normal file
70
doc/yambar-modules-sway-xkb.5.scd
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
yambar-modules-sway-xkb(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
sway-xkb - This module monitor input devices' active XKB layout
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module uses *Sway* extensions to the I3 IPC API to monitor input
|
||||||
|
devices' active XKB layout. As such, it requires Sway to be running.
|
||||||
|
|
||||||
|
*Note* that the _content_ configuration option is a *template*;
|
||||||
|
*sway-xkb* will instantiate a particle list, where each item is
|
||||||
|
instantiated from this template, and represents an input device.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| id
|
||||||
|
: string
|
||||||
|
: Input device identifier
|
||||||
|
| layout
|
||||||
|
: string
|
||||||
|
: The input device's currently active XKB layout
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Req*
|
||||||
|
:[ *Description*
|
||||||
|
| identifiers
|
||||||
|
: list of strings
|
||||||
|
: yes
|
||||||
|
: Identifiers of input devices to monitor. Use _swaymsg -t get_inputs_ to see available devices.
|
||||||
|
| content
|
||||||
|
: particle
|
||||||
|
: yes
|
||||||
|
: A particle template; each existing input device will be instantiated with this template.
|
||||||
|
| left-spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Space, in pixels, in the left side of each rendered input device
|
||||||
|
| right-spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Space, in pixels, on the right side of each rendered input device
|
||||||
|
| spacing
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: Short-hand for setting both _left-spacing_ and _right-spacing_
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- sway-xkb:
|
||||||
|
identifiers:
|
||||||
|
- 1523:7:HID_05f3:0007
|
||||||
|
- 7247:2:USB_USB_Keykoard
|
||||||
|
spacing: 5
|
||||||
|
content: {string: {text: "{id}: {layout}"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules-xkb*(5), *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
10
doc/yambar-modules-sway.5.scd
Normal file
10
doc/yambar-modules-sway.5.scd
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
yambar-modules-sway(5)
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Please use the i3 (*yambar-modules-i3*(5)) module, as it is fully compatible with Sway
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-modules-i3*(5)
|
||||||
|
|
52
doc/yambar-modules-xkb.5.scd
Normal file
52
doc/yambar-modules-xkb.5.scd
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
yambar-modules-xkb(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
xkb - This module monitors the currently active XKB keyboard layout
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module monitors the currently active XKB keyboard layout and
|
||||||
|
lock-key states.
|
||||||
|
|
||||||
|
Note: this module is X11 only. It does not work in Wayland.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| name
|
||||||
|
: string
|
||||||
|
: Name of currently selected layout, long version (e.g. "English (US)")
|
||||||
|
| symbol
|
||||||
|
: string
|
||||||
|
: Name of currently selected layout, short version (e.g. "us")
|
||||||
|
| caps_lock
|
||||||
|
: bool
|
||||||
|
: True if *CapsLock* is enabled
|
||||||
|
| num_lock
|
||||||
|
: bool
|
||||||
|
: True if *NumLock* is enabled
|
||||||
|
| scroll_lock
|
||||||
|
: bool
|
||||||
|
: True if *ScrollLock* is enabled
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
No additional attributes supported, only the generic ones (see
|
||||||
|
*GENERIC CONFIGURATION* in *yambar-modules*(5))
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- xkb:
|
||||||
|
content:
|
||||||
|
string: {text: "{symbol}"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules-sway-xkb*(5), *yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
45
doc/yambar-modules-xwindow.5.scd
Normal file
45
doc/yambar-modules-xwindow.5.scd
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
yambar-modules-xwindow(5)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
xwindow - This module provides the application name and window title
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
This module provides the application name and window title of the
|
||||||
|
currently focused window.
|
||||||
|
|
||||||
|
Note: this module is X11 only. It does not work in Wayland. If you are
|
||||||
|
running Sway, take a look at the *i3* module and its _application_ and
|
||||||
|
_title_ tags.
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
|
||||||
|
[[ *Name*
|
||||||
|
:[ *Type*
|
||||||
|
:[ *Description*
|
||||||
|
| application
|
||||||
|
: string
|
||||||
|
: Name of the application that owns the currently focused window
|
||||||
|
| title
|
||||||
|
: string
|
||||||
|
: The title of the currently focused window
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
|
||||||
|
No additional attributes supported, only the generic ones (see
|
||||||
|
*GENERIC CONFIGURATION* in *yambar-modules*(5))
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
```
|
||||||
|
bar:
|
||||||
|
left:
|
||||||
|
- xwindow:
|
||||||
|
content:
|
||||||
|
string: {text: "{application}: {title}"}
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
*yambar-modules*(5), *yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
|
@ -133,835 +133,41 @@ following attributes are supported by all modules:
|
||||||
: Foreground (text) color of the content particle. This is an
|
: Foreground (text) color of the content particle. This is an
|
||||||
inherited attribute.
|
inherited attribute.
|
||||||
|
|
||||||
# ALSA
|
# BUILT-IN MODULES
|
||||||
|
|
||||||
Monitors an alsa soundcard for volume and mute/unmute changes.
|
Available modules have their own pages:
|
||||||
|
|
||||||
## TAGS
|
*yambar-modules-alsa*(5)
|
||||||
|
|
||||||
[[ *Name*
|
*yambar-modules-backlight*(5)
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| volume
|
|
||||||
: range
|
|
||||||
: Volume level, with min and max as start and end range values
|
|
||||||
| percent
|
|
||||||
: range
|
|
||||||
: Volume level, as a percentage
|
|
||||||
| muted
|
|
||||||
: bool
|
|
||||||
: True if muted, otherwise false
|
|
||||||
|
|
||||||
|
*yambar-modules-battery*(5)
|
||||||
|
|
||||||
## CONFIGURATION
|
*yambar-modules-clock*(5)
|
||||||
|
|
||||||
[[ *Name*
|
*yambar-modules-i3*(5)
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| card
|
|
||||||
: string
|
|
||||||
: yes
|
|
||||||
: The soundcard name. _Default_ might work.
|
|
||||||
| mixer
|
|
||||||
: string
|
|
||||||
: yes
|
|
||||||
: Mixer channel to monitor. _Master_ might work.
|
|
||||||
|
|
||||||
## EXAMPLES
|
*yambar-modules-label*(5)
|
||||||
|
|
||||||
```
|
*yambar-modules-mpd*(5)
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- alsa:
|
|
||||||
card: hw:PCH
|
|
||||||
mixer: Master
|
|
||||||
content: {string: {text: "{volume}"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# BACKLIGHT
|
*yambar-modules-network*(5)
|
||||||
|
|
||||||
This module reads monitor backlight status from
|
*yambar-modules-removables*(5)
|
||||||
_/sys/class/backlight_, and uses *udev* to monitor for changes.
|
|
||||||
|
|
||||||
## TAGS
|
*yambar-modules-river*(5)
|
||||||
|
|
||||||
[[ *Name*
|
*yambar-modules-script*(5)
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| brightness
|
|
||||||
: range
|
|
||||||
: The current brightness level, in absolute value
|
|
||||||
| percent
|
|
||||||
: range
|
|
||||||
: The current brightness level, in percent
|
|
||||||
|
|
||||||
## CONFIGURATION
|
*yambar-modules-sway-xkb*(5)
|
||||||
|
|
||||||
[[ *Name*
|
*yambar-modules-sway*(5)
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: yes
|
|
||||||
: The backlight device's name (one of the names in */sys/class/backlight*)
|
|
||||||
|
|
||||||
## EXAMPLES
|
*yambar-modules-xkb*(5)
|
||||||
|
|
||||||
```
|
*yambar-modules-xwindow*(5)
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- backlight:
|
|
||||||
name: intel_backlight
|
|
||||||
content:
|
|
||||||
string: {text: "backlight: {percent}%"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# BATTERY
|
|
||||||
|
|
||||||
This module reads battery status from _/sys/class/power_supply_ and
|
|
||||||
uses *udev* to monitor for changes.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: Battery device name
|
|
||||||
| manufacturer
|
|
||||||
: string
|
|
||||||
: Name of the battery manufacturer
|
|
||||||
| model
|
|
||||||
: string
|
|
||||||
: Battery model name
|
|
||||||
| state
|
|
||||||
: string
|
|
||||||
: One of *full*, *charging*, *discharging* or *unknown*
|
|
||||||
| capacity
|
|
||||||
: range
|
|
||||||
: capacity left, in percent
|
|
||||||
| estimate
|
|
||||||
: string
|
|
||||||
: Estimated time left (to empty while discharging, or to full while
|
|
||||||
charging), formatted as HH:MM.
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: yes
|
|
||||||
: Battery device name (one of the names in */sys/class/power_supply*)
|
|
||||||
| poll-interval
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: How often, in seconds, to poll for capacity changes (default=*60*). Set to `0` to disable polling (*warning*: many batteries do not support asynchronous reporting).
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- battery:
|
|
||||||
name: BAT0
|
|
||||||
poll-interval: 30
|
|
||||||
content:
|
|
||||||
string: {text: "BAT: {capacity}% {estimate}"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# CLOCK
|
|
||||||
|
|
||||||
This module provides the current date and time.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| time
|
|
||||||
: string
|
|
||||||
: Current time, formatted using the _time-format_ attribute
|
|
||||||
| date
|
|
||||||
: string
|
|
||||||
: Current date, formatted using the _date-format_ attribute
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| time-format
|
|
||||||
: string
|
|
||||||
: no
|
|
||||||
: *strftime* formatter for the _time_ tag (default=*%H:%M*)
|
|
||||||
| date-format
|
|
||||||
: string
|
|
||||||
: no
|
|
||||||
: *strftime* formatter for the _date_ date (default=*%x*)
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- clock:
|
|
||||||
time-format: "%H:%M %Z"
|
|
||||||
content:
|
|
||||||
string: {text: "{date} {time}"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# I3 (and Sway)
|
|
||||||
|
|
||||||
This module monitors i3 and sway workspaces.
|
|
||||||
|
|
||||||
Unlike other modules where the _content_ attribute is just a single
|
|
||||||
*particle*, the i3 module's _content_ is an associative array mapping
|
|
||||||
i3/sway workspace names to a particle.
|
|
||||||
|
|
||||||
You can add an empty workspace name, *""*, as a catch-all workspace
|
|
||||||
particle. The *i3* module will fallback to this entry if it cannot
|
|
||||||
find the workspace name in the _content_ map.
|
|
||||||
|
|
||||||
It also recognizes the special name *current*, which always represents
|
|
||||||
the currently focused workspace. On Sway, this can be used together
|
|
||||||
with the _application_ and _title_ tags to replace the X11-only
|
|
||||||
*xwindow* module.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: The workspace name
|
|
||||||
| visible
|
|
||||||
: bool
|
|
||||||
: True if the workspace is currently visible (on any output)
|
|
||||||
| focused
|
|
||||||
: bool
|
|
||||||
: True if the workspace is currently focused
|
|
||||||
| urgent
|
|
||||||
: bool
|
|
||||||
: True if the workspace has the urgent flag set
|
|
||||||
| state
|
|
||||||
: string
|
|
||||||
: One of *urgent*, *focused*, *unfocused* or *invisible* (note:
|
|
||||||
*unfocused* is when it is visible, but neither focused nor urgent).
|
|
||||||
| application
|
|
||||||
: string
|
|
||||||
: Name of application currently focused on this workspace (Sway only - use the *xwindow* module in i3)
|
|
||||||
| title
|
|
||||||
: string
|
|
||||||
: This workspace's focused window's title
|
|
||||||
| mode
|
|
||||||
: string
|
|
||||||
: The name of the current mode
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| content
|
|
||||||
: associative array
|
|
||||||
: yes
|
|
||||||
: Unlike other modules, _content_ is an associative array mapping
|
|
||||||
workspace names to particles. Use *""* to specify a default
|
|
||||||
fallback particle, or *current* for the currently active workspace.
|
|
||||||
| sort
|
|
||||||
: enum
|
|
||||||
: no
|
|
||||||
: How to sort the list of workspaces; one of _none_, _ascending_ or _descending_, defaults to _none_.
|
|
||||||
| left-spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Space, in pixels, on the left-side of each rendered workspace particle
|
|
||||||
| right-spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Space, in pixels, on the right-side of each rendered workspace particle
|
|
||||||
| spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Short-hand for setting both _left-spacing_ and _right-spacing_
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
This renders all workspace names, with an *\** indicating the
|
|
||||||
currently focused one. It also renders the currently focused
|
|
||||||
application name and window title.
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- i3:
|
|
||||||
content:
|
|
||||||
"":
|
|
||||||
map:
|
|
||||||
tag: state
|
|
||||||
default: {string: {text: "{name}"}}
|
|
||||||
values:
|
|
||||||
focused: {string: {text: "{name}*"}}
|
|
||||||
current: { string: {text: "{application}: {title}"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# LABEL
|
|
||||||
|
|
||||||
This module renders the provided _content_ particle, but provides no
|
|
||||||
additional data.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
No additional attributes supported, only the generic ones (see
|
|
||||||
*GENERIC CONFIGURATION*)
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- label:
|
|
||||||
content: {string: {text: hello world}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# MPD
|
|
||||||
|
|
||||||
This module provides MPD status such as currently playing
|
|
||||||
artist/album/song.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| state
|
|
||||||
: string
|
|
||||||
: One of *offline*, *stopped*, *paused* or *playing*
|
|
||||||
| repeat
|
|
||||||
: bool
|
|
||||||
: True if the *repeat* flag is set
|
|
||||||
| random
|
|
||||||
: bool
|
|
||||||
: True if the *random* flag is set
|
|
||||||
| consume
|
|
||||||
: bool
|
|
||||||
: True if the *consume* flag is set
|
|
||||||
| volume
|
|
||||||
: range
|
|
||||||
: Volume of MPD in percentage
|
|
||||||
| album
|
|
||||||
: string
|
|
||||||
: Currently playing album (also valid in *paused* state)
|
|
||||||
| artist
|
|
||||||
: string
|
|
||||||
: Artist of currently playing song (also valid in *paused* state)
|
|
||||||
| title
|
|
||||||
: string
|
|
||||||
: Title of currently playing song (also valid in *paused* state)
|
|
||||||
| pos
|
|
||||||
: string
|
|
||||||
: *%M:%S*-formatted string describing the song's current position
|
|
||||||
(also see _elapsed_)
|
|
||||||
| end
|
|
||||||
: string
|
|
||||||
: *%M:%S*-formatted string describing the song's total length (also
|
|
||||||
see _duration_)
|
|
||||||
| elapsed
|
|
||||||
: realtime
|
|
||||||
: Position in currently playing song, in milliseconds. Can be used
|
|
||||||
with a _progress-bar_ particle.
|
|
||||||
| duration
|
|
||||||
: int
|
|
||||||
: Length of currently playing song, in milliseconds
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| host
|
|
||||||
: string
|
|
||||||
: yes
|
|
||||||
: Hostname/IP/unix-socket to connect to
|
|
||||||
| port
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: TCP port to connect to
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- mpd:
|
|
||||||
host: /run/mpd/socket
|
|
||||||
content:
|
|
||||||
string: {text: "{artist} - {album} - {title} ({end})"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# NETWORK
|
|
||||||
|
|
||||||
This module monitors network connection state; disconnected/connected
|
|
||||||
state and MAC/IP addresses.
|
|
||||||
|
|
||||||
Note: while the module internally tracks all assigned IPv4/IPv6
|
|
||||||
addresses, it currently exposes only a single IPv4 and a single IPv6
|
|
||||||
address.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: Network interface name
|
|
||||||
| index
|
|
||||||
: int
|
|
||||||
: Network interface index
|
|
||||||
| carrier
|
|
||||||
: bool
|
|
||||||
: True if the interface has CARRIER. That is, if it is physically connected.
|
|
||||||
| state
|
|
||||||
: string
|
|
||||||
: One of *unknown*, *not present*, *down*, *lower layers down*,
|
|
||||||
*testing*, *dormant* or *up*. You are probably interested in *down* and *up*.
|
|
||||||
| mac
|
|
||||||
: string
|
|
||||||
: MAC address
|
|
||||||
| ipv4
|
|
||||||
: string
|
|
||||||
: IPv4 address assigned to the interface, or *""* if none
|
|
||||||
| ipv6
|
|
||||||
: string
|
|
||||||
: IPv6 address assigned to the interface, or *""* if none
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: Name of network interface to monitor
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- network:
|
|
||||||
name: wlp3s0
|
|
||||||
content:
|
|
||||||
string: {text: "{name}: {state} ({ipv4})"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# REMOVABLES
|
|
||||||
|
|
||||||
This module detects removable drives (USB sticks, CD-ROMs) and
|
|
||||||
instantiates the provided _content_ particle for each detected drive.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| vendor
|
|
||||||
: string
|
|
||||||
: Name of the drive vendor
|
|
||||||
| model
|
|
||||||
: string
|
|
||||||
: Drive model name
|
|
||||||
| optical
|
|
||||||
: bool
|
|
||||||
: True if the drive is an optical drive (CD-ROM, DVD-ROM etc)
|
|
||||||
| device
|
|
||||||
: string
|
|
||||||
: Volume device name (typically */dev/sd?*)
|
|
||||||
| size
|
|
||||||
: range
|
|
||||||
: The volume's size, in bytes. The tag's maximum value is set to the
|
|
||||||
underlying block device's size
|
|
||||||
| label
|
|
||||||
: string
|
|
||||||
: The volume's label, or its size if it has no label
|
|
||||||
| mounted
|
|
||||||
: bool
|
|
||||||
: True if the volume is mounted
|
|
||||||
| mount_point
|
|
||||||
: string
|
|
||||||
: Path where the volume is mounted, or *""* if it is not mounted
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| left-spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Space, in pixels, in the left side of each rendered volume
|
|
||||||
| right-spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Space, in pixels, on the right side of each rendered volume
|
|
||||||
| spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Short-hand for setting both _left-spacing_ and _right-spacing_
|
|
||||||
| ignore
|
|
||||||
: list of strings
|
|
||||||
: no
|
|
||||||
: List of device paths that should be ignored (e.g. /dev/mmcblk0, or /dev/mmcblk0p1)
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
right:
|
|
||||||
- removables:
|
|
||||||
content:
|
|
||||||
map:
|
|
||||||
tag: mounted
|
|
||||||
values:
|
|
||||||
false:
|
|
||||||
string:
|
|
||||||
on-click: udisksctl mount -b {device}
|
|
||||||
text: "{label}"
|
|
||||||
true:
|
|
||||||
string:
|
|
||||||
on-click: udisksctl unmount -b {device}
|
|
||||||
text: "{label}"
|
|
||||||
deco: {underline: {size: 2, color: ffffffff}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# RIVER
|
|
||||||
|
|
||||||
This module uses river's (https://github.com/ifreund/river, a dynamic
|
|
||||||
tiling Wayland compositor) status protocol to provide information
|
|
||||||
about the river tags.
|
|
||||||
|
|
||||||
It has an interface similar to the i3/sway module.
|
|
||||||
|
|
||||||
The configuration for the river module specifies one _title_ particle,
|
|
||||||
which will be instantiated with tags representing the currently active
|
|
||||||
seat and the currently focused view's title.
|
|
||||||
|
|
||||||
It also specifies a _content_ template particle, which is instantiated
|
|
||||||
once for all 32 river tags. This means you probably want to use a
|
|
||||||
*map* particle to hide unused river tags.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| id
|
|
||||||
: int
|
|
||||||
: River tag number
|
|
||||||
| visible
|
|
||||||
: bool
|
|
||||||
: True if the river tag is focused by at least one output (i.e. visible on at least one monitor).
|
|
||||||
| focused
|
|
||||||
: bool
|
|
||||||
: True if the river tag is _visible_ and has keyboard focus.
|
|
||||||
| occupied
|
|
||||||
: bool
|
|
||||||
: True if the river tag has views (i.e. windows).
|
|
||||||
| state
|
|
||||||
: string
|
|
||||||
: Set to *focused* if _focused_ is true, *unfocused* if _visible_ is true, but _focused_ is false, or *invisible* if the river tag is not visible on any monitors.
|
|
||||||
| seat
|
|
||||||
: string
|
|
||||||
: The name of the currently active seat (*title* particle only, see CONFIGURATION)
|
|
||||||
| title
|
|
||||||
: string
|
|
||||||
: The focused view's title (*title* particle only, see CONFIGURATION)
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| title
|
|
||||||
: particle
|
|
||||||
: no
|
|
||||||
: Particle that will be instantiated with the _seat_ and _title_ tags.
|
|
||||||
| content
|
|
||||||
: particle
|
|
||||||
: yes
|
|
||||||
: Template particle that will be instantiated once for all of the 32 river tags.
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- river:
|
|
||||||
title: {string: { text: "{seat} - {title}" }}
|
|
||||||
content:
|
|
||||||
map:
|
|
||||||
tag: occupied
|
|
||||||
values:
|
|
||||||
false: {empty: {}}
|
|
||||||
true:
|
|
||||||
string:
|
|
||||||
margin: 5
|
|
||||||
text: "{id}: {state}"
|
|
||||||
```
|
|
||||||
|
|
||||||
# SCRIPT
|
|
||||||
|
|
||||||
This module executes a user-provided script (or binary!) that writes
|
|
||||||
tags on its stdout.
|
|
||||||
|
|
||||||
The script can either exit immediately after writing a set of tags, in
|
|
||||||
which case yambar will display those tags until yambar is
|
|
||||||
terminated. Or, the script can continue executing and update yambar
|
|
||||||
with new tag sets, either periodically, or when there is new data to
|
|
||||||
feed to yambar.
|
|
||||||
|
|
||||||
Tag sets, or _transactions_, are separated by an empty line. Each
|
|
||||||
_tag_ is a single line on the format:
|
|
||||||
|
|
||||||
```
|
|
||||||
name|type|value
|
|
||||||
```
|
|
||||||
|
|
||||||
Where _name_ is what you also use to refer to the tag in the yambar
|
|
||||||
configuration, _type_ is one of the tag types defined in
|
|
||||||
*yambar-tags*(5), and _value_ is the tag’s value.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
var1|string|hello
|
|
||||||
var2|int|13
|
|
||||||
|
|
||||||
var1|string|world
|
|
||||||
var2|int|37
|
|
||||||
```
|
|
||||||
|
|
||||||
The example above consists of two transactions. Each transaction has
|
|
||||||
two tags: one string tag and one integer tag. The second transaction
|
|
||||||
replaces the tags from the first transaction.
|
|
||||||
|
|
||||||
Supported _types_ are:
|
|
||||||
|
|
||||||
- string
|
|
||||||
- int
|
|
||||||
- bool
|
|
||||||
- float
|
|
||||||
- range:n-m (e.g. *var|range:0-100|57*)
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
User defined.
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| path
|
|
||||||
: string
|
|
||||||
: yes
|
|
||||||
: Path to script/binary to execute. Must be an absolute path.
|
|
||||||
| args
|
|
||||||
: list of strings
|
|
||||||
: no
|
|
||||||
: Arguments to pass to the script/binary.
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
Here is an "hello world" example script:
|
|
||||||
|
|
||||||
```
|
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
echo "test|string|hello"
|
|
||||||
echo ""
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
echo "test|string|world"
|
|
||||||
echo ""
|
|
||||||
sleep 3
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
This script will emit a single string tag, _test_, and alternate its
|
|
||||||
value between *hello* and *world* every three seconds.
|
|
||||||
|
|
||||||
A corresponding yambar configuration could look like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- script:
|
|
||||||
path: /path/to/script.sh
|
|
||||||
args: []
|
|
||||||
content: {string: {text: "{test}"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# SWAY-XKB
|
|
||||||
|
|
||||||
This module uses *Sway* extensions to the I3 IPC API to monitor input
|
|
||||||
devices' active XKB layout. As such, it requires Sway to be running.
|
|
||||||
|
|
||||||
*Note* that the _content_ configuration option is a *template*;
|
|
||||||
*sway-xkb* will instantiate a particle list, where each item is
|
|
||||||
instantiated from this template, and represents an input device.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| id
|
|
||||||
: string
|
|
||||||
: Input device identifier
|
|
||||||
| layout
|
|
||||||
: string
|
|
||||||
: The input device's currently active XKB layout
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Req*
|
|
||||||
:[ *Description*
|
|
||||||
| identifiers
|
|
||||||
: list of strings
|
|
||||||
: yes
|
|
||||||
: Identifiers of input devices to monitor. Use _swaymsg -t get_inputs_ to see available devices.
|
|
||||||
| content
|
|
||||||
: particle
|
|
||||||
: yes
|
|
||||||
: A particle template; each existing input device will be instantiated with this template.
|
|
||||||
| left-spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Space, in pixels, in the left side of each rendered input device
|
|
||||||
| right-spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Space, in pixels, on the right side of each rendered input device
|
|
||||||
| spacing
|
|
||||||
: int
|
|
||||||
: no
|
|
||||||
: Short-hand for setting both _left-spacing_ and _right-spacing_
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- sway-xkb:
|
|
||||||
identifiers:
|
|
||||||
- 1523:7:HID_05f3:0007
|
|
||||||
- 7247:2:USB_USB_Keykoard
|
|
||||||
spacing: 5
|
|
||||||
content: {string: {text: "{id}: {layout}"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# XKB
|
|
||||||
|
|
||||||
This module monitors the currently active XKB keyboard layout and
|
|
||||||
lock-key states.
|
|
||||||
|
|
||||||
Note: this module is X11 only. It does not work in Wayland.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| name
|
|
||||||
: string
|
|
||||||
: Name of currently selected layout, long version (e.g. "English (US)")
|
|
||||||
| symbol
|
|
||||||
: string
|
|
||||||
: Name of currently selected layout, short version (e.g. "us")
|
|
||||||
| caps_lock
|
|
||||||
: bool
|
|
||||||
: True if *CapsLock* is enabled
|
|
||||||
| num_lock
|
|
||||||
: bool
|
|
||||||
: True if *NumLock* is enabled
|
|
||||||
| scroll_lock
|
|
||||||
: bool
|
|
||||||
: True if *ScrollLock* is enabled
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
No additional attributes supported, only the generic ones (see
|
|
||||||
*GENERIC CONFIGURATION*)
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- xkb:
|
|
||||||
content:
|
|
||||||
string: {text: "{symbol}"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# XWINDOW
|
|
||||||
|
|
||||||
This module provides the application name and window title of the
|
|
||||||
currently focused window.
|
|
||||||
|
|
||||||
Note: this module is X11 only. It does not work in Wayland. If you are
|
|
||||||
running Sway, take a look at the *i3* module and its _application_ and
|
|
||||||
_title_ tags.
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
|
|
||||||
[[ *Name*
|
|
||||||
:[ *Type*
|
|
||||||
:[ *Description*
|
|
||||||
| application
|
|
||||||
: string
|
|
||||||
: Name of the application that owns the currently focused window
|
|
||||||
| title
|
|
||||||
: string
|
|
||||||
: The title of the currently focused window
|
|
||||||
|
|
||||||
## CONFIGURATION
|
|
||||||
|
|
||||||
No additional attributes supported, only the generic ones (see
|
|
||||||
*GENERIC CONFIGURATION*)
|
|
||||||
|
|
||||||
## EXAMPLES
|
|
||||||
|
|
||||||
```
|
|
||||||
bar:
|
|
||||||
left:
|
|
||||||
- xwindow:
|
|
||||||
content:
|
|
||||||
string: {text: "{application}: {title}"}
|
|
||||||
```
|
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
*yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,12 @@ types that are frequently used:
|
||||||
: color
|
: color
|
||||||
: no
|
: no
|
||||||
: Default foreground (text) color to use
|
: Default foreground (text) color to use
|
||||||
|
| trackpad-sensitivity
|
||||||
|
: int
|
||||||
|
: no
|
||||||
|
: How easy it is to trigger wheel-up and wheel-down on-click
|
||||||
|
handlers. Higher values means you need to drag your finger a longer
|
||||||
|
distance. The default is 30.
|
||||||
| left
|
| left
|
||||||
: list
|
: list
|
||||||
: no
|
: no
|
||||||
|
|
|
@ -45,6 +45,16 @@ bar:
|
||||||
- urgent: &urgent
|
- urgent: &urgent
|
||||||
foreground: 000000ff
|
foreground: 000000ff
|
||||||
deco: {stack: [background: {color: bc2b3fff}, <<: *std_underline]}
|
deco: {stack: [background: {color: bc2b3fff}, <<: *std_underline]}
|
||||||
|
- map: &i3_mode
|
||||||
|
tag: mode
|
||||||
|
default:
|
||||||
|
- string:
|
||||||
|
margin: 5
|
||||||
|
text: "{mode}"
|
||||||
|
deco: {background: {color: cc421dff}}
|
||||||
|
- empty: {right-margin: 7}
|
||||||
|
values:
|
||||||
|
default: {empty: {}}
|
||||||
content:
|
content:
|
||||||
"":
|
"":
|
||||||
map:
|
map:
|
||||||
|
@ -100,11 +110,14 @@ bar:
|
||||||
left-margin: 7
|
left-margin: 7
|
||||||
tag: application
|
tag: application
|
||||||
values:
|
values:
|
||||||
"": {string: {text: "{title}"}}
|
"":
|
||||||
|
- map: {<<: *i3_mode}
|
||||||
|
- string: {text: "{title}"}
|
||||||
default:
|
default:
|
||||||
list:
|
list:
|
||||||
spacing: 0
|
spacing: 0
|
||||||
items:
|
items:
|
||||||
|
- map: {<<: *i3_mode}
|
||||||
- string: {text: "{application}", max: 10, foreground: ffa0a0ff}
|
- string: {text: "{application}", max: 10, foreground: ffa0a0ff}
|
||||||
- string: {text: ": "}
|
- string: {text: ": "}
|
||||||
- string: {text: "{title}", max: 35}
|
- string: {text: "{title}", max: 35}
|
||||||
|
@ -258,6 +271,21 @@ bar:
|
||||||
full:
|
full:
|
||||||
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
- string: {text: "{capacity}% full"}
|
- string: {text: "{capacity}% full"}
|
||||||
|
not charging:
|
||||||
|
- ramp:
|
||||||
|
tag: capacity
|
||||||
|
items:
|
||||||
|
- string: {text: , foreground: ff0000ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: ffa600ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||||
|
- string: {text: "{capacity}%"}
|
||||||
- clock:
|
- clock:
|
||||||
time-format: "%H:%M %Z"
|
time-format: "%H:%M %Z"
|
||||||
content:
|
content:
|
||||||
|
|
145
examples/scripts/dwl-tags.sh
Executable file
145
examples/scripts/dwl-tags.sh
Executable file
|
@ -0,0 +1,145 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# dwl-tags.sh - display dwl tags
|
||||||
|
#
|
||||||
|
# USAGE: dwl-tags.sh 1
|
||||||
|
#
|
||||||
|
# REQUIREMENTS:
|
||||||
|
# - inotifywait ( 'inotify-tools' on arch )
|
||||||
|
# - Launch dwl with `dwl > ~.cache/dwltags` or change $fname
|
||||||
|
#
|
||||||
|
# TAGS:
|
||||||
|
# Name Type Return
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# {tag_N} string dwl tags name
|
||||||
|
# {tag_N_occupied} bool dwl tags state occupied
|
||||||
|
# {tag_N_focused} bool dwl tags state focused
|
||||||
|
# {layout} string dwl layout
|
||||||
|
# {title} string client title
|
||||||
|
#
|
||||||
|
# Now the fun part
|
||||||
|
#
|
||||||
|
# Exemple configuration:
|
||||||
|
#
|
||||||
|
# - script:
|
||||||
|
# path: /absolute/path/to/dwl-tags.sh
|
||||||
|
# args: [1]
|
||||||
|
# anchors:
|
||||||
|
# - occupied: &occupied {foreground: 57bbf4ff}
|
||||||
|
# - focused: &focused {foreground: fc65b0ff}
|
||||||
|
# - default: &default {foreground: d2ccd6ff}
|
||||||
|
# content:
|
||||||
|
# - map:
|
||||||
|
# margin: 4
|
||||||
|
# tag: tag_0_occupied
|
||||||
|
# values:
|
||||||
|
# true:
|
||||||
|
# map:
|
||||||
|
# tag: tag_0_focused
|
||||||
|
# values:
|
||||||
|
# true: {string: {text: "{tag_0}", <<: *focused}}
|
||||||
|
# false: {string: {text: "{tag_0}", <<: *occupied}}
|
||||||
|
# false:
|
||||||
|
# map:
|
||||||
|
# tag: tag_0_focused
|
||||||
|
# values:
|
||||||
|
# true: {string: {text: "{tag_0}", <<: *focused}}
|
||||||
|
# false: {string: {text: "{tag_0}", <<: *default}}
|
||||||
|
# ...
|
||||||
|
# ...
|
||||||
|
# ...
|
||||||
|
# - map:
|
||||||
|
# margin: 4
|
||||||
|
# tag: tag_8_occupied
|
||||||
|
# values:
|
||||||
|
# true:
|
||||||
|
# map:
|
||||||
|
# tag: tag_8_focused
|
||||||
|
# values:
|
||||||
|
# true: {string: {text: "{tag_8}", <<: *focused}}
|
||||||
|
# false: {string: {text: "{tag_8}", <<: *occupied}}
|
||||||
|
# false:
|
||||||
|
# map:
|
||||||
|
# tag: tag_8_focused
|
||||||
|
# values:
|
||||||
|
# true: {string: {text: "{tag_8}", <<: *focused}}
|
||||||
|
# false: {string: {text: "{tag_8}", <<: *default}}
|
||||||
|
# - list:
|
||||||
|
# spacing: 3
|
||||||
|
# items:
|
||||||
|
# - string: {text: "{layout}"}
|
||||||
|
# - string: {text: "{title}"}
|
||||||
|
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
declare output title layout activetags selectedtags
|
||||||
|
declare -a tags name
|
||||||
|
readonly fname="$HOME"/.cache/dwltags
|
||||||
|
|
||||||
|
|
||||||
|
_cycle() {
|
||||||
|
tags=( "1" "2" "3" "4" "5" "6" "7" "8" "9" )
|
||||||
|
|
||||||
|
# Name of tag (optional)
|
||||||
|
# If there is no name, number are used
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# name=( "" "" "" "Media" )
|
||||||
|
# -> return "" "" "" "Media" 5 6 7 8 9)
|
||||||
|
name=()
|
||||||
|
|
||||||
|
for tag in "${!tags[@]}"; do
|
||||||
|
mask=$((1<<tag))
|
||||||
|
|
||||||
|
tag_name="tag"
|
||||||
|
declare "${tag_name}_${tag}"
|
||||||
|
name[tag]="${name[tag]:-${tags[tag]}}"
|
||||||
|
|
||||||
|
printf -- '%s\n' "${tag_name}_${tag}|string|${name[tag]}"
|
||||||
|
|
||||||
|
if (( "${selectedtags}" & mask )) 2>/dev/null; then
|
||||||
|
printf -- '%s\n' "${tag_name}_${tag}_focused|bool|true"
|
||||||
|
printf -- '%s\n' "title|string|${title}"
|
||||||
|
else
|
||||||
|
printf '%s\n' "${tag_name}_${tag}_focused|bool|false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( "${activetags}" & mask )) 2>/dev/null; then
|
||||||
|
printf -- '%s\n' "${tag_name}_${tag}_occupied|bool|true"
|
||||||
|
else
|
||||||
|
printf -- '%s\n' "${tag_name}_${tag}_occupied|bool|false"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
printf -- '%s\n' "layout|string|${layout}"
|
||||||
|
printf -- '%s\n' ""
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Call the function here so the tags are displayed at dwl launch
|
||||||
|
_cycle
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
|
||||||
|
[[ ! -f "${fname}" ]] && printf -- '%s\n' \
|
||||||
|
"You need to redirect dwl stdout to ~/.cache/dwltags" >&2
|
||||||
|
|
||||||
|
inotifywait -qq --event modify "${fname}"
|
||||||
|
|
||||||
|
# Get info from the file
|
||||||
|
output="$(tail -n4 "${fname}")"
|
||||||
|
title="$(echo "${output}" | grep title | cut -d ' ' -f 3- )"
|
||||||
|
#selmon="$(echo "${output}" | grep 'selmon')"
|
||||||
|
layout="$(echo "${output}" | grep layout | cut -d ' ' -f 3- )"
|
||||||
|
|
||||||
|
# Get the tag bit mask as a decimal
|
||||||
|
activetags="$(echo "${output}" | grep tags | awk '{print $3}')"
|
||||||
|
selectedtags="$(echo "${output}" | grep tags | awk '{print $4}')"
|
||||||
|
|
||||||
|
_cycle
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
unset -v output title layout activetags selectedtags
|
||||||
|
unset -v tags name
|
||||||
|
|
80
examples/scripts/pacman.sh
Executable file
80
examples/scripts/pacman.sh
Executable file
|
@ -0,0 +1,80 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# pacman.sh - display number of packages update available
|
||||||
|
# by default check every hour
|
||||||
|
#
|
||||||
|
# USAGE: pacman.sh
|
||||||
|
#
|
||||||
|
# TAGS:
|
||||||
|
# Name Type Return
|
||||||
|
# -------------------------------------------
|
||||||
|
# {pacman} int number of pacman packages
|
||||||
|
# {aur} int number of aur packages
|
||||||
|
# {pkg} int sum of both
|
||||||
|
#
|
||||||
|
# Exemples configuration:
|
||||||
|
# - script:
|
||||||
|
# path: /absolute/path/to/pacman.sh
|
||||||
|
# args: []
|
||||||
|
# content: { string: { text: "{pacman} + {aur} = {pkg}" } }
|
||||||
|
#
|
||||||
|
# To display a message when there is no update:
|
||||||
|
# - script:
|
||||||
|
# path: /absolute/path/to/pacman.sh
|
||||||
|
# args: []
|
||||||
|
# content:
|
||||||
|
# map:
|
||||||
|
# tag: pkg
|
||||||
|
# default: { string: { text: "{pacman} + {aur} = {pkg}" } }
|
||||||
|
# values:
|
||||||
|
# 0: {string: {text: no updates}}
|
||||||
|
|
||||||
|
|
||||||
|
declare interval aur_helper pacman_num aur_num pkg_num
|
||||||
|
|
||||||
|
# Error message in STDERR
|
||||||
|
_err() {
|
||||||
|
printf -- '%s\n' "[$(date +'%Y-%m-%d %H:%M:%S')]: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display tags before yambar fetch the updates number
|
||||||
|
printf -- '%s\n' "pacman|int|0"
|
||||||
|
printf -- '%s\n' "aur|int|0"
|
||||||
|
printf -- '%s\n' "pkg|int|0"
|
||||||
|
printf -- '%s\n' ""
|
||||||
|
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
# Change interval
|
||||||
|
# NUMBER[SUFFIXE]
|
||||||
|
# Possible suffix:
|
||||||
|
# "s" seconds / "m" minutes / "h" hours / "d" days
|
||||||
|
interval="1h"
|
||||||
|
|
||||||
|
# Change your aur manager
|
||||||
|
aur_helper="paru"
|
||||||
|
|
||||||
|
# Get number of packages to update
|
||||||
|
pacman_num=$(checkupdates | wc -l)
|
||||||
|
|
||||||
|
if ! hash "${aur_helper}" >/dev/null 2>&1; then
|
||||||
|
_err "aur helper not found, change it in the script"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
aur_num=$("${aur_helper}" -Qmu | wc -l)
|
||||||
|
fi
|
||||||
|
|
||||||
|
pkg_num=$(( pacman_num + aur_num ))
|
||||||
|
|
||||||
|
printf -- '%s\n' "pacman|int|${pacman_num}"
|
||||||
|
printf -- '%s\n' "aur|int|${aur_num}"
|
||||||
|
printf -- '%s\n' "pkg|int|${pkg_num}"
|
||||||
|
printf -- '%s\n' ""
|
||||||
|
|
||||||
|
sleep "${interval}"
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
unset -v interval aur_helper pacman_num aur_num pkg_num
|
||||||
|
unset -f _err
|
||||||
|
|
97
external/wlr-layer-shell-unstable-v1.xml
vendored
97
external/wlr-layer-shell-unstable-v1.xml
vendored
|
@ -25,7 +25,7 @@
|
||||||
THIS SOFTWARE.
|
THIS SOFTWARE.
|
||||||
</copyright>
|
</copyright>
|
||||||
|
|
||||||
<interface name="zwlr_layer_shell_v1" version="3">
|
<interface name="zwlr_layer_shell_v1" version="4">
|
||||||
<description summary="create surfaces that are layers of the desktop">
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
Clients can use this interface to assign the surface_layer role to
|
Clients can use this interface to assign the surface_layer role to
|
||||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||||
|
@ -47,6 +47,12 @@
|
||||||
or manipulate a buffer prior to the first layer_surface.configure call
|
or manipulate a buffer prior to the first layer_surface.configure call
|
||||||
must also be treated as errors.
|
must also be treated as errors.
|
||||||
|
|
||||||
|
After creating a layer_surface object and setting it up, the client
|
||||||
|
must perform an initial commit without any buffer attached.
|
||||||
|
The compositor will reply with a layer_surface.configure event.
|
||||||
|
The client must acknowledge it and is then allowed to attach a buffer
|
||||||
|
to map the surface.
|
||||||
|
|
||||||
You may pass NULL for output to allow the compositor to decide which
|
You may pass NULL for output to allow the compositor to decide which
|
||||||
output to use. Generally this will be the one that the user most
|
output to use. Generally this will be the one that the user most
|
||||||
recently interacted with.
|
recently interacted with.
|
||||||
|
@ -94,7 +100,7 @@
|
||||||
</request>
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="zwlr_layer_surface_v1" version="3">
|
<interface name="zwlr_layer_surface_v1" version="4">
|
||||||
<description summary="layer metadata interface">
|
<description summary="layer metadata interface">
|
||||||
An interface that may be implemented by a wl_surface, for surfaces that
|
An interface that may be implemented by a wl_surface, for surfaces that
|
||||||
are designed to be rendered as a layer of a stacked desktop-like
|
are designed to be rendered as a layer of a stacked desktop-like
|
||||||
|
@ -103,6 +109,14 @@
|
||||||
Layer surface state (layer, size, anchor, exclusive zone,
|
Layer surface state (layer, size, anchor, exclusive zone,
|
||||||
margin, interactivity) is double-buffered, and will be applied at the
|
margin, interactivity) is double-buffered, and will be applied at the
|
||||||
time wl_surface.commit of the corresponding wl_surface is called.
|
time wl_surface.commit of the corresponding wl_surface is called.
|
||||||
|
|
||||||
|
Attaching a null buffer to a layer surface unmaps it.
|
||||||
|
|
||||||
|
Unmapping a layer_surface means that the surface cannot be shown by the
|
||||||
|
compositor until it is explicitly mapped again. The layer_surface
|
||||||
|
returns to the state it had right after layer_shell.get_layer_surface.
|
||||||
|
The client can re-map the surface by performing a commit without any
|
||||||
|
buffer attached, waiting for a configure event and handling it as usual.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<request name="set_size">
|
<request name="set_size">
|
||||||
|
@ -189,21 +203,85 @@
|
||||||
<arg name="left" type="int"/>
|
<arg name="left" type="int"/>
|
||||||
</request>
|
</request>
|
||||||
|
|
||||||
|
<enum name="keyboard_interactivity">
|
||||||
|
<description summary="types of keyboard interaction possible for a layer shell surface">
|
||||||
|
Types of keyboard interaction possible for layer shell surfaces. The
|
||||||
|
rationale for this is twofold: (1) some applications are not interested
|
||||||
|
in keyboard events and not allowing them to be focused can improve the
|
||||||
|
desktop experience; (2) some applications will want to take exclusive
|
||||||
|
keyboard focus.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="none" value="0">
|
||||||
|
<description summary="no keyboard focus is possible">
|
||||||
|
This value indicates that this surface is not interested in keyboard
|
||||||
|
events and the compositor should never assign it the keyboard focus.
|
||||||
|
|
||||||
|
This is the default value, set for newly created layer shell surfaces.
|
||||||
|
|
||||||
|
This is useful for e.g. desktop widgets that display information or
|
||||||
|
only have interaction with non-keyboard input devices.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
<entry name="exclusive" value="1">
|
||||||
|
<description summary="request exclusive keyboard focus">
|
||||||
|
Request exclusive keyboard focus if this surface is above the shell surface layer.
|
||||||
|
|
||||||
|
For the top and overlay layers, the seat will always give
|
||||||
|
exclusive keyboard focus to the top-most layer which has keyboard
|
||||||
|
interactivity set to exclusive. If this layer contains multiple
|
||||||
|
surfaces with keyboard interactivity set to exclusive, the compositor
|
||||||
|
determines the one receiving keyboard events in an implementation-
|
||||||
|
defined manner. In this case, no guarantee is made when this surface
|
||||||
|
will receive keyboard focus (if ever).
|
||||||
|
|
||||||
|
For the bottom and background layers, the compositor is allowed to use
|
||||||
|
normal focus semantics.
|
||||||
|
|
||||||
|
This setting is mainly intended for applications that need to ensure
|
||||||
|
they receive all keyboard events, such as a lock screen or a password
|
||||||
|
prompt.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
<entry name="on_demand" value="2" since="4">
|
||||||
|
<description summary="request regular keyboard focus semantics">
|
||||||
|
This requests the compositor to allow this surface to be focused and
|
||||||
|
unfocused by the user in an implementation-defined manner. The user
|
||||||
|
should be able to unfocus this surface even regardless of the layer
|
||||||
|
it is on.
|
||||||
|
|
||||||
|
Typically, the compositor will want to use its normal mechanism to
|
||||||
|
manage keyboard focus between layer shell surfaces with this setting
|
||||||
|
and regular toplevels on the desktop layer (e.g. click to focus).
|
||||||
|
Nevertheless, it is possible for a compositor to require a special
|
||||||
|
interaction to focus or unfocus layer shell surfaces (e.g. requiring
|
||||||
|
a click even if focus follows the mouse normally, or providing a
|
||||||
|
keybinding to switch focus between layers).
|
||||||
|
|
||||||
|
This setting is mainly intended for desktop shell components (e.g.
|
||||||
|
panels) that allow keyboard interaction. Using this option can allow
|
||||||
|
implementing a desktop shell that can be fully usable without the
|
||||||
|
mouse.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
</enum>
|
||||||
|
|
||||||
<request name="set_keyboard_interactivity">
|
<request name="set_keyboard_interactivity">
|
||||||
<description summary="requests keyboard events">
|
<description summary="requests keyboard events">
|
||||||
Set to 1 to request that the seat send keyboard events to this layer
|
Set how keyboard events are delivered to this surface. By default,
|
||||||
surface. For layers below the shell surface layer, the seat will use
|
layer shell surfaces do not receive keyboard events; this request can
|
||||||
normal focus semantics. For layers above the shell surface layers, the
|
be used to change this.
|
||||||
seat will always give exclusive keyboard focus to the top-most layer
|
|
||||||
which has keyboard interactivity set to true.
|
This setting is inherited by child surfaces set by the get_popup
|
||||||
|
request.
|
||||||
|
|
||||||
Layer surfaces receive pointer, touch, and tablet events normally. If
|
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||||
you do not want to receive them, set the input region on your surface
|
you do not want to receive them, set the input region on your surface
|
||||||
to an empty region.
|
to an empty region.
|
||||||
|
|
||||||
Events is double-buffered, see wl_surface.commit.
|
Keyboard interactivity is double-buffered, see wl_surface.commit.
|
||||||
</description>
|
</description>
|
||||||
<arg name="keyboard_interactivity" type="uint"/>
|
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
|
||||||
</request>
|
</request>
|
||||||
|
|
||||||
<request name="get_popup">
|
<request name="get_popup">
|
||||||
|
@ -288,6 +366,7 @@
|
||||||
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||||
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||||
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||||
|
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
<enum name="anchor" bitfield="true">
|
<enum name="anchor" bitfield="true">
|
||||||
|
|
2
external/wlr-protocols
vendored
2
external/wlr-protocols
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 16a28885bc92869d8e589e725e7bf018432c47e4
|
Subproject commit d1598e82240d6e8ca57729495a94d4e11d222033
|
9
main.c
9
main.c
|
@ -273,7 +273,14 @@ main(int argc, char *const *argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_WARNING);
|
log_init(log_colorize, log_syslog, LOG_FACILITY_DAEMON, LOG_CLASS_INFO);
|
||||||
|
|
||||||
|
_Static_assert(LOG_CLASS_ERROR + 1 == FCFT_LOG_CLASS_ERROR,
|
||||||
|
"fcft log level enum offset");
|
||||||
|
_Static_assert((int)LOG_COLORIZE_ALWAYS == (int)FCFT_LOG_COLORIZE_ALWAYS,
|
||||||
|
"fcft colorize enum mismatch");
|
||||||
|
fcft_log_init(
|
||||||
|
(enum fcft_log_colorize)log_colorize, log_syslog, FCFT_LOG_CLASS_INFO);
|
||||||
|
|
||||||
const struct sigaction sa = {.sa_handler = &signal_handler};
|
const struct sigaction sa = {.sa_handler = &signal_handler};
|
||||||
sigaction(SIGINT, &sa, NULL);
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
|
|
@ -65,7 +65,7 @@ backend_wayland = wayland_client.found() and wayland_cursor.found()
|
||||||
|
|
||||||
# "My" dependencies, fallback to subproject
|
# "My" dependencies, fallback to subproject
|
||||||
tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist')
|
tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist')
|
||||||
fcft = dependency('fcft', version: ['>=2.0.0', '<3.0.0'], fallback: 'fcft')
|
fcft = dependency('fcft', version: ['>=2.4.0', '<3.0.0'], fallback: 'fcft')
|
||||||
|
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
['-D_GNU_SOURCE'] +
|
['-D_GNU_SOURCE'] +
|
||||||
|
@ -100,7 +100,7 @@ version = custom_target(
|
||||||
'generate_version',
|
'generate_version',
|
||||||
build_always_stale: true,
|
build_always_stale: true,
|
||||||
output: 'version.h',
|
output: 'version.h',
|
||||||
command: [generate_version_sh, meson.project_version(), '@SOURCE_DIR@', '@OUTPUT@'])
|
command: [generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@OUTPUT@'])
|
||||||
|
|
||||||
yambar = executable(
|
yambar = executable(
|
||||||
'yambar',
|
'yambar',
|
||||||
|
|
2
module.h
2
module.h
|
@ -26,6 +26,8 @@ struct module {
|
||||||
/* refresh_in() should schedule a module content refresh after the
|
/* refresh_in() should schedule a module content refresh after the
|
||||||
* specified number of milliseconds */
|
* specified number of milliseconds */
|
||||||
bool (*refresh_in)(struct module *mod, long milli_seconds);
|
bool (*refresh_in)(struct module *mod, long milli_seconds);
|
||||||
|
|
||||||
|
const char *(*description)(struct module *mod);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct module *module_common_new(void);
|
struct module *module_common_new(void);
|
||||||
|
|
|
@ -39,6 +39,15 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
static char desc[32];
|
||||||
|
struct private *m = mod->private;
|
||||||
|
snprintf(desc, sizeof(desc), "alsa(%s)", m->card);
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -287,6 +296,7 @@ alsa_new(const char *card, const char *mixer, struct particle *label)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "backlight";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -216,6 +222,7 @@ backlight_new(const char *device, struct particle *label)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "../config-verify.h"
|
#include "../config-verify.h"
|
||||||
#include "../plugin.h"
|
#include "../plugin.h"
|
||||||
|
|
||||||
enum state { STATE_FULL, STATE_CHARGING, STATE_DISCHARGING };
|
enum state { STATE_FULL, STATE_NOTCHARGING, STATE_CHARGING, STATE_DISCHARGING };
|
||||||
|
|
||||||
struct private {
|
struct private {
|
||||||
struct particle *label;
|
struct particle *label;
|
||||||
|
@ -57,6 +57,15 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
static char desc[32];
|
||||||
|
struct private *m = mod->private;
|
||||||
|
snprintf(desc, sizeof(desc), "bat(%s)", m->battery);
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -65,6 +74,7 @@ content(struct module *mod)
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
|
|
||||||
assert(m->state == STATE_FULL ||
|
assert(m->state == STATE_FULL ||
|
||||||
|
m->state == STATE_NOTCHARGING ||
|
||||||
m->state == STATE_CHARGING ||
|
m->state == STATE_CHARGING ||
|
||||||
m->state == STATE_DISCHARGING);
|
m->state == STATE_DISCHARGING);
|
||||||
|
|
||||||
|
@ -79,7 +89,7 @@ content(struct module *mod)
|
||||||
? m->energy_full - m->energy : m->energy;
|
? m->energy_full - m->energy : m->energy;
|
||||||
|
|
||||||
double hours_as_float;
|
double hours_as_float;
|
||||||
if (m->state == STATE_FULL)
|
if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING)
|
||||||
hours_as_float = 0.0;
|
hours_as_float = 0.0;
|
||||||
else if (m->power > 0)
|
else if (m->power > 0)
|
||||||
hours_as_float = (double)energy / m->power;
|
hours_as_float = (double)energy / m->power;
|
||||||
|
@ -93,7 +103,7 @@ content(struct module *mod)
|
||||||
? m->charge_full - m->charge : m->charge;
|
? m->charge_full - m->charge : m->charge;
|
||||||
|
|
||||||
double hours_as_float;
|
double hours_as_float;
|
||||||
if (m->state == STATE_FULL)
|
if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING)
|
||||||
hours_as_float = 0.0;
|
hours_as_float = 0.0;
|
||||||
else if (m->current > 0)
|
else if (m->current > 0)
|
||||||
hours_as_float = (double)charge / m->current;
|
hours_as_float = (double)charge / m->current;
|
||||||
|
@ -117,6 +127,7 @@ content(struct module *mod)
|
||||||
tag_new_string(mod, "model", m->model),
|
tag_new_string(mod, "model", m->model),
|
||||||
tag_new_string(mod, "state",
|
tag_new_string(mod, "state",
|
||||||
m->state == STATE_FULL ? "full" :
|
m->state == STATE_FULL ? "full" :
|
||||||
|
m->state == STATE_NOTCHARGING ? "not charging" :
|
||||||
m->state == STATE_CHARGING ? "charging" :
|
m->state == STATE_CHARGING ? "charging" :
|
||||||
m->state == STATE_DISCHARGING ? "discharging" :
|
m->state == STATE_DISCHARGING ? "discharging" :
|
||||||
"unknown"),
|
"unknown"),
|
||||||
|
@ -349,12 +360,12 @@ update_status(struct module *mod)
|
||||||
state = STATE_DISCHARGING;
|
state = STATE_DISCHARGING;
|
||||||
} else if (strcmp(status, "Full") == 0)
|
} else if (strcmp(status, "Full") == 0)
|
||||||
state = STATE_FULL;
|
state = STATE_FULL;
|
||||||
|
else if (strcmp(status, "Not charging") == 0)
|
||||||
|
state = STATE_NOTCHARGING;
|
||||||
else if (strcmp(status, "Charging") == 0)
|
else if (strcmp(status, "Charging") == 0)
|
||||||
state = STATE_CHARGING;
|
state = STATE_CHARGING;
|
||||||
else if (strcmp(status, "Discharging") == 0)
|
else if (strcmp(status, "Discharging") == 0)
|
||||||
state = STATE_DISCHARGING;
|
state = STATE_DISCHARGING;
|
||||||
else if (strcmp(status, "Not charging") == 0)
|
|
||||||
state = STATE_DISCHARGING;
|
|
||||||
else if (strcmp(status, "Unknown") == 0)
|
else if (strcmp(status, "Unknown") == 0)
|
||||||
state = STATE_DISCHARGING;
|
state = STATE_DISCHARGING;
|
||||||
else {
|
else {
|
||||||
|
@ -434,8 +445,7 @@ run(struct module *mod)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!update_status(mod))
|
if (update_status(mod))
|
||||||
break;
|
|
||||||
bar->refresh(bar);
|
bar->refresh(bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,6 +471,7 @@ battery_new(const char *battery, struct particle *label, int poll_interval_secs)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "clock";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +66,7 @@ content(struct module *mod)
|
||||||
return exposable;
|
return exposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
static int
|
static int
|
||||||
run(struct module *mod)
|
run(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -161,6 +168,7 @@ clock_new(struct particle *label, const char *date_format, const char *time_form
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -608,6 +608,12 @@ ws_content_for_name(struct private *m, const char *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "i3/sway";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -710,6 +716,7 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count,
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "label";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -45,6 +51,7 @@ label_new(struct particle *label)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "mpd";
|
||||||
|
}
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
timespec_diff_milli_seconds(const struct timespec *a, const struct timespec *b)
|
timespec_diff_milli_seconds(const struct timespec *a, const struct timespec *b)
|
||||||
{
|
{
|
||||||
|
@ -588,6 +594,7 @@ mpd_new(const char *host, uint16_t port, struct particle *label)
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
mod->refresh_in = &refresh_in;
|
mod->refresh_in = &refresh_in;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,16 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
static char desc[32];
|
||||||
|
struct private *m = mod->private;
|
||||||
|
|
||||||
|
snprintf(desc, sizeof(desc), "net(%s)", m->iface);
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -526,6 +536,7 @@ network_new(const char *iface, struct particle *label)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "removables";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -596,6 +602,7 @@ removables_new(struct particle *label, int left_spacing, int right_spacing,
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <tllist.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
#define LOG_MODULE "river"
|
#define LOG_MODULE "river"
|
||||||
#define LOG_ENABLE_DBG 1
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "../log.h"
|
#include "../log.h"
|
||||||
#include "../plugin.h"
|
#include "../plugin.h"
|
||||||
#include "../particles/dynlist.h"
|
#include "../particles/dynlist.h"
|
||||||
|
@ -65,6 +65,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "river";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -184,14 +190,15 @@ focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1
|
||||||
uint32_t tags)
|
uint32_t tags)
|
||||||
{
|
{
|
||||||
struct output *output = data;
|
struct output *output = data;
|
||||||
struct module *mod = output->m->mod;
|
|
||||||
|
if (output->focused == tags)
|
||||||
|
return;
|
||||||
|
|
||||||
LOG_DBG("output: %s: focused tags: 0x%08x", output->name, tags);
|
LOG_DBG("output: %s: focused tags: 0x%08x", output->name, tags);
|
||||||
|
|
||||||
|
struct module *mod = output->m->mod;
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
{
|
|
||||||
output->focused = tags;
|
output->focused = tags;
|
||||||
}
|
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
mod->bar->refresh(mod->bar);
|
mod->bar->refresh(mod->bar);
|
||||||
}
|
}
|
||||||
|
@ -310,8 +317,6 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||||
struct private *m = seat->m;
|
struct private *m = seat->m;
|
||||||
struct module *mod = m->mod;
|
struct module *mod = m->mod;
|
||||||
|
|
||||||
mtx_lock(&mod->lock);
|
|
||||||
{
|
|
||||||
struct output *output = NULL;
|
struct output *output = NULL;
|
||||||
tll_foreach(m->outputs, it) {
|
tll_foreach(m->outputs, it) {
|
||||||
if (it->item.wl_output == wl_output) {
|
if (it->item.wl_output == wl_output) {
|
||||||
|
@ -325,11 +330,13 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||||
if (output == NULL)
|
if (output == NULL)
|
||||||
LOG_WARN("seat: %s: couldn't find output we are mapped on", seat->name);
|
LOG_WARN("seat: %s: couldn't find output we are mapped on", seat->name);
|
||||||
|
|
||||||
|
if (seat->output != output) {
|
||||||
|
mtx_lock(&mod->lock);
|
||||||
seat->output = output;
|
seat->output = output;
|
||||||
}
|
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
mod->bar->refresh(mod->bar);
|
mod->bar->refresh(mod->bar);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
unfocused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||||
|
@ -367,6 +374,12 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
struct module *mod = seat->m->mod;
|
struct module *mod = seat->m->mod;
|
||||||
|
|
||||||
|
if (seat->title == NULL && title == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (seat->title != NULL && title != NULL && strcmp(seat->title, title) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
LOG_DBG("seat: %s: focused view: %s", seat->name, title);
|
LOG_DBG("seat: %s: focused view: %s", seat->name, title);
|
||||||
|
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
|
@ -645,6 +658,7 @@ river_new(struct particle *template, struct particle *title)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
m->mod = mod;
|
m->mod = mod;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
108
modules/script.c
108
modules/script.c
|
@ -25,6 +25,8 @@ struct private {
|
||||||
char *path;
|
char *path;
|
||||||
size_t argc;
|
size_t argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
|
int poll_interval;
|
||||||
|
bool aborted;
|
||||||
|
|
||||||
struct particle *content;
|
struct particle *content;
|
||||||
|
|
||||||
|
@ -56,6 +58,19 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
static char desc[32];
|
||||||
|
struct private *m = mod->private;
|
||||||
|
|
||||||
|
char *path = strdup(m->path);
|
||||||
|
snprintf(desc, sizeof(desc), "script(%s)", basename(path));
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -180,7 +195,7 @@ process_line(struct module *mod, const char *line, size_t len)
|
||||||
|
|
||||||
long end = 0;
|
long end = 0;
|
||||||
for (size_t i = 0; i < end_len; i++) {
|
for (size_t i = 0; i < end_len; i++) {
|
||||||
if (!(_end[i] >= '0' && _end[i] < '9')) {
|
if (!(_end[i] >= '0' && _end[i] <= '9')) {
|
||||||
LOG_ERR(
|
LOG_ERR(
|
||||||
"tag range end is not an integer: %.*s",
|
"tag range end is not an integer: %.*s",
|
||||||
(int)end_len, _end);
|
(int)end_len, _end);
|
||||||
|
@ -318,7 +333,7 @@ data_received(struct module *mod, const char *data, size_t len)
|
||||||
static int
|
static int
|
||||||
run_loop(struct module *mod, pid_t pid, int comm_fd)
|
run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
struct pollfd fds[] = {
|
struct pollfd fds[] = {
|
||||||
|
@ -347,19 +362,18 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||||
data_received(mod, data, amount);
|
data_received(mod, data, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[0].revents & POLLHUP) {
|
if (fds[0].revents & (POLLHUP | POLLIN)) {
|
||||||
/* Aborted */
|
/* Aborted */
|
||||||
|
struct private *m = mod->private;
|
||||||
|
m->aborted = true;
|
||||||
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[1].revents & POLLHUP) {
|
if (fds[1].revents & POLLHUP) {
|
||||||
/* Child's stdout closed */
|
/* Child's stdout closed */
|
||||||
LOG_DBG("script pipe closed (script terminated?)");
|
LOG_DBG("script pipe closed (script terminated?)");
|
||||||
break;
|
ret = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (fds[0].revents & POLLIN) {
|
|
||||||
/* Aborted */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,7 +382,7 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
run(struct module *mod)
|
execute_script(struct module *mod)
|
||||||
{
|
{
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
|
|
||||||
|
@ -552,9 +566,75 @@ run(struct module *mod)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
run(struct module *mod)
|
||||||
|
{
|
||||||
|
struct private *m = mod->private;
|
||||||
|
|
||||||
|
int ret = 1;
|
||||||
|
bool keep_going = true;
|
||||||
|
|
||||||
|
while (keep_going && !m->aborted) {
|
||||||
|
ret = execute_script(mod);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
if (m->aborted)
|
||||||
|
break;
|
||||||
|
if (m->poll_interval < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
struct timeval now;
|
||||||
|
if (gettimeofday(&now, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to get current time");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval poll_interval = {.tv_sec = m->poll_interval};
|
||||||
|
|
||||||
|
struct timeval timeout;
|
||||||
|
timeradd(&now, &poll_interval, &timeout);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
||||||
|
|
||||||
|
struct timeval now;
|
||||||
|
if (gettimeofday(&now, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to get current time");
|
||||||
|
keep_going = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timercmp(&now, &timeout, <)) {
|
||||||
|
/* We’ve reached the timeout, it’s time to execute the script again */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval time_left;
|
||||||
|
timersub(&timeout, &now, &time_left);
|
||||||
|
|
||||||
|
int r = poll(fds, 1, time_left.tv_sec * 1000 + time_left.tv_usec / 1000);
|
||||||
|
if (r < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
LOG_ERRNO("failed to poll");
|
||||||
|
keep_going = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r > 0) {
|
||||||
|
m->aborted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static struct module *
|
static struct module *
|
||||||
script_new(const char *path, size_t argc, const char *const argv[static argc],
|
script_new(const char *path, size_t argc, const char *const argv[static argc],
|
||||||
struct particle *_content)
|
int poll_interval, struct particle *_content)
|
||||||
{
|
{
|
||||||
struct private *m = calloc(1, sizeof(*m));
|
struct private *m = calloc(1, sizeof(*m));
|
||||||
m->path = strdup(path);
|
m->path = strdup(path);
|
||||||
|
@ -563,12 +643,14 @@ script_new(const char *path, size_t argc, const char *const argv[static argc],
|
||||||
m->argv = malloc(argc * sizeof(m->argv[0]));
|
m->argv = malloc(argc * sizeof(m->argv[0]));
|
||||||
for (size_t i = 0; i < argc; i++)
|
for (size_t i = 0; i < argc; i++)
|
||||||
m->argv[i] = strdup(argv[i]);
|
m->argv[i] = strdup(argv[i]);
|
||||||
|
m->poll_interval = poll_interval;
|
||||||
|
|
||||||
struct module *mod = module_common_new();
|
struct module *mod = module_common_new();
|
||||||
mod->private = m;
|
mod->private = m;
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,6 +660,7 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
const struct yml_node *path = yml_get_value(node, "path");
|
const struct yml_node *path = yml_get_value(node, "path");
|
||||||
const struct yml_node *args = yml_get_value(node, "args");
|
const struct yml_node *args = yml_get_value(node, "args");
|
||||||
const struct yml_node *c = yml_get_value(node, "content");
|
const struct yml_node *c = yml_get_value(node, "content");
|
||||||
|
const struct yml_node *poll_interval = yml_get_value(node, "poll-interval");
|
||||||
|
|
||||||
size_t argc = args != NULL ? yml_list_length(args) : 0;
|
size_t argc = args != NULL ? yml_list_length(args) : 0;
|
||||||
const char *argv[argc];
|
const char *argv[argc];
|
||||||
|
@ -593,7 +676,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
||||||
}
|
}
|
||||||
|
|
||||||
return script_new(
|
return script_new(
|
||||||
yml_value_as_string(path), argc, argv, conf_to_particle(c, inherited));
|
yml_value_as_string(path), argc, argv,
|
||||||
|
poll_interval != NULL ? yml_value_as_int(poll_interval) : -1,
|
||||||
|
conf_to_particle(c, inherited));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -623,6 +708,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node)
|
||||||
static const struct attr_info attrs[] = {
|
static const struct attr_info attrs[] = {
|
||||||
{"path", true, &conf_verify_path},
|
{"path", true, &conf_verify_path},
|
||||||
{"args", false, &conf_verify_args},
|
{"args", false, &conf_verify_args},
|
||||||
|
{"poll-interval", false, &conf_verify_int},
|
||||||
MODULE_COMMON_ATTRS,
|
MODULE_COMMON_ATTRS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "sway-xkb";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -99,6 +105,15 @@ handle_input_reply(int type, const struct json_object *json, void *_mod)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const char *id = json_object_get_string(identifier);
|
const char *id = json_object_get_string(identifier);
|
||||||
|
|
||||||
|
struct json_object *type;
|
||||||
|
if (!json_object_object_get_ex(obj, "type", &type))
|
||||||
|
return false;
|
||||||
|
if (strcmp(json_object_get_string(type), "keyboard") != 0) {
|
||||||
|
LOG_DBG("ignoring non-keyboard input '%s'", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
struct input *input = NULL;
|
struct input *input = NULL;
|
||||||
for (size_t i = 0; i < m->num_inputs; i++) {
|
for (size_t i = 0; i < m->num_inputs; i++) {
|
||||||
struct input *maybe_input = &m->inputs[i];
|
struct input *maybe_input = &m->inputs[i];
|
||||||
|
@ -166,6 +181,15 @@ handle_input_event(int type, const struct json_object *json, void *_mod)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const char *id = json_object_get_string(identifier);
|
const char *id = json_object_get_string(identifier);
|
||||||
|
|
||||||
|
struct json_object *input_type;
|
||||||
|
if (!json_object_object_get_ex(obj, "type", &input_type))
|
||||||
|
return false;
|
||||||
|
if (strcmp(json_object_get_string(input_type), "keyboard") != 0) {
|
||||||
|
LOG_DBG("ignoring non-keyboard input '%s'", id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct input *input = NULL;
|
struct input *input = NULL;
|
||||||
for (size_t i = 0; i < m->num_inputs; i++) {
|
for (size_t i = 0; i < m->num_inputs; i++) {
|
||||||
struct input *maybe_input = &m->inputs[i];
|
struct input *maybe_input = &m->inputs[i];
|
||||||
|
@ -289,6 +313,7 @@ sway_xkb_new(struct particle *template, const char *identifiers[],
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,12 @@ destroy(struct module *mod)
|
||||||
module_default_destroy(mod);
|
module_default_destroy(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "xkb";
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
content(struct module *mod)
|
content(struct module *mod)
|
||||||
{
|
{
|
||||||
|
@ -650,6 +656,7 @@ xkb_new(struct particle *label)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,12 @@ struct private {
|
||||||
xcb_window_t active_win;
|
xcb_window_t active_win;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description(struct module *mod)
|
||||||
|
{
|
||||||
|
return "xwindow";
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_active_window(struct private *m)
|
update_active_window(struct private *m)
|
||||||
{
|
{
|
||||||
|
@ -332,6 +338,7 @@ xwindow_new(struct particle *label)
|
||||||
mod->run = &run;
|
mod->run = &run;
|
||||||
mod->destroy = &destroy;
|
mod->destroy = &destroy;
|
||||||
mod->content = &content;
|
mod->content = &content;
|
||||||
|
mod->description = &description;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
particle.c
74
particle.c
|
@ -22,31 +22,41 @@ particle_default_destroy(struct particle *particle)
|
||||||
if (particle->deco != NULL)
|
if (particle->deco != NULL)
|
||||||
particle->deco->destroy(particle->deco);
|
particle->deco->destroy(particle->deco);
|
||||||
fcft_destroy(particle->font);
|
fcft_destroy(particle->font);
|
||||||
free(particle->on_click_template);
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||||
|
free(particle->on_click_templates[i]);
|
||||||
free(particle);
|
free(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct particle *
|
struct particle *
|
||||||
particle_common_new(int left_margin, int right_margin,
|
particle_common_new(int left_margin, int right_margin,
|
||||||
const char *on_click_template,
|
const char **on_click_templates,
|
||||||
struct fcft_font *font, pixman_color_t foreground,
|
struct fcft_font *font, pixman_color_t foreground,
|
||||||
struct deco *deco)
|
struct deco *deco)
|
||||||
{
|
{
|
||||||
struct particle *p = calloc(1, sizeof(*p));
|
struct particle *p = calloc(1, sizeof(*p));
|
||||||
p->left_margin = left_margin;
|
p->left_margin = left_margin;
|
||||||
p->right_margin = right_margin;
|
p->right_margin = right_margin;
|
||||||
p->on_click_template =
|
|
||||||
on_click_template != NULL ? strdup(on_click_template) : NULL;
|
|
||||||
p->foreground = foreground;
|
p->foreground = foreground;
|
||||||
p->font = font;
|
p->font = font;
|
||||||
p->deco = deco;
|
p->deco = deco;
|
||||||
|
|
||||||
|
if (on_click_templates != NULL) {
|
||||||
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
||||||
|
if (on_click_templates[i] != NULL) {
|
||||||
|
p->have_on_click_template = true;
|
||||||
|
p->on_click_templates[i] = strdup(on_click_templates[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
exposable_default_destroy(struct exposable *exposable)
|
exposable_default_destroy(struct exposable *exposable)
|
||||||
{
|
{
|
||||||
free(exposable->on_click);
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||||
|
free(exposable->on_click[i]);
|
||||||
free(exposable);
|
free(exposable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,20 +151,37 @@ err:
|
||||||
|
|
||||||
void
|
void
|
||||||
exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
enum mouse_event event, int x, int y)
|
enum mouse_event event, enum mouse_button btn,
|
||||||
|
int x, int y)
|
||||||
{
|
{
|
||||||
LOG_DBG("on_mouse: exposable=%p, event=%s, x=%d, y=%d (on-click=%s)",
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
||||||
exposable, event == ON_MOUSE_MOTION ? "motion" : "click", x, y,
|
static const char *button_name[] = {
|
||||||
exposable->on_click);
|
[MOUSE_BTN_NONE] = "none",
|
||||||
|
[MOUSE_BTN_LEFT] = "left",
|
||||||
|
[MOUSE_BTN_MIDDLE] = "middle",
|
||||||
|
[MOUSE_BTN_RIGHT] = "right",
|
||||||
|
[MOUSE_BTN_COUNT] = "count",
|
||||||
|
[MOUSE_BTN_WHEEL_UP] = "wheel-up",
|
||||||
|
[MOUSE_BTN_WHEEL_DOWN] = "wheel-down",
|
||||||
|
};
|
||||||
|
LOG_DBG("on_mouse: exposable=%p, event=%s, btn=%s, x=%d, y=%d (on-click=%s)",
|
||||||
|
exposable, event == ON_MOUSE_MOTION ? "motion" : "click",
|
||||||
|
button_name[btn], x, y, exposable->on_click[btn]);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* If we have a handler, change cursor to a hand */
|
/* If we have a handler, change cursor to a hand */
|
||||||
bar->set_cursor(bar, exposable->on_click == NULL ? "left_ptr" : "hand2");
|
const char *cursor =
|
||||||
|
(exposable->particle != NULL &&
|
||||||
|
exposable->particle->have_on_click_template)
|
||||||
|
? "hand2"
|
||||||
|
: "left_ptr";
|
||||||
|
bar->set_cursor(bar, cursor);
|
||||||
|
|
||||||
/* If this is a mouse click, and we have a handler, execute it */
|
/* If this is a mouse click, and we have a handler, execute it */
|
||||||
if (exposable->on_click != NULL && event == ON_MOUSE_CLICK) {
|
if (exposable->on_click[btn] != NULL && event == ON_MOUSE_CLICK) {
|
||||||
/* Need a writeable copy, whose scope *we* control */
|
/* Need a writeable copy, whose scope *we* control */
|
||||||
char *cmd = strdup(exposable->on_click);
|
char *cmd = strdup(exposable->on_click[btn]);
|
||||||
LOG_DBG("cmd = \"%s\"", exposable->on_click);
|
LOG_DBG("cmd = \"%s\"", exposable->on_click[btn]);
|
||||||
|
|
||||||
char **argv;
|
char **argv;
|
||||||
if (!tokenize_cmdline(cmd, &argv)) {
|
if (!tokenize_cmdline(cmd, &argv)) {
|
||||||
|
@ -172,15 +199,15 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
|
|
||||||
int wstatus;
|
int wstatus;
|
||||||
if (waitpid(pid, &wstatus, 0) == -1)
|
if (waitpid(pid, &wstatus, 0) == -1)
|
||||||
LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click);
|
LOG_ERRNO("%s: failed to wait for on_click handler", exposable->on_click[btn]);
|
||||||
|
|
||||||
if (WIFEXITED(wstatus)) {
|
if (WIFEXITED(wstatus)) {
|
||||||
if (WEXITSTATUS(wstatus) != 0)
|
if (WEXITSTATUS(wstatus) != 0)
|
||||||
LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click);
|
LOG_ERRNO_P("%s: failed to execute", WEXITSTATUS(wstatus), exposable->on_click[btn]);
|
||||||
} else
|
} else
|
||||||
LOG_ERR("%s: did not exit normally", exposable->on_click);
|
LOG_ERR("%s: did not exit normally", exposable->on_click[btn]);
|
||||||
|
|
||||||
LOG_DBG("%s: launched", exposable->on_click);
|
LOG_DBG("%s: launched", exposable->on_click[btn]);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Use a pipe with O_CLOEXEC to communicate exec() failure
|
* Use a pipe with O_CLOEXEC to communicate exec() failure
|
||||||
|
@ -250,6 +277,7 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
|
|
||||||
/* Close *all* other FDs (e.g. script modules' FDs) */
|
/* Close *all* other FDs (e.g. script modules' FDs) */
|
||||||
for (int i = STDERR_FILENO + 1; i < 65536; i++)
|
for (int i = STDERR_FILENO + 1; i < 65536; i++)
|
||||||
|
if (i != pipe_fds[1])
|
||||||
close(i);
|
close(i);
|
||||||
|
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
|
@ -267,6 +295,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
|
|
||||||
int _errno = 0;
|
int _errno = 0;
|
||||||
ssize_t ret = read(pipe_fds[0], &_errno, sizeof(_errno));
|
ssize_t ret = read(pipe_fds[0], &_errno, sizeof(_errno));
|
||||||
|
close(pipe_fds[0]);
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/* Pipe was closed - child succeeded with exec() */
|
/* Pipe was closed - child succeeded with exec() */
|
||||||
_exit(0);
|
_exit(0);
|
||||||
|
@ -281,11 +311,17 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct exposable *
|
struct exposable *
|
||||||
exposable_common_new(const struct particle *particle, const char *on_click)
|
exposable_common_new(const struct particle *particle, const struct tag_set *tags)
|
||||||
{
|
{
|
||||||
struct exposable *exposable = calloc(1, sizeof(*exposable));
|
struct exposable *exposable = calloc(1, sizeof(*exposable));
|
||||||
exposable->particle = particle;
|
exposable->particle = particle;
|
||||||
exposable->on_click = on_click != NULL ? strdup(on_click) : NULL;
|
|
||||||
|
if (particle != NULL && particle->have_on_click_template) {
|
||||||
|
tags_expand_templates(
|
||||||
|
exposable->on_click,
|
||||||
|
(const char **)particle->on_click_templates,
|
||||||
|
MOUSE_BTN_COUNT, tags);
|
||||||
|
}
|
||||||
exposable->destroy = &exposable_default_destroy;
|
exposable->destroy = &exposable_default_destroy;
|
||||||
exposable->on_mouse = &exposable_default_on_mouse;
|
exposable->on_mouse = &exposable_default_on_mouse;
|
||||||
return exposable;
|
return exposable;
|
||||||
|
|
36
particle.h
36
particle.h
|
@ -7,13 +7,31 @@
|
||||||
#include "decoration.h"
|
#include "decoration.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
|
||||||
|
enum mouse_event {
|
||||||
|
ON_MOUSE_MOTION,
|
||||||
|
ON_MOUSE_CLICK,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mouse_button {
|
||||||
|
MOUSE_BTN_NONE,
|
||||||
|
MOUSE_BTN_LEFT,
|
||||||
|
MOUSE_BTN_MIDDLE,
|
||||||
|
MOUSE_BTN_RIGHT,
|
||||||
|
MOUSE_BTN_WHEEL_UP,
|
||||||
|
MOUSE_BTN_WHEEL_DOWN,
|
||||||
|
|
||||||
|
MOUSE_BTN_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
struct bar;
|
struct bar;
|
||||||
|
|
||||||
struct particle {
|
struct particle {
|
||||||
void *private;
|
void *private;
|
||||||
|
|
||||||
int left_margin, right_margin;
|
int left_margin, right_margin;
|
||||||
char *on_click_template;
|
|
||||||
|
bool have_on_click_template;
|
||||||
|
char *on_click_templates[MOUSE_BTN_COUNT];
|
||||||
|
|
||||||
pixman_color_t foreground;
|
pixman_color_t foreground;
|
||||||
struct fcft_font *font;
|
struct fcft_font *font;
|
||||||
|
@ -24,17 +42,13 @@ struct particle {
|
||||||
const struct tag_set *tags);
|
const struct tag_set *tags);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mouse_event {
|
|
||||||
ON_MOUSE_MOTION,
|
|
||||||
ON_MOUSE_CLICK,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct exposable {
|
struct exposable {
|
||||||
const struct particle *particle;
|
const struct particle *particle;
|
||||||
void *private;
|
void *private;
|
||||||
|
|
||||||
int width; /* Should be set by begin_expose(), at latest */
|
int width; /* Should be set by begin_expose(), at latest */
|
||||||
char *on_click;
|
char *on_click[MOUSE_BTN_COUNT];
|
||||||
|
|
||||||
void (*destroy)(struct exposable *exposable);
|
void (*destroy)(struct exposable *exposable);
|
||||||
int (*begin_expose)(struct exposable *exposable);
|
int (*begin_expose)(struct exposable *exposable);
|
||||||
|
@ -42,31 +56,31 @@ struct exposable {
|
||||||
int x, int y, int height);
|
int x, int y, int height);
|
||||||
|
|
||||||
void (*on_mouse)(struct exposable *exposable, struct bar *bar,
|
void (*on_mouse)(struct exposable *exposable, struct bar *bar,
|
||||||
enum mouse_event event, int x, int y);
|
enum mouse_event event, enum mouse_button btn, int x, int y);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct particle *particle_common_new(
|
struct particle *particle_common_new(
|
||||||
int left_margin, int right_margin, const char *on_click_template,
|
int left_margin, int right_margin, const char *on_click_templates[],
|
||||||
struct fcft_font *font, pixman_color_t foreground, struct deco *deco);
|
struct fcft_font *font, pixman_color_t foreground, struct deco *deco);
|
||||||
|
|
||||||
void particle_default_destroy(struct particle *particle);
|
void particle_default_destroy(struct particle *particle);
|
||||||
|
|
||||||
struct exposable *exposable_common_new(
|
struct exposable *exposable_common_new(
|
||||||
const struct particle *particle, const char *on_click);
|
const struct particle *particle, const struct tag_set *tags);
|
||||||
void exposable_default_destroy(struct exposable *exposable);
|
void exposable_default_destroy(struct exposable *exposable);
|
||||||
void exposable_render_deco(
|
void exposable_render_deco(
|
||||||
const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height);
|
const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height);
|
||||||
|
|
||||||
void exposable_default_on_mouse(
|
void exposable_default_on_mouse(
|
||||||
struct exposable *exposable, struct bar *bar,
|
struct exposable *exposable, struct bar *bar,
|
||||||
enum mouse_event event, int x, int y);
|
enum mouse_event event, enum mouse_button btn, int x, int y);
|
||||||
|
|
||||||
/* List of attributes *all* particles implement */
|
/* List of attributes *all* particles implement */
|
||||||
#define PARTICLE_COMMON_ATTRS \
|
#define PARTICLE_COMMON_ATTRS \
|
||||||
{"margin", false, &conf_verify_int}, \
|
{"margin", false, &conf_verify_int}, \
|
||||||
{"left-margin", false, &conf_verify_int}, \
|
{"left-margin", false, &conf_verify_int}, \
|
||||||
{"right-margin", false, &conf_verify_int}, \
|
{"right-margin", false, &conf_verify_int}, \
|
||||||
{"on-click", false, &conf_verify_string}, \
|
{"on-click", false, &conf_verify_on_click}, \
|
||||||
{"font", false, &conf_verify_font}, \
|
{"font", false, &conf_verify_font}, \
|
||||||
{"foreground", false, &conf_verify_color}, \
|
{"foreground", false, &conf_verify_color}, \
|
||||||
{"deco", false, &conf_verify_decoration}, \
|
{"deco", false, &conf_verify_decoration}, \
|
||||||
|
|
|
@ -67,13 +67,12 @@ dynlist_expose(const struct exposable *exposable, pixman_image_t *pix, int x, in
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mouse(struct exposable *exposable, struct bar *bar,
|
on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
enum mouse_event event, int x, int y)
|
enum mouse_event event, enum mouse_button btn, int x, int y)
|
||||||
{
|
{
|
||||||
//const struct particle *p = exposable->particle;
|
|
||||||
const struct private *e = exposable->private;
|
const struct private *e = exposable->private;
|
||||||
|
|
||||||
if (exposable->on_click != NULL) {
|
if (exposable->on_click[btn] != NULL) {
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
if (x >= px && x < px + e->exposables[i]->width) {
|
if (x >= px && x < px + e->exposables[i]->width) {
|
||||||
if (e->exposables[i]->on_mouse != NULL) {
|
if (e->exposables[i]->on_mouse != NULL) {
|
||||||
e->exposables[i]->on_mouse(
|
e->exposables[i]->on_mouse(
|
||||||
e->exposables[i], bar, event, x - px, y);
|
e->exposables[i], bar, event, btn, x - px, y);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +90,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("on_mouse missed all sub-particles");
|
LOG_DBG("on_mouse missed all sub-particles");
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct exposable *
|
struct exposable *
|
||||||
|
|
|
@ -22,13 +22,9 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
instantiate(const struct particle *particle, const struct tag_set *tags)
|
instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
{
|
{
|
||||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
|
|
||||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
|
||||||
exposable->begin_expose = &begin_expose;
|
exposable->begin_expose = &begin_expose;
|
||||||
exposable->expose = &expose;
|
exposable->expose = &expose;
|
||||||
|
|
||||||
free(on_click);
|
|
||||||
return exposable;
|
return exposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,14 +75,17 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mouse(struct exposable *exposable, struct bar *bar,
|
on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
enum mouse_event event, int x, int y)
|
enum mouse_event event, enum mouse_button btn, int x, int y)
|
||||||
{
|
{
|
||||||
const struct particle *p = exposable->particle;
|
const struct particle *p = exposable->particle;
|
||||||
const struct eprivate *e = exposable->private;
|
const struct eprivate *e = exposable->private;
|
||||||
|
|
||||||
if (exposable->on_click != NULL) {
|
if ((event == ON_MOUSE_MOTION &&
|
||||||
|
exposable->particle->have_on_click_template) ||
|
||||||
|
exposable->on_click[btn] != NULL)
|
||||||
|
{
|
||||||
/* We have our own handler */
|
/* We have our own handler */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +94,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
if (x >= px && x < px + e->exposables[i]->width) {
|
if (x >= px && x < px + e->exposables[i]->width) {
|
||||||
if (e->exposables[i]->on_mouse != NULL) {
|
if (e->exposables[i]->on_mouse != NULL) {
|
||||||
e->exposables[i]->on_mouse(
|
e->exposables[i]->on_mouse(
|
||||||
e->exposables[i], bar, event, x - px, y);
|
e->exposables[i], bar, event, btn, x - px, y);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +103,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're between sub-particles (or in the left/right margin) */
|
/* We're between sub-particles (or in the left/right margin) */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
|
@ -121,16 +124,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
assert(e->exposables[i] != NULL);
|
assert(e->exposables[i] != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
|
|
||||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
|
||||||
exposable->private = e;
|
exposable->private = e;
|
||||||
exposable->destroy = &exposable_destroy;
|
exposable->destroy = &exposable_destroy;
|
||||||
exposable->begin_expose = &begin_expose;
|
exposable->begin_expose = &begin_expose;
|
||||||
exposable->expose = &expose;
|
exposable->expose = &expose;
|
||||||
exposable->on_mouse = &on_mouse;
|
exposable->on_mouse = &on_mouse;
|
||||||
|
|
||||||
free(on_click);
|
|
||||||
return exposable;
|
return exposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,26 +61,29 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
int x, int y)
|
enum mouse_button btn, int x, int y)
|
||||||
{
|
{
|
||||||
const struct particle *p = exposable->particle;
|
const struct particle *p = exposable->particle;
|
||||||
const struct eprivate *e = exposable->private;
|
const struct eprivate *e = exposable->private;
|
||||||
|
|
||||||
if (exposable->on_click != NULL) {
|
if ((event == ON_MOUSE_MOTION &&
|
||||||
|
exposable->particle->have_on_click_template) ||
|
||||||
|
exposable->on_click[btn] != NULL)
|
||||||
|
{
|
||||||
/* We have our own handler */
|
/* We have our own handler */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int px = p->left_margin;
|
int px = p->left_margin;
|
||||||
if (x >= px && x < px + e->exposable->width) {
|
if (x >= px && x < px + e->exposable->width) {
|
||||||
if (e->exposable->on_mouse != NULL)
|
if (e->exposable->on_mouse != NULL)
|
||||||
e->exposable->on_mouse(e->exposable, bar, event, x - px, y);
|
e->exposable->on_mouse(e->exposable, bar, event, btn, x - px, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the left- or right margin */
|
/* In the left- or right margin */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
|
@ -119,15 +122,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
|
|
||||||
assert(e->exposable != NULL);
|
assert(e->exposable != NULL);
|
||||||
|
|
||||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
|
||||||
exposable->private = e;
|
exposable->private = e;
|
||||||
exposable->destroy = &exposable_destroy;
|
exposable->destroy = &exposable_destroy;
|
||||||
exposable->begin_expose = &begin_expose;
|
exposable->begin_expose = &begin_expose;
|
||||||
exposable->expose = &expose;
|
exposable->expose = &expose;
|
||||||
exposable->on_mouse = &on_mouse;
|
exposable->on_mouse = &on_mouse;
|
||||||
|
|
||||||
free(on_click);
|
|
||||||
return exposable;
|
return exposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,10 +85,13 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
int x, int y)
|
enum mouse_button btn, int x, int y)
|
||||||
{
|
{
|
||||||
if (exposable->on_click == NULL) {
|
if ((event == ON_MOUSE_MOTION &&
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable->particle->have_on_click_template) ||
|
||||||
|
exposable->on_click[btn] != NULL)
|
||||||
|
{
|
||||||
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +123,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
/* Mouse is over the start-marker */
|
/* Mouse is over the start-marker */
|
||||||
struct exposable *start = e->exposables[0];
|
struct exposable *start = e->exposables[0];
|
||||||
if (start->on_mouse != NULL)
|
if (start->on_mouse != NULL)
|
||||||
start->on_mouse(start, bar, event, x - p->left_margin, y);
|
start->on_mouse(start, bar, event, btn, x - p->left_margin, y);
|
||||||
} else {
|
} else {
|
||||||
/* Mouse if over left margin */
|
/* Mouse if over left margin */
|
||||||
bar->set_cursor(bar, "left_ptr");
|
bar->set_cursor(bar, "left_ptr");
|
||||||
|
@ -139,7 +142,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
/* Mouse is over the end-marker */
|
/* Mouse is over the end-marker */
|
||||||
struct exposable *end = e->exposables[e->count - 1];
|
struct exposable *end = e->exposables[e->count - 1];
|
||||||
if (end->on_mouse != NULL)
|
if (end->on_mouse != NULL)
|
||||||
end->on_mouse(end, bar, event, x - x_offset - clickable_width, y);
|
end->on_mouse(end, bar, event, btn, x - x_offset - clickable_width, y);
|
||||||
} else {
|
} else {
|
||||||
/* Mouse is over the right margin */
|
/* Mouse is over the right margin */
|
||||||
bar->set_cursor(bar, "left_ptr");
|
bar->set_cursor(bar, "left_ptr");
|
||||||
|
@ -148,7 +151,9 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remember the original handler, so that we can restore it */
|
/* Remember the original handler, so that we can restore it */
|
||||||
char *original = exposable->on_click;
|
char *original[MOUSE_BTN_COUNT];
|
||||||
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++)
|
||||||
|
original[i] = exposable->on_click[i];
|
||||||
|
|
||||||
if (event == ON_MOUSE_CLICK) {
|
if (event == ON_MOUSE_CLICK) {
|
||||||
long where = clickable_width > 0
|
long where = clickable_width > 0
|
||||||
|
@ -160,17 +165,21 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
.count = 1,
|
.count = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
exposable->on_click = tags_expand_template(exposable->on_click, &tags);
|
tags_expand_templates(
|
||||||
|
exposable->on_click, (const char **)exposable->on_click,
|
||||||
|
MOUSE_BTN_COUNT, &tags);
|
||||||
tag_set_destroy(&tags);
|
tag_set_destroy(&tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call default implementation, which will execute our handler */
|
/* Call default implementation, which will execute our handler */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
|
|
||||||
if (event == ON_MOUSE_CLICK) {
|
if (event == ON_MOUSE_CLICK) {
|
||||||
/* Reset handler string */
|
/* Reset handler string */
|
||||||
free(exposable->on_click);
|
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
||||||
exposable->on_click = original;
|
free(exposable->on_click[i]);
|
||||||
|
exposable->on_click[i] = original[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,10 +222,7 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
for (size_t i = 0; i < epriv->count; i++)
|
for (size_t i = 0; i < epriv->count; i++)
|
||||||
assert(epriv->exposables[i] != NULL);
|
assert(epriv->exposables[i] != NULL);
|
||||||
|
|
||||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
|
|
||||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
|
||||||
free(on_click);
|
|
||||||
|
|
||||||
exposable->private = epriv;
|
exposable->private = epriv;
|
||||||
exposable->destroy = &exposable_destroy;
|
exposable->destroy = &exposable_destroy;
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "ramp"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "../log.h"
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
#include "../config-verify.h"
|
#include "../config-verify.h"
|
||||||
#include "../particle.h"
|
#include "../particle.h"
|
||||||
|
@ -54,26 +57,29 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
||||||
int x, int y)
|
enum mouse_button btn, int x, int y)
|
||||||
{
|
{
|
||||||
const struct particle *p = exposable->particle;
|
const struct particle *p = exposable->particle;
|
||||||
const struct eprivate *e = exposable->private;
|
const struct eprivate *e = exposable->private;
|
||||||
|
|
||||||
if (exposable->on_click != NULL) {
|
if ((event == ON_MOUSE_MOTION &&
|
||||||
|
exposable->particle->have_on_click_template) ||
|
||||||
|
exposable->on_click[btn] != NULL)
|
||||||
|
{
|
||||||
/* We have our own handler */
|
/* We have our own handler */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int px = p->left_margin;
|
int px = p->left_margin;
|
||||||
if (x >= px && x < px + e->exposable->width) {
|
if (x >= px && x < px + e->exposable->width) {
|
||||||
if (e->exposable->on_mouse != NULL)
|
if (e->exposable->on_mouse != NULL)
|
||||||
e->exposable->on_mouse(e->exposable, bar, event, x - px, y);
|
e->exposable->on_mouse(e->exposable, bar, event, btn, x - px, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the left- or right margin */
|
/* In the left- or right margin */
|
||||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -102,6 +108,26 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
long min = tag != NULL ? tag->min(tag) : 0;
|
long min = tag != NULL ? tag->min(tag) : 0;
|
||||||
long max = tag != NULL ? tag->max(tag) : 0;
|
long max = tag != NULL ? tag->max(tag) : 0;
|
||||||
|
|
||||||
|
if (min > max) {
|
||||||
|
LOG_WARN(
|
||||||
|
"tag's minimum value is greater than its maximum: "
|
||||||
|
"tag=\"%s\", min=%ld, max=%ld", p->tag, min, max);
|
||||||
|
min = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < min) {
|
||||||
|
LOG_WARN(
|
||||||
|
"tag's value is less than its minimum value: "
|
||||||
|
"tag=\"%s\", min=%ld, value=%ld", p->tag, min, value);
|
||||||
|
value = min;
|
||||||
|
}
|
||||||
|
if (value > max) {
|
||||||
|
LOG_WARN(
|
||||||
|
"tag's value is greater than its maximum value: "
|
||||||
|
"tag=\"%s\", max=%ld, value=%ld", p->tag, max, value);
|
||||||
|
value = max;
|
||||||
|
}
|
||||||
|
|
||||||
assert(value >= min && value <= max);
|
assert(value >= min && value <= max);
|
||||||
assert(max >= min);
|
assert(max >= min);
|
||||||
|
|
||||||
|
@ -123,15 +149,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
e->exposable = pp->instantiate(pp, tags);
|
e->exposable = pp->instantiate(pp, tags);
|
||||||
assert(e->exposable != NULL);
|
assert(e->exposable != NULL);
|
||||||
|
|
||||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
|
||||||
exposable->private = e;
|
exposable->private = e;
|
||||||
exposable->destroy = &exposable_destroy;
|
exposable->destroy = &exposable_destroy;
|
||||||
exposable->begin_expose = &begin_expose;
|
exposable->begin_expose = &begin_expose;
|
||||||
exposable->expose = &expose;
|
exposable->expose = &expose;
|
||||||
exposable->on_mouse = &on_mouse;
|
exposable->on_mouse = &on_mouse;
|
||||||
|
|
||||||
free(on_click);
|
|
||||||
return exposable;
|
return exposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,25 @@
|
||||||
#include "../particle.h"
|
#include "../particle.h"
|
||||||
#include "../plugin.h"
|
#include "../plugin.h"
|
||||||
|
|
||||||
|
struct text_run_cache {
|
||||||
|
uint64_t hash;
|
||||||
|
struct fcft_text_run *run;
|
||||||
|
int width;
|
||||||
|
bool in_use;
|
||||||
|
};
|
||||||
|
|
||||||
struct private {
|
struct private {
|
||||||
char *text;
|
char *text;
|
||||||
size_t max_len;
|
size_t max_len;
|
||||||
|
|
||||||
|
size_t cache_size;
|
||||||
|
struct text_run_cache *cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct eprivate {
|
struct eprivate {
|
||||||
/* Set when instantiating */
|
ssize_t cache_idx;
|
||||||
char *text;
|
|
||||||
|
|
||||||
const struct fcft_glyph **glyphs;
|
const struct fcft_glyph **glyphs;
|
||||||
|
const struct fcft_glyph **allocated_glyphs;
|
||||||
long *kern_x;
|
long *kern_x;
|
||||||
int num_glyphs;
|
int num_glyphs;
|
||||||
};
|
};
|
||||||
|
@ -29,8 +38,7 @@ exposable_destroy(struct exposable *exposable)
|
||||||
{
|
{
|
||||||
struct eprivate *e = exposable->private;
|
struct eprivate *e = exposable->private;
|
||||||
|
|
||||||
free(e->text);
|
free(e->allocated_glyphs);
|
||||||
free(e->glyphs);
|
|
||||||
free(e->kern_x);
|
free(e->kern_x);
|
||||||
free(e);
|
free(e);
|
||||||
exposable_default_destroy(exposable);
|
exposable_default_destroy(exposable);
|
||||||
|
@ -40,42 +48,19 @@ static int
|
||||||
begin_expose(struct exposable *exposable)
|
begin_expose(struct exposable *exposable)
|
||||||
{
|
{
|
||||||
struct eprivate *e = exposable->private;
|
struct eprivate *e = exposable->private;
|
||||||
struct fcft_font *font = exposable->particle->font;
|
struct private *p = exposable->particle->private;
|
||||||
|
|
||||||
e->glyphs = NULL;
|
exposable->width =
|
||||||
e->num_glyphs = 0;
|
exposable->particle->left_margin +
|
||||||
|
|
||||||
size_t chars = mbstowcs(NULL, e->text, 0);
|
|
||||||
if (chars != (size_t)-1) {
|
|
||||||
wchar_t wtext[chars + 1];
|
|
||||||
mbstowcs(wtext, e->text, chars + 1);
|
|
||||||
|
|
||||||
e->glyphs = malloc(chars * sizeof(e->glyphs[0]));
|
|
||||||
e->kern_x = calloc(chars, sizeof(e->kern_x[0]));
|
|
||||||
|
|
||||||
/* Convert text to glyph masks/images. */
|
|
||||||
for (size_t i = 0; i < chars; i++) {
|
|
||||||
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
|
|
||||||
font, wtext[i], FCFT_SUBPIXEL_NONE);
|
|
||||||
|
|
||||||
if (glyph == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
e->glyphs[e->num_glyphs++] = glyph;
|
|
||||||
|
|
||||||
if (i == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exposable->width = exposable->particle->left_margin +
|
|
||||||
exposable->particle->right_margin;
|
exposable->particle->right_margin;
|
||||||
|
|
||||||
|
if (e->cache_idx >= 0) {
|
||||||
|
exposable->width += p->cache[e->cache_idx].width;
|
||||||
|
} else {
|
||||||
/* Calculate the size we need to render the glyphs */
|
/* Calculate the size we need to render the glyphs */
|
||||||
for (int i = 0; i < e->num_glyphs; i++)
|
for (int i = 0; i < e->num_glyphs; i++)
|
||||||
exposable->width += e->kern_x[i] + e->glyphs[i]->advance.x;
|
exposable->width += e->kern_x[i] + e->glyphs[i]->advance.x;
|
||||||
|
}
|
||||||
|
|
||||||
return exposable->width;
|
return exposable->width;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +73,11 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
const struct eprivate *e = exposable->private;
|
const struct eprivate *e = exposable->private;
|
||||||
const struct fcft_font *font = exposable->particle->font;
|
const struct fcft_font *font = exposable->particle->font;
|
||||||
|
|
||||||
|
if (e->cache_idx >= 0) {
|
||||||
|
struct private *priv = exposable->particle->private;
|
||||||
|
priv->cache[e->cache_idx].in_use = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (e->num_glyphs == 0)
|
if (e->num_glyphs == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -139,48 +129,153 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
sdbm_hash(const char *s)
|
||||||
|
{
|
||||||
|
uint64_t hash = 0;
|
||||||
|
|
||||||
|
for (; *s != '\0'; s++) {
|
||||||
|
int c = *s;
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
static struct exposable *
|
static struct exposable *
|
||||||
instantiate(const struct particle *particle, const struct tag_set *tags)
|
instantiate(const struct particle *particle, const struct tag_set *tags)
|
||||||
{
|
{
|
||||||
const struct private *p = particle->private;
|
struct private *p = (struct private *)particle->private;
|
||||||
struct eprivate *e = calloc(1, sizeof(*e));
|
struct eprivate *e = calloc(1, sizeof(*e));
|
||||||
|
struct fcft_font *font = particle->font;
|
||||||
|
|
||||||
e->text = tags_expand_template(p->text, tags);
|
wchar_t *wtext = NULL;
|
||||||
e->glyphs = NULL;
|
char *text = tags_expand_template(p->text, tags);
|
||||||
|
|
||||||
|
e->glyphs = e->allocated_glyphs = NULL;
|
||||||
e->num_glyphs = 0;
|
e->num_glyphs = 0;
|
||||||
|
e->kern_x = NULL;
|
||||||
|
e->cache_idx = -1;
|
||||||
|
|
||||||
|
uint64_t hash = sdbm_hash(text);
|
||||||
|
|
||||||
|
/* First, check if we have this string cached */
|
||||||
|
for (size_t i = 0; i < p->cache_size; i++) {
|
||||||
|
if (p->cache[i].hash == hash) {
|
||||||
|
assert(p->cache[i].run != NULL);
|
||||||
|
|
||||||
|
p->cache[i].in_use = true;
|
||||||
|
e->cache_idx = i;
|
||||||
|
e->glyphs = p->cache[i].run->glyphs;
|
||||||
|
e->num_glyphs = p->cache[i].run->count;
|
||||||
|
e->kern_x = calloc(p->cache[i].run->count, sizeof(e->kern_x[0]));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not in cache - we need to rasterize it. First, convert to wchar */
|
||||||
|
size_t chars = mbstowcs(NULL, text, 0);
|
||||||
|
if (chars == (size_t)-1)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
wtext = malloc((chars + 1) * sizeof(wtext[0]));
|
||||||
|
mbstowcs(wtext, text, chars + 1);
|
||||||
|
|
||||||
|
/* Truncate, if necessary */
|
||||||
if (p->max_len > 0) {
|
if (p->max_len > 0) {
|
||||||
const size_t len = strlen(e->text);
|
const size_t len = wcslen(wtext);
|
||||||
if (len > p->max_len) {
|
if (len > p->max_len) {
|
||||||
|
|
||||||
size_t end = p->max_len;
|
size_t end = p->max_len;
|
||||||
if (end >= 3) {
|
if (end >= 1) {
|
||||||
/* "allocate" room for three dots at the end */
|
/* "allocate" room for three dots at the end */
|
||||||
end -= 3;
|
end -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mucho importante - don't cut in the middle of a utf8 multibyte */
|
if (p->max_len > 1) {
|
||||||
while (end > 0 && e->text[end - 1] >> 7)
|
wtext[end] = L'…';
|
||||||
end--;
|
wtext[end + 1] = L'\0';
|
||||||
|
chars = end + 1;
|
||||||
if (p->max_len > 3) {
|
} else {
|
||||||
for (size_t i = 0; i < 3; i++)
|
wtext[end] = L'\0';
|
||||||
e->text[end + i] = '.';
|
chars = 0;
|
||||||
e->text[end + 3] = '\0';
|
}
|
||||||
} else
|
|
||||||
e->text[end] = '\0';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
e->kern_x = calloc(chars, sizeof(e->kern_x[0]));
|
||||||
|
|
||||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) {
|
||||||
|
struct fcft_text_run *run = fcft_text_run_rasterize(
|
||||||
|
font, chars, wtext, FCFT_SUBPIXEL_NONE);
|
||||||
|
|
||||||
|
if (run != NULL) {
|
||||||
|
int w = 0;
|
||||||
|
for (size_t i = 0; i < run->count; i++)
|
||||||
|
w += run->glyphs[i]->advance.x;
|
||||||
|
|
||||||
|
ssize_t cache_idx = -1;
|
||||||
|
for (size_t i = 0; i < p->cache_size; i++) {
|
||||||
|
if (p->cache[i].run == NULL || !p->cache[i].in_use) {
|
||||||
|
fcft_text_run_destroy(p->cache[i].run);
|
||||||
|
cache_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache_idx < 0) {
|
||||||
|
size_t new_size = p->cache_size + 1;
|
||||||
|
struct text_run_cache *new_cache = realloc(
|
||||||
|
p->cache, new_size * sizeof(new_cache[0]));
|
||||||
|
|
||||||
|
p->cache_size = new_size;
|
||||||
|
p->cache = new_cache;
|
||||||
|
cache_idx = new_size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(cache_idx >= 0 && cache_idx < p->cache_size);
|
||||||
|
p->cache[cache_idx].hash = hash;
|
||||||
|
p->cache[cache_idx].run = run;
|
||||||
|
p->cache[cache_idx].width = w;
|
||||||
|
p->cache[cache_idx].in_use = true;
|
||||||
|
|
||||||
|
e->cache_idx = cache_idx;
|
||||||
|
e->num_glyphs = run->count;
|
||||||
|
e->glyphs = run->glyphs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->glyphs == NULL) {
|
||||||
|
e->allocated_glyphs = malloc(chars * sizeof(e->glyphs[0]));
|
||||||
|
|
||||||
|
/* Convert text to glyph masks/images. */
|
||||||
|
for (size_t i = 0; i < chars; i++) {
|
||||||
|
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
|
||||||
|
font, wtext[i], FCFT_SUBPIXEL_NONE);
|
||||||
|
|
||||||
|
if (glyph == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
e->allocated_glyphs[e->num_glyphs++] = glyph;
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
e->glyphs = e->allocated_glyphs;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
free(wtext);
|
||||||
|
free(text);
|
||||||
|
|
||||||
|
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||||
exposable->private = e;
|
exposable->private = e;
|
||||||
exposable->destroy = &exposable_destroy;
|
exposable->destroy = &exposable_destroy;
|
||||||
exposable->begin_expose = &begin_expose;
|
exposable->begin_expose = &begin_expose;
|
||||||
exposable->expose = &expose;
|
exposable->expose = &expose;
|
||||||
|
|
||||||
free(on_click);
|
|
||||||
return exposable;
|
return exposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +283,9 @@ static void
|
||||||
particle_destroy(struct particle *particle)
|
particle_destroy(struct particle *particle)
|
||||||
{
|
{
|
||||||
struct private *p = particle->private;
|
struct private *p = particle->private;
|
||||||
|
for (size_t i = 0; i < p->cache_size; i++)
|
||||||
|
fcft_text_run_destroy(p->cache[i].run);
|
||||||
|
free(p->cache);
|
||||||
free(p->text);
|
free(p->text);
|
||||||
free(p);
|
free(p);
|
||||||
particle_default_destroy(particle);
|
particle_default_destroy(particle);
|
||||||
|
@ -199,6 +297,8 @@ string_new(struct particle *common, const char *text, size_t max_len)
|
||||||
struct private *p = calloc(1, sizeof(*p));
|
struct private *p = calloc(1, sizeof(*p));
|
||||||
p->text = strdup(text);
|
p->text = strdup(text);
|
||||||
p->max_len = max_len;
|
p->max_len = max_len;
|
||||||
|
p->cache_size = 0;
|
||||||
|
p->cache = NULL;
|
||||||
|
|
||||||
common->private = p;
|
common->private = p;
|
||||||
common->destroy = &particle_destroy;
|
common->destroy = &particle_destroy;
|
||||||
|
|
3
subprojects/fcft.wrap
Normal file
3
subprojects/fcft.wrap
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[wrap-git]
|
||||||
|
url = https://codeberg.org/dnkl/fcft.git
|
||||||
|
revision = master
|
3
subprojects/tllist.wrap
Normal file
3
subprojects/tllist.wrap
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[wrap-git]
|
||||||
|
url = https://codeberg.org/dnkl/tllist.git
|
||||||
|
revision = master
|
13
tag.c
13
tag.c
|
@ -446,8 +446,9 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lookup tag */
|
/* Lookup tag */
|
||||||
const struct tag *tag = tag_for_name(tags, tag_name);
|
const struct tag *tag = NULL;
|
||||||
if (tag == NULL) {
|
|
||||||
|
if (tag_name == NULL || (tag = tag_for_name(tags, tag_name)) == NULL) {
|
||||||
/* No such tag, copy as-is instead */
|
/* No such tag, copy as-is instead */
|
||||||
sbuf_append_at_most(&formatted, template, begin - template + 1);
|
sbuf_append_at_most(&formatted, template, begin - template + 1);
|
||||||
template = begin + 1;
|
template = begin + 1;
|
||||||
|
@ -532,3 +533,11 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
||||||
|
|
||||||
return formatted.s;
|
return formatted.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tags_expand_templates(char *expanded[], const char *template[], size_t nmemb,
|
||||||
|
const struct tag_set *tags)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < nmemb; i++)
|
||||||
|
expanded[i] = tags_expand_template(template[i], tags);
|
||||||
|
}
|
||||||
|
|
3
tag.h
3
tag.h
|
@ -50,3 +50,6 @@ void tag_set_destroy(struct tag_set *set);
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
char *tags_expand_template(const char *template, const struct tag_set *tags);
|
char *tags_expand_template(const char *template, const struct tag_set *tags);
|
||||||
|
void tags_expand_templates(
|
||||||
|
char *expanded[], const char *template[], size_t nmemb,
|
||||||
|
const struct tag_set *tags);
|
||||||
|
|
Loading…
Add table
Reference in a new issue