forked from external/yambar
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:
|
||||
- musl-dev
|
||||
- eudev-libs
|
||||
|
@ -21,20 +21,22 @@ packages:
|
|||
- alsa-lib-dev
|
||||
- ttf-dejavu
|
||||
- gcovr
|
||||
- python3
|
||||
- py3-pip
|
||||
|
||||
sources:
|
||||
- https://git.sr.ht/~dnkl/yambar
|
||||
- https://codeberg.org/dnkl/yambar
|
||||
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: daniel@ekloef.se
|
||||
# triggers:
|
||||
# - action: email
|
||||
# condition: failure
|
||||
# to: <comitter>
|
||||
|
||||
tasks:
|
||||
- install-gcovr: |
|
||||
python2 -m ensurepip --user --upgrade
|
||||
python2 -m pip install --user --upgrade pip
|
||||
python2 -m pip install --user --upgrade setuptools
|
||||
- codespell: |
|
||||
pip install codespell
|
||||
cd yambar
|
||||
~/.local/bin/codespell README.md CHANGELOG.md *.c *.h doc/*.scd
|
||||
- setup: |
|
||||
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
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
/bld/
|
||||
/pkg/
|
||||
/src/
|
||||
/subprojects/
|
||||
/subprojects/*
|
||||
!/subprojects/*.wrap
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
image: alpine:edge
|
||||
image: alpine:latest
|
||||
|
||||
stages:
|
||||
- info
|
||||
|
@ -8,7 +8,6 @@ variables:
|
|||
GIT_SUBMODULE_STRATEGY: normal
|
||||
|
||||
before_script:
|
||||
- echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
- apk update
|
||||
- apk add musl-dev eudev-libs eudev-dev linux-headers meson ninja gcc scdoc
|
||||
- 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 ttf-dejavu
|
||||
- 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:
|
||||
stage: info
|
||||
|
@ -92,3 +87,12 @@ plugins_as_shared_modules:
|
|||
- meson --buildtype=debug -Dcore-plugins-as-shared-libraries=true ../../
|
||||
- ninja -k0
|
||||
- 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
|
||||
|
||||
### 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
|
||||
|
||||
* 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
|
||||
### Removed
|
||||
### Fixed
|
||||
|
||||
* Crash when merging non-dictionary anchors in the YAML configuration
|
||||
(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
|
||||
### Contributors
|
||||
|
||||
* [novakane](https://codeberg.org/novakane)
|
||||
* [mz](https://codeberg.org/mz)
|
||||
|
||||
## 1.6.1
|
||||
|
||||
|
|
2
PKGBUILD
2
PKGBUILD
|
@ -15,7 +15,7 @@ depends=(
|
|||
'libudev.so'
|
||||
'json-c'
|
||||
'libmpdclient'
|
||||
'fcft>=2.0.0')
|
||||
'fcft>=2.4.0')
|
||||
optdepends=('xcb-util-errors: better X error messages')
|
||||
source=()
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ depends=(
|
|||
'libudev.so'
|
||||
'json-c'
|
||||
'libmpdclient'
|
||||
'fcft>=2.0.0')
|
||||
'fcft>=2.4.0')
|
||||
source=()
|
||||
|
||||
pkgver() {
|
||||
|
|
15
README.md
15
README.md
|
@ -1,5 +1,8 @@
|
|||
# Yambar
|
||||
|
||||
[](https://repology.org/project/yambar/versions)
|
||||
|
||||
|
||||
## Index
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
|
@ -87,18 +90,6 @@ Available modules:
|
|||
|
||||
## 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:
|
||||
```sh
|
||||
mkdir -p bld/release && cd bld/release
|
||||
|
|
|
@ -10,7 +10,7 @@ struct backend {
|
|||
void (*loop)(struct bar *bar,
|
||||
void (*expose)(const struct bar *bar),
|
||||
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 (*refresh)(const struct bar *bar);
|
||||
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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <threads.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
|
@ -149,7 +151,8 @@ set_cursor(struct bar *bar, const char *cursor)
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -171,7 +174,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
|||
mx += bar->left_spacing;
|
||||
if (x >= mx && x < mx + e->width) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -185,7 +188,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
|||
mx += bar->left_spacing;
|
||||
if (x >= mx && x < mx + e->width) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -203,7 +206,7 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
|||
mx += bar->left_spacing;
|
||||
if (x >= mx && x < mx + e->width) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -213,6 +216,20 @@ on_mouse(struct bar *_bar, enum mouse_event event, int x, int y)
|
|||
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
|
||||
run(struct bar *_bar)
|
||||
|
@ -240,18 +257,21 @@ run(struct bar *_bar)
|
|||
|
||||
mod->abort_fd = _bar->abort_fd;
|
||||
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++) {
|
||||
struct module *mod = bar->center.mods[i];
|
||||
|
||||
mod->abort_fd = _bar->abort_fd;
|
||||
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++) {
|
||||
struct module *mod = bar->right.mods[i];
|
||||
|
||||
mod->abort_fd = _bar->abort_fd;
|
||||
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");
|
||||
|
@ -388,6 +408,7 @@ bar_new(const struct bar_config *config)
|
|||
priv->right_spacing = config->right_spacing;
|
||||
priv->left_margin = config->left_margin;
|
||||
priv->right_margin = config->right_margin;
|
||||
priv->trackpad_sensitivity = config->trackpad_sensitivity;
|
||||
priv->border.width = config->border.width;
|
||||
priv->border.color = config->border.color;
|
||||
priv->border.left_margin = config->border.left_margin;
|
||||
|
|
|
@ -25,6 +25,7 @@ struct bar_config {
|
|||
int height;
|
||||
int left_spacing, right_spacing;
|
||||
int left_margin, right_margin;
|
||||
int trackpad_sensitivity;
|
||||
|
||||
pixman_color_t background;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ struct private {
|
|||
int height;
|
||||
int left_spacing, right_spacing;
|
||||
int left_margin, right_margin;
|
||||
int trackpad_sensitivity;
|
||||
|
||||
pixman_color_t background;
|
||||
|
||||
|
|
100
bar/wayland.c
100
bar/wayland.c
|
@ -6,9 +6,11 @@
|
|||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#include <pixman.h>
|
||||
#include <wayland-client.h>
|
||||
|
@ -111,8 +113,12 @@ struct wayland_backend {
|
|||
struct buffer *next_buffer; /* Bar is rendering to this one */
|
||||
struct buffer *pending_buffer; /* Finished, but not yet rendered */
|
||||
|
||||
double aggregated_scroll;
|
||||
bool have_discrete;
|
||||
|
||||
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
|
||||
|
@ -245,6 +251,8 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
|||
struct seat *seat = data;
|
||||
struct wayland_backend *backend = seat->backend;
|
||||
|
||||
backend->have_discrete = false;
|
||||
|
||||
if (backend->active_seat == seat)
|
||||
backend->active_seat = NULL;
|
||||
}
|
||||
|
@ -261,33 +269,81 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
backend->active_seat = seat;
|
||||
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
|
||||
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||
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 wayland_backend *backend = seat->backend;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
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_CLICK, seat->pointer.x, seat->pointer.y);
|
||||
backend->bar, ON_MOUSE_CLICK, btn, seat->pointer.x, seat->pointer.y);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
||||
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
|
||||
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
|
||||
|
@ -300,12 +356,36 @@ static void
|
|||
wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||
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
|
||||
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
||||
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 = {
|
||||
|
@ -465,6 +545,7 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
|
|||
const char *name)
|
||||
{
|
||||
struct monitor *mon = data;
|
||||
free(mon->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) {
|
||||
const uint32_t required = 3;
|
||||
const uint32_t required = 5;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
|
@ -1009,11 +1090,14 @@ cleanup(struct bar *_bar)
|
|||
static void
|
||||
loop(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 wayland_backend *backend = bar->backend.data;
|
||||
|
||||
pthread_setname_np(pthread_self(), "bar(wayland)");
|
||||
|
||||
backend->bar_expose = expose;
|
||||
backend->bar_on_mouse = on_mouse;
|
||||
|
||||
|
|
16
bar/xcb.c
16
bar/xcb.c
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <pixman.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
@ -311,11 +312,14 @@ cleanup(struct bar *_bar)
|
|||
static void
|
||||
loop(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 xcb_backend *backend = bar->backend.data;
|
||||
|
||||
pthread_setname_np(pthread_self(), "bar(xcb)");
|
||||
|
||||
const int fd = xcb_get_file_descriptor(backend->conn);
|
||||
|
||||
while (true) {
|
||||
|
@ -354,7 +358,7 @@ loop(struct bar *_bar,
|
|||
|
||||
case XCB_MOTION_NOTIFY: {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -363,7 +367,13 @@ loop(struct bar *_bar,
|
|||
|
||||
case XCB_BUTTON_RELEASE: {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,26 @@ conf_verify_dict(keychain_t *chain, const struct yml_node *node,
|
|||
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
|
||||
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},
|
||||
{"right", false, &verify_module_list},
|
||||
|
||||
{"trackpad-sensitivity", false, &conf_verify_int},
|
||||
|
||||
{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,
|
||||
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_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) :
|
||||
right_margin != NULL ? yml_value_as_int(right_margin) : 0;
|
||||
|
||||
const char *on_click_template
|
||||
= on_click != NULL ? yml_value_as_string(on_click) : NULL;
|
||||
const char *on_click_templates[MOUSE_BTN_COUNT] = {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;
|
||||
|
||||
/*
|
||||
|
@ -159,7 +188,7 @@ conf_to_particle(const struct yml_node *node, struct conf_inherit inherited)
|
|||
|
||||
/* Instantiate base/common particle */
|
||||
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);
|
||||
|
||||
|
@ -223,6 +252,12 @@ conf_to_bar(const struct yml_node *bar, enum bar_backend backend)
|
|||
if (right_margin != NULL)
|
||||
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");
|
||||
if (border != NULL) {
|
||||
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)
|
||||
|
||||
foreach man_src : ['yambar.1.scd', 'yambar.5.scd', 'yambar-decorations.5.scd',
|
||||
'yambar-modules.5.scd', 'yambar-particles.5.scd',
|
||||
'yambar-tags.5.scd']
|
||||
'yambar-modules-alsa.5.scd', 'yambar-modules-backlight.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('.')
|
||||
name = parts[-3]
|
||||
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
|
||||
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*
|
||||
:[ *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-backlight*(5)
|
||||
|
||||
*yambar-modules-battery*(5)
|
||||
|
||||
## CONFIGURATION
|
||||
*yambar-modules-clock*(5)
|
||||
|
||||
[[ *Name*
|
||||
:[ *Type*
|
||||
:[ *Req*
|
||||
:[ *Description*
|
||||
| card
|
||||
: string
|
||||
: yes
|
||||
: The soundcard name. _Default_ might work.
|
||||
| mixer
|
||||
: string
|
||||
: yes
|
||||
: Mixer channel to monitor. _Master_ might work.
|
||||
*yambar-modules-i3*(5)
|
||||
|
||||
## EXAMPLES
|
||||
*yambar-modules-label*(5)
|
||||
|
||||
```
|
||||
bar:
|
||||
left:
|
||||
- alsa:
|
||||
card: hw:PCH
|
||||
mixer: Master
|
||||
content: {string: {text: "{volume}"}}
|
||||
```
|
||||
*yambar-modules-mpd*(5)
|
||||
|
||||
# BACKLIGHT
|
||||
*yambar-modules-network*(5)
|
||||
|
||||
This module reads monitor backlight status from
|
||||
_/sys/class/backlight_, and uses *udev* to monitor for changes.
|
||||
*yambar-modules-removables*(5)
|
||||
|
||||
## TAGS
|
||||
*yambar-modules-river*(5)
|
||||
|
||||
[[ *Name*
|
||||
:[ *Type*
|
||||
:[ *Description*
|
||||
| brightness
|
||||
: range
|
||||
: The current brightness level, in absolute value
|
||||
| percent
|
||||
: range
|
||||
: The current brightness level, in percent
|
||||
*yambar-modules-script*(5)
|
||||
|
||||
## CONFIGURATION
|
||||
*yambar-modules-sway-xkb*(5)
|
||||
|
||||
[[ *Name*
|
||||
:[ *Type*
|
||||
:[ *Req*
|
||||
:[ *Description*
|
||||
| name
|
||||
: string
|
||||
: yes
|
||||
: The backlight device's name (one of the names in */sys/class/backlight*)
|
||||
*yambar-modules-sway*(5)
|
||||
|
||||
## EXAMPLES
|
||||
*yambar-modules-xkb*(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}"}
|
||||
```
|
||||
*yambar-modules-xwindow*(5)
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*yambar-particles*(5), *yambar-tags*(5), *yambar-decorations*(5)
|
||||
|
||||
|
|
|
@ -109,6 +109,12 @@ types that are frequently used:
|
|||
: color
|
||||
: no
|
||||
: 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
|
||||
: list
|
||||
: no
|
||||
|
|
|
@ -45,6 +45,16 @@ bar:
|
|||
- urgent: &urgent
|
||||
foreground: 000000ff
|
||||
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:
|
||||
"":
|
||||
map:
|
||||
|
@ -100,11 +110,14 @@ bar:
|
|||
left-margin: 7
|
||||
tag: application
|
||||
values:
|
||||
"": {string: {text: "{title}"}}
|
||||
"":
|
||||
- map: {<<: *i3_mode}
|
||||
- string: {text: "{title}"}
|
||||
default:
|
||||
list:
|
||||
spacing: 0
|
||||
items:
|
||||
- map: {<<: *i3_mode}
|
||||
- string: {text: "{application}", max: 10, foreground: ffa0a0ff}
|
||||
- string: {text: ": "}
|
||||
- string: {text: "{title}", max: 35}
|
||||
|
@ -258,6 +271,21 @@ bar:
|
|||
full:
|
||||
- string: {text: , foreground: 00ff00ff, font: *awesome}
|
||||
- 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:
|
||||
time-format: "%H:%M %Z"
|
||||
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.
|
||||
</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">
|
||||
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
|
||||
|
@ -47,6 +47,12 @@
|
|||
or manipulate a buffer prior to the first layer_surface.configure call
|
||||
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
|
||||
output to use. Generally this will be the one that the user most
|
||||
recently interacted with.
|
||||
|
@ -94,7 +100,7 @@
|
|||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_layer_surface_v1" version="3">
|
||||
<interface name="zwlr_layer_surface_v1" version="4">
|
||||
<description summary="layer metadata interface">
|
||||
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
|
||||
|
@ -103,6 +109,14 @@
|
|||
Layer surface state (layer, size, anchor, exclusive zone,
|
||||
margin, interactivity) is double-buffered, and will be applied at the
|
||||
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>
|
||||
|
||||
<request name="set_size">
|
||||
|
@ -189,21 +203,85 @@
|
|||
<arg name="left" type="int"/>
|
||||
</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">
|
||||
<description summary="requests keyboard events">
|
||||
Set to 1 to request that the seat send keyboard events to this layer
|
||||
surface. For layers below the shell surface layer, the seat will use
|
||||
normal focus semantics. For layers above the shell surface layers, the
|
||||
seat will always give exclusive keyboard focus to the top-most layer
|
||||
which has keyboard interactivity set to true.
|
||||
Set how keyboard events are delivered to this surface. By default,
|
||||
layer shell surfaces do not receive keyboard events; this request can
|
||||
be used to change this.
|
||||
|
||||
This setting is inherited by child surfaces set by the get_popup
|
||||
request.
|
||||
|
||||
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||
you do not want to receive them, set the input region on your surface
|
||||
to an empty region.
|
||||
|
||||
Events is double-buffered, see wl_surface.commit.
|
||||
Keyboard interactivity is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="keyboard_interactivity" type="uint"/>
|
||||
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
|
||||
</request>
|
||||
|
||||
<request name="get_popup">
|
||||
|
@ -288,6 +366,7 @@
|
|||
<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_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
|
||||
</enum>
|
||||
|
||||
<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};
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
|
|
|
@ -65,7 +65,7 @@ backend_wayland = wayland_client.found() and wayland_cursor.found()
|
|||
|
||||
# "My" dependencies, fallback to subproject
|
||||
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(
|
||||
['-D_GNU_SOURCE'] +
|
||||
|
@ -100,7 +100,7 @@ version = custom_target(
|
|||
'generate_version',
|
||||
build_always_stale: true,
|
||||
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',
|
||||
|
|
2
module.h
2
module.h
|
@ -26,6 +26,8 @@ struct module {
|
|||
/* refresh_in() should schedule a module content refresh after the
|
||||
* specified number of milliseconds */
|
||||
bool (*refresh_in)(struct module *mod, long milli_seconds);
|
||||
|
||||
const char *(*description)(struct module *mod);
|
||||
};
|
||||
|
||||
struct module *module_common_new(void);
|
||||
|
|
|
@ -39,6 +39,15 @@ destroy(struct module *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 *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -287,6 +296,7 @@ alsa_new(const char *card, const char *mixer, struct particle *label)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "backlight";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -216,6 +222,7 @@ backlight_new(const char *device, struct particle *label)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "../config-verify.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 particle *label;
|
||||
|
@ -57,6 +57,15 @@ destroy(struct module *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 *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -65,6 +74,7 @@ content(struct module *mod)
|
|||
mtx_lock(&mod->lock);
|
||||
|
||||
assert(m->state == STATE_FULL ||
|
||||
m->state == STATE_NOTCHARGING ||
|
||||
m->state == STATE_CHARGING ||
|
||||
m->state == STATE_DISCHARGING);
|
||||
|
||||
|
@ -79,7 +89,7 @@ content(struct module *mod)
|
|||
? m->energy_full - m->energy : m->energy;
|
||||
|
||||
double hours_as_float;
|
||||
if (m->state == STATE_FULL)
|
||||
if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING)
|
||||
hours_as_float = 0.0;
|
||||
else if (m->power > 0)
|
||||
hours_as_float = (double)energy / m->power;
|
||||
|
@ -93,7 +103,7 @@ content(struct module *mod)
|
|||
? m->charge_full - m->charge : m->charge;
|
||||
|
||||
double hours_as_float;
|
||||
if (m->state == STATE_FULL)
|
||||
if (m->state == STATE_FULL || m->state == STATE_NOTCHARGING)
|
||||
hours_as_float = 0.0;
|
||||
else if (m->current > 0)
|
||||
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, "state",
|
||||
m->state == STATE_FULL ? "full" :
|
||||
m->state == STATE_NOTCHARGING ? "not charging" :
|
||||
m->state == STATE_CHARGING ? "charging" :
|
||||
m->state == STATE_DISCHARGING ? "discharging" :
|
||||
"unknown"),
|
||||
|
@ -349,12 +360,12 @@ update_status(struct module *mod)
|
|||
state = STATE_DISCHARGING;
|
||||
} else if (strcmp(status, "Full") == 0)
|
||||
state = STATE_FULL;
|
||||
else if (strcmp(status, "Not charging") == 0)
|
||||
state = STATE_NOTCHARGING;
|
||||
else if (strcmp(status, "Charging") == 0)
|
||||
state = STATE_CHARGING;
|
||||
else if (strcmp(status, "Discharging") == 0)
|
||||
state = STATE_DISCHARGING;
|
||||
else if (strcmp(status, "Not charging") == 0)
|
||||
state = STATE_DISCHARGING;
|
||||
else if (strcmp(status, "Unknown") == 0)
|
||||
state = STATE_DISCHARGING;
|
||||
else {
|
||||
|
@ -434,8 +445,7 @@ run(struct module *mod)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!update_status(mod))
|
||||
break;
|
||||
if (update_status(mod))
|
||||
bar->refresh(bar);
|
||||
}
|
||||
|
||||
|
@ -461,6 +471,7 @@ battery_new(const char *battery, struct particle *label, int poll_interval_secs)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "clock";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -60,6 +66,7 @@ content(struct module *mod)
|
|||
return exposable;
|
||||
}
|
||||
|
||||
#include <pthread.h>
|
||||
static int
|
||||
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->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -608,6 +608,12 @@ ws_content_for_name(struct private *m, const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "i3/sway";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -710,6 +716,7 @@ i3_new(struct i3_workspaces workspaces[], size_t workspace_count,
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "label";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -45,6 +51,7 @@ label_new(struct particle *label)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "mpd";
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
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->content = &content;
|
||||
mod->refresh_in = &refresh_in;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,16 @@ destroy(struct module *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 *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -526,6 +536,7 @@ network_new(const char *iface, struct particle *label)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "removables";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -596,6 +602,7 @@ removables_new(struct particle *label, int left_spacing, int right_spacing,
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <tllist.h>
|
||||
|
||||
#define LOG_MODULE "river"
|
||||
#define LOG_ENABLE_DBG 1
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "../log.h"
|
||||
#include "../plugin.h"
|
||||
#include "../particles/dynlist.h"
|
||||
|
@ -65,6 +65,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "river";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -184,14 +190,15 @@ focused_tags(void *data, struct zriver_output_status_v1 *zriver_output_status_v1
|
|||
uint32_t tags)
|
||||
{
|
||||
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);
|
||||
|
||||
struct module *mod = output->m->mod;
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
output->focused = tags;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
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 module *mod = m->mod;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
{
|
||||
struct output *output = NULL;
|
||||
tll_foreach(m->outputs, it) {
|
||||
if (it->item.wl_output == wl_output) {
|
||||
|
@ -325,10 +330,12 @@ focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
|||
if (output == NULL)
|
||||
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;
|
||||
}
|
||||
mtx_unlock(&mod->lock);
|
||||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -367,6 +374,12 @@ focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1,
|
|||
struct seat *seat = data;
|
||||
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);
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
|
@ -645,6 +658,7 @@ river_new(struct particle *template, struct particle *title)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
m->mod = mod;
|
||||
return mod;
|
||||
}
|
||||
|
|
108
modules/script.c
108
modules/script.c
|
@ -25,6 +25,8 @@ struct private {
|
|||
char *path;
|
||||
size_t argc;
|
||||
char **argv;
|
||||
int poll_interval;
|
||||
bool aborted;
|
||||
|
||||
struct particle *content;
|
||||
|
||||
|
@ -56,6 +58,19 @@ destroy(struct module *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 *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -180,7 +195,7 @@ process_line(struct module *mod, const char *line, size_t len)
|
|||
|
||||
long end = 0;
|
||||
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(
|
||||
"tag range end is not an integer: %.*s",
|
||||
(int)end_len, _end);
|
||||
|
@ -318,7 +333,7 @@ data_received(struct module *mod, const char *data, size_t len)
|
|||
static int
|
||||
run_loop(struct module *mod, pid_t pid, int comm_fd)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret = 1;
|
||||
|
||||
while (true) {
|
||||
struct pollfd fds[] = {
|
||||
|
@ -347,19 +362,18 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
|
|||
data_received(mod, data, amount);
|
||||
}
|
||||
|
||||
if (fds[0].revents & POLLHUP) {
|
||||
if (fds[0].revents & (POLLHUP | POLLIN)) {
|
||||
/* Aborted */
|
||||
struct private *m = mod->private;
|
||||
m->aborted = true;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fds[1].revents & POLLHUP) {
|
||||
/* Child's stdout closed */
|
||||
LOG_DBG("script pipe closed (script terminated?)");
|
||||
break;
|
||||
}
|
||||
|
||||
if (fds[0].revents & POLLIN) {
|
||||
/* Aborted */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +382,7 @@ run_loop(struct module *mod, pid_t pid, int comm_fd)
|
|||
}
|
||||
|
||||
static int
|
||||
run(struct module *mod)
|
||||
execute_script(struct module *mod)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
|
@ -552,9 +566,75 @@ run(struct module *mod)
|
|||
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 *
|
||||
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));
|
||||
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]));
|
||||
for (size_t i = 0; i < argc; i++)
|
||||
m->argv[i] = strdup(argv[i]);
|
||||
m->poll_interval = poll_interval;
|
||||
|
||||
struct module *mod = module_common_new();
|
||||
mod->private = m;
|
||||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
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 *args = yml_get_value(node, "args");
|
||||
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;
|
||||
const char *argv[argc];
|
||||
|
@ -593,7 +676,9 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -623,6 +708,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node)
|
|||
static const struct attr_info attrs[] = {
|
||||
{"path", true, &conf_verify_path},
|
||||
{"args", false, &conf_verify_args},
|
||||
{"poll-interval", false, &conf_verify_int},
|
||||
MODULE_COMMON_ATTRS,
|
||||
};
|
||||
|
||||
|
|
|
@ -52,6 +52,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "sway-xkb";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -99,6 +105,15 @@ handle_input_reply(int type, const struct json_object *json, void *_mod)
|
|||
return false;
|
||||
|
||||
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;
|
||||
for (size_t i = 0; i < m->num_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;
|
||||
|
||||
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;
|
||||
for (size_t i = 0; i < m->num_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->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,12 @@ destroy(struct module *mod)
|
|||
module_default_destroy(mod);
|
||||
}
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "xkb";
|
||||
}
|
||||
|
||||
static struct exposable *
|
||||
content(struct module *mod)
|
||||
{
|
||||
|
@ -650,6 +656,7 @@ xkb_new(struct particle *label)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,12 @@ struct private {
|
|||
xcb_window_t active_win;
|
||||
};
|
||||
|
||||
static const char *
|
||||
description(struct module *mod)
|
||||
{
|
||||
return "xwindow";
|
||||
}
|
||||
|
||||
static void
|
||||
update_active_window(struct private *m)
|
||||
{
|
||||
|
@ -332,6 +338,7 @@ xwindow_new(struct particle *label)
|
|||
mod->run = &run;
|
||||
mod->destroy = &destroy;
|
||||
mod->content = &content;
|
||||
mod->description = &description;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
74
particle.c
74
particle.c
|
@ -22,31 +22,41 @@ particle_default_destroy(struct particle *particle)
|
|||
if (particle->deco != NULL)
|
||||
particle->deco->destroy(particle->deco);
|
||||
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);
|
||||
}
|
||||
|
||||
struct particle *
|
||||
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 deco *deco)
|
||||
{
|
||||
struct particle *p = calloc(1, sizeof(*p));
|
||||
p->left_margin = left_margin;
|
||||
p->right_margin = right_margin;
|
||||
p->on_click_template =
|
||||
on_click_template != NULL ? strdup(on_click_template) : NULL;
|
||||
p->foreground = foreground;
|
||||
p->font = font;
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -141,20 +151,37 @@ err:
|
|||
|
||||
void
|
||||
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)",
|
||||
exposable, event == ON_MOUSE_MOTION ? "motion" : "click", x, y,
|
||||
exposable->on_click);
|
||||
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
||||
static const char *button_name[] = {
|
||||
[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 */
|
||||
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 (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 */
|
||||
char *cmd = strdup(exposable->on_click);
|
||||
LOG_DBG("cmd = \"%s\"", exposable->on_click);
|
||||
char *cmd = strdup(exposable->on_click[btn]);
|
||||
LOG_DBG("cmd = \"%s\"", exposable->on_click[btn]);
|
||||
|
||||
char **argv;
|
||||
if (!tokenize_cmdline(cmd, &argv)) {
|
||||
|
@ -172,15 +199,15 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
|
||||
int wstatus;
|
||||
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 (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
|
||||
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 {
|
||||
/*
|
||||
* 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) */
|
||||
for (int i = STDERR_FILENO + 1; i < 65536; i++)
|
||||
if (i != pipe_fds[1])
|
||||
close(i);
|
||||
|
||||
execvp(argv[0], argv);
|
||||
|
@ -267,6 +295,8 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
|
||||
int _errno = 0;
|
||||
ssize_t ret = read(pipe_fds[0], &_errno, sizeof(_errno));
|
||||
close(pipe_fds[0]);
|
||||
|
||||
if (ret == 0) {
|
||||
/* Pipe was closed - child succeeded with exec() */
|
||||
_exit(0);
|
||||
|
@ -281,11 +311,17 @@ exposable_default_on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
}
|
||||
|
||||
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));
|
||||
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->on_mouse = &exposable_default_on_mouse;
|
||||
return exposable;
|
||||
|
|
36
particle.h
36
particle.h
|
@ -7,13 +7,31 @@
|
|||
#include "decoration.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 particle {
|
||||
void *private;
|
||||
|
||||
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;
|
||||
struct fcft_font *font;
|
||||
|
@ -24,17 +42,13 @@ struct particle {
|
|||
const struct tag_set *tags);
|
||||
};
|
||||
|
||||
enum mouse_event {
|
||||
ON_MOUSE_MOTION,
|
||||
ON_MOUSE_CLICK,
|
||||
};
|
||||
|
||||
struct exposable {
|
||||
const struct particle *particle;
|
||||
void *private;
|
||||
|
||||
int width; /* Should be set by begin_expose(), at latest */
|
||||
char *on_click;
|
||||
char *on_click[MOUSE_BTN_COUNT];
|
||||
|
||||
void (*destroy)(struct exposable *exposable);
|
||||
int (*begin_expose)(struct exposable *exposable);
|
||||
|
@ -42,31 +56,31 @@ struct exposable {
|
|||
int x, int y, int height);
|
||||
|
||||
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(
|
||||
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);
|
||||
|
||||
void particle_default_destroy(struct particle *particle);
|
||||
|
||||
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_render_deco(
|
||||
const struct exposable *exposable, pixman_image_t *pix, int x, int y, int height);
|
||||
|
||||
void 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);
|
||||
|
||||
/* List of attributes *all* particles implement */
|
||||
#define PARTICLE_COMMON_ATTRS \
|
||||
{"margin", false, &conf_verify_int}, \
|
||||
{"left-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}, \
|
||||
{"foreground", false, &conf_verify_color}, \
|
||||
{"deco", false, &conf_verify_decoration}, \
|
||||
|
|
|
@ -67,13 +67,12 @@ dynlist_expose(const struct exposable *exposable, pixman_image_t *pix, int x, in
|
|||
|
||||
static 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)
|
||||
{
|
||||
//const struct particle *p = exposable->particle;
|
||||
const struct private *e = exposable->private;
|
||||
|
||||
if (exposable->on_click != NULL) {
|
||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
||||
if (exposable->on_click[btn] != NULL) {
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -82,7 +81,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
if (x >= px && x < px + e->exposables[i]->width) {
|
||||
if (e->exposables[i]->on_mouse != NULL) {
|
||||
e->exposables[i]->on_mouse(
|
||||
e->exposables[i], bar, event, x - px, y);
|
||||
e->exposables[i], bar, event, btn, x - px, y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -91,7 +90,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
}
|
||||
|
||||
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 *
|
||||
|
|
|
@ -22,13 +22,9 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
|||
static struct exposable *
|
||||
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, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,14 +75,17 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
|||
|
||||
static 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)
|
||||
{
|
||||
const struct particle *p = exposable->particle;
|
||||
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 */
|
||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +94,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
if (x >= px && x < px + e->exposables[i]->width) {
|
||||
if (e->exposables[i]->on_mouse != NULL) {
|
||||
e->exposables[i]->on_mouse(
|
||||
e->exposables[i], bar, event, x - px, y);
|
||||
e->exposables[i], bar, event, btn, x - px, y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -100,7 +103,7 @@ on_mouse(struct exposable *exposable, struct bar *bar,
|
|||
}
|
||||
|
||||
/* 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 *
|
||||
|
@ -121,16 +124,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
assert(e->exposables[i] != NULL);
|
||||
}
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
exposable->on_mouse = &on_mouse;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,26 +61,29 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
|||
|
||||
static void
|
||||
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 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 */
|
||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
int px = p->left_margin;
|
||||
if (x >= px && x < px + e->exposable->width) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 *
|
||||
|
@ -119,15 +122,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
|
||||
assert(e->exposable != NULL);
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
exposable->on_mouse = &on_mouse;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,10 +85,13 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
|||
|
||||
static void
|
||||
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) {
|
||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
||||
if ((event == ON_MOUSE_MOTION &&
|
||||
exposable->particle->have_on_click_template) ||
|
||||
exposable->on_click[btn] != NULL)
|
||||
{
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -120,7 +123,7 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
/* Mouse is over the start-marker */
|
||||
struct exposable *start = e->exposables[0];
|
||||
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 {
|
||||
/* Mouse if over left margin */
|
||||
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 */
|
||||
struct exposable *end = e->exposables[e->count - 1];
|
||||
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 {
|
||||
/* Mouse is over the right margin */
|
||||
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 */
|
||||
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) {
|
||||
long where = clickable_width > 0
|
||||
|
@ -160,17 +165,21 @@ on_mouse(struct exposable *exposable, struct bar *bar, enum mouse_event event,
|
|||
.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);
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
/* Reset handler string */
|
||||
free(exposable->on_click);
|
||||
exposable->on_click = original;
|
||||
for (size_t i = 0; i < MOUSE_BTN_COUNT; i++) {
|
||||
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++)
|
||||
assert(epriv->exposables[i] != NULL);
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
free(on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
|
||||
exposable->private = epriv;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOG_MODULE "ramp"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "../log.h"
|
||||
#include "../config.h"
|
||||
#include "../config-verify.h"
|
||||
#include "../particle.h"
|
||||
|
@ -54,26 +57,29 @@ expose(const struct exposable *exposable, pixman_image_t *pix, int x, int y, int
|
|||
|
||||
static void
|
||||
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 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 */
|
||||
exposable_default_on_mouse(exposable, bar, event, x, y);
|
||||
exposable_default_on_mouse(exposable, bar, event, btn, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
int px = p->left_margin;
|
||||
if (x >= px && x < px + e->exposable->width) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
@ -102,6 +108,26 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
long min = tag != NULL ? tag->min(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(max >= min);
|
||||
|
||||
|
@ -123,15 +149,12 @@ instantiate(const struct particle *particle, const struct tag_set *tags)
|
|||
e->exposable = pp->instantiate(pp, tags);
|
||||
assert(e->exposable != NULL);
|
||||
|
||||
char *on_click = tags_expand_template(particle->on_click_template, tags);
|
||||
struct exposable *exposable = exposable_common_new(particle, on_click);
|
||||
struct exposable *exposable = exposable_common_new(particle, tags);
|
||||
exposable->private = e;
|
||||
exposable->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
exposable->on_mouse = &on_mouse;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,16 +10,25 @@
|
|||
#include "../particle.h"
|
||||
#include "../plugin.h"
|
||||
|
||||
struct text_run_cache {
|
||||
uint64_t hash;
|
||||
struct fcft_text_run *run;
|
||||
int width;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
struct private {
|
||||
char *text;
|
||||
size_t max_len;
|
||||
|
||||
size_t cache_size;
|
||||
struct text_run_cache *cache;
|
||||
};
|
||||
|
||||
struct eprivate {
|
||||
/* Set when instantiating */
|
||||
char *text;
|
||||
|
||||
ssize_t cache_idx;
|
||||
const struct fcft_glyph **glyphs;
|
||||
const struct fcft_glyph **allocated_glyphs;
|
||||
long *kern_x;
|
||||
int num_glyphs;
|
||||
};
|
||||
|
@ -29,8 +38,7 @@ exposable_destroy(struct exposable *exposable)
|
|||
{
|
||||
struct eprivate *e = exposable->private;
|
||||
|
||||
free(e->text);
|
||||
free(e->glyphs);
|
||||
free(e->allocated_glyphs);
|
||||
free(e->kern_x);
|
||||
free(e);
|
||||
exposable_default_destroy(exposable);
|
||||
|
@ -40,42 +48,19 @@ static int
|
|||
begin_expose(struct exposable *exposable)
|
||||
{
|
||||
struct eprivate *e = exposable->private;
|
||||
struct fcft_font *font = exposable->particle->font;
|
||||
struct private *p = exposable->particle->private;
|
||||
|
||||
e->glyphs = NULL;
|
||||
e->num_glyphs = 0;
|
||||
|
||||
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->width =
|
||||
exposable->particle->left_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 */
|
||||
for (int i = 0; i < e->num_glyphs; i++)
|
||||
exposable->width += e->kern_x[i] + e->glyphs[i]->advance.x;
|
||||
}
|
||||
|
||||
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 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)
|
||||
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 *
|
||||
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 fcft_font *font = particle->font;
|
||||
|
||||
e->text = tags_expand_template(p->text, tags);
|
||||
e->glyphs = NULL;
|
||||
wchar_t *wtext = NULL;
|
||||
char *text = tags_expand_template(p->text, tags);
|
||||
|
||||
e->glyphs = e->allocated_glyphs = NULL;
|
||||
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) {
|
||||
const size_t len = strlen(e->text);
|
||||
const size_t len = wcslen(wtext);
|
||||
if (len > p->max_len) {
|
||||
|
||||
size_t end = p->max_len;
|
||||
if (end >= 3) {
|
||||
if (end >= 1) {
|
||||
/* "allocate" room for three dots at the end */
|
||||
end -= 3;
|
||||
end -= 1;
|
||||
}
|
||||
|
||||
/* Mucho importante - don't cut in the middle of a utf8 multibyte */
|
||||
while (end > 0 && e->text[end - 1] >> 7)
|
||||
end--;
|
||||
|
||||
if (p->max_len > 3) {
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
e->text[end + i] = '.';
|
||||
e->text[end + 3] = '\0';
|
||||
} else
|
||||
e->text[end] = '\0';
|
||||
if (p->max_len > 1) {
|
||||
wtext[end] = L'…';
|
||||
wtext[end + 1] = L'\0';
|
||||
chars = end + 1;
|
||||
} else {
|
||||
wtext[end] = L'\0';
|
||||
chars = 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->destroy = &exposable_destroy;
|
||||
exposable->begin_expose = &begin_expose;
|
||||
exposable->expose = &expose;
|
||||
|
||||
free(on_click);
|
||||
return exposable;
|
||||
}
|
||||
|
||||
|
@ -188,6 +283,9 @@ static void
|
|||
particle_destroy(struct particle *particle)
|
||||
{
|
||||
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);
|
||||
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));
|
||||
p->text = strdup(text);
|
||||
p->max_len = max_len;
|
||||
p->cache_size = 0;
|
||||
p->cache = NULL;
|
||||
|
||||
common->private = p;
|
||||
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 */
|
||||
const struct tag *tag = tag_for_name(tags, tag_name);
|
||||
if (tag == NULL) {
|
||||
const struct tag *tag = NULL;
|
||||
|
||||
if (tag_name == NULL || (tag = tag_for_name(tags, tag_name)) == NULL) {
|
||||
/* No such tag, copy as-is instead */
|
||||
sbuf_append_at_most(&formatted, template, begin - template + 1);
|
||||
template = begin + 1;
|
||||
|
@ -532,3 +533,11 @@ tags_expand_template(const char *template, const struct tag_set *tags)
|
|||
|
||||
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 */
|
||||
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