1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
/* An example slosh shader plugin.
*
* Two effects, chosen to show the whole of what a shader is given: where the
* cell is, how big the pane is, where the cursor is, what time it is, and the
* three parameters the config can set (`color`, `amount`, and one number).
*
* Build it with `make`, drop the .so in ~/.config/slosh/shaders/, and name
* it in your config exactly like a built-in:
*
* shaders {
* checker amount=40 band=4
* pulse amount=60 color="#ff5fd7"
* }
*
* The rules a shader must keep (they are the reason shaders can be this
* simple):
*
* - write c->fg, c->bg and c->attrs; never c->text, c->len or c->width
* - look at one cell only: no neighbours, no history, no other panes
* - return quickly and always. This runs per cell, per pass, per frame:
* tens of thousands of calls at 120Hz. It must not allocate, block, do
* I/O, or loop for a length it does not control.
*
* A shader is native code in the multiplexer's own process. It can do
* anything the session can. Only install ones you trust.
*/
#include "shader_abi.h"
/* The mix every colour effect needs: `amount` of `to`, 0..255. */
static uint8_t mix8(uint8_t from, uint8_t to, uint8_t amount) {
return (uint8_t)(((int)from * (255 - amount) + (int)to * amount) / 255);
}
static color_t mix(color_t from, color_t to, uint8_t amount) {
return (color_t){true, mix8(from.r, to.r, amount), mix8(from.g, to.g, amount),
mix8(from.b, to.b, amount)};
}
/* A checkerboard, `param` cells to a square (`band=` in the config, which is
* one of the names that number answers to). Positional only: it looks at
* ctx->x and ctx->y and nothing else, which is the cheapest kind of effect
* and the kind most worth writing. */
static void sh_checker(const shader_t *sh, const shade_ctx_t *ctx, cell_t *c) {
uint16_t n = sh->param ? sh->param : 4;
/* Cells are about twice as tall as they are wide, so a square in cells is
* half as many rows as columns. Ignore this and your checkerboard is a
* pattern of letterboxes. */
bool dark = (((ctx->x / n) + (ctx->y / (n / 2 ? n / 2 : 1))) & 1) != 0;
if (!dark) return;
const color_t black = {true, 0, 0, 0};
c->fg = mix(c->fg, black, sh->amount);
c->bg = mix(c->bg, black, sh->amount);
}
/* A slow pulse towards `color`, brightest at the cursor and fading with
* distance from it. Uses ctx->now_ms, so it repaints continuously while it is
* on screen — animation is allowed, but it is the expensive kind of effect
* and it is worth knowing that is what you asked for. */
static void sh_pulse(const shader_t *sh, const shade_ctx_t *ctx, cell_t *c) {
if (!ctx->has_cursor) return;
/* A triangle wave over four seconds: 0 -> 255 -> 0, no floating point and
* no trig, because neither buys anything a byte channel can show. */
int phase = (int)(ctx->now_ms % 4000);
int wave = phase < 2000 ? phase * 255 / 2000 : (4000 - phase) * 255 / 2000;
int dx = (int)ctx->x - (int)ctx->cursor_x;
int dy = ((int)ctx->y - (int)ctx->cursor_y) * 2; /* cells, again */
int d2 = dx * dx + dy * dy;
int reach = 20 * 20;
if (d2 >= reach) return;
int near = (reach - d2) * 255 / reach;
uint8_t a = (uint8_t)((int)sh->amount * wave / 255 * near / 255);
c->fg = mix(c->fg, sh->color, a);
}
static const shader_def_t SHADERS[] = {
{"checker", sh_checker},
{"pulse", sh_pulse},
};
SLOSH_SHADER_PLUGIN("example", SHADERS)
|