diff options
| author | user <user@clank> | 2026-08-17 14:22:38 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-17 14:22:38 +0200 |
| commit | ec91ccffe631ae34b111bf6161fd0e3c487f684c (patch) | |
| tree | 4a9fdbb720d9bf03edd5f538d1d87afd2bf51bfb /src | |
| parent | sl0ppty: clang-format, and a hook that runs it on what you staged (diff) | |
| download | slosh-ec91ccffe631ae34b111bf6161fd0e3c487f684c.tar.gz slosh-ec91ccffe631ae34b111bf6161fd0e3c487f684c.tar.xz slosh-ec91ccffe631ae34b111bf6161fd0e3c487f684c.zip | |
sl0ppty: clang-format the tree
The bumpy one. `make fmt` over every `.c` and `.h` outside `vendor/`: 32 files,
1,843 lines rewritten, no behaviour changed. Nothing here is worth reading -- the
next commit adds it to `.git-blame-ignore-revs` so `git blame` walks past it.
Two thirds of it is alignment the formatter now owns: trailing comments in a
struct, continuation lines under a signature. The rest is the hand-packed tables
opening up to one entry per line -- `ACTIONS`, `NAMED_KEYS`, `ACTION_HELP`. That
is a real loss of density and a real gain in diffs: adding an action was a reflow
of the block and is now one line. Anything worth keeping packed can be fenced with
`// clang-format off`; nothing was, on the grounds that a table is the thing a
formatter is for.
**One change is not formatting.** `src/config.h` had three trailing comments glued
onto one field:
uint16_t hover_delay_ms; /* how long the pointer must rest to arm a guide */
/* how long an announcement stays up */ /* rows per wheel notch */
-- `toast_ms`'s and `scroll_lines`' comments, landed on their neighbour by an
earlier edit, documenting the wrong things ever since. It is in this commit
because it is what caught it: clang-format could not settle on that line, wrapping
it one way and then re-indenting the wrap on a second pass, so `make fmt` was not
idempotent. The comments are back where they belong and it is.
Verified after the reformat, not before: `make clean && make all` is
warning-free, `make retest` is 1801 checks green, and `make fmt` twice in a row
changes nothing the second time. A formatter that alters behaviour would be worse
than none, so the gate was the whole suite rather than a reading of the diff.
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.c | 1103 | ||||
| -rw-r--r-- | src/app.h | 21 | ||||
| -rw-r--r-- | src/client.c | 26 | ||||
| -rw-r--r-- | src/cmd.c | 53 | ||||
| -rw-r--r-- | src/config.c | 464 | ||||
| -rw-r--r-- | src/config.h | 18 | ||||
| -rw-r--r-- | src/expr.c | 370 | ||||
| -rw-r--r-- | src/graphics.c | 6 | ||||
| -rw-r--r-- | src/graphics.h | 22 | ||||
| -rw-r--r-- | src/input.c | 326 | ||||
| -rw-r--r-- | src/input.h | 26 | ||||
| -rw-r--r-- | src/json.c | 26 | ||||
| -rw-r--r-- | src/json.h | 2 | ||||
| -rw-r--r-- | src/jsonval.c | 102 | ||||
| -rw-r--r-- | src/jsonval.h | 7 | ||||
| -rw-r--r-- | src/kdl.c | 17 | ||||
| -rw-r--r-- | src/kdl.h | 3 | ||||
| -rw-r--r-- | src/main.c | 57 | ||||
| -rw-r--r-- | src/osc5577.c | 76 | ||||
| -rw-r--r-- | src/pane.c | 85 | ||||
| -rw-r--r-- | src/project.c | 6 | ||||
| -rw-r--r-- | src/proto.c | 6 | ||||
| -rw-r--r-- | src/pty.c | 5 | ||||
| -rw-r--r-- | src/screen.c | 46 | ||||
| -rw-r--r-- | src/server.c | 122 | ||||
| -rw-r--r-- | src/shader.c | 41 | ||||
| -rw-r--r-- | src/shader_abi.h | 28 | ||||
| -rw-r--r-- | src/sl0ppty.h | 4 |
28 files changed, 1752 insertions, 1316 deletions
@@ -213,10 +213,10 @@ struct node { * meet there. Recomputed every frame with the layout it describes. */ typedef struct { rect_t r; - uint32_t h_id[2]; /* row boundaries meeting here: these move vertically */ + uint32_t h_id[2]; /* row boundaries meeting here: these move vertically */ size_t h_edge[2]; size_t nh; - uint32_t v_id[2]; /* column boundaries: these move horizontally */ + uint32_t v_id[2]; /* column boundaries: these move horizontally */ size_t v_edge[2]; size_t nv; } corner_t; @@ -266,17 +266,17 @@ struct app { DRAG_BORDER, DRAG_TAB, } kind; - uint32_t src; /* pane being dragged, or the split being resized */ - uint32_t target; /* pane under the pointer, for the drop highlight */ + uint32_t src; /* pane being dragged, or the split being resized */ + uint32_t target; /* pane under the pointer, for the drop highlight */ /* ...or the tab under it, when a pane is dragged over the strip: the same * gesture with a different kind of destination. Never both -- the pointer is * over one thing, and two highlights would be two promises. */ uint32_t tab_target; bool new_tab_target; /* the strip's `+`: a tab of its own */ - size_t edge; /* which boundary of that split */ - uint16_t x, y; /* where the pointer was at the last event */ - bool moved; /* a press that never moves is a click */ - char side; /* border press: 'l' 'r' 't' 'b' */ + size_t edge; /* which boundary of that split */ + uint16_t x, y; /* where the pointer was at the last event */ + bool moved; /* a press that never moves is a click */ + char side; /* border press: 'l' 'r' 't' 'b' */ /* A corner drag carries the boundaries it is moving, not an index into * the corner list: that list is rebuilt from the layout every frame, and * the layout is the thing being changed. Halfway through a drag the @@ -324,12 +324,7 @@ struct app { * rather than two that would drift apart, for the same reason the rename * editor is. Only one can be open, which is what makes sharing the query * and the selection honest rather than a saving. */ - enum { - PICK_NONE = 0, - PICK_FINDER, - PICK_PALETTE, - PICK_WORKSPACES - } picker; + enum { PICK_NONE = 0, PICK_FINDER, PICK_PALETTE, PICK_WORKSPACES } picker; /* What the project picker is listing: scanned once when it opens, because * draw_picker asks for its rows every frame and a readdir per repaint is a * filesystem walk at 120Hz. Once per opening is when the answer has to be @@ -479,8 +474,9 @@ static void on_pane_shader(app_t *a, pane_t *p, const char *payload) { return; } if (!chrome && !content) { - snprintf(reply, sizeof reply, - "\033]5577;1;shader-reply;error;where must be content or chrome\033\\"); + snprintf( + reply, sizeof reply, + "\033]5577;1;shader-reply;error;where must be content or chrome\033\\"); pane_write(p, reply, strlen(reply)); return; } @@ -488,12 +484,12 @@ static void on_pane_shader(app_t *a, pane_t *p, const char *payload) { char err[192] = {0}; const char *text = semi ? semi + 1 : ""; size_t nchrome = 0, ncontent = 0; - bool ok = app_set_pane_shaders(a, b.found->id, chrome, text, &nchrome, &ncontent, - err, sizeof err); + bool ok = app_set_pane_shaders(a, b.found->id, chrome, text, &nchrome, + &ncontent, err, sizeof err); if (ok) snprintf(reply, sizeof reply, - "\033]5577;1;shader-reply;ok;%zu chrome, %zu content\033\\", nchrome, - ncontent); + "\033]5577;1;shader-reply;ok;%zu chrome, %zu content\033\\", + nchrome, ncontent); else snprintf(reply, sizeof reply, "\033]5577;1;shader-reply;error;%s\033\\", err[0] ? err : "refused"); @@ -514,8 +510,8 @@ static void on_pane_shader_load(app_t *a, pane_t *p, const char *payload) { err, sizeof err); if (ok) snprintf(reply, sizeof reply, - "\033]5577;1;shader-reply;ok;%zu chrome, %zu content\033\\", nchrome, - ncontent); + "\033]5577;1;shader-reply;ok;%zu chrome, %zu content\033\\", + nchrome, ncontent); else snprintf(reply, sizeof reply, "\033]5577;1;shader-reply;error;%s\033\\", err[0] ? err : "refused"); @@ -575,7 +571,8 @@ static void toasts_expire(app_t *a) { int app_next_deadline_ms(app_t *a) { int64_t soonest = -1; for (size_t i = 0; i < a->ntoasts; i++) - if (soonest < 0 || a->toasts[i].until < soonest) soonest = a->toasts[i].until; + if (soonest < 0 || a->toasts[i].until < soonest) + soonest = a->toasts[i].until; if (a->ptr_valid && a->painted) { int64_t due = a->ptr_still_since + CFG.hover_delay_ms; @@ -635,14 +632,15 @@ static const char *hint_for(app_t *a, const char *action) { if (strncmp(action, "scrollbottom:", 13) == 0) return "back to the bottom"; if (strncmp(action, "panetitle:", 10) == 0) return "double-click to rename \u00b7 drag to move"; - if (strncmp(action, "title:", 6) == 0) return "drag to move \u00b7 click to split up"; + if (strncmp(action, "title:", 6) == 0) + return "drag to move \u00b7 click to split up"; if (strncmp(action, "border:", 7) == 0) { const char *side = strrchr(action, ':'); switch (side && side[1] ? side[1] : 0) { - case 'l': return "click to split left"; - case 'r': return "click to split right"; - case 't': return "click to split up"; - default: return "click to split down"; + case 'l': return "click to split left"; + case 'r': return "click to split right"; + case 't': return "click to split up"; + default: return "click to split down"; } } if (strncmp(action, "edge:", 5) == 0) return "drag to resize"; @@ -681,7 +679,8 @@ static void draw_min_bar(app_t *a, screen_t *s) { uint16_t right = (uint16_t)(bar.x + bar.w); /* A legend, so a row of bare names is not a mystery. */ - x = (uint16_t)(x + screen_text(s, x, bar.y, CFG.min_mark, MINBAR, NO_COLOR, 0)); + x = (uint16_t)(x + + screen_text(s, x, bar.y, CFG.min_mark, MINBAR, NO_COLOR, 0)); x = (uint16_t)(x + 1); for (size_t i = 0; i < nmin && x < right; i++) { @@ -806,9 +805,11 @@ static void draw_status_line(app_t *a, screen_t *s) { const char *pt = pane_title(f->pane); const char *pp = f->purpose[0] ? f->purpose : NULL; if (pp) - n += (size_t)snprintf(line + n, sizeof line - n, "%s%s", n ? " \u00b7 " : "", pp); + n += (size_t)snprintf(line + n, sizeof line - n, "%s%s", + n ? " \u00b7 " : "", pp); else if (pt && *pt) - n += (size_t)snprintf(line + n, sizeof line - n, "%s%s", n ? " \u00b7 " : "", pt); + n += (size_t)snprintf(line + n, sizeof line - n, "%s%s", + n ? " \u00b7 " : "", pt); } if (n > (size_t)(right - x)) line[right - x] = 0; if (n) screen_text(s, x, y, line, STATUS_C, NO_COLOR, 0); @@ -898,7 +899,10 @@ static void on_pane_notify(pane_t *p, const char *title, const char *body, if (title && *title && body && *body) snprintf(msg, sizeof msg, "%s: %s", title, body); else - snprintf(msg, sizeof msg, "%s", (title && *title) ? title : body ? body : ""); + snprintf(msg, sizeof msg, "%s", + (title && *title) ? title + : body ? body + : ""); app_toast((app_t *)ud, msg); } @@ -1343,8 +1347,8 @@ static void layout_node(node_t *n, rect_t r, layout_ctx_t *ctx) { return; } - uint16_t gap = n->dir == SPLIT_COLS ? (uint16_t)(CFG.gap * CFG.gap_aspect) - : CFG.gap; + uint16_t gap = + n->dir == SPLIT_COLS ? (uint16_t)(CFG.gap * CFG.gap_aspect) : CFG.gap; uint16_t total = n->dir == SPLIT_COLS ? r.w : r.h; /* Does every child clear the floor? If not, this node collapses — a local @@ -1589,8 +1593,10 @@ static void place_beside(app_t *a, tab_t *t, node_t *leaf, node_t *node, sp->kids[before ? 1 : 0] = leaf; sp->kids[before ? 0 : 1] = node; sp->parent = leaf->parent; - if (leaf->parent) replace_child(leaf->parent, leaf, sp); - else t->root = sp; + if (leaf->parent) + replace_child(leaf->parent, leaf, sp); + else + t->root = sp; leaf->parent = sp; node->parent = sp; } @@ -1698,7 +1704,7 @@ static void split_focus_ui(app_t *a, split_dir_t dir) { static split_dir_t preferred_dir(const node_t *leaf) { uint16_t aspect = CFG.gap_aspect ? CFG.gap_aspect : 1; return (uint32_t)leaf->rect.w >= (uint32_t)leaf->rect.h * aspect ? SPLIT_COLS - : SPLIT_ROWS; + : SPLIT_ROWS; } /* Split whichever way there is room for, preferring the longer side. @@ -1714,8 +1720,10 @@ static void split_focus_auto(app_t *a) { split_dir_t want = preferred_dir(n); split_dir_t other = want == SPLIT_COLS ? SPLIT_ROWS : SPLIT_COLS; split_dir_t dir; - if (split_fits(n, want)) dir = want; - else if (split_fits(n, other)) dir = other; + if (split_fits(n, want)) + dir = want; + else if (split_fits(n, other)) + dir = other; else { app_toast(a, "no room to split"); return; @@ -1762,8 +1770,10 @@ static void close_leaf(app_t *a, node_t *leaf) { node_t *survivor = p->kids[0]; if (p->nkids == 1) { /* a split with one child is just that child */ survivor->parent = p->parent; - if (p->parent) replace_child(p->parent, p, survivor); - else t->root = survivor; + if (p->parent) + replace_child(p->parent, p, survivor); + else + t->root = survivor; free(p->kids); free(p); } @@ -1785,9 +1795,9 @@ static bool keep_corpse(const pane_t *p) { * having had one. */ if (pane_ephemeral(p)) return false; switch (CFG.keep_dead) { - case KEEP_DEAD_ALL: return true; - case KEEP_DEAD_NONE: return false; - default: return pane_label(p)[0] != 0; + case KEEP_DEAD_ALL: return true; + case KEEP_DEAD_NONE: return false; + default: return pane_label(p)[0] != 0; } } @@ -1853,8 +1863,10 @@ bool app_close_tab(app_t *a, uint32_t id) { for (size_t i = 0; i < a->ntabs; i++) { if (a->tabs[i].id != id) continue; tab_remove(a, i); - if (a->ntabs == 0) a->quit = true; - else layout(a); + if (a->ntabs == 0) + a->quit = true; + else + layout(a); return true; } return false; @@ -1936,9 +1948,12 @@ static void move_tab(app_t *a, size_t from, size_t to) { memmove(&a->tabs[to + 1], &a->tabs[to], (from - to) * sizeof *a->tabs); a->tabs[to] = moved; - if (a->cur == from) a->cur = to; - else if (a->cur > from && a->cur <= to) a->cur--; - else if (a->cur >= to && a->cur < from) a->cur++; + if (a->cur == from) + a->cur = to; + else if (a->cur > from && a->cur <= to) + a->cur--; + else if (a->cur >= to && a->cur < from) + a->cur++; } bool app_move_tab(app_t *a, uint32_t id, size_t index) { @@ -2089,8 +2104,10 @@ static bool detach_leaf(app_t *a, node_t *leaf, size_t *from, bool *emptied) { node_t *survivor = p->kids[0]; if (p->nkids == 1) { /* a split with one child is just that child */ survivor->parent = p->parent; - if (p->parent) replace_child(p->parent, p, survivor); - else t->root = survivor; + if (p->parent) + replace_child(p->parent, p, survivor); + else + t->root = survivor; free(p->kids); free(p); } @@ -2108,7 +2125,8 @@ static bool detach_leaf(app_t *a, node_t *leaf, size_t *from, bool *emptied) { * or the pane is already in that tab. The tab you are looking at does not change; * whether a caller follows the pane is a question about intent, and this is the * mechanism. */ -bool app_move_pane_to_tab(app_t *a, uint32_t pane_id, uint32_t tab_id, bool rows) { +bool app_move_pane_to_tab(app_t *a, uint32_t pane_id, uint32_t tab_id, + bool rows) { node_t *leaf = pane_id ? pane_by_id(a, pane_id) : cur(a)->focus; if (!leaf || leaf->kind != NODE_LEAF) return false; tab_t *dest = tab_by_id(a, tab_id); @@ -2147,7 +2165,8 @@ bool app_move_pane_to_tab(app_t *a, uint32_t pane_id, uint32_t tab_id, bool rows * Worth having as its own call rather than "move to a tab you make first": a pane * that is the only thing in its tab would otherwise have its tab removed from under * the new one, and the order in which those two happen is exactly the bug. */ -uint32_t app_move_pane_to_new_tab(app_t *a, uint32_t pane_id, const char *name) { +uint32_t app_move_pane_to_new_tab(app_t *a, uint32_t pane_id, + const char *name) { node_t *leaf = pane_id ? pane_by_id(a, pane_id) : cur(a)->focus; if (!leaf || leaf->kind != NODE_LEAF) return 0; size_t src_ti = tab_of(a, leaf); @@ -2226,7 +2245,8 @@ bool app_edit_config(app_t *a) { } } - const char *editor = CFG.editor && *CFG.editor ? CFG.editor : getenv("EDITOR"); + const char *editor = + CFG.editor && *CFG.editor ? CFG.editor : getenv("EDITOR"); /* vi is the one editor a POSIX system is required to have. Better a wrong * guess you can see and change than a pane that opens empty. */ if (!editor || !*editor) editor = "vi"; @@ -2250,8 +2270,8 @@ bool app_edit_config(app_t *a) { bool app_rerun_pane(app_t *a, uint32_t id) { node_t *n = id ? pane_by_id(a, id) : (a->ntabs ? cur(a)->focus : NULL); if (!n) return false; - bool ok = pane_suspended(n->pane) ? pane_start(n->pane) - : pane_restart(n->pane); + bool ok = + pane_suspended(n->pane) ? pane_start(n->pane) : pane_restart(n->pane); if (!ok) { app_toast(a, "cannot run it again"); return false; @@ -2299,8 +2319,8 @@ static node_t *build_pane(app_t *a, const kdl_node_t *node, const char *cwd, * resolving it twice would re-root a path against itself. */ char cwdbuf[1024]; const char *own = kdl_prop(node, "cwd", NULL); - const char *node_cwd = own ? path_resolve(own, base, cwdbuf, sizeof cwdbuf) - : cwd; + const char *node_cwd = + own ? path_resolve(own, base, cwdbuf, sizeof cwdbuf) : cwd; /* a split: children, in order, in one direction */ size_t kids = 0; @@ -2355,8 +2375,7 @@ static node_t *build_pane(app_t *a, const kdl_node_t *node, const char *cwd, } node_t *leaf = leaf_new_ex(a, command ? argv : default_argv(a), node_cwd, - suspended, - command ? command : ""); + suspended, command ? command : ""); if (!leaf) return NULL; leaf->weight = (int)kdl_prop_int(node, "weight", WEIGHT_UNIT); if (leaf->weight < WEIGHT_MIN) leaf->weight = WEIGHT_UNIT; @@ -2395,8 +2414,10 @@ bool app_apply_layout(app_t *a, const kdl_node_t *root, bool replace, * are its root's -- including `cwd`, which build_pane reads off the body * for itself. `base` as the inherited value is what makes a layout with no * `cwd` anywhere in it start in the directory it was checked into. */ - kdl_node_t body = {.name = (char *)"pane", .kids = t->kids, - .nkids = t->nkids, .props = t->props, + kdl_node_t body = {.name = (char *)"pane", + .kids = t->kids, + .nkids = t->nkids, + .props = t->props, .nprops = t->nprops}; node_t *tree = build_pane(a, &body, base, base); if (!tree) tree = leaf_new(a); @@ -2425,7 +2446,8 @@ bool app_apply_layout(app_t *a, const kdl_node_t *root, bool replace, if (n && n->name && config_is_setting(n->name)) { if (err) snprintf(err, errcap, - "this is a config, not a layout: `%s` is a setting", n->name); + "this is a config, not a layout: `%s` is a setting", + n->name); return false; } } @@ -2482,8 +2504,8 @@ bool app_apply_layout_file(app_t *a, const char *path, bool replace, char *err, const char *file = path_expand(path, pathbuf, sizeof pathbuf); kdl_node_t *root = kdl_parse_file(file, err, errcap); if (!root) return false; - bool ok = app_apply_layout(a, root, replace, - path_dir(file, dirbuf, sizeof dirbuf), err, errcap); + bool ok = app_apply_layout( + a, root, replace, path_dir(file, dirbuf, sizeof dirbuf), err, errcap); kdl_free(root); return ok; } @@ -2503,9 +2525,8 @@ bool app_apply_layout_file(app_t *a, const char *path, bool replace, char *err, */ /* Read by build_pane, on a `pane` node or on a tab acting as its own root. */ -static const char *const PANE_PROPS[] = {"split", "weight", "cwd", - "command", "focus", "purpose", - "suspended"}; +static const char *const PANE_PROPS[] = { + "split", "weight", "cwd", "command", "focus", "purpose", "suspended"}; /* Read by app_apply_layout off the tab itself. */ static const char *const TAB_PROPS[] = {"name", "active"}; @@ -2609,7 +2630,8 @@ size_t layout_check(const kdl_node_t *root, const char *file, const kdl_node_t *n = root->kids[i]; if (!n || !n->name) continue; if (lay && n == lay) continue; - if (strcmp(n->name, "tab") == 0) continue; /* a bare list of tabs is legal */ + if (strcmp(n->name, "tab") == 0) + continue; /* a bare list of tabs is legal */ if (config_is_setting(n->name)) { lc_say(&c, n->line, "this is a config, not a layout: `%s` is a setting", n->name); @@ -2623,7 +2645,8 @@ size_t layout_check(const kdl_node_t *root, const char *file, const kdl_node_t *n = lay->kids[i]; if (!n || !n->name) continue; if (strcmp(n->name, "tab") != 0) { - if (lay != root) lc_say(&c, n->line, "a layout holds tabs, not `%s`", n->name); + if (lay != root) + lc_say(&c, n->line, "a layout holds tabs, not `%s`", n->name); continue; } tabs++; @@ -2827,7 +2850,7 @@ size_t app_workspace_close(app_t *a, const char *slug) { * out of asking the same question the picker asks rather than being a rule of * its own. */ static bool tab_project(app_t *a, const tab_t *t, const char *path, - project_t *out) { + project_t *out) { int depth = 2; const char *const *roots = workspace_roots(&depth); if (path && *path) return project_find(roots, depth, path, out); @@ -2844,8 +2867,8 @@ static bool tab_project(app_t *a, const tab_t *t, const char *path, if (!t->focus) return false; char cwdbuf[4096]; - return project_find(roots, depth, live_cwd(t->focus->pane, cwdbuf, sizeof cwdbuf), - out); + return project_find(roots, depth, + live_cwd(t->focus->pane, cwdbuf, sizeof cwdbuf), out); } bool app_workspace_save(app_t *a, uint32_t tab, const char *path, int suspend, @@ -2870,10 +2893,11 @@ bool app_workspace_save(app_t *a, uint32_t tab, const char *path, int suspend, * the wrong tab was focused, so it is said rather than obeyed. */ if (strncmp(t->purpose, "project:", 8) == 0 && strcmp(t->purpose, p.slug) != 0) { - snprintf(err, errcap, - "this tab is another project's workspace (%s): save it from its own" - " tab, or pass that tab's id", - t->purpose); + snprintf( + err, errcap, + "this tab is another project's workspace (%s): save it from its own" + " tab, or pass that tab's id", + t->purpose); return false; } snprintf(out->path, sizeof out->path, "%s/%s", p.path, PROJECT_LAYOUT_FILE); @@ -2884,8 +2908,8 @@ bool app_workspace_save(app_t *a, uint32_t tab, const char *path, int suspend, return false; } - dump_layout_t o = {.tab = t->id, .base = p.path, .suspend = suspend, - .for_project = true}; + dump_layout_t o = { + .tab = t->id, .base = p.path, .suspend = suspend, .for_project = true}; char *kdl = app_dump_layout(a, &o); if (!kdl || !o.tabs) { free(kdl); @@ -2950,8 +2974,8 @@ static void transfer_weight(node_t *from, node_t *to, int amount) { node_t *sp = from->parent; if (sp && sp->nkids >= 2) { uint16_t floor_ = sp->dir == SPLIT_COLS ? MIN_PANE_COLS : MIN_PANE_ROWS; - uint16_t gap = sp->dir == SPLIT_COLS ? (uint16_t)(CFG.gap * CFG.gap_aspect) - : CFG.gap; + uint16_t gap = + sp->dir == SPLIT_COLS ? (uint16_t)(CFG.gap * CFG.gap_aspect) : CFG.gap; uint16_t span = sp->dir == SPLIT_COLS ? sp->rect.w : sp->rect.h; uint16_t gaps = (uint16_t)(gap * (sp->nkids - 1)); uint16_t avail = span > gaps ? (uint16_t)(span - gaps) : span; @@ -2989,11 +3013,15 @@ static void resize_focus(app_t *a, int dx, int dy) { int dir = dx ? dx : dy; if (i + 1 < p->nkids) { - if (dir > 0) transfer_weight(p->kids[i + 1], n, WEIGHT_STEP); - else transfer_weight(n, p->kids[i + 1], WEIGHT_STEP); + if (dir > 0) + transfer_weight(p->kids[i + 1], n, WEIGHT_STEP); + else + transfer_weight(n, p->kids[i + 1], WEIGHT_STEP); } else if (i > 0) { - if (dir > 0) transfer_weight(n, p->kids[i - 1], WEIGHT_STEP); - else transfer_weight(p->kids[i - 1], n, WEIGHT_STEP); + if (dir > 0) + transfer_weight(n, p->kids[i - 1], WEIGHT_STEP); + else + transfer_weight(p->kids[i - 1], n, WEIGHT_STEP); } layout(a); } @@ -3166,7 +3194,8 @@ static void dir_cb(node_t *n, void *ud) { if (n == d->from) return; /* centres, in a coordinate space where a row counts double so that * "nearest" means the same thing horizontally and vertically */ - long fx = d->from->rect.x * 2 + d->from->rect.w, fy = d->from->rect.y * 2 + d->from->rect.h; + long fx = d->from->rect.x * 2 + d->from->rect.w, + fy = d->from->rect.y * 2 + d->from->rect.h; long nx = n->rect.x * 2 + n->rect.w, ny = n->rect.y * 2 + n->rect.h; long along = d->dx ? (nx - fx) * d->dx : (ny - fy) * d->dy; if (along <= 0) return; /* not in that direction */ @@ -3267,9 +3296,9 @@ static void draw_pane_status(app_t *a, screen_t *s, node_t *leaf, color_t fg, char label[80]; snprintf(label, sizeof label, "[%s]", row[i].label); bool hot = ptr_on(a, x, y, w, 1); - uint16_t drawn = screen_text(s, x, y, label, focused || hot ? BTN_FG : fg, - focused || hot ? BTN_BG : BTN_BG_IDLE, - hot ? ATTR_BOLD : 0); + uint16_t drawn = + screen_text(s, x, y, label, focused || hot ? BTN_FG : fg, + focused || hot ? BTN_BG : BTN_BG_IDLE, hot ? ATTR_BOLD : 0); hit_add(&s->hits, x, y, drawn, 1, row[i].action); } @@ -3502,7 +3531,8 @@ static void draw_frame(app_t *a, screen_t *s, node_t *leaf) { uint16_t iw = (uint16_t)(nd + 4); /* space, arrow, space, digits, space */ if (iw + 2 < avail) { uint16_t ix = (uint16_t)((has_btn ? btn_x : x1) - iw); - uint16_t drawn = screen_text(s, ix, r.y, ind, SCROLL_FG, SCROLL_BG, ATTR_BOLD); + uint16_t drawn = + screen_text(s, ix, r.y, ind, SCROLL_FG, SCROLL_BG, ATTR_BOLD); char action[48]; snprintf(action, sizeof action, "scrollbottom:%u", leaf->id); hit_add(&s->hits, ix, r.y, drawn, 1, action); @@ -3519,10 +3549,10 @@ static void draw_frame(app_t *a, screen_t *s, node_t *leaf) { /* The purpose editor says which label it is: typed into the same cell as a * rename, an unlabelled caret would leave you guessing which one you are * changing -- and the two are edited from keys one shift apart. */ - int len = tagging - ? snprintf(buf, sizeof buf, " purpose %s\u2588 ", a->rename_buf) - : naming ? snprintf(buf, sizeof buf, " %s\u2588 ", a->rename_buf) - : snprintf(buf, sizeof buf, " %s ", title); + int len = + tagging ? snprintf(buf, sizeof buf, " purpose %s\u2588 ", a->rename_buf) + : naming ? snprintf(buf, sizeof buf, " %s\u2588 ", a->rename_buf) + : snprintf(buf, sizeof buf, " %s ", title); /* snprintf reports what it *would* have written; clamp to what it did, or * the scroll below would move bytes that are not there. */ if (len < 0) len = 0; @@ -3552,11 +3582,10 @@ static void draw_frame(app_t *a, screen_t *s, node_t *leaf) { /* An editor announces itself: the name sits in the button colours while it * is being typed, so a half-finished rename can never be mistaken for what * the pane is actually called. */ - uint16_t drawn = - screen_text(s, tx, r.y, buf, - editing ? RENAME_FG : (focused ? TITLE_FOCUS : TITLE_IDLE), - editing ? RENAME_BG : NO_COLOR, - editing || focused ? ATTR_BOLD : 0); + uint16_t drawn = screen_text( + s, tx, r.y, buf, + editing ? RENAME_FG : (focused ? TITLE_FOCUS : TITLE_IDLE), + editing ? RENAME_BG : NO_COLOR, editing || focused ? ATTR_BOLD : 0); /* The title names the pane; it is not an edge. It carves its own cells out * of the top row's handle so that resting there arms no split guide and @@ -3620,7 +3649,8 @@ static void draw_collapsed(app_t *a, screen_t *s, node_t *n) { } else if (pane_scrolled(leaf->pane)) { uint32_t above = 0, total = 0; pane_scroll_pos(leaf->pane, &above, &total); - snprintf(words, sizeof words, "\u25b2 %u", total > above ? total - above : 0); + snprintf(words, sizeof words, "\u25b2 %u", + total > above ? total - above : 0); status = words; } char line[256]; @@ -3724,9 +3754,9 @@ bool app_load_pane_shaders(app_t *a, uint32_t pane_id, const char *path, inband_chain_t con = {0}, chr = {0}; expr_prog_t *exprs[SHADE_MAX * 2]; size_t nexprs = 0; - size_t got = config_parse_chain_file(path, CFG.frame_focus, con.sh, &con.n, - chr.sh, &chr.n, exprs, &nexprs, err, - errcap); + size_t got = + config_parse_chain_file(path, CFG.frame_focus, con.sh, &con.n, chr.sh, + &chr.n, exprs, &nexprs, err, errcap); if (!got) { for (size_t i = 0; i < nexprs; i++) expr_free(exprs[i]); if (err && errcap && !err[0]) snprintf(err, errcap, "nothing in it to run"); @@ -4024,17 +4054,20 @@ static void draw_corners(app_t *a, screen_t *s) { bool active = a->drag.kind == DRAG_EDGE && (a->drag.c_nh || a->drag.c_nv) && a->drag.c_nh && c->nh && a->drag.c_h[0] == c->h_id[0] && - a->drag.c_hedge[0] == c->h_edge[0] && a->drag.c_v[0] == c->v_id[0]; + a->drag.c_hedge[0] == c->h_edge[0] && + a->drag.c_v[0] == c->v_id[0]; /* (C) The mark appears the moment the pointer is on it, before the dwell * that arms the two boundaries. A crossing is two cells wide and gives no * other sign it is anything: something has to say it is there, and the * ghost costs nothing if you were only passing through. */ - bool over = a->drag.kind == DRAG_NONE && ptr_on(a, c->r.x, c->r.y, c->r.w, c->r.h); + bool over = + a->drag.kind == DRAG_NONE && ptr_on(a, c->r.x, c->r.y, c->r.w, c->r.h); if (!active && !over) continue; bool armed = active || now_ms_() - a->ptr_still_since >= CFG.hover_delay_ms; for (uint16_t x = c->r.x; x < c->r.x + c->r.w; x++) - screen_text(s, x, c->r.y, armed ? (active ? "\u256c" : "\u253c") : "\u253c", - RESIZE_C, NO_COLOR, active ? ATTR_BOLD : 0); + screen_text(s, x, c->r.y, + armed ? (active ? "\u256c" : "\u253c") : "\u253c", RESIZE_C, + NO_COLOR, active ? ATTR_BOLD : 0); } } @@ -4071,7 +4104,8 @@ static void draw_cb(node_t *n, void *ud) { w = n->content.w; if (w > 1) { line[w - 1] = 0; - line[w - 2] = 0xe2; /* fall back to a plain dot rather than a cut UTF-8 */ + line[w - 2] = + 0xe2; /* fall back to a plain dot rather than a cut UTF-8 */ line[w - 2] = '.'; } else { line[w] = 0; @@ -4120,8 +4154,8 @@ static void draw_tab_strip(app_t *a, screen_t *s) { /* Right side first, so a long tab list can never eat the indicators — the * same budgeting rule as the split button and the OSC buttons. */ - uint16_t right = (uint16_t)(s->cols > CFG.status_pad ? s->cols - CFG.status_pad - : s->cols); + uint16_t right = + (uint16_t)(s->cols > CFG.status_pad ? s->cols - CFG.status_pad : s->cols); char info[64]; size_t np = app_pane_count(a); snprintf(info, sizeof info, "%zu pane%s", np, np == 1 ? "" : "s"); @@ -4152,16 +4186,17 @@ static void draw_tab_strip(app_t *a, screen_t *s) { /* A pane that rang in a tab you are not looking at is invisible without * this, and that is the case the whole indicator exists for. */ bool rang = CFG.bell_indicator && tab_has_bell(t); - if (nm[0]) snprintf(label, sizeof label, " %zu:%s ", i + 1, nm); - else snprintf(label, sizeof label, " %zu ", i + 1); + if (nm[0]) + snprintf(label, sizeof label, " %zu:%s ", i + 1, nm); + else + snprintf(label, sizeof label, " %zu ", i + 1); /* Renaming: the tab's own cell becomes the editor, in the editor's * colours, so a half-typed name can never be mistaken for the tab's real * one. The caret is part of the label, so the width below — and therefore * the hit — is the width of what is actually drawn. */ bool editing = a->renaming == RENAME_TAB && a->rename_id == t->id; - if (editing) - snprintf(label, sizeof label, " %s\u2588 ", a->rename_buf); + if (editing) snprintf(label, sizeof label, " %s\u2588 ", a->rename_buf); bool active = i == a->cur; uint16_t attrs = active ? ATTR_BOLD : 0; @@ -4170,12 +4205,11 @@ static void draw_tab_strip(app_t *a, screen_t *s) { * hover colour if that width turns out to be under the pointer — which * costs a repaint of a few cells and guarantees the lit cells are the * registered ones. */ - uint16_t w = screen_text(s, x, y, label, - editing ? RENAME_FG - : (active ? TAB_ACTIVE_FG : TAB_IDLE), - editing ? RENAME_BG - : (active ? TAB_ACTIVE_BG : NO_COLOR), - editing ? ATTR_BOLD : attrs); + uint16_t w = + screen_text(s, x, y, label, + editing ? RENAME_FG : (active ? TAB_ACTIVE_FG : TAB_IDLE), + editing ? RENAME_BG : (active ? TAB_ACTIVE_BG : NO_COLOR), + editing ? ATTR_BOLD : attrs); /* Hovering keeps the active tab's fill — it is still the tab you are in — * so its feedback lands on the text instead. An inactive tab has no fill * to keep, and brightens. */ @@ -4321,8 +4355,10 @@ static void find_corners(app_t *a) { if (g[j].sp->dir != SPLIT_COLS) continue; rect_t v = g[j].r; - bool touch_y = v.y <= (uint16_t)(h.y + h.h) && h.y <= (uint16_t)(v.y + v.h); - bool touch_x = h.x <= (uint16_t)(v.x + v.w) && v.x <= (uint16_t)(h.x + h.w); + bool touch_y = + v.y <= (uint16_t)(h.y + h.h) && h.y <= (uint16_t)(v.y + v.h); + bool touch_x = + h.x <= (uint16_t)(v.x + v.w) && v.x <= (uint16_t)(h.x + h.w); if (!touch_y || !touch_x) continue; rect_t cr = {v.x, h.y, v.w, h.h}; @@ -4395,15 +4431,18 @@ static void draw_resize_hint(app_t *a, screen_t *s, node_t *split, size_t idx, int ci = -1; if (a->drag.kind == DRAG_EDGE && (a->drag.c_nv || a->drag.c_nh)) { for (size_t k = 0; k < a->drag.c_nh; k++) - if (a->drag.c_h[k] == split->id && a->drag.c_hedge[k] == idx) active = true; + if (a->drag.c_h[k] == split->id && a->drag.c_hedge[k] == idx) + active = true; for (size_t k = 0; k < a->drag.c_nv; k++) - if (a->drag.c_v[k] == split->id && a->drag.c_vedge[k] == idx) active = true; + if (a->drag.c_v[k] == split->id && a->drag.c_vedge[k] == idx) + active = true; } else if (a->drag.kind == DRAG_NONE) { ci = corner_at(a, a->ptr_x, a->ptr_y); } if (ci >= 0 && (size_t)ci < a->ncorners && corner_uses(&a->corners[ci], split->id, idx)) { - if (false) active = true; + if (false) + active = true; else if (now_ms_() - a->ptr_still_since >= CFG.hover_delay_ms) { uint16_t attrs0 = 0; if (split->dir == SPLIT_COLS) { @@ -4506,8 +4545,8 @@ static bool ci_contains(const char *hay, const char *needle) { size_t nl = strlen(needle); for (const char *p = hay; *p; p++) { size_t i = 0; - while (i < nl && p[i] && tolower((unsigned char)p[i]) == - tolower((unsigned char)needle[i])) + while (i < nl && p[i] && + tolower((unsigned char)p[i]) == tolower((unsigned char)needle[i])) i++; if (i == nl) return true; } @@ -4549,9 +4588,8 @@ typedef struct { } help_row_t; static size_t help_rows(help_row_t *out, size_t cap) { - static const char *const GROUPS[] = {"panes", "focus", "size", - "tabs", "projects", "scroll", - "session"}; + static const char *const GROUPS[] = {"panes", "focus", "size", "tabs", + "projects", "scroll", "session"}; size_t n = 0; for (size_t g = 0; g < sizeof GROUPS / sizeof *GROUPS && n < cap; g++) { bool titled = false; @@ -4571,7 +4609,8 @@ static size_t help_rows(help_row_t *out, size_t cap) { * thing to learn. */ bool tabish = act == ACT_SELECT_TAB_1 && bound >= ACT_SELECT_TAB_1; if (bound != (action_t)act && !tabish) continue; - if (tabish && bound != ACT_SELECT_TAB_1 && bound != ACT_SELECT_TAB_1 + 8) + if (tabish && bound != ACT_SELECT_TAB_1 && + bound != ACT_SELECT_TAB_1 + 8) continue; /* first and last, shown as a range */ char one[24]; @@ -4662,8 +4701,10 @@ static rect_t modal_frame(app_t *a, screen_t *s, uint16_t w, uint16_t h, for (uint16_t xx = x; xx < x + w; xx++) screen_text(s, xx, yy, " ", NO_COLOR, MODAL_BG, 0); - const char *tl = CFG.rounded ? "\u256d" : "\u250c", *tr = CFG.rounded ? "\u256e" : "\u2510"; - const char *bl = CFG.rounded ? "\u2570" : "\u2514", *br = CFG.rounded ? "\u256f" : "\u2518"; + const char *tl = CFG.rounded ? "\u256d" : "\u250c", + *tr = CFG.rounded ? "\u256e" : "\u2510"; + const char *bl = CFG.rounded ? "\u2570" : "\u2514", + *br = CFG.rounded ? "\u256f" : "\u2518"; screen_text(s, x, y, tl, MODAL_BORDER, MODAL_BG, 0); screen_text(s, x1, y, tr, MODAL_BORDER, MODAL_BG, 0); screen_text(s, x, y1, bl, MODAL_BORDER, MODAL_BG, 0); @@ -4738,8 +4779,11 @@ static void draw_help(app_t *a, screen_t *s) { if (two) { size_t half = (n + 1) / 2; split = half; - while (split < n && rows[split].group) split++; /* to the next heading */ - if (split >= n) { split = half; while (split > 1 && rows[split].group) split--; } + while (split < n && rows[split].group) split++; /* to the next heading */ + if (split >= n) { + split = half; + while (split > 1 && rows[split].group) split--; + } /* Land on the heading itself, not on the blank row above it: a column * that starts with a blank line looks like a mistake. */ if (split < n && !rows[split].group && !rows[split].label) split++; @@ -4782,8 +4826,9 @@ static void draw_help(app_t *a, screen_t *s) { if (row >= shown && rows[i].label) hidden++; } - rect_t in = modal_frame(a, s, (uint16_t)(body + 6), (uint16_t)(shown + 6 + (hidden ? 1 : 0)), - "keys", "closehelp"); + rect_t in = modal_frame(a, s, (uint16_t)(body + 6), + (uint16_t)(shown + 6 + (hidden ? 1 : 0)), "keys", + "closehelp"); uint16_t x1 = (uint16_t)(in.x + in.w); /* the right border */ /* Everything here is prefix-then-key, and saying so once beats repeating @@ -4847,8 +4892,8 @@ typedef struct { char left[24]; char mid[80]; char right[80]; - bool here; /* mark this row: the pane you are already in */ - char action[48]; /* what a click on it does */ + bool here; /* mark this row: the pane you are already in */ + char action[48]; /* what a click on it does */ } pick_row_t; /* A band of background, for a row that is selected or under the pointer. The @@ -4980,8 +5025,8 @@ static size_t workspace_rows(app_t *a, pick_row_t *out, size_t max) { const char *root_end = p->path + keep; const char *root_start = root_end; while (root_start > p->path && root_start[-1] != '/') root_start--; - snprintf(r->left, sizeof r->left, "%.*s", - (int)(root_end - root_start), root_start); + snprintf(r->left, sizeof r->left, "%.*s", (int)(root_end - root_start), + root_start); snprintf(r->mid, sizeof r->mid, "%s", p->name); /* What it is, or what it already is: an open workspace says how many panes * it has, and a project with no layout says so, which reads as the @@ -5026,8 +5071,9 @@ static void draw_picker(app_t *a, screen_t *s) { bool palette = a->picker == PICK_PALETTE; bool projects = a->picker == PICK_WORKSPACES; const char *title = palette ? "commands" : projects ? "projects" : "find"; - const char *close = palette ? "closepalette" - : projects ? "closeprojects" : "closefind"; + const char *close = palette ? "closepalette" + : projects ? "closeprojects" + : "closefind"; /* The window of results on screen. Kept around the selection rather than * anchored at the top, so arrowing past the bottom scrolls instead of @@ -5059,8 +5105,9 @@ static void draw_picker(app_t *a, screen_t *s) { if (!n) { /* An empty box says the picker is broken; this says the query is. */ - const char *none = palette ? "no command matches that" - : (a->query[0] ? "no pane matches that" : "no panes"); + const char *none = + palette ? "no command matches that" + : (a->query[0] ? "no pane matches that" : "no panes"); help_text(s, (uint16_t)(in.x + 2), ly, none, (uint16_t)(in.w - 2), HINT_C, MODAL_BG, 0); return; @@ -5112,15 +5159,17 @@ static void draw_picker(app_t *a, screen_t *s) { /* What is off the top and bottom of the window. Without this a list that * scrolls looks like a list that ends. */ if (first) - screen_text(s, (uint16_t)(in.x + in.w - 1), ly, "\u2191", HINT_C, MODAL_BG, 0); + screen_text(s, (uint16_t)(in.x + in.w - 1), ly, "\u2191", HINT_C, MODAL_BG, + 0); if (first + shown < n) - screen_text(s, (uint16_t)(in.x + in.w - 1), - (uint16_t)(ly + shown - 1), "\u2193", HINT_C, MODAL_BG, 0); + screen_text(s, (uint16_t)(in.x + in.w - 1), (uint16_t)(ly + shown - 1), + "\u2193", HINT_C, MODAL_BG, 0); /* The count belongs where the eye already goes for "how much is there", and * it is the answer to "is my query too narrow, or is there nothing?". */ char foot[64]; - snprintf(foot, sizeof foot, " %zu of %zu \u00b7 \u2191\u2193 enter ", a->sel + 1, n); + snprintf(foot, sizeof foot, " %zu of %zu \u00b7 \u2191\u2193 enter ", + a->sel + 1, n); if (in.w > cells(foot)) screen_text(s, (uint16_t)(in.x + (in.w - cells(foot)) / 2), (uint16_t)(in.y + in.h), foot, HINT_C, MODAL_BG, 0); @@ -5153,8 +5202,10 @@ static bool push_pane_a_tab(app_t *a, bool forward) { for (size_t i = 0; i < a->ntabs; i++) if (a->tabs[i].id == tid) at = i; char msg[128]; - if (name[0]) snprintf(msg, sizeof msg, "moved to tab %zu (%s)", at + 1, name); - else snprintf(msg, sizeof msg, "moved to tab %zu", at + 1); + if (name[0]) + snprintf(msg, sizeof msg, "moved to tab %zu (%s)", at + 1, name); + else + snprintf(msg, sizeof msg, "moved to tab %zu", at + 1); app_toast(a, msg); return true; } @@ -5176,7 +5227,8 @@ static void drop_pane_on_strip(app_t *a) { uint32_t tid = a->drag.tab_target; char name[64] = {0}; for (size_t i = 0; i < a->ntabs; i++) - if (a->tabs[i].id == tid) snprintf(name, sizeof name, "%s", a->tabs[i].name); + if (a->tabs[i].id == tid) + snprintf(name, sizeof name, "%s", a->tabs[i].name); if (!app_move_pane_to_tab(a, src, tid, false)) { /* The one refusal a drag can reach: the tab it is already in. */ @@ -5187,8 +5239,10 @@ static void drop_pane_on_strip(app_t *a) { for (size_t i = 0; i < a->ntabs; i++) if (a->tabs[i].id == tid) at = i; char msg[128]; - if (name[0]) snprintf(msg, sizeof msg, "moved to tab %zu (%s)", at + 1, name); - else snprintf(msg, sizeof msg, "moved to tab %zu", at + 1); + if (name[0]) + snprintf(msg, sizeof msg, "moved to tab %zu (%s)", at + 1, name); + else + snprintf(msg, sizeof msg, "moved to tab %zu", at + 1); app_toast(a, msg); } @@ -5230,7 +5284,7 @@ static void picker_move(app_t *a, int d) { } int64_t sel = (int64_t)a->sel + d; int64_t last = (int64_t)n - 1; - if (sel < 0) sel = d == -1 ? last : 0; /* a page up stops at the top */ + if (sel < 0) sel = d == -1 ? last : 0; /* a page up stops at the top */ if (sel > last) sel = d == 1 ? 0 : last; a->sel = (size_t)sel; } @@ -5266,57 +5320,42 @@ static bool picker_key(app_t *a, const input_event_t *ev) { } switch (ev->key) { - case GHOSTTY_KEY_ESCAPE: - a->picker = PICK_NONE; - return true; - case GHOSTTY_KEY_ENTER: { - pick_row_t rows[128]; - size_t n = picker_rows(a, rows, 128); - /* The row's own action, the one a click on it would run: a picker that + case GHOSTTY_KEY_ESCAPE: a->picker = PICK_NONE; return true; + case GHOSTTY_KEY_ENTER: { + pick_row_t rows[128]; + size_t n = picker_rows(a, rows, 128); + /* The row's own action, the one a click on it would run: a picker that * chose by keyboard down one path and by mouse down another would be * two pickers wearing one box. */ - char action[48] = ""; - if (a->sel < n) snprintf(action, sizeof action, "%s", rows[a->sel].action); - a->picker = PICK_NONE; - if (*action) picker_accept(a, action); - return true; - } - case GHOSTTY_KEY_TAB: - picker_move(a, shift ? -1 : 1); - return true; - case GHOSTTY_KEY_ARROW_DOWN: - picker_move(a, 1); - return true; - case GHOSTTY_KEY_ARROW_UP: - picker_move(a, -1); - return true; - case GHOSTTY_KEY_PAGE_DOWN: - picker_move(a, FINDER_ROWS); - return true; - case GHOSTTY_KEY_PAGE_UP: - picker_move(a, -FINDER_ROWS); - return true; - case GHOSTTY_KEY_HOME: - a->sel = 0; - return true; - case GHOSTTY_KEY_END: { - size_t n = picker_count(a); - a->sel = n ? n - 1 : 0; - return true; - } - case GHOSTTY_KEY_BACKSPACE: { - /* A character, not a byte: the query can hold whatever a pane's title + char action[48] = ""; + if (a->sel < n) snprintf(action, sizeof action, "%s", rows[a->sel].action); + a->picker = PICK_NONE; + if (*action) picker_accept(a, action); + return true; + } + case GHOSTTY_KEY_TAB: picker_move(a, shift ? -1 : 1); return true; + case GHOSTTY_KEY_ARROW_DOWN: picker_move(a, 1); return true; + case GHOSTTY_KEY_ARROW_UP: picker_move(a, -1); return true; + case GHOSTTY_KEY_PAGE_DOWN: picker_move(a, FINDER_ROWS); return true; + case GHOSTTY_KEY_PAGE_UP: picker_move(a, -FINDER_ROWS); return true; + case GHOSTTY_KEY_HOME: a->sel = 0; return true; + case GHOSTTY_KEY_END: { + size_t n = picker_count(a); + a->sel = n ? n - 1 : 0; + return true; + } + case GHOSTTY_KEY_BACKSPACE: { + /* A character, not a byte: the query can hold whatever a pane's title * did, and half a UTF-8 sequence is not a search. (The rename editor * learned this first; this one was still eating bytes.) */ - size_t l = strlen(a->query); - while (l && ((unsigned char)a->query[l - 1] & 0xC0) == 0x80) l--; - if (l) l--; - a->query[l] = 0; - a->sel = 0; - return true; - } - default: - break; + size_t l = strlen(a->query); + while (l && ((unsigned char)a->query[l - 1] & 0xC0) == 0x80) l--; + if (l) l--; + a->query[l] = 0; + a->sel = 0; + return true; + } + default: break; } if (!ctrl && ev->text_len && (unsigned char)ev->text[0] >= 0x20) { size_t l = strlen(a->query); @@ -5419,23 +5458,18 @@ static bool rename_key(app_t *a, const input_event_t *ev) { if (ev->kind != EV_KEY || ev->action == KEY_RELEASE) return true; switch (ev->key) { - case GHOSTTY_KEY_ESCAPE: - rename_end(a, false); - return true; - case GHOSTTY_KEY_ENTER: - rename_end(a, true); - return true; - case GHOSTTY_KEY_BACKSPACE: { - /* A character, not a byte: a title is whatever the program could set, + case GHOSTTY_KEY_ESCAPE: rename_end(a, false); return true; + case GHOSTTY_KEY_ENTER: rename_end(a, true); return true; + case GHOSTTY_KEY_BACKSPACE: { + /* A character, not a byte: a title is whatever the program could set, * and half a UTF-8 sequence is not a name. */ - size_t l = strlen(a->rename_buf); - while (l && ((unsigned char)a->rename_buf[l - 1] & 0xC0) == 0x80) l--; - if (l) l--; - a->rename_buf[l] = 0; - return true; - } - default: - break; + size_t l = strlen(a->rename_buf); + while (l && ((unsigned char)a->rename_buf[l - 1] & 0xC0) == 0x80) l--; + if (l) l--; + a->rename_buf[l] = 0; + return true; + } + default: break; } if (ev->text_len && (unsigned char)ev->text[0] >= 0x20) { size_t l = strlen(a->rename_buf); @@ -5477,7 +5511,7 @@ void app_compose(app_t *a, screen_t *s) { if (a->picker || a->help) draw_scrim(a, s); if (a->picker) draw_picker(a, s); if (a->help) draw_help(a, s); - draw_toasts(a, s); /* and above even that: it is transient */ + draw_toasts(a, s); /* and above even that: it is transient */ } /* ---- kitty graphics ------------------------------------------------------ */ @@ -5534,34 +5568,36 @@ static void gfx_from_pane(pane_t *p, const pane_gfx_t *g, void *ud) { } if (!cols || !rows) return; - gfx_place(c->a->gfx, &(gfx_req_t){ - .pane = leaf->id, - .src_id = g->image_id, - .gen = g->generation, - .place_id = g->place_id, - .col = (uint16_t)(leaf->content.x + g->col), - .row = (uint16_t)(leaf->content.y + g->row), - .cols = cols, - .rows = rows, - /* Carried through untouched: the pane's own clipping already dealt with + gfx_place( + c->a->gfx, + &(gfx_req_t){ + .pane = leaf->id, + .src_id = g->image_id, + .gen = g->generation, + .place_id = g->place_id, + .col = (uint16_t)(leaf->content.x + g->col), + .row = (uint16_t)(leaf->content.y + g->row), + .cols = cols, + .rows = rows, + /* Carried through untouched: the pane's own clipping already dealt with * it, and the offset is relative to the first cell either way. */ - .x_off = g->x_off, - .y_off = g->y_off, - /* Clipped the same way the cell counts were, and zero when the program + .x_off = g->x_off, + .y_off = g->y_off, + /* Clipped the same way the cell counts were, and zero when the program * never asked to scale. */ - .scale_cols = g->req_cols ? cols : 0, - .scale_rows = g->req_rows ? rows : 0, - .sx = g->sx, - .sy = g->sy, - .sw = sw, - .sh = sh, - .px_w = g->src_w, - .px_h = g->src_h, - .format = g->format, - .compression = g->compression, - .data = g->data, - .data_len = g->data_len, - }); + .scale_cols = g->req_cols ? cols : 0, + .scale_rows = g->req_rows ? rows : 0, + .sx = g->sx, + .sy = g->sy, + .sw = sw, + .sh = sh, + .px_w = g->src_w, + .px_h = g->src_h, + .format = g->format, + .compression = g->compression, + .data = g->data, + .data_len = g->data_len, + }); } static void gfx_leaf_cb(node_t *n, void *ud) { @@ -5644,9 +5680,10 @@ static void drag_edge(app_t *a, node_t *sp, size_t i, int cells) { int amount = (int)((long)labs(cells) * total / (span ? span : 1)); if (amount <= 0) amount = 1; - - if (cells > 0) transfer_weight(sp->kids[i + 1], sp->kids[i], amount); - else transfer_weight(sp->kids[i], sp->kids[i + 1], amount); + if (cells > 0) + transfer_weight(sp->kids[i + 1], sp->kids[i], amount); + else + transfer_weight(sp->kids[i], sp->kids[i + 1], amount); layout(a); } @@ -5834,7 +5871,8 @@ static void do_action(app_t *a, const char *action, const input_event_t *ev) { /* A press always focuses; a hover only when it is allowed to. The event * is forwarded either way, so a pane that tracks the mouse still sees the * pointer cross it whether or not focus moved. */ - if (ev->maction != MOUSE_MOTION || hover_focus_allowed(a)) cur(a)->focus = n; + if (ev->maction != MOUSE_MOTION || hover_focus_allowed(a)) + cur(a)->focus = n; /* translate to pane-local coordinates before forwarding */ input_event_t local = *ev; local.mx = (uint16_t)(ev->mx - n->content.x); @@ -5915,147 +5953,149 @@ static bool run_action(app_t *a, action_t act) { return true; } switch (act) { - case ACT_SPLIT: split_focus_auto(a); return true; - case ACT_SPLIT_COLS: split_focus_ui(a, SPLIT_COLS); return true; - case ACT_SPLIT_ROWS: split_focus_ui(a, SPLIT_ROWS); return true; - case ACT_ZOOM: app_toggle_zoom(a, 0); return true; - case ACT_MINIMIZE: - if (!app_minimize(a, 0)) app_toast(a, "nothing else to show"); - return true; - case ACT_CLOSE_PANE: - if (cur(a)->focus) close_leaf(a, cur(a)->focus); - return true; - case ACT_SET_PURPOSE: - /* Nothing to tag in an empty tab, and a purpose belongs to a pane rather + case ACT_SPLIT: split_focus_auto(a); return true; + case ACT_SPLIT_COLS: split_focus_ui(a, SPLIT_COLS); return true; + case ACT_SPLIT_ROWS: split_focus_ui(a, SPLIT_ROWS); return true; + case ACT_ZOOM: app_toggle_zoom(a, 0); return true; + case ACT_MINIMIZE: + if (!app_minimize(a, 0)) app_toast(a, "nothing else to show"); + return true; + case ACT_CLOSE_PANE: + if (cur(a)->focus) close_leaf(a, cur(a)->focus); + return true; + case ACT_SET_PURPOSE: + /* Nothing to tag in an empty tab, and a purpose belongs to a pane rather * than to the space one would occupy. */ - if (cur(a)->focus) purpose_begin(a, cur(a)->focus->id); - return true; - case ACT_RERUN: - /* The keyboard's half of a dead pane's [re-run] button. Refused on a + if (cur(a)->focus) purpose_begin(a, cur(a)->focus->id); + return true; + case ACT_RERUN: + /* The keyboard's half of a dead pane's [re-run] button. Refused on a * live pane rather than restarting it: "run it again" would mean * killing something that is still working, which is not what anybody * pressing it is asking for. */ - if (cur(a)->focus && pane_alive(cur(a)->focus->pane) && - !pane_suspended(cur(a)->focus->pane)) - app_toast(a, "still running"); - else - app_rerun_pane(a, 0); - return true; - case ACT_FOCUS_LEFT: focus_dir(a, -1, 0); return true; - case ACT_FOCUS_RIGHT: focus_dir(a, 1, 0); return true; - case ACT_FOCUS_UP: focus_dir(a, 0, -1); return true; - case ACT_FOCUS_DOWN: focus_dir(a, 0, 1); return true; - case ACT_FOCUS_NEXT: focus_next(a); return true; - case ACT_NEW_TAB: app_new_tab(a, ""); return true; - case ACT_CLOSE_TAB: { - /* The last tab is refused rather than obeyed. `app_close_tab` ends the + if (cur(a)->focus && pane_alive(cur(a)->focus->pane) && + !pane_suspended(cur(a)->focus->pane)) + app_toast(a, "still running"); + else + app_rerun_pane(a, 0); + return true; + case ACT_FOCUS_LEFT: focus_dir(a, -1, 0); return true; + case ACT_FOCUS_RIGHT: focus_dir(a, 1, 0); return true; + case ACT_FOCUS_UP: focus_dir(a, 0, -1); return true; + case ACT_FOCUS_DOWN: focus_dir(a, 0, 1); return true; + case ACT_FOCUS_NEXT: focus_next(a); return true; + case ACT_NEW_TAB: app_new_tab(a, ""); return true; + case ACT_CLOSE_TAB: { + /* The last tab is refused rather than obeyed. `app_close_tab` ends the * session when nothing is left, which is right for a *request* -- a script * asking to close the only tab means it -- and a trap for a key: one that * closes a tab four times and ends your session the fifth is a key you * cannot press without counting first. `quit` is how you mean that, and it * is one letter away. */ - if (a->ntabs < 2) { - app_toast(a, "last tab: quit the session instead"); - return true; - } - /* Said out loud with the count, because everything in it goes with it and - * the panes it kills are the ones you were not looking at. */ - size_t n = count_leaves(cur(a)->root); - char said[64]; - snprintf(said, sizeof said, "closed tab \u00b7 %zu pane%s", n, - n == 1 ? "" : "s"); - if (app_close_tab(a, cur(a)->id)) app_toast(a, said); + if (a->ntabs < 2) { + app_toast(a, "last tab: quit the session instead"); return true; } - case ACT_NEXT_TAB: app_cycle_tab(a, 1); return true; - case ACT_PREV_TAB: app_cycle_tab(a, -1); return true; - case ACT_RESIZE_LEFT: resize_focus(a, -1, 0); return true; - case ACT_RESIZE_RIGHT: resize_focus(a, 1, 0); return true; - case ACT_RESIZE_UP: resize_focus(a, 0, -1); return true; - case ACT_RESIZE_DOWN: resize_focus(a, 0, 1); return true; - case ACT_CLEAR_SHADERS: - /* Says which of the two happened. "Nothing to undo" and "undone" look + /* Said out loud with the count, because everything in it goes with it and + * the panes it kills are the ones you were not looking at. */ + size_t n = count_leaves(cur(a)->root); + char said[64]; + snprintf(said, sizeof said, "closed tab \u00b7 %zu pane%s", n, + n == 1 ? "" : "s"); + if (app_close_tab(a, cur(a)->id)) app_toast(a, said); + return true; + } + case ACT_NEXT_TAB: app_cycle_tab(a, 1); return true; + case ACT_PREV_TAB: app_cycle_tab(a, -1); return true; + case ACT_RESIZE_LEFT: resize_focus(a, -1, 0); return true; + case ACT_RESIZE_RIGHT: resize_focus(a, 1, 0); return true; + case ACT_RESIZE_UP: resize_focus(a, 0, -1); return true; + case ACT_RESIZE_DOWN: resize_focus(a, 0, 1); return true; + case ACT_CLEAR_SHADERS: + /* Says which of the two happened. "Nothing to undo" and "undone" look * identical on a pane that was never painted, and a key that might have * done nothing is a key you press again. */ - app_toast(a, app_clear_pane_shaders(a, 0) ? "shaders cleared" - : "no shaders on this pane"); - return true; - case ACT_PANE_TO_NEXT_TAB: - case ACT_PANE_TO_PREV_TAB: - return push_pane_a_tab(a, act == ACT_PANE_TO_NEXT_TAB); - case ACT_PANE_TO_NEW_TAB: { - uint32_t made = app_move_pane_to_new_tab(a, 0, ""); - app_toast(a, made ? "into a tab of its own" - : "it is the only pane in this tab"); - return true; - } - case ACT_EQUALIZE: - if (!app_equalize_splits(a)) app_toast(a, "nothing to even out"); - return true; - case ACT_ROTATE_LAYOUT: rotate_layout_ui(a); return true; - case ACT_SCROLL_UP: pane_scroll(cur(a)->focus->pane, -CFG.scroll_lines); return true; - case ACT_SCROLL_DOWN: pane_scroll(cur(a)->focus->pane, CFG.scroll_lines); return true; - case ACT_SCROLL_PAGE_UP: - pane_scroll(cur(a)->focus->pane, -(int)cur(a)->focus->content.h); - return true; - case ACT_SCROLL_PAGE_DOWN: - pane_scroll(cur(a)->focus->pane, (int)cur(a)->focus->content.h); - return true; - case ACT_SCROLL_TOP: pane_scroll_edge(cur(a)->focus->pane, true); return true; - case ACT_SCROLL_BOTTOM: pane_scroll_edge(cur(a)->focus->pane, false); return true; - case ACT_FINDER: - case ACT_PALETTE: - a->picker = act == ACT_PALETTE ? PICK_PALETTE : PICK_FINDER; - a->query[0] = 0; - a->sel = 0; - return true; - case ACT_WORKSPACES: { - /* Scanned here, once, and kept only while the picker is up: draw_picker + app_toast(a, app_clear_pane_shaders(a, 0) ? "shaders cleared" + : "no shaders on this pane"); + return true; + case ACT_PANE_TO_NEXT_TAB: + case ACT_PANE_TO_PREV_TAB: + return push_pane_a_tab(a, act == ACT_PANE_TO_NEXT_TAB); + case ACT_PANE_TO_NEW_TAB: { + uint32_t made = app_move_pane_to_new_tab(a, 0, ""); + app_toast(a, made ? "into a tab of its own" + : "it is the only pane in this tab"); + return true; + } + case ACT_EQUALIZE: + if (!app_equalize_splits(a)) app_toast(a, "nothing to even out"); + return true; + case ACT_ROTATE_LAYOUT: rotate_layout_ui(a); return true; + case ACT_SCROLL_UP: + pane_scroll(cur(a)->focus->pane, -CFG.scroll_lines); + return true; + case ACT_SCROLL_DOWN: + pane_scroll(cur(a)->focus->pane, CFG.scroll_lines); + return true; + case ACT_SCROLL_PAGE_UP: + pane_scroll(cur(a)->focus->pane, -(int)cur(a)->focus->content.h); + return true; + case ACT_SCROLL_PAGE_DOWN: + pane_scroll(cur(a)->focus->pane, (int)cur(a)->focus->content.h); + return true; + case ACT_SCROLL_TOP: pane_scroll_edge(cur(a)->focus->pane, true); return true; + case ACT_SCROLL_BOTTOM: + pane_scroll_edge(cur(a)->focus->pane, false); + return true; + case ACT_FINDER: + case ACT_PALETTE: + a->picker = act == ACT_PALETTE ? PICK_PALETTE : PICK_FINDER; + a->query[0] = 0; + a->sel = 0; + return true; + case ACT_WORKSPACES: { + /* Scanned here, once, and kept only while the picker is up: draw_picker * asks for its rows every frame. Opening is also exactly when the list * has to be right, which is why nothing older than this keystroke is * ever shown. */ - if (!app_project_roots_set()) { - app_toast(a, "no project roots: set project_roots in your config"); - return true; - } - free(a->projects); - a->projects = calloc(PROJECTS_MAX, sizeof *a->projects); - a->nprojects = a->projects ? app_projects(a->projects, PROJECTS_MAX) : 0; - if (!a->nprojects) { - app_toast(a, "no projects under your project_roots"); - return true; - } - a->picker = PICK_WORKSPACES; - a->query[0] = 0; - a->sel = 0; + if (!app_project_roots_set()) { + app_toast(a, "no project roots: set project_roots in your config"); return true; } - case ACT_SAVE_WORKSPACE: { - app_workspace_save_t w; - char err[256] = {0}; - /* `commands` rather than as-is: the pane running this morning's dev server - * should be in the file asleep, not started on every open. */ - if (!app_workspace_save(a, 0, NULL, DUMP_SUSPEND_COMMANDS, true, &w, err, - sizeof err)) { - app_toast(a, err[0] ? err : "cannot save that"); - return true; - } - char said[96]; - snprintf(said, sizeof said, "%s %s \u00b7 %zu pane%s, %zu suspended", - w.replaced ? "replaced" : "wrote", PROJECT_LAYOUT_FILE, w.panes, - w.panes == 1 ? "" : "s", w.suspended); - app_toast(a, said); + free(a->projects); + a->projects = calloc(PROJECTS_MAX, sizeof *a->projects); + a->nprojects = a->projects ? app_projects(a->projects, PROJECTS_MAX) : 0; + if (!a->nprojects) { + app_toast(a, "no projects under your project_roots"); return true; } - case ACT_EDIT_CONFIG: - app_edit_config(a); - return true; - case ACT_HELP: - a->help = !a->help; + a->picker = PICK_WORKSPACES; + a->query[0] = 0; + a->sel = 0; + return true; + } + case ACT_SAVE_WORKSPACE: { + app_workspace_save_t w; + char err[256] = {0}; + /* `commands` rather than as-is: the pane running this morning's dev server + * should be in the file asleep, not started on every open. */ + if (!app_workspace_save(a, 0, NULL, DUMP_SUSPEND_COMMANDS, true, &w, err, + sizeof err)) { + app_toast(a, err[0] ? err : "cannot save that"); return true; - case ACT_DETACH: a->detach = true; return true; - case ACT_QUIT: a->quit = true; return true; - default: return false; + } + char said[96]; + snprintf(said, sizeof said, "%s %s \u00b7 %zu pane%s, %zu suspended", + w.replaced ? "replaced" : "wrote", PROJECT_LAYOUT_FILE, w.panes, + w.panes == 1 ? "" : "s", w.suspended); + app_toast(a, said); + return true; + } + case ACT_EDIT_CONFIG: app_edit_config(a); return true; + case ACT_HELP: a->help = !a->help; return true; + case ACT_DETACH: a->detach = true; return true; + case ACT_QUIT: a->quit = true; return true; + default: return false; } } @@ -6105,7 +6145,8 @@ void app_event(app_t *a, const input_event_t *ev) { if (a->prefix) { a->prefix = false; /* prefix twice sends the prefix itself, whatever it is bound to */ - if (is_prefix || config_lookup(&CFG, ev->key, mods) == ACT_LITERAL_PREFIX) { + if (is_prefix || + config_lookup(&CFG, ev->key, mods) == ACT_LITERAL_PREFIX) { input_event_t literal = *ev; literal.key = CFG.prefix_key; literal.mods = CFG.prefix_mods; @@ -6136,199 +6177,197 @@ void app_event(app_t *a, const input_event_t *ev) { } switch (ev->kind) { - case EV_KEY: - /* the first keystroke starts a suspended pane, and is not forwarded */ - if (pane_suspended(cur(a)->focus->pane)) { - pane_start(cur(a)->focus->pane); - break; - } - pane_send_key(cur(a)->focus->pane, ev); + case EV_KEY: + /* the first keystroke starts a suspended pane, and is not forwarded */ + if (pane_suspended(cur(a)->focus->pane)) { + pane_start(cur(a)->focus->pane); break; - case EV_MOUSE: { - /* Mouse routing is a hit-list lookup, never a re-derivation of geometry: + } + pane_send_key(cur(a)->focus->pane, ev); + break; + case EV_MOUSE: { + /* Mouse routing is a hit-list lookup, never a re-derivation of geometry: * the list was filled by the pass that painted what the user clicked. */ - if (!a->painted) break; - const char *action = hit_test(&a->painted->hits, ev->mx, ev->my); + if (!a->painted) break; + const char *action = hit_test(&a->painted->hits, ev->mx, ev->my); - /* Clicking away keeps the name, the way leaving a field commits it. + /* Clicking away keeps the name, the way leaving a field commits it. * Clicking the thing's own label again does not, so a stray second * double-click lands in the editor instead of closing it. */ - if (a->renaming && ev->maction == MOUSE_PRESS) { - char own[48]; - snprintf(own, sizeof own, - a->renaming == RENAME_PANE ? "panetitle:%u" : "tab:%u", - a->rename_id); - if (!action || strcmp(action, own) != 0) rename_end(a, true); - } + if (a->renaming && ev->maction == MOUSE_PRESS) { + char own[48]; + snprintf(own, sizeof own, + a->renaming == RENAME_PANE ? "panetitle:%u" : "tab:%u", + a->rename_id); + if (!action || strcmp(action, own) != 0) rename_end(a, true); + } - if (!a->ptr_valid || ev->mx != a->ptr_x || ev->my != a->ptr_y) - a->ptr_still_since = now_ms_(); - a->ptr_x = ev->mx; - a->ptr_y = ev->my; - a->ptr_valid = true; + if (!a->ptr_valid || ev->mx != a->ptr_x || ev->my != a->ptr_y) + a->ptr_still_since = now_ms_(); + a->ptr_x = ev->mx; + a->ptr_y = ev->my; + a->ptr_valid = true; - if (a->drag.kind != DRAG_NONE) { - if (ev->maction == MOUSE_MOTION) { - a->drag.moved = true; - if (a->drag.kind == DRAG_SELECT) { - node_t *n = pane_by_id(a, a->drag.src); - if (n) - pane_select_extend(n->pane, (uint16_t)(ev->mx - n->content.x), - (uint16_t)(ev->my - n->content.y)); - } else if (a->drag.kind == DRAG_EDGE && - (a->drag.c_nv || a->drag.c_nh)) { - /* One axis to each: the row boundary takes the vertical movement + if (a->drag.kind != DRAG_NONE) { + if (ev->maction == MOUSE_MOTION) { + a->drag.moved = true; + if (a->drag.kind == DRAG_SELECT) { + node_t *n = pane_by_id(a, a->drag.src); + if (n) + pane_select_extend(n->pane, (uint16_t)(ev->mx - n->content.x), + (uint16_t)(ev->my - n->content.y)); + } else if (a->drag.kind == DRAG_EDGE && + (a->drag.c_nv || a->drag.c_nh)) { + /* One axis to each: the row boundary takes the vertical movement * and the column boundaries the horizontal, so the corner follows * the pointer in both at once. The column boundaries above and * below get the same delta, which is what keeps them one line. */ - int dx = (int)ev->mx - (int)a->drag.x; - int dy = (int)ev->my - (int)a->drag.y; - for (size_t i = 0; i < a->drag.c_nh; i++) - drag_edge(a, split_by_id(a, a->drag.c_h[i]), a->drag.c_hedge[i], - dy); - for (size_t i = 0; i < a->drag.c_nv; i++) - drag_edge(a, split_by_id(a, a->drag.c_v[i]), a->drag.c_vedge[i], - dx); - } else if (a->drag.kind == DRAG_EDGE) { - node_t *sp = split_by_id(a, a->drag.src); - int cells = sp && sp->dir == SPLIT_COLS - ? (int)ev->mx - (int)a->drag.x - : (int)ev->my - (int)a->drag.y; - drag_edge(a, sp, a->drag.edge, cells); - } else if (a->drag.kind == DRAG_TAB) { - /* Reordered as you drag, rather than dropped at the end: the + int dx = (int)ev->mx - (int)a->drag.x; + int dy = (int)ev->my - (int)a->drag.y; + for (size_t i = 0; i < a->drag.c_nh; i++) + drag_edge(a, split_by_id(a, a->drag.c_h[i]), a->drag.c_hedge[i], + dy); + for (size_t i = 0; i < a->drag.c_nv; i++) + drag_edge(a, split_by_id(a, a->drag.c_v[i]), a->drag.c_vedge[i], + dx); + } else if (a->drag.kind == DRAG_EDGE) { + node_t *sp = split_by_id(a, a->drag.src); + int cells = sp && sp->dir == SPLIT_COLS + ? (int)ev->mx - (int)a->drag.x + : (int)ev->my - (int)a->drag.y; + drag_edge(a, sp, a->drag.edge, cells); + } else if (a->drag.kind == DRAG_TAB) { + /* Reordered as you drag, rather than dropped at the end: the * strip is the only thing that could show an insertion point, and * a strip that already shows the result needs no such invention. * Off the strip entirely, nothing moves — dragging away is not a * cancel, it is simply not a move. */ - size_t from = tab_index(a, a->drag.src); - if (from != (size_t)-1 && action) { - size_t to = (size_t)-1; - if (strncmp(action, "tab:", 4) == 0) - to = tab_index(a, (uint32_t)strtoul(action + 4, NULL, 10)); - else if (strcmp(action, "newtab") == 0) - to = a->ntabs - 1; /* past the last tab means last */ - if (to != (size_t)-1) move_tab(a, from, to); - } - } else if (a->drag.kind == DRAG_TITLE && action && - strncmp(action, "tab:", 4) == 0) { - /* Over the strip: this pane is going to that tab. */ - a->drag.tab_target = (uint32_t)strtoul(action + 4, NULL, 10); - a->drag.new_tab_target = false; - a->drag.target = 0; - } else if (a->drag.kind == DRAG_TITLE && action && - strcmp(action, "newtab") == 0) { - /* The button that makes a tab, used as somewhere to put one pane. */ - a->drag.new_tab_target = true; - a->drag.tab_target = 0; - a->drag.target = 0; - } else if (action && strncmp(action, "title:", 6) == 0) { - a->drag.target = (uint32_t)strtoul(action + 6, NULL, 10); - a->drag.tab_target = 0; - a->drag.new_tab_target = false; - } else if (action && strncmp(action, "panetitle:", 10) == 0) { - /* Dropping onto a pane's name is dropping onto that pane. */ - a->drag.target = (uint32_t)strtoul(action + 10, NULL, 10); - a->drag.tab_target = 0; - a->drag.new_tab_target = false; - } else if (action && strncmp(action, "pane:", 5) == 0) { - a->drag.target = (uint32_t)strtoul(action + 5, NULL, 10); - a->drag.tab_target = 0; - a->drag.new_tab_target = false; + size_t from = tab_index(a, a->drag.src); + if (from != (size_t)-1 && action) { + size_t to = (size_t)-1; + if (strncmp(action, "tab:", 4) == 0) + to = tab_index(a, (uint32_t)strtoul(action + 4, NULL, 10)); + else if (strcmp(action, "newtab") == 0) + to = a->ntabs - 1; /* past the last tab means last */ + if (to != (size_t)-1) move_tab(a, from, to); } - a->drag.x = ev->mx; - a->drag.y = ev->my; - } else if (ev->maction == MOUSE_RELEASE) { - /* A press that never moved is a click, and a click on an edge + } else if (a->drag.kind == DRAG_TITLE && action && + strncmp(action, "tab:", 4) == 0) { + /* Over the strip: this pane is going to that tab. */ + a->drag.tab_target = (uint32_t)strtoul(action + 4, NULL, 10); + a->drag.new_tab_target = false; + a->drag.target = 0; + } else if (a->drag.kind == DRAG_TITLE && action && + strcmp(action, "newtab") == 0) { + /* The button that makes a tab, used as somewhere to put one pane. */ + a->drag.new_tab_target = true; + a->drag.tab_target = 0; + a->drag.target = 0; + } else if (action && strncmp(action, "title:", 6) == 0) { + a->drag.target = (uint32_t)strtoul(action + 6, NULL, 10); + a->drag.tab_target = 0; + a->drag.new_tab_target = false; + } else if (action && strncmp(action, "panetitle:", 10) == 0) { + /* Dropping onto a pane's name is dropping onto that pane. */ + a->drag.target = (uint32_t)strtoul(action + 10, NULL, 10); + a->drag.tab_target = 0; + a->drag.new_tab_target = false; + } else if (action && strncmp(action, "pane:", 5) == 0) { + a->drag.target = (uint32_t)strtoul(action + 5, NULL, 10); + a->drag.tab_target = 0; + a->drag.new_tab_target = false; + } + a->drag.x = ev->mx; + a->drag.y = ev->my; + } else if (ev->maction == MOUSE_RELEASE) { + /* A press that never moved is a click, and a click on an edge * splits toward it. */ - if (!a->drag.moved && a->drag.side && - (a->drag.kind == DRAG_BORDER || a->drag.kind == DRAG_TITLE)) { - node_t *n = pane_by_id(a, a->drag.src); - if (n) { - char side = a->drag.side; - bool before = side == 'l' || side == 't'; - split_dir_t dir = side_dir(side); - /* The guide already declined to offer this, so the click that + if (!a->drag.moved && a->drag.side && + (a->drag.kind == DRAG_BORDER || a->drag.kind == DRAG_TITLE)) { + node_t *n = pane_by_id(a, a->drag.src); + if (n) { + char side = a->drag.side; + bool before = side == 'l' || side == 't'; + split_dir_t dir = side_dir(side); + /* The guide already declined to offer this, so the click that * the guide would have explained must decline too — otherwise * the border silently does something it just said it would not. */ - if (!split_fits(n, dir)) { - app_toast(a, dir == SPLIT_COLS ? "no room to split across" - : "no room to split down"); - } else { - split_node(a, n, dir, before); - app_toast(a, side == 'l' ? "split left" - : side == 'r' ? "split right" - : side == 't' ? "split up" - : "split down"); - } + if (!split_fits(n, dir)) { + app_toast(a, dir == SPLIT_COLS ? "no room to split across" + : "no room to split down"); + } else { + split_node(a, n, dir, before); + app_toast(a, side == 'l' ? "split left" + : side == 'r' ? "split right" + : side == 't' ? "split up" + : "split down"); } - a->drag.kind = DRAG_NONE; - a->drag.src = a->drag.target = 0; - break; } - if (a->drag.kind == DRAG_SELECT) { - /* Releasing copies, which is the whole point: no menu, no chord, + a->drag.kind = DRAG_NONE; + a->drag.src = a->drag.target = 0; + break; + } + if (a->drag.kind == DRAG_SELECT) { + /* Releasing copies, which is the whole point: no menu, no chord, * the selection *is* the copy. */ - node_t *n = pane_by_id(a, a->drag.src); - if (n) { - set_clipboard(a, pane_selection_text(n->pane)); - pane_select_done(n->pane); - } + node_t *n = pane_by_id(a, a->drag.src); + if (n) { + set_clipboard(a, pane_selection_text(n->pane)); + pane_select_done(n->pane); } - if (a->drag.kind == DRAG_TITLE && - (a->drag.tab_target || a->drag.new_tab_target)) { - /* Dropped on the strip: the pane changes tab rather than places. */ - drop_pane_on_strip(a); - } else if (a->drag.kind == DRAG_TITLE && - a->drag.target != a->drag.src) { - swap_panes(a, a->drag.src, a->drag.target); - } - a->drag.kind = DRAG_NONE; - a->drag.src = a->drag.target = a->drag.tab_target = 0; - a->drag.new_tab_target = false; } - break; /* a drag owns the mouse until the button comes up */ + if (a->drag.kind == DRAG_TITLE && + (a->drag.tab_target || a->drag.new_tab_target)) { + /* Dropped on the strip: the pane changes tab rather than places. */ + drop_pane_on_strip(a); + } else if (a->drag.kind == DRAG_TITLE && + a->drag.target != a->drag.src) { + swap_panes(a, a->drag.src, a->drag.target); + } + a->drag.kind = DRAG_NONE; + a->drag.src = a->drag.target = a->drag.tab_target = 0; + a->drag.new_tab_target = false; } + break; /* a drag owns the mouse until the button comes up */ + } - /* Any press dismisses the cheatsheet, wherever it lands -- including on + /* Any press dismisses the cheatsheet, wherever it lands -- including on * its own close button, which is there because a modal without a way * out that you can *see* is a modal people hunt for the way out of. */ - if (a->help) { - if (ev->maction == MOUSE_PRESS) a->help = false; - break; - } + if (a->help) { + if (ev->maction == MOUSE_PRESS) a->help = false; + break; + } - /* The finder owns the pointer as well as the keyboard. A press on one + /* The finder owns the pointer as well as the keyboard. A press on one * of its rows chooses; a press anywhere else dismisses it and does * nothing further -- clicking past a modal must not also land on the * layout behind it, which is how you end up focusing a pane you were * trying to click *away* from. Motion still falls through, so rows * light up under the pointer. */ - if (a->picker && ev->maction != MOUSE_MOTION) { - bool own = action && (strncmp(action, "find:", 5) == 0 || - strncmp(action, "run:", 4) == 0 || - strncmp(action, "open:", 5) == 0 || - strcmp(action, "closefind") == 0 || - strcmp(action, "closepalette") == 0 || - strcmp(action, "closeprojects") == 0); - if (!own) { - if (ev->maction == MOUSE_PRESS) a->picker = PICK_NONE; - break; - } + if (a->picker && ev->maction != MOUSE_MOTION) { + bool own = action && (strncmp(action, "find:", 5) == 0 || + strncmp(action, "run:", 4) == 0 || + strncmp(action, "open:", 5) == 0 || + strcmp(action, "closefind") == 0 || + strcmp(action, "closepalette") == 0 || + strcmp(action, "closeprojects") == 0); + if (!own) { + if (ev->maction == MOUSE_PRESS) a->picker = PICK_NONE; + break; } - - if (action) do_action(a, action, ev); - break; } - case EV_PASTE: - pane_send_paste(cur(a)->focus->pane, ev->paste, ev->paste_len); - break; - default: - break; + + if (action) do_action(a, action, ev); + break; + } + case EV_PASTE: + pane_send_paste(cur(a)->focus->pane, ev->paste, ev->paste_len); + break; + default: break; } } - /* ---- dumping a session back out as a layout ------------------------------ * * The inverse of apply-layout, and the thing that makes a restart survivable: @@ -6371,8 +6410,10 @@ static void sb_quoted(strbuf_t *b, const char *key, const char *val) { if (!val || !*val) return; sb_add(b, " %s=\"", key); for (const char *p = val; *p; p++) { - if (*p == '"' || *p == '\\') sb_add(b, "\\%c", *p); - else if ((unsigned char)*p >= 0x20) sb_add(b, "%c", *p); + if (*p == '"' || *p == '\\') + sb_add(b, "\\%c", *p); + else if ((unsigned char)*p >= 0x20) + sb_add(b, "%c", *p); } sb_add(b, "\""); } @@ -6438,10 +6479,10 @@ static const char *dump_command(const node_t *n, char *buf, size_t cap) { * `suspended=true` under this policy would start a dev server on every open. */ static bool dump_suspended(const char *command, const node_t *n, int policy) { switch (policy) { - case DUMP_SUSPEND_NONE: return false; - case DUMP_SUSPEND_ALL: return true; - case DUMP_SUSPEND_COMMANDS: return command && *command; - default: return pane_suspended(n->pane); + case DUMP_SUSPEND_NONE: return false; + case DUMP_SUSPEND_ALL: return true; + case DUMP_SUSPEND_COMMANDS: return command && *command; + default: return pane_suspended(n->pane); } } @@ -147,12 +147,12 @@ size_t app_projects(project_t *out, size_t max); uint32_t app_workspace_tab(app_t *a, const char *slug); typedef struct { - uint32_t tab; /* the tab to land in */ - char purpose[64]; /* the workspace's identity */ - char path[512]; /* the project on disk */ - size_t tabs; /* tabs it adopted */ - size_t honoured; /* tabs whose own declared purpose was left alone */ - bool created; /* false when it was already open and was focused */ + uint32_t tab; /* the tab to land in */ + char purpose[64]; /* the workspace's identity */ + char path[512]; /* the project on disk */ + size_t tabs; /* tabs it adopted */ + size_t honoured; /* tabs whose own declared purpose was left alone */ + bool created; /* false when it was already open and was focused */ } app_workspace_open_t; /* Open a project's layout as a workspace, or focus the one already open -- @@ -167,10 +167,10 @@ bool app_workspace_open(app_t *a, const char *name, bool suspended, size_t app_workspace_close(app_t *a, const char *slug); typedef struct { - char path[512]; /* the file written */ - char purpose[64]; /* the workspace the tab now carries */ + char path[512]; /* the file written */ + char purpose[64]; /* the workspace the tab now carries */ size_t panes, suspended; - bool replaced; /* there was a layout there before */ + bool replaced; /* there was a layout there before */ } app_workspace_save_t; /* Write one tab out as a project's layout: the same dump `dump-layout` answers @@ -205,7 +205,8 @@ bool app_minimize(app_t *a, uint32_t id); * A move drops what the old tab thought about the pane: a zoom naming it, and its * minimised flag. Carried across, the first would zoom a pane that has left and the * second would file the arrival in a strip nobody asked for. */ -bool app_move_pane_to_tab(app_t *a, uint32_t pane_id, uint32_t tab_id, bool rows); +bool app_move_pane_to_tab(app_t *a, uint32_t pane_id, uint32_t tab_id, + bool rows); /* The same, into a tab of its own; returns that tab's id, or 0 -- including for a * pane that is already alone in its tab, which has nowhere to go. */ diff --git a/src/client.c b/src/client.c index 2c22af9..b1155b8 100644 --- a/src/client.c +++ b/src/client.c @@ -16,14 +16,14 @@ #include "proto.h" -#define TERM_ENTER \ - "\x1b[?1049h\x1b[0m\x1b[2J" \ - "\x1b[?1002h\x1b[?1003h\x1b[?1006h" \ - "\x1b[?1004h" \ - "\x1b[?2004h" \ +#define TERM_ENTER \ + "\x1b[?1049h\x1b[0m\x1b[2J" \ + "\x1b[?1002h\x1b[?1003h\x1b[?1006h" \ + "\x1b[?1004h" \ + "\x1b[?2004h" \ "\x1b[>1u" -#define TERM_LEAVE \ - "\x1b[<u\x1b[?2004l\x1b[?1004l\x1b[?1006l\x1b[?1003l\x1b[?1002l" \ +#define TERM_LEAVE \ + "\x1b[<u\x1b[?2004l\x1b[?1004l\x1b[?1006l\x1b[?1003l\x1b[?1002l" \ "\x1b[?25h\x1b[0m\x1b[?1049l" static volatile sig_atomic_t g_winch = 0, g_quit = 0; @@ -31,8 +31,10 @@ static struct termios g_saved; static bool g_saved_ok = false; static void on_sig(int s) { - if (s == SIGWINCH) g_winch = 1; - else g_quit = 1; + if (s == SIGWINCH) + g_winch = 1; + else + g_quit = 1; } static void restore(void) { @@ -160,8 +162,10 @@ int client_run(int fd) { restore(); g_saved_ok = false; - if (exit_reason == EXIT_DETACHED) fprintf(stderr, "[detached]\n"); - else if (exit_reason == EXIT_REPLACED) fprintf(stderr, "[replaced by another client]\n"); + if (exit_reason == EXIT_DETACHED) + fprintf(stderr, "[detached]\n"); + else if (exit_reason == EXIT_REPLACED) + fprintf(stderr, "[replaced by another client]\n"); return 0; } @@ -21,21 +21,21 @@ size_t cmd_unescape(const char *in, uint8_t *out, size_t cap) { } p++; switch (*p) { - case 'e': out[n++] = 0x1b; break; - case 'n': out[n++] = '\n'; break; - case 'r': out[n++] = '\r'; break; - case 't': out[n++] = '\t'; break; - case '0': out[n++] = 0; break; - case '\\': out[n++] = '\\'; break; - case 'x': { - int hi = 0, lo = 0; - sscanf(p + 1, "%1x%1x", &hi, &lo); - out[n++] = (uint8_t)(hi * 16 + lo); - p += 2; - break; - } - case 0: return n; - default: out[n++] = (uint8_t)*p; break; + case 'e': out[n++] = 0x1b; break; + case 'n': out[n++] = '\n'; break; + case 'r': out[n++] = '\r'; break; + case 't': out[n++] = '\t'; break; + case '0': out[n++] = 0; break; + case '\\': out[n++] = '\\'; break; + case 'x': { + int hi = 0, lo = 0; + sscanf(p + 1, "%1x%1x", &hi, &lo); + out[n++] = (uint8_t)(hi * 16 + lo); + p += 2; + break; + } + case 0: return n; + default: out[n++] = (uint8_t)*p; break; } } return n; @@ -195,7 +195,8 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in, uint32_t tid = (uint32_t)jv_geti(req, "tab", 0); bool rows = strcmp(jv_gets(req, "dir", "cols"), "rows") == 0; if (!tid) { - uint32_t made = app_move_pane_to_new_tab(a, pid, jv_gets(req, "name", "")); + uint32_t made = + app_move_pane_to_new_tab(a, pid, jv_gets(req, "name", "")); if (!made) return jerr("cannot move it to a tab of its own"); return jok_int("tab", (long long)made); } @@ -238,8 +239,8 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in, uint32_t id = (uint32_t)jv_geti(req, "id", 0); const char *name = jv_gets(req, "name", ""); bool pane = strcmp(target, "pane") == 0; - bool ok = pane ? app_set_pane_name(a, id, name) - : app_set_tab_name(a, id, name); + bool ok = + pane ? app_set_pane_name(a, id, name) : app_set_tab_name(a, id, name); return ok ? jok_int(NULL, 0) : jerr(pane ? "no such pane" : "no such tab"); } if (strcmp(cmd, "set-purpose") == 0) { @@ -257,7 +258,8 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in, if (strcmp(cmd, "dump-layout") == 0) { dump_layout_t o = {.tab = (uint32_t)jv_geti(req, "tab", 0), .base = jv_gets(req, "relative_to", NULL)}; - o.suspend = suspend_policy(jv_gets(req, "suspend", NULL), DUMP_SUSPEND_ASIS); + o.suspend = + suspend_policy(jv_gets(req, "suspend", NULL), DUMP_SUSPEND_ASIS); if (o.suspend < 0) return jerr("suspend is as-is, none, commands or all"); char *kdl = app_dump_layout(a, &o); if (o.tab && !o.tabs) { @@ -280,9 +282,9 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in, const char *text = jv_gets(req, "kdl", NULL); bool replace = jv_getb(req, "replace", false); char err[256] = {0}; - bool ok = text ? app_apply_layout_text(a, text, replace, err, sizeof err) - : path ? app_apply_layout_file(a, path, replace, err, sizeof err) - : false; + bool ok = text ? app_apply_layout_text(a, text, replace, err, sizeof err) + : path ? app_apply_layout_file(a, path, replace, err, sizeof err) + : false; if (!ok) return jerr(err[0] ? err : "need a path or kdl"); app_resize(a, s->cols, s->rows); s->force_full = true; @@ -368,8 +370,8 @@ static char *cmd_json(app_t *a, screen_t *s, input_parser_t *in, return jok_int("closed", (long long)n); } if (strcmp(cmd, "save-workspace") == 0) { - int suspend = suspend_policy(jv_gets(req, "suspend", NULL), - DUMP_SUSPEND_COMMANDS); + int suspend = + suspend_policy(jv_gets(req, "suspend", NULL), DUMP_SUSPEND_COMMANDS); if (suspend < 0) return jerr("suspend is as-is, none, commands or all"); app_workspace_save_t w; char err[256] = {0}; @@ -523,7 +525,8 @@ char *cmd_exec(app_t *a, screen_t *s, input_parser_t *in, const char *line, return app_tabs_json(a); } if (strcmp(verb, "dump-layout") == 0) { - app_compose(a, s); /* the layout is a function of the frame: compose first */ + app_compose(a, + s); /* the layout is a function of the frame: compose first */ return app_dump_layout(a, NULL); } /* The bare forms the harness and a person at a shell use. Same functions, so diff --git a/src/config.c b/src/config.c index 556f7f8..4958883 100644 --- a/src/config.c +++ b/src/config.c @@ -16,32 +16,44 @@ static const struct { action_t action; } ACTIONS[] = { {"split", ACT_SPLIT}, - {"split-cols", ACT_SPLIT_COLS}, {"split-rows", ACT_SPLIT_ROWS}, - {"close-pane", ACT_CLOSE_PANE}, {"rerun", ACT_RERUN}, + {"split-cols", ACT_SPLIT_COLS}, + {"split-rows", ACT_SPLIT_ROWS}, + {"close-pane", ACT_CLOSE_PANE}, + {"rerun", ACT_RERUN}, {"zoom", ACT_ZOOM}, - {"minimize", ACT_MINIMIZE}, {"set-purpose", ACT_SET_PURPOSE}, + {"minimize", ACT_MINIMIZE}, + {"set-purpose", ACT_SET_PURPOSE}, {"focus-left", ACT_FOCUS_LEFT}, - {"focus-right", ACT_FOCUS_RIGHT}, {"focus-up", ACT_FOCUS_UP}, - {"focus-down", ACT_FOCUS_DOWN}, {"focus-next", ACT_FOCUS_NEXT}, - {"resize-left", ACT_RESIZE_LEFT}, {"resize-right", ACT_RESIZE_RIGHT}, - {"resize-up", ACT_RESIZE_UP}, {"resize-down", ACT_RESIZE_DOWN}, + {"focus-right", ACT_FOCUS_RIGHT}, + {"focus-up", ACT_FOCUS_UP}, + {"focus-down", ACT_FOCUS_DOWN}, + {"focus-next", ACT_FOCUS_NEXT}, + {"resize-left", ACT_RESIZE_LEFT}, + {"resize-right", ACT_RESIZE_RIGHT}, + {"resize-up", ACT_RESIZE_UP}, + {"resize-down", ACT_RESIZE_DOWN}, {"equalize", ACT_EQUALIZE}, {"clear-shaders", ACT_CLEAR_SHADERS}, {"pane-to-next-tab", ACT_PANE_TO_NEXT_TAB}, {"pane-to-prev-tab", ACT_PANE_TO_PREV_TAB}, {"pane-to-new-tab", ACT_PANE_TO_NEW_TAB}, {"rotate-layout", ACT_ROTATE_LAYOUT}, - {"scroll-up", ACT_SCROLL_UP}, {"scroll-down", ACT_SCROLL_DOWN}, + {"scroll-up", ACT_SCROLL_UP}, + {"scroll-down", ACT_SCROLL_DOWN}, {"scroll-page-up", ACT_SCROLL_PAGE_UP}, {"scroll-page-down", ACT_SCROLL_PAGE_DOWN}, - {"scroll-top", ACT_SCROLL_TOP}, {"scroll-bottom", ACT_SCROLL_BOTTOM}, - {"new-tab", ACT_NEW_TAB}, {"next-tab", ACT_NEXT_TAB}, + {"scroll-top", ACT_SCROLL_TOP}, + {"scroll-bottom", ACT_SCROLL_BOTTOM}, + {"new-tab", ACT_NEW_TAB}, + {"next-tab", ACT_NEXT_TAB}, {"close-tab", ACT_CLOSE_TAB}, - {"prev-tab", ACT_PREV_TAB}, {"finder", ACT_FINDER}, + {"prev-tab", ACT_PREV_TAB}, + {"finder", ACT_FINDER}, {"palette", ACT_PALETTE}, {"workspaces", ACT_WORKSPACES}, {"save-workspace", ACT_SAVE_WORKSPACE}, - {"detach", ACT_DETACH}, {"quit", ACT_QUIT}, + {"detach", ACT_DETACH}, + {"quit", ACT_QUIT}, {"literal-prefix", ACT_LITERAL_PREFIX}, {"help", ACT_HELP}, {"edit-config", ACT_EDIT_CONFIG}, @@ -51,16 +63,26 @@ static const struct { const char *name; int key; } NAMED_KEYS[] = { - {"left", GHOSTTY_KEY_ARROW_LEFT}, {"right", GHOSTTY_KEY_ARROW_RIGHT}, - {"up", GHOSTTY_KEY_ARROW_UP}, {"down", GHOSTTY_KEY_ARROW_DOWN}, - {"enter", GHOSTTY_KEY_ENTER}, {"tab", GHOSTTY_KEY_TAB}, - {"escape", GHOSTTY_KEY_ESCAPE}, {"space", GHOSTTY_KEY_SPACE}, - {"backspace", GHOSTTY_KEY_BACKSPACE}, {"home", GHOSTTY_KEY_HOME}, - {"end", GHOSTTY_KEY_END}, {"pageup", GHOSTTY_KEY_PAGE_UP}, - {"pagedown", GHOSTTY_KEY_PAGE_DOWN},{"delete", GHOSTTY_KEY_DELETE}, - {"insert", GHOSTTY_KEY_INSERT}, {"backslash", GHOSTTY_KEY_BACKSLASH}, - {"minus", GHOSTTY_KEY_MINUS}, {"slash", GHOSTTY_KEY_SLASH}, - {"comma", GHOSTTY_KEY_COMMA}, {"period", GHOSTTY_KEY_PERIOD}, + {"left", GHOSTTY_KEY_ARROW_LEFT}, + {"right", GHOSTTY_KEY_ARROW_RIGHT}, + {"up", GHOSTTY_KEY_ARROW_UP}, + {"down", GHOSTTY_KEY_ARROW_DOWN}, + {"enter", GHOSTTY_KEY_ENTER}, + {"tab", GHOSTTY_KEY_TAB}, + {"escape", GHOSTTY_KEY_ESCAPE}, + {"space", GHOSTTY_KEY_SPACE}, + {"backspace", GHOSTTY_KEY_BACKSPACE}, + {"home", GHOSTTY_KEY_HOME}, + {"end", GHOSTTY_KEY_END}, + {"pageup", GHOSTTY_KEY_PAGE_UP}, + {"pagedown", GHOSTTY_KEY_PAGE_DOWN}, + {"delete", GHOSTTY_KEY_DELETE}, + {"insert", GHOSTTY_KEY_INSERT}, + {"backslash", GHOSTTY_KEY_BACKSLASH}, + {"minus", GHOSTTY_KEY_MINUS}, + {"slash", GHOSTTY_KEY_SLASH}, + {"comma", GHOSTTY_KEY_COMMA}, + {"period", GHOSTTY_KEY_PERIOD}, }; /* A character to a key, and whether typing it needs shift. @@ -79,37 +101,37 @@ static int key_from_char(char c, bool *shifted) { } if (c >= '0' && c <= '9') return GHOSTTY_KEY_DIGIT_0 + (c - '0'); switch (c) { - case '\\': return GHOSTTY_KEY_BACKSLASH; - case '-': return GHOSTTY_KEY_MINUS; - case '=': return GHOSTTY_KEY_EQUAL; - case '[': return GHOSTTY_KEY_BRACKET_LEFT; - case ']': return GHOSTTY_KEY_BRACKET_RIGHT; - case ';': return GHOSTTY_KEY_SEMICOLON; - case '\'': return GHOSTTY_KEY_QUOTE; - case ',': return GHOSTTY_KEY_COMMA; - case '.': return GHOSTTY_KEY_PERIOD; - case '/': return GHOSTTY_KEY_SLASH; - case '`': return GHOSTTY_KEY_BACKQUOTE; - case ' ': return GHOSTTY_KEY_SPACE; - default: break; + case '\\': return GHOSTTY_KEY_BACKSLASH; + case '-': return GHOSTTY_KEY_MINUS; + case '=': return GHOSTTY_KEY_EQUAL; + case '[': return GHOSTTY_KEY_BRACKET_LEFT; + case ']': return GHOSTTY_KEY_BRACKET_RIGHT; + case ';': return GHOSTTY_KEY_SEMICOLON; + case '\'': return GHOSTTY_KEY_QUOTE; + case ',': return GHOSTTY_KEY_COMMA; + case '.': return GHOSTTY_KEY_PERIOD; + case '/': return GHOSTTY_KEY_SLASH; + case '`': return GHOSTTY_KEY_BACKQUOTE; + case ' ': return GHOSTTY_KEY_SPACE; + default: break; } /* The shifted half of the same keys, which config.kdl has always said were * writable and the parser has always refused: `bind "?" "help"` is in the * file it ships and in every config `--dump-config` has ever written. */ if (shifted) *shifted = true; switch (c) { - case '|': return GHOSTTY_KEY_BACKSLASH; - case '_': return GHOSTTY_KEY_MINUS; - case '+': return GHOSTTY_KEY_EQUAL; - case '{': return GHOSTTY_KEY_BRACKET_LEFT; - case '}': return GHOSTTY_KEY_BRACKET_RIGHT; - case ':': return GHOSTTY_KEY_SEMICOLON; - case '"': return GHOSTTY_KEY_QUOTE; - case '<': return GHOSTTY_KEY_COMMA; - case '>': return GHOSTTY_KEY_PERIOD; - case '?': return GHOSTTY_KEY_SLASH; - case '~': return GHOSTTY_KEY_BACKQUOTE; - default: break; + case '|': return GHOSTTY_KEY_BACKSLASH; + case '_': return GHOSTTY_KEY_MINUS; + case '+': return GHOSTTY_KEY_EQUAL; + case '{': return GHOSTTY_KEY_BRACKET_LEFT; + case '}': return GHOSTTY_KEY_BRACKET_RIGHT; + case ':': return GHOSTTY_KEY_SEMICOLON; + case '"': return GHOSTTY_KEY_QUOTE; + case '<': return GHOSTTY_KEY_COMMA; + case '>': return GHOSTTY_KEY_PERIOD; + case '?': return GHOSTTY_KEY_SLASH; + case '~': return GHOSTTY_KEY_BACKQUOTE; + default: break; } if (shifted) *shifted = false; return GHOSTTY_KEY_UNIDENTIFIED; @@ -120,8 +142,10 @@ static const struct { const char *glyph; int key; } ARROW_GLYPHS[] = { - {"\u2190", GHOSTTY_KEY_ARROW_LEFT}, {"\u2192", GHOSTTY_KEY_ARROW_RIGHT}, - {"\u2191", GHOSTTY_KEY_ARROW_UP}, {"\u2193", GHOSTTY_KEY_ARROW_DOWN}, + {"\u2190", GHOSTTY_KEY_ARROW_LEFT}, + {"\u2192", GHOSTTY_KEY_ARROW_RIGHT}, + {"\u2191", GHOSTTY_KEY_ARROW_UP}, + {"\u2193", GHOSTTY_KEY_ARROW_DOWN}, }; /* `implied` comes back true when the shift in `out_mods` came from the character @@ -136,17 +160,33 @@ static bool parse_chord_ex(const char *text, int *out_key, uint16_t *out_mods, const char *p = text; if (implied) *implied = false; for (;;) { - if (strncmp(p, "ctrl+", 5) == 0) { mods |= MOD_CTRL; p += 5; } - else if (strncmp(p, "alt+", 4) == 0) { mods |= MOD_ALT; p += 4; } - else if (strncmp(p, "shift+", 6) == 0) { mods |= MOD_SHIFT; p += 6; } - else if (strncmp(p, "super+", 6) == 0) { mods |= MOD_SUPER; p += 6; } + if (strncmp(p, "ctrl+", 5) == 0) { + mods |= MOD_CTRL; + p += 5; + } else if (strncmp(p, "alt+", 4) == 0) { + mods |= MOD_ALT; + p += 4; + } else if (strncmp(p, "shift+", 6) == 0) { + mods |= MOD_SHIFT; + p += 6; + } else if (strncmp(p, "super+", 6) == 0) { + mods |= MOD_SUPER; + p += 6; + } /* The cheatsheet's own shorthand. It prints `C-a`, `M-x`, `S-tab`, and the * whole claim of that sheet is that what it shows is what you would write * -- which was false for every chord it printed with a modifier. */ - else if (strncmp(p, "C-", 2) == 0) { mods |= MOD_CTRL; p += 2; } - else if (strncmp(p, "M-", 2) == 0) { mods |= MOD_ALT; p += 2; } - else if (strncmp(p, "S-", 2) == 0) { mods |= MOD_SHIFT; p += 2; } - else break; + else if (strncmp(p, "C-", 2) == 0) { + mods |= MOD_CTRL; + p += 2; + } else if (strncmp(p, "M-", 2) == 0) { + mods |= MOD_ALT; + p += 2; + } else if (strncmp(p, "S-", 2) == 0) { + mods |= MOD_SHIFT; + p += 2; + } else + break; } if (!*p) return false; @@ -218,7 +258,8 @@ static void complain(config_t *c, char *err, size_t errcap, int line, const char *file = c && c->loading ? c->loading : NULL; const char *base = file ? strrchr(file, '/') : NULL; if (file && line > 0) - snprintf(full, sizeof full, "%s:%d: %s", base ? base + 1 : file, line, text); + snprintf(full, sizeof full, "%s:%d: %s", base ? base + 1 : file, line, + text); else if (file) snprintf(full, sizeof full, "%s: %s", base ? base + 1 : file, text); else @@ -236,8 +277,8 @@ size_t config_messages(const config_t *c, const char **out, size_t max) { } static const char *const PSTATE_NAMES[PSTATE_COUNT] = { - "dragging", "drop_hover", "drop_target", "dead", "suspended", - "bell", "scrolled", "unfocused", + "dragging", "drop_hover", "drop_target", "dead", + "suspended", "bell", "scrolled", "unfocused", }; const char *pane_state_name(pane_state_t s) { @@ -257,8 +298,8 @@ const char *pane_state_name(pane_state_t s) { * rather than run at some guessed strength. */ static bool parse_shader_entry(const kdl_node_t *k, color_t default_color, bool default_chrome, bool *on_chrome, - shader_t *out, - expr_prog_t **expr, char *why, size_t whycap) { + shader_t *out, expr_prog_t **expr, char *why, + size_t whycap) { *expr = NULL; /* Every parameter a shader takes is a property, so a bare argument is not a @@ -267,7 +308,8 @@ static bool parse_shader_entry(const kdl_node_t *k, color_t default_color, * spare word, and answering "ok" to that is how you spend ten minutes * wondering why the second one does nothing. */ if (k->nargs) { - snprintf(why, whycap, "unexpected argument for %s: %s (separate entries with `;`)", + snprintf(why, whycap, + "unexpected argument for %s: %s (separate entries with `;`)", k->name, kdl_arg(k, 0, "")); return false; } @@ -276,7 +318,8 @@ static bool parse_shader_entry(const kdl_node_t *k, color_t default_color, * one we know, for the same reason a bad `amount` is: the entry says what it * wants and we cannot do it, and running it over the contents because "chrom" * was a typo would be a surprise nobody asked for. */ - const char *where = kdl_prop(k, "where", default_chrome ? "chrome" : "content"); + const char *where = + kdl_prop(k, "where", default_chrome ? "chrome" : "content"); *on_chrome = strcmp(where, "chrome") == 0; if (!*on_chrome && strcmp(where, "content") != 0) { snprintf(why, whycap, "bad where for %s: %s (content or chrome)", k->name, @@ -290,8 +333,10 @@ static bool parse_shader_entry(const kdl_node_t *k, color_t default_color, * rectangle. Refused the same way a bad `where` is, and for the same reason. */ const char *chan = kdl_prop(k, "channel", "both"); uint8_t channels = SHADE_BOTH; - if (strcmp(chan, "fg") == 0) channels = SHADE_FG; - else if (strcmp(chan, "bg") == 0) channels = SHADE_BG; + if (strcmp(chan, "fg") == 0) + channels = SHADE_FG; + else if (strcmp(chan, "bg") == 0) + channels = SHADE_BG; else if (strcmp(chan, "both") != 0) { snprintf(why, whycap, "bad channel for %s: %s (fg, bg or both)", k->name, chan); @@ -350,7 +395,8 @@ static bool parse_shader_entry(const kdl_node_t *k, color_t default_color, /* A bad colour is worth saying and not worth dropping the pass over: the * shader runs at the default colour, which is visible, rather than not at * all, which is not. */ - if (bad_colour) snprintf(why, whycap, "bad colour for shader %s: %s", k->name, cs); + if (bad_colour) + snprintf(why, whycap, "bad colour for shader %s: %s", k->name, cs); return true; } @@ -404,9 +450,10 @@ static void parse_shader_list(config_t *c, const kdl_node_t *node, * for their rect are dropped, and the first refusal lands in `err`. */ static size_t route_entries(const kdl_node_t *const *kids, size_t nkids, color_t default_color, bool default_chrome, - shader_t *content, size_t *ncontent, shader_t *chrome, - size_t *nchrome, expr_prog_t **exprs, size_t *nexprs, - char *err, size_t errcap) { + shader_t *content, size_t *ncontent, + shader_t *chrome, size_t *nchrome, + expr_prog_t **exprs, size_t *nexprs, char *err, + size_t errcap) { size_t total = 0; for (size_t i = 0; i < nkids; i++) { const kdl_node_t *k = kids[i]; @@ -443,9 +490,9 @@ static size_t route_entries(const kdl_node_t *const *kids, size_t nkids, * parser, so what you prototype is what you can paste and the other way round. */ size_t config_parse_chain_doc(const char *text, color_t default_color, bool default_chrome, shader_t *content, - size_t *ncontent, shader_t *chrome, size_t *nchrome, - expr_prog_t **exprs, size_t *nexprs, char *err, - size_t errcap) { + size_t *ncontent, shader_t *chrome, + size_t *nchrome, expr_prog_t **exprs, + size_t *nexprs, char *err, size_t errcap) { *ncontent = 0; *nchrome = 0; *nexprs = 0; @@ -455,7 +502,8 @@ size_t config_parse_chain_doc(const char *text, color_t default_color, char perr[192] = {0}; kdl_node_t *root = kdl_parse(text, perr, sizeof perr); if (!root) { - if (err && errcap) snprintf(err, errcap, "%s", perr[0] ? perr : "bad chain"); + if (err && errcap) + snprintf(err, errcap, "%s", perr[0] ? perr : "bad chain"); return 0; } @@ -467,13 +515,13 @@ size_t config_parse_chain_doc(const char *text, color_t default_color, const kdl_node_t *k = root->kids[i]; if (k && k->name && strcmp(k->name, "shaders") == 0) block = k; } - const kdl_node_t *const *kids = - block ? (const kdl_node_t *const *)block->kids - : (const kdl_node_t *const *)root->kids; + const kdl_node_t *const *kids = block ? (const kdl_node_t *const *)block->kids + : (const kdl_node_t *const *)root->kids; size_t nkids = block ? block->nkids : root->nkids; - size_t n = route_entries(kids, nkids, default_color, default_chrome, content, - ncontent, chrome, nchrome, exprs, nexprs, err, errcap); + size_t n = + route_entries(kids, nkids, default_color, default_chrome, content, + ncontent, chrome, nchrome, exprs, nexprs, err, errcap); kdl_free(root); return n; } @@ -492,8 +540,8 @@ size_t config_parse_chain_doc(const char *text, color_t default_color, size_t config_parse_chain_file(const char *path, color_t default_color, shader_t *content, size_t *ncontent, shader_t *chrome, size_t *nchrome, - expr_prog_t **exprs, size_t *nexprs, - char *err, size_t errcap) { + expr_prog_t **exprs, size_t *nexprs, char *err, + size_t errcap) { *ncontent = 0; *nchrome = 0; *nexprs = 0; @@ -595,8 +643,7 @@ static const struct { {"toast_bg", offsetof(config_t, toast_bg)}, }; -#define THEME_COLOR(c, i) \ - ((color_t *)((char *)(c) + THEME_COLORS[i].off)) +#define THEME_COLOR(c, i) ((color_t *)((char *)(c) + THEME_COLORS[i].off)) static void bind_add(config_t *c, int key, uint16_t mods, action_t action, bool direct) { @@ -672,10 +719,11 @@ void config_defaults(config_t *c) { * * Single width, and not emoji: many terminals draw those two columns wide * while chrome here is booked as one. */ - snprintf(c->zoom_mark, sizeof c->zoom_mark, "\u25a1"); /* □ fill the tab */ - snprintf(c->zoom_on_mark, sizeof c->zoom_on_mark, "\u25a0"); /* ■ ...and back */ - snprintf(c->close_mark, sizeof c->close_mark, "\u2715"); /* ✕ close */ - snprintf(c->min_mark, sizeof c->min_mark, "\u25ac"); /* ▬ into the strip */ + snprintf(c->zoom_mark, sizeof c->zoom_mark, "\u25a1"); /* □ fill the tab */ + snprintf(c->zoom_on_mark, sizeof c->zoom_on_mark, + "\u25a0"); /* ■ ...and back */ + snprintf(c->close_mark, sizeof c->close_mark, "\u2715"); /* ✕ close */ + snprintf(c->min_mark, sizeof c->min_mark, "\u25ac"); /* ▬ into the strip */ snprintf(c->newtab_mark, sizeof c->newtab_mark, "+"); c->bell_indicator = true; snprintf(c->bell_mark, sizeof c->bell_mark, "\u2022"); @@ -936,8 +984,8 @@ void config_defaults(config_t *c) { bind_add(c, GHOSTTY_KEY_D, 0, ACT_DETACH, false); bind_add(c, GHOSTTY_KEY_Q, 0, ACT_QUIT, false); for (int i = 0; i < 9; i++) - bind_add(c, GHOSTTY_KEY_DIGIT_1 + i, 0, - (action_t)(ACT_SELECT_TAB_1 + i), false); + bind_add(c, GHOSTTY_KEY_DIGIT_1 + i, 0, (action_t)(ACT_SELECT_TAB_1 + i), + false); } void config_free(config_t *c) { @@ -953,7 +1001,6 @@ void config_free(config_t *c) { memset(c, 0, sizeof *c); } - /* ---- naming things, for the cheatsheet ---------------------------------- */ /* Phrases rather than the config's names: `split-cols` is what you write, @@ -1063,10 +1110,14 @@ void config_chord_name(int key, uint16_t mods, char *out, size_t cap) { int key; const char *plain, *shifted; } PUNCT[] = { - {GHOSTTY_KEY_SLASH, "/", "?"}, {GHOSTTY_KEY_BACKSLASH, "\\", "|"}, - {GHOSTTY_KEY_MINUS, "-", "_"}, {GHOSTTY_KEY_EQUAL, "=", "+"}, - {GHOSTTY_KEY_COMMA, ",", "<"}, {GHOSTTY_KEY_PERIOD, ".", ">"}, - {GHOSTTY_KEY_SEMICOLON, ";", ":"}, {GHOSTTY_KEY_QUOTE, "'", "\""}, + {GHOSTTY_KEY_SLASH, "/", "?"}, + {GHOSTTY_KEY_BACKSLASH, "\\", "|"}, + {GHOSTTY_KEY_MINUS, "-", "_"}, + {GHOSTTY_KEY_EQUAL, "=", "+"}, + {GHOSTTY_KEY_COMMA, ",", "<"}, + {GHOSTTY_KEY_PERIOD, ".", ">"}, + {GHOSTTY_KEY_SEMICOLON, ";", ":"}, + {GHOSTTY_KEY_QUOTE, "'", "\""}, {GHOSTTY_KEY_BRACKET_LEFT, "[", "{"}, {GHOSTTY_KEY_BRACKET_RIGHT, "]", "}"}, {GHOSTTY_KEY_BACKQUOTE, "`", "~"}, @@ -1077,8 +1128,10 @@ void config_chord_name(int key, uint16_t mods, char *out, size_t cap) { int key; const char *glyph; } ARROWS[] = { - {GHOSTTY_KEY_ARROW_LEFT, "\u2190"}, {GHOSTTY_KEY_ARROW_RIGHT, "\u2192"}, - {GHOSTTY_KEY_ARROW_UP, "\u2191"}, {GHOSTTY_KEY_ARROW_DOWN, "\u2193"}, + {GHOSTTY_KEY_ARROW_LEFT, "\u2190"}, + {GHOSTTY_KEY_ARROW_RIGHT, "\u2192"}, + {GHOSTTY_KEY_ARROW_UP, "\u2191"}, + {GHOSTTY_KEY_ARROW_DOWN, "\u2193"}, }; for (size_t i = 0; i < sizeof PUNCT / sizeof *PUNCT; i++) @@ -1089,7 +1142,8 @@ void config_chord_name(int key, uint16_t mods, char *out, size_t cap) { if (shifted) mods &= (uint16_t)~MOD_SHIFT; /* spent on the glyph */ } for (size_t i = 0; i < sizeof ARROWS / sizeof *ARROWS; i++) - if (ARROWS[i].key == key) snprintf(base, sizeof base, "%s", ARROWS[i].glyph); + if (ARROWS[i].key == key) + snprintf(base, sizeof base, "%s", ARROWS[i].glyph); if (!base[0]) for (size_t i = 0; i < sizeof NAMED_KEYS / sizeof *NAMED_KEYS; i++) @@ -1108,14 +1162,14 @@ void config_chord_name(int key, uint16_t mods, char *out, size_t cap) { } } if (!base[0] && key >= GHOSTTY_KEY_DIGIT_0 && key <= GHOSTTY_KEY_DIGIT_9) - snprintf(base, sizeof base, "%c", (char)('0' + (key - GHOSTTY_KEY_DIGIT_0))); + snprintf(base, sizeof base, "%c", + (char)('0' + (key - GHOSTTY_KEY_DIGIT_0))); if (!base[0]) snprintf(base, sizeof base, "?"); snprintf(out, cap, "%s%s%s%s", mods & MOD_CTRL ? "C-" : "", mods & MOD_ALT ? "M-" : "", mods & MOD_SHIFT ? "S-" : "", base); } - /* ---- rendering a config back out ---------------------------------------- * * Every knob with its default, as a file you could have written -- the name @@ -1167,8 +1221,10 @@ static const char *yesno(bool v) { return v ? "true" : "false"; } static void cb_qval(cfgbuf_t *b, const char *val) { cb_add(b, "\""); for (const char *q = val; q && *q; q++) { - if (*q == '"' || *q == '\\') cb_add(b, "\\%c", *q); - else cb_add(b, "%c", *q); + if (*q == '"' || *q == '\\') + cb_add(b, "\\%c", *q); + else + cb_add(b, "%c", *q); } cb_add(b, "\""); } @@ -1196,10 +1252,14 @@ static void cb_qstr(cfgbuf_t *b, const char *key, const char *val, static void cb_chord(cfgbuf_t *b, int key, uint16_t mods) { char chord[32] = {0}; size_t n = 0; - if (mods & MOD_CTRL) n += (size_t)snprintf(chord + n, sizeof chord - n, "ctrl+"); - if (mods & MOD_ALT) n += (size_t)snprintf(chord + n, sizeof chord - n, "alt+"); - if (mods & MOD_SHIFT) n += (size_t)snprintf(chord + n, sizeof chord - n, "shift+"); - if (mods & MOD_SUPER) n += (size_t)snprintf(chord + n, sizeof chord - n, "super+"); + if (mods & MOD_CTRL) + n += (size_t)snprintf(chord + n, sizeof chord - n, "ctrl+"); + if (mods & MOD_ALT) + n += (size_t)snprintf(chord + n, sizeof chord - n, "alt+"); + if (mods & MOD_SHIFT) + n += (size_t)snprintf(chord + n, sizeof chord - n, "shift+"); + if (mods & MOD_SUPER) + n += (size_t)snprintf(chord + n, sizeof chord - n, "super+"); const char *name = NULL; for (size_t i = 0; i < sizeof NAMED_KEYS / sizeof *NAMED_KEYS; i++) @@ -1208,16 +1268,20 @@ static void cb_chord(cfgbuf_t *b, int key, uint16_t mods) { if (name) { snprintf(chord + n, sizeof chord - n, "%s", name); } else if (key >= GHOSTTY_KEY_A && key <= GHOSTTY_KEY_Z) { - snprintf(chord + n, sizeof chord - n, "%c", (char)('a' + (key - GHOSTTY_KEY_A))); + snprintf(chord + n, sizeof chord - n, "%c", + (char)('a' + (key - GHOSTTY_KEY_A))); } else if (key >= GHOSTTY_KEY_DIGIT_0 && key <= GHOSTTY_KEY_DIGIT_9) { snprintf(chord + n, sizeof chord - n, "%c", (char)('0' + (key - GHOSTTY_KEY_DIGIT_0))); } else { /* The punctuation the parser knows, by the character you press. */ - static const struct { int key; char ch; } PUNCT[] = { - {GHOSTTY_KEY_EQUAL, '='}, {GHOSTTY_KEY_BRACKET_LEFT, '['}, + static const struct { + int key; + char ch; + } PUNCT[] = { + {GHOSTTY_KEY_EQUAL, '='}, {GHOSTTY_KEY_BRACKET_LEFT, '['}, {GHOSTTY_KEY_BRACKET_RIGHT, ']'}, {GHOSTTY_KEY_SEMICOLON, ';'}, - {GHOSTTY_KEY_QUOTE, '\''}, {GHOSTTY_KEY_BACKQUOTE, '`'}, + {GHOSTTY_KEY_QUOTE, '\''}, {GHOSTTY_KEY_BACKQUOTE, '`'}, }; char ch = 0; for (size_t i = 0; i < sizeof PUNCT / sizeof *PUNCT; i++) @@ -1249,8 +1313,10 @@ static void cb_chain(cfgbuf_t *b, const char *indent, const shader_t *sh, sh[i].color.b); if (sh[i].param) cb_add(b, " at=%u", sh[i].param); if (chrome) cb_add(b, " where=\"chrome\""); - if (sh[i].channels == SHADE_FG) cb_add(b, " channel=\"fg\""); - else if (sh[i].channels == SHADE_BG) cb_add(b, " channel=\"bg\""); + if (sh[i].channels == SHADE_FG) + cb_add(b, " channel=\"fg\""); + else if (sh[i].channels == SHADE_BG) + cb_add(b, " channel=\"bg\""); cb_add(b, "\n"); } } @@ -1265,12 +1331,14 @@ char *config_render(const config_t *c) { cb_add(&b, "// anything you do not want to pin; what is missing is a\n"); cb_add(&b, "// default, and defaults are allowed to improve.\n"); cb_add(&b, "//\n"); - cb_add(&b, "// The commented reference -- what each setting is for, and why\n"); + cb_add(&b, + "// The commented reference -- what each setting is for, and why\n"); cb_add(&b, "// it exists -- is config/config.kdl in the source tree.\n\n"); cb_add(&b, "// ---- geometry ----\n"); cb_add(&b, "gap %u\n", c->gap); - cb_add(&b, "gap_aspect %u // columns per row, so a gap looks square\n", + cb_add(&b, + "gap_aspect %u // columns per row, so a gap looks square\n", c->gap_aspect); /* The shortest form that means what is in force, because `padding 0` reads * better than `padding 0 0 0 0` and round-trips to the same four numbers. */ @@ -1284,12 +1352,14 @@ char *config_render(const config_t *c) { c->pad_right, c->pad_bottom, c->pad_left); cb_add(&b, "rounded %s\n", yesno(c->rounded)); cb_add(&b, "title_align \"%s\"\n", - c->title_align == ALIGN_LEFT ? "left" - : c->title_align == ALIGN_RIGHT ? "right" : "center"); + c->title_align == ALIGN_LEFT ? "left" + : c->title_align == ALIGN_RIGHT ? "right" + : "center"); cb_add(&b, "title_inset %u\n", c->title_inset); cb_add(&b, "min_pane cols=%u rows=%u // below this a pane collapses\n", c->min_pane_cols, c->min_pane_rows); - cb_add(&b, "min_split cols=%u rows=%u // below this a split is not offered\n", + cb_add(&b, + "min_split cols=%u rows=%u // below this a split is not offered\n", c->min_split_cols, c->min_split_rows); cb_add(&b, "\n// ---- what is on screen ----\n"); @@ -1298,9 +1368,11 @@ char *config_render(const config_t *c) { cb_add(&b, "status_line %s // the line along the bottom\n", yesno(c->status_line)); cb_add(&b, "status_pad %u\n", c->status_pad); - cb_add(&b, "hints %s // what the pointer is on, in the middle\n", + cb_add(&b, + "hints %s // what the pointer is on, in the middle\n", yesno(c->hints)); - cb_add(&b, "version_banner %s // ...and which build this is, when idle\n", + cb_add(&b, + "version_banner %s // ...and which build this is, when idle\n", yesno(c->version_banner)); cb_add(&b, "pane_buttons %s // the marks in a frame's top-right\n", yesno(c->pane_buttons)); @@ -1316,10 +1388,13 @@ char *config_render(const config_t *c) { cb_add(&b, "focus_follows_mouse %s\n", yesno(c->focus_follows_mouse)); cb_add(&b, "in_band_shaders %s\n", yesno(c->in_band_shaders)); cb_add(&b, "scroll_lines %u\n", c->scroll_lines); - cb_add(&b, "scrollback %zu // lines of history per pane; 0 for none\n", + cb_add(&b, + "scrollback %zu // lines of history per pane; 0 for none\n", c->scrollback); - cb_add(&b, "scrollback_bytes %zu // the ceiling that count runs into; 0 for none\n", - c->scrollback_bytes); + cb_add( + &b, + "scrollback_bytes %zu // the ceiling that count runs into; 0 for none\n", + c->scrollback_bytes); cb_add(&b, "toast_ms %u\n", c->toast_ms); cb_add(&b, "hover_delay_ms %u\n", c->hover_delay_ms); cb_add(&b, "double_click_ms %u\n", c->double_click_ms); @@ -1329,17 +1404,26 @@ char *config_render(const config_t *c) { c->anim_ms); cb_add(&b, "modal_scrim %u // how far a modal pushes the rest back\n", c->modal_scrim); - cb_add(&b, "dim_unfocused %u // ...and how far the panes you are not in\n", + cb_add(&b, + "dim_unfocused %u // ...and how far the panes you are not in\n", c->dim_unfocused); - cb_add(&b, "keep_dead \"%s\" // which dead panes stay: commands, all, none\n", - c->keep_dead == KEEP_DEAD_ALL ? "all" - : c->keep_dead == KEEP_DEAD_NONE ? "none" : "commands"); - if (c->shell) cb_qstr(&b, "shell", c->shell, NULL); - else cb_add(&b, "// shell \"/bin/zsh\" // unset: $SHELL\n"); - if (c->editor) cb_qstr(&b, "editor", c->editor, NULL); - else cb_add(&b, "// editor \"nvim\" // unset: $EDITOR, then vi\n"); - if (c->shader_dir) cb_qstr(&b, "shader_dir", c->shader_dir, NULL); - else cb_add(&b, "// shader_dir \"~/.config/sl0ppty/shaders\"\n"); + cb_add(&b, + "keep_dead \"%s\" // which dead panes stay: commands, all, none\n", + c->keep_dead == KEEP_DEAD_ALL ? "all" + : c->keep_dead == KEEP_DEAD_NONE ? "none" + : "commands"); + if (c->shell) + cb_qstr(&b, "shell", c->shell, NULL); + else + cb_add(&b, "// shell \"/bin/zsh\" // unset: $SHELL\n"); + if (c->editor) + cb_qstr(&b, "editor", c->editor, NULL); + else + cb_add(&b, "// editor \"nvim\" // unset: $EDITOR, then vi\n"); + if (c->shader_dir) + cb_qstr(&b, "shader_dir", c->shader_dir, NULL); + else + cb_add(&b, "// shader_dir \"~/.config/sl0ppty/shaders\"\n"); if (c->nproject_roots) { cb_add(&b, "project_roots"); for (size_t i = 0; i < c->nproject_roots; i++) { @@ -1351,8 +1435,10 @@ char *config_render(const config_t *c) { cb_add(&b, "// project_roots \"~/dev\" \"~/work\" depth=%d\n", c->project_depth); } - if (c->project_layout) cb_qstr(&b, "project_layout", c->project_layout, NULL); - else cb_add(&b, "// project_layout \"~/.config/sl0ppty/project.layout.kdl\"\n"); + if (c->project_layout) + cb_qstr(&b, "project_layout", c->project_layout, NULL); + else + cb_add(&b, "// project_layout \"~/.config/sl0ppty/project.layout.kdl\"\n"); cb_add(&b, "\n// ---- colour ----\ntheme {\n"); for (size_t i = 0; i < sizeof THEME_COLORS / sizeof *THEME_COLORS; i++) @@ -1363,19 +1449,23 @@ char *config_render(const config_t *c) { * none", where nothing at all says "we forgot to tell you". Chrome passes * live in the same blocks, marked, because that is how they are written. */ cb_add(&b, "\n// ---- colour passes over every pane ----\n"); - cb_add(&b, "// (contrib/shaders has thirty-odd to paste; contrib/shadertoy.html\n"); - cb_add(&b, "// previews them. where=\"chrome\" runs a pass over the frame\n"); + cb_add( + &b, + "// (contrib/shaders has thirty-odd to paste; contrib/shadertoy.html\n"); + cb_add(&b, + "// previews them. where=\"chrome\" runs a pass over the frame\n"); cb_add(&b, "// instead of the contents)\nshaders {\n"); cb_chain(&b, " ", c->shaders, c->nshaders, false); cb_chain(&b, " ", c->chrome_shaders, c->nchrome_shaders, true); cb_add(&b, "}\n"); - cb_add(&b, "\n// ---- what a pane looks like in a given state ----\nstates {\n"); + cb_add(&b, + "\n// ---- what a pane looks like in a given state ----\nstates {\n"); for (int st = 0; st < PSTATE_COUNT; st++) { cb_add(&b, " %s {\n", pane_state_name((pane_state_t)st)); cb_chain(&b, " ", c->state_shaders[st], c->state_n[st], false); - cb_chain(&b, " ", c->chrome_state_shaders[st], - c->chrome_state_n[st], true); + cb_chain(&b, " ", c->chrome_state_shaders[st], c->chrome_state_n[st], + true); cb_add(&b, " }\n"); } cb_add(&b, "}\n"); @@ -1468,7 +1558,8 @@ const char *config_default_path(void) { return path; } const char *xdg = getenv("XDG_CONFIG_HOME"); - if (xdg && *xdg) snprintf(path, sizeof path, "%s/sl0ppty/config.kdl", xdg); + if (xdg && *xdg) + snprintf(path, sizeof path, "%s/sl0ppty/config.kdl", xdg); else { const char *home = getenv("HOME"); snprintf(path, sizeof path, "%s/.config/sl0ppty/config.kdl", @@ -1558,8 +1649,9 @@ static void complain_unknown_top(config_t *c, const kdl_node_t *root, char *err, if (!n || !n->name) continue; if (config_is_setting(n->name)) continue; if (strcmp(n->name, "layout") == 0) - complain(c, err, errcap, n->line, - "this is a layout, not a config: `sl0ppty --layout` reads those"); + complain( + c, err, errcap, n->line, + "this is a layout, not a config: `sl0ppty --layout` reads those"); else complain(c, err, errcap, n->line, "unknown setting: %s", n->name); } @@ -1583,8 +1675,10 @@ static void include_path(const char *base_file, const char *ref, char *out, char dir[512]; snprintf(dir, sizeof dir, "%s", base_file); char *slash = strrchr(dir, '/'); - if (slash) *slash = 0; - else snprintf(dir, sizeof dir, "."); + if (slash) + *slash = 0; + else + snprintf(dir, sizeof dir, "."); snprintf(out, cap, "%s/%s", dir, r); } @@ -1604,8 +1698,9 @@ static void include_path(const char *base_file, const char *ref, char *out, * applied while *its* include did not still has something to say, and the first * version only looked at the return value — so a bad file two levels down was * reported by nobody, because the file in the middle had parsed fine. */ -static void apply_includes(config_t *c, const kdl_node_t *root, const char *path, - int depth, char *err, size_t errcap) { +static void apply_includes(config_t *c, const kdl_node_t *root, + const char *path, int depth, char *err, + size_t errcap) { for (size_t i = 0; i < root->nkids; i++) { const kdl_node_t *n = root->kids[i]; if (!n || !n->name || strcmp(n->name, "include") != 0) continue; @@ -1623,7 +1718,8 @@ static void apply_includes(config_t *c, const kdl_node_t *root, const char *path snprintf(ierr, sizeof ierr, "cannot include it"); /* Already carries its own file and line: it was produced by the file * that has the problem, not by this one. */ - if (ierr[0] && err && errcap && !err[0]) snprintf(err, errcap, "%s", ierr); + if (ierr[0] && err && errcap && !err[0]) + snprintf(err, errcap, "%s", ierr); } } } @@ -1705,7 +1801,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, size_t n = pn->nargs < 4 ? pn->nargs : 4; for (size_t i = 0; i < n; i++) v[i] = kdl_arg_int(pn, i, 0); if (pn->nargs == 1) { - c->pad_top = c->pad_right = c->pad_bottom = c->pad_left = (uint16_t)v[0]; + c->pad_top = c->pad_right = c->pad_bottom = c->pad_left = + (uint16_t)v[0]; } else if (pn->nargs == 2) { c->pad_top = c->pad_bottom = (uint16_t)v[0]; c->pad_right = c->pad_left = (uint16_t)v[1]; @@ -1717,7 +1814,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, } else { complain(c, err, errcap, pn->line, "padding takes 1, 2 or 4 values (all, vertical horizontal, or " - "top right bottom left), not %zu", pn->nargs); + "top right bottom left), not %zu", + pn->nargs); } } } @@ -1727,16 +1825,19 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, kdl_arg_bool(kdl_child(root, "status_line"), 0, c->status_line); c->status_pad = (uint16_t)kdl_arg_int(kdl_child(root, "status_pad"), 0, c->status_pad); - c->focus_follows_mouse = kdl_arg_bool(kdl_child(root, "focus_follows_mouse"), 0, - c->focus_follows_mouse); - c->in_band_shaders = kdl_arg_bool(kdl_child(root, "in_band_shaders"), 0, - c->in_band_shaders); + c->focus_follows_mouse = kdl_arg_bool(kdl_child(root, "focus_follows_mouse"), + 0, c->focus_follows_mouse); + c->in_band_shaders = + kdl_arg_bool(kdl_child(root, "in_band_shaders"), 0, c->in_band_shaders); const char *align = kdl_arg(kdl_child(root, "title_align"), 0, NULL); if (align) { - if (strcmp(align, "left") == 0) c->title_align = ALIGN_LEFT; - else if (strcmp(align, "right") == 0) c->title_align = ALIGN_RIGHT; - else c->title_align = ALIGN_CENTER; + if (strcmp(align, "left") == 0) + c->title_align = ALIGN_LEFT; + else if (strcmp(align, "right") == 0) + c->title_align = ALIGN_RIGHT; + else + c->title_align = ALIGN_CENTER; } c->title_inset = @@ -1763,10 +1864,12 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, * behaviour. */ const char *kd = kdl_arg(kdl_child(root, "keep_dead"), 0, NULL); if (kd) { - if (!strcmp(kd, "all") || !strcmp(kd, "true")) c->keep_dead = KEEP_DEAD_ALL; + if (!strcmp(kd, "all") || !strcmp(kd, "true")) + c->keep_dead = KEEP_DEAD_ALL; else if (!strcmp(kd, "none") || !strcmp(kd, "false")) c->keep_dead = KEEP_DEAD_NONE; - else if (!strcmp(kd, "commands")) c->keep_dead = KEEP_DEAD_COMMANDS; + else if (!strcmp(kd, "commands")) + c->keep_dead = KEEP_DEAD_COMMANDS; else complain(c, err, errcap, kdl_child(root, "keep_dead")->line, "keep_dead: %s (want commands, all or none)", kd); @@ -1783,10 +1886,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, const kdl_node_t *mins = kdl_child(root, "min_split"); if (mins) { - c->min_split_cols = - (uint16_t)kdl_prop_int(mins, "cols", c->min_split_cols); - c->min_split_rows = - (uint16_t)kdl_prop_int(mins, "rows", c->min_split_rows); + c->min_split_cols = (uint16_t)kdl_prop_int(mins, "cols", c->min_split_cols); + c->min_split_rows = (uint16_t)kdl_prop_int(mins, "rows", c->min_split_rows); } const kdl_node_t *minp = kdl_child(root, "min_pane"); @@ -1794,8 +1895,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, c->min_pane_cols = (uint16_t)kdl_prop_int(minp, "cols", c->min_pane_cols); c->min_pane_rows = (uint16_t)kdl_prop_int(minp, "rows", c->min_pane_rows); } - c->scroll_lines = - (uint16_t)kdl_arg_int(kdl_child(root, "scroll_lines"), 0, c->scroll_lines); + c->scroll_lines = (uint16_t)kdl_arg_int(kdl_child(root, "scroll_lines"), 0, + c->scroll_lines); /* Read as a `long` so a negative is *refused* rather than wrapping into a * limit of eighteen quintillion lines, which is how "unlimited" gets into a * program by accident. There is no unlimited on purpose: with 64 panes it is a @@ -1803,33 +1904,35 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, const kdl_node_t *sbn = kdl_child(root, "scrollback"); if (sbn) { long v = kdl_arg_int(sbn, 0, (long)c->scrollback); - if (v < 0) complain(c, err, errcap, sbn->line, - "scrollback is a number of lines, 0 for none, not %ld", v); - else c->scrollback = (size_t)v; + if (v < 0) + complain(c, err, errcap, sbn->line, + "scrollback is a number of lines, 0 for none, not %ld", v); + else + c->scrollback = (size_t)v; } const kdl_node_t *sbb = kdl_child(root, "scrollback_bytes"); if (sbb) { long v = kdl_arg_int(sbb, 0, (long)c->scrollback_bytes); - if (v < 0) complain(c, err, errcap, sbb->line, - "scrollback_bytes is a byte ceiling, 0 for none, not %ld", - v); - else c->scrollback_bytes = (size_t)v; + if (v < 0) + complain(c, err, errcap, sbb->line, + "scrollback_bytes is a byte ceiling, 0 for none, not %ld", v); + else + c->scrollback_bytes = (size_t)v; } - c->toast_ms = (uint16_t)kdl_arg_int(kdl_child(root, "toast_ms"), 0, c->toast_ms); - c->hover_delay_ms = (uint16_t)kdl_arg_int(kdl_child(root, "hover_delay_ms"), 0, - c->hover_delay_ms); + c->toast_ms = + (uint16_t)kdl_arg_int(kdl_child(root, "toast_ms"), 0, c->toast_ms); + c->hover_delay_ms = (uint16_t)kdl_arg_int(kdl_child(root, "hover_delay_ms"), + 0, c->hover_delay_ms); c->double_click_ms = (uint16_t)kdl_arg_int(kdl_child(root, "double_click_ms"), 0, c->double_click_ms); const char *ws = kdl_arg(kdl_child(root, "word_separators"), 0, NULL); if (ws) snprintf(c->word_separators, sizeof c->word_separators, "%s", ws); - c->anim_ms = - (uint16_t)kdl_arg_int(kdl_child(root, "anim_ms"), 0, c->anim_ms); + c->anim_ms = (uint16_t)kdl_arg_int(kdl_child(root, "anim_ms"), 0, c->anim_ms); { long v = kdl_arg_int(kdl_child(root, "modal_scrim"), 0, c->modal_scrim); c->modal_scrim = (uint8_t)(v < 0 ? 0 : v > 255 ? 255 : v); } - const char *sh = kdl_arg(kdl_child(root, "shell"), 0, NULL); if (sh) { free(c->shell); @@ -1859,8 +1962,10 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, } else { snprintf(dir, sizeof dir, "%s", path); char *slash = strrchr(dir, '/'); - if (slash) slash[1] = 0; - else dir[0] = 0; + if (slash) + slash[1] = 0; + else + dir[0] = 0; snprintf(dir + strlen(dir), sizeof dir - strlen(dir), "shaders"); } char lerr[256] = {0}; @@ -1883,7 +1988,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err, for (size_t i = 0; i < proots->nargs; i++) { if (c->nproject_roots >= PROJECT_ROOTS_MAX) { complain(c, err, errcap, proots->line, - "project_roots takes at most %d directories", PROJECT_ROOTS_MAX); + "project_roots takes at most %d directories", + PROJECT_ROOTS_MAX); break; } c->project_roots[c->nproject_roots++] = strdup(proots->args[i]); diff --git a/src/config.h b/src/config.h index 00ea5cc..6609ea3 100644 --- a/src/config.h +++ b/src/config.h @@ -32,7 +32,7 @@ typedef enum { * and then exited is a pane that exited. It ends when you look at the pane, * which is what answering a bell is. */ PSTATE_BELL, - PSTATE_SCROLLED, /* looking at scrollback rather than the present */ + PSTATE_SCROLLED, /* looking at scrollback rather than the present */ PSTATE_UNFOCUSED, PSTATE_COUNT, } pane_state_t; @@ -183,7 +183,7 @@ typedef struct { * number and a different question. Never used below min_pane: refusing to * offer something the layout would collapse anyway is the floor. */ uint16_t min_split_cols, min_split_rows; - uint16_t scroll_lines; + uint16_t scroll_lines; /* rows per wheel notch */ /* How much history a pane keeps. `scrollback` is the number people mean -- * lines -- and `scrollback_bytes` is the ceiling that keeps one very wide, * heavily styled pane from spending the machine to honour it: lib-vt applies @@ -194,8 +194,8 @@ typedef struct { * and a silently wrapped limit is worse than a refused one. */ size_t scrollback; size_t scrollback_bytes; - uint16_t toast_ms; - uint16_t hover_delay_ms; /* how long the pointer must rest to arm a guide */ /* how long an announcement stays up */ /* rows per wheel notch */ + uint16_t toast_ms; /* how long an announcement stays up */ + uint16_t hover_delay_ms; /* how long the pointer must rest to arm a guide */ uint16_t double_click_ms; /* how close two clicks must be to be a double */ /* What a double-click's word stops at, listed as *separators* rather than as * word characters: anything unlisted belongs to a word, so text nobody @@ -446,9 +446,9 @@ size_t config_files(const config_t *c, const char **out, size_t max); * many entries were understood, writing the first refusal to `err`. */ size_t config_parse_chain_doc(const char *text, color_t default_color, bool default_chrome, shader_t *content, - size_t *ncontent, shader_t *chrome, size_t *nchrome, - expr_prog_t **exprs, size_t *nexprs, char *err, - size_t errcap); + size_t *ncontent, shader_t *chrome, + size_t *nchrome, expr_prog_t **exprs, + size_t *nexprs, char *err, size_t errcap); /* The same, from a file: a `shaders { }` block as `contrib/chrome` and * `contrib/shaders` ship them, read with the parser that already knows this @@ -460,8 +460,8 @@ size_t config_parse_chain_doc(const char *text, color_t default_color, size_t config_parse_chain_file(const char *path, color_t default_color, shader_t *content, size_t *ncontent, shader_t *chrome, size_t *nchrome, - expr_prog_t **exprs, size_t *nexprs, - char *err, size_t errcap); + expr_prog_t **exprs, size_t *nexprs, char *err, + size_t errcap); /* Whether a name is one of the config's own settings. For the layout loader, which * shares this syntax and needs to tell a config apart from a session file -- one @@ -11,21 +11,54 @@ enum { OP_PUSH, /* imm */ OP_VAR, /* imm = which */ - OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_NEG, - OP_LT, OP_GT, OP_LE, OP_GE, OP_EQ, OP_NE, - OP_AND, OP_OR, OP_NOT, + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_NEG, + OP_LT, + OP_GT, + OP_LE, + OP_GE, + OP_EQ, + OP_NE, + OP_AND, + OP_OR, + OP_NOT, /* On the 32-bit value, with JavaScript's semantics to the letter: the shift * count is masked to five bits and `>>` keeps the sign. Not a free choice -- * contrib/shadertoy.html reimplements this language in JS and a test compares * the two, so `x << 33` has to mean the same thing in both. */ - OP_BAND, OP_BXOR, OP_BOR, OP_BNOT, OP_SHL, OP_SHR, - OP_MIN, OP_MAX, OP_ABS, OP_CLAMP, OP_DIST, OP_SIN, OP_COS, + OP_BAND, + OP_BXOR, + OP_BOR, + OP_BNOT, + OP_SHL, + OP_SHR, + OP_MIN, + OP_MAX, + OP_ABS, + OP_CLAMP, + OP_DIST, + OP_SIN, + OP_COS, OP_SELECT, /* cond a b -> a or b, both already evaluated */ }; /* Variable slots, in the order the names table lists them. */ -enum { V_X, V_Y, V_COLS, V_ROWS, V_CURX, V_CURY, V_CURSOR, V_FOCUSED, V_T, - V_SINCE }; +enum { + V_X, + V_Y, + V_COLS, + V_ROWS, + V_CURX, + V_CURY, + V_CURSOR, + V_FOCUSED, + V_T, + V_SINCE +}; /* Angles are counted in degrees here (see `isin`), and pi is not a number in an * angle language -- it is a half turn. So `PI` is 180 and `TAU` is 360, and a @@ -190,7 +223,8 @@ static void parse_primary(parser_t *ps, expr_prog_t *pr) { if ((*ps->p >= 'a' && *ps->p <= 'z') || (*ps->p >= 'A' && *ps->p <= 'Z') || *ps->p == '_') { const char *s = ps->p; - while ((*ps->p >= 'a' && *ps->p <= 'z') || (*ps->p >= 'A' && *ps->p <= 'Z') || + while ((*ps->p >= 'a' && *ps->p <= 'z') || + (*ps->p >= 'A' && *ps->p <= 'Z') || (*ps->p >= '0' && *ps->p <= '9') || *ps->p == '_') ps->p++; size_t len = (size_t)(ps->p - s); @@ -210,9 +244,9 @@ static void parse_primary(parser_t *ps, expr_prog_t *pr) { int args; uint8_t op; } FNS[] = { - {"min", 2, OP_MIN}, {"max", 2, OP_MAX}, {"abs", 1, OP_ABS}, - {"clamp", 3, OP_CLAMP}, {"dist", 4, OP_DIST}, - {"sin", 1, OP_SIN}, {"cos", 1, OP_COS}, + {"min", 2, OP_MIN}, {"max", 2, OP_MAX}, {"abs", 1, OP_ABS}, + {"clamp", 3, OP_CLAMP}, {"dist", 4, OP_DIST}, {"sin", 1, OP_SIN}, + {"cos", 1, OP_COS}, }; for (size_t i = 0; i < sizeof FNS / sizeof *FNS; i++) { if (strcmp(FNS[i].name, name) != 0) continue; @@ -265,10 +299,14 @@ static void parse_mul(parser_t *ps, expr_prog_t *pr) { if (ps->failed) return; skip_ws(ps); uint8_t op; - if (*ps->p == '*') op = OP_MUL; - else if (*ps->p == '/') op = OP_DIV; - else if (*ps->p == '%') op = OP_MOD; - else return; + if (*ps->p == '*') + op = OP_MUL; + else if (*ps->p == '/') + op = OP_DIV; + else if (*ps->p == '%') + op = OP_MOD; + else + return; ps->p++; parse_primary(ps, pr); emit(ps, pr, op, 0); @@ -281,9 +319,12 @@ static void parse_add(parser_t *ps, expr_prog_t *pr) { if (ps->failed) return; skip_ws(ps); uint8_t op; - if (*ps->p == '+') op = OP_ADD; - else if (*ps->p == '-') op = OP_SUB; - else return; + if (*ps->p == '+') + op = OP_ADD; + else if (*ps->p == '-') + op = OP_SUB; + else + return; ps->p++; parse_mul(ps, pr); emit(ps, pr, op, 0); @@ -303,9 +344,12 @@ static void parse_shift(parser_t *ps, expr_prog_t *pr) { if (ps->failed) return; skip_ws(ps); uint8_t op; - if (eat(ps, "<<")) op = OP_SHL; - else if (eat(ps, ">>")) op = OP_SHR; - else return; + if (eat(ps, "<<")) + op = OP_SHL; + else if (eat(ps, ">>")) + op = OP_SHR; + else + return; parse_add(ps, pr); emit(ps, pr, op, 0); } @@ -356,13 +400,22 @@ static void parse_cmp(parser_t *ps, expr_prog_t *pr) { uint8_t op; /* Longest first: `<` would otherwise eat the `<` of `<=` and leave an `=` * that parses as nothing. */ - if (eat(ps, "<=")) op = OP_LE; - else if (eat(ps, ">=")) op = OP_GE; - else if (eat(ps, "==")) op = OP_EQ; - else if (eat(ps, "!=")) op = OP_NE; - else if (*ps->p == '<') { ps->p++; op = OP_LT; } - else if (*ps->p == '>') { ps->p++; op = OP_GT; } - else return; + if (eat(ps, "<=")) + op = OP_LE; + else if (eat(ps, ">=")) + op = OP_GE; + else if (eat(ps, "==")) + op = OP_EQ; + else if (eat(ps, "!=")) + op = OP_NE; + else if (*ps->p == '<') { + ps->p++; + op = OP_LT; + } else if (*ps->p == '>') { + ps->p++; + op = OP_GT; + } else + return; parse_bor(ps, pr); emit(ps, pr, op, 0); } @@ -404,7 +457,6 @@ static void parse_expr(parser_t *ps, expr_prog_t *pr) { emit(ps, pr, OP_SELECT, 0); } - /* ---- trigonometry, in the only arithmetic this language has --------------- * * `sin(d)` and `cos(d)` take **degrees** and answer -255..255. Degrees because @@ -420,8 +472,8 @@ static void parse_expr(parser_t *ps, expr_prog_t *pr) { * approximations of the same curve do. */ static const int16_t SIN_Q[91] = { - 0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, - 57, 62, 66, 70, 75, 79, 83, 87, 91, 96, 100, 104, 108, + 0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, + 57, 62, 66, 70, 75, 79, 83, 87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146, 150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186, 190, 192, 195, 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225, 227, 229, @@ -444,15 +496,19 @@ static int vm_run(const expr_prog_t *pr, const expr_env_t *env) { int32_t st[MAX_STACK]; int sp = 0; const int32_t vars[] = { - [V_X] = env->x, [V_Y] = env->y, - [V_COLS] = env->cols, [V_ROWS] = env->rows, - [V_CURX] = env->curx, [V_CURY] = env->cury, - [V_CURSOR] = env->cursor, [V_FOCUSED] = env->focused, + [V_X] = env->x, + [V_Y] = env->y, + [V_COLS] = env->cols, + [V_ROWS] = env->rows, + [V_CURX] = env->curx, + [V_CURY] = env->cury, + [V_CURSOR] = env->cursor, + [V_FOCUSED] = env->focused, [V_T] = (int32_t)(env->t & 0x7fffffff), /* Clamped, not wrapped: a pane that has been unfocused for a month * should read as "a long time", and a `since` that rolled over would * make an effect restart for no reason anybody could see. */ - [V_SINCE] = (int32_t)(env->since < 0 ? 0 + [V_SINCE] = (int32_t)(env->since < 0 ? 0 : env->since > 0x7fffffff ? 0x7fffffff : env->since), }; @@ -464,92 +520,160 @@ static int vm_run(const expr_prog_t *pr, const expr_env_t *env) { * a predictable branch and turns that into a wrong colour, not a smash. */ if (sp >= MAX_STACK - 1) return 0; switch (in->op) { - case OP_PUSH: st[sp++] = in->imm; break; - case OP_VAR: st[sp++] = vars[in->imm]; break; - case OP_NEG: if (sp) st[sp - 1] = -st[sp - 1]; break; - case OP_NOT: if (sp) st[sp - 1] = !st[sp - 1]; break; - case OP_BNOT: if (sp) st[sp - 1] = (int32_t)~(uint32_t)st[sp - 1]; break; - case OP_ABS: if (sp) st[sp - 1] = st[sp - 1] < 0 ? -st[sp - 1] : st[sp - 1]; break; - case OP_SIN: if (sp) st[sp - 1] = isin(st[sp - 1]); break; - case OP_COS: if (sp) st[sp - 1] = isin(st[sp - 1] + 90); break; - default: { - int need = in->op == OP_CLAMP ? 3 : in->op == OP_DIST ? 4 - : in->op == OP_SELECT ? 3 : 2; - if (sp < need) return 0; - int32_t b = st[sp - 1], a = st[sp - 2]; - switch (in->op) { - case OP_ADD: st[sp - 2] = a + b; sp--; break; - case OP_SUB: st[sp - 2] = a - b; sp--; break; - case OP_MUL: st[sp - 2] = a * b; sp--; break; - /* Defined rather than trapping: an expression that divides by zero + case OP_PUSH: st[sp++] = in->imm; break; + case OP_VAR: st[sp++] = vars[in->imm]; break; + case OP_NEG: + if (sp) st[sp - 1] = -st[sp - 1]; + break; + case OP_NOT: + if (sp) st[sp - 1] = !st[sp - 1]; + break; + case OP_BNOT: + if (sp) st[sp - 1] = (int32_t)~(uint32_t)st[sp - 1]; + break; + case OP_ABS: + if (sp) st[sp - 1] = st[sp - 1] < 0 ? -st[sp - 1] : st[sp - 1]; + break; + case OP_SIN: + if (sp) st[sp - 1] = isin(st[sp - 1]); + break; + case OP_COS: + if (sp) st[sp - 1] = isin(st[sp - 1] + 90); + break; + default: { + int need = in->op == OP_CLAMP ? 3 + : in->op == OP_DIST ? 4 + : in->op == OP_SELECT ? 3 + : 2; + if (sp < need) return 0; + int32_t b = st[sp - 1], a = st[sp - 2]; + switch (in->op) { + case OP_ADD: + st[sp - 2] = a + b; + sp--; + break; + case OP_SUB: + st[sp - 2] = a - b; + sp--; + break; + case OP_MUL: + st[sp - 2] = a * b; + sp--; + break; + /* Defined rather than trapping: an expression that divides by zero * should look wrong, not take the session with it. */ - case OP_DIV: st[sp - 2] = b ? a / b : 0; sp--; break; - case OP_MOD: st[sp - 2] = b ? a % b : 0; sp--; break; - case OP_LT: st[sp - 2] = a < b; sp--; break; - case OP_GT: st[sp - 2] = a > b; sp--; break; - case OP_LE: st[sp - 2] = a <= b; sp--; break; - case OP_GE: st[sp - 2] = a >= b; sp--; break; - case OP_EQ: st[sp - 2] = a == b; sp--; break; - case OP_NE: st[sp - 2] = a != b; sp--; break; - case OP_AND: st[sp - 2] = a && b; sp--; break; - case OP_BAND: st[sp - 2] = (int32_t)((uint32_t)a & (uint32_t)b); sp--; break; - case OP_BXOR: st[sp - 2] = (int32_t)((uint32_t)a ^ (uint32_t)b); sp--; break; - case OP_BOR: st[sp - 2] = (int32_t)((uint32_t)a | (uint32_t)b); sp--; break; - /* Count masked to five bits, like JS, so a silly shift is defined + case OP_DIV: + st[sp - 2] = b ? a / b : 0; + sp--; + break; + case OP_MOD: + st[sp - 2] = b ? a % b : 0; + sp--; + break; + case OP_LT: + st[sp - 2] = a < b; + sp--; + break; + case OP_GT: + st[sp - 2] = a > b; + sp--; + break; + case OP_LE: + st[sp - 2] = a <= b; + sp--; + break; + case OP_GE: + st[sp - 2] = a >= b; + sp--; + break; + case OP_EQ: + st[sp - 2] = a == b; + sp--; + break; + case OP_NE: + st[sp - 2] = a != b; + sp--; + break; + case OP_AND: + st[sp - 2] = a && b; + sp--; + break; + case OP_BAND: + st[sp - 2] = (int32_t)((uint32_t)a & (uint32_t)b); + sp--; + break; + case OP_BXOR: + st[sp - 2] = (int32_t)((uint32_t)a ^ (uint32_t)b); + sp--; + break; + case OP_BOR: + st[sp - 2] = (int32_t)((uint32_t)a | (uint32_t)b); + sp--; + break; + /* Count masked to five bits, like JS, so a silly shift is defined * rather than undefined. Shifted unsigned because moving a bit into * the sign of a signed value is undefined in C and simply wraps in JS, * which is what the preview will show. */ - case OP_SHL: - st[sp - 2] = (int32_t)((uint32_t)a << (b & 31)); - sp--; - break; - /* Arithmetic: the sign is kept, so `-8 >> 1` is -4. Written out rather + case OP_SHL: + st[sp - 2] = (int32_t)((uint32_t)a << (b & 31)); + sp--; + break; + /* Arithmetic: the sign is kept, so `-8 >> 1` is -4. Written out rather * than left to the compiler, because a signed right shift is * implementation-defined in C and this one has to match JS. */ - case OP_SHR: { - unsigned k = (unsigned)(b & 31); - uint32_t u = (uint32_t)a; - st[sp - 2] = (int32_t)(a < 0 ? ~((~u) >> k) : u >> k); - sp--; - break; - } - case OP_OR: st[sp - 2] = a || b; sp--; break; - case OP_MIN: st[sp - 2] = a < b ? a : b; sp--; break; - case OP_MAX: st[sp - 2] = a > b ? a : b; sp--; break; - case OP_CLAMP: { - int32_t hi = st[sp - 1], lo = st[sp - 2], v = st[sp - 3]; - st[sp - 3] = v < lo ? lo : v > hi ? hi : v; - sp -= 2; - break; - } - case OP_SELECT: { - int32_t f = st[sp - 1], t = st[sp - 2], c = st[sp - 3]; - st[sp - 3] = c ? t : f; - sp -= 2; - break; - } - case OP_DIST: { - /* A cell is about twice as tall as it is wide, so a distance in + case OP_SHR: { + unsigned k = (unsigned)(b & 31); + uint32_t u = (uint32_t)a; + st[sp - 2] = (int32_t)(a < 0 ? ~((~u) >> k) : u >> k); + sp--; + break; + } + case OP_OR: + st[sp - 2] = a || b; + sp--; + break; + case OP_MIN: + st[sp - 2] = a < b ? a : b; + sp--; + break; + case OP_MAX: + st[sp - 2] = a > b ? a : b; + sp--; + break; + case OP_CLAMP: { + int32_t hi = st[sp - 1], lo = st[sp - 2], v = st[sp - 3]; + st[sp - 3] = v < lo ? lo : v > hi ? hi : v; + sp -= 2; + break; + } + case OP_SELECT: { + int32_t f = st[sp - 1], t = st[sp - 2], c = st[sp - 3]; + st[sp - 3] = c ? t : f; + sp -= 2; + break; + } + case OP_DIST: { + /* A cell is about twice as tall as it is wide, so a distance in * cells counts a row double -- the same correction the layout's * own centre-distance makes. Without it every "circle" here is an * ellipse, which is the first thing anyone notices. */ - int32_t y2 = st[sp - 1], x2 = st[sp - 2], y1 = st[sp - 3], - x1 = st[sp - 4]; - int32_t dx = x1 - x2, dy = (y1 - y2) * 2; - int32_t d2 = dx * dx + dy * dy; - /* Integer square root, so `dist` is a distance and not its + int32_t y2 = st[sp - 1], x2 = st[sp - 2], y1 = st[sp - 3], + x1 = st[sp - 4]; + int32_t dx = x1 - x2, dy = (y1 - y2) * 2; + int32_t d2 = dx * dx + dy * dy; + /* Integer square root, so `dist` is a distance and not its * square: expressions that scale linearly with it are what people * write, and squaring is easy to ask for and hard to undo. */ - int32_t r = 0; - while ((r + 1) * (r + 1) <= d2 && r < 4096) r++; - st[sp - 4] = r; - sp -= 3; - break; - } - default: return 0; - } + int32_t r = 0; + while ((r + 1) * (r + 1) <= d2 && r < 4096) r++; + st[sp - 4] = r; + sp -= 3; break; } + default: return 0; + } + break; + } } } return sp ? st[sp - 1] : 0; @@ -564,18 +688,18 @@ static bool stack_fits(const expr_prog_t *pr) { int sp = 0, peak = 0; for (size_t i = 0; i < pr->n; i++) { switch (pr->code[i].op) { - case OP_PUSH: - case OP_VAR: sp++; break; - case OP_NEG: - case OP_NOT: - case OP_BNOT: - case OP_SIN: - case OP_COS: - case OP_ABS: break; - case OP_CLAMP: - case OP_SELECT: sp -= 2; break; - case OP_DIST: sp -= 3; break; - default: sp--; break; + case OP_PUSH: + case OP_VAR: sp++; break; + case OP_NEG: + case OP_NOT: + case OP_BNOT: + case OP_SIN: + case OP_COS: + case OP_ABS: break; + case OP_CLAMP: + case OP_SELECT: sp -= 2; break; + case OP_DIST: sp -= 3; break; + default: sp--; break; } if (sp > peak) peak = sp; if (sp < 0) return false; @@ -629,7 +753,9 @@ int expr_eval(const expr_prog_t *p, const expr_env_t *env) { return vm_run(p, env); } -static uint8_t clamp8(int v) { return (uint8_t)(v < 0 ? 0 : v > 255 ? 255 : v); } +static uint8_t clamp8(int v) { + return (uint8_t)(v < 0 ? 0 : v > 255 ? 255 : v); +} bool expr_amount_map(expr_prog_t *p, const expr_env_t *env, uint8_t **out) { if (!p || !out) return false; diff --git a/src/graphics.c b/src/graphics.c index 8ee0177..26391fd 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -102,7 +102,8 @@ void gfx_begin(graphics_t *g) { * of range for a char -- the compiler catches that one, but the same shape * with a digit that stays in range ("\x1b1") compiles and emits one wrong * byte. Escape then digit is always two strings. */ - out_str(g, "\x1b" "7"); /* DECSC */ + out_str(g, "\x1b" + "7"); /* DECSC */ } static gfx_image_t *img_find(graphics_t *g, uint32_t pane, uint32_t src_id) { @@ -259,7 +260,8 @@ char *gfx_flush(graphics_t *g, size_t *out_len) { g->len = 0; if (g->out) g->out[0] = 0; } else { - out_str(g, "\x1b" "8"); /* DECRC */ + out_str(g, "\x1b" + "8"); /* DECRC */ } *out_len = g->len; diff --git a/src/graphics.h b/src/graphics.h index cc495ec..2967b2a 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -19,10 +19,10 @@ typedef struct graphics graphics_t; /* One image, as this session knows it. */ typedef struct { - uint32_t pane; /* which pane transmitted it */ - uint32_t src_id; /* the id the program chose, unique only within its pane */ - uint32_t out_id; /* the id we give the client, unique across the session */ - uint64_t gen; /* image generation, so a changed image is re-sent */ + uint32_t pane; /* which pane transmitted it */ + uint32_t src_id; /* the id the program chose, unique only within its pane */ + uint32_t out_id; /* the id we give the client, unique across the session */ + uint64_t gen; /* image generation, so a changed image is re-sent */ bool sent; } gfx_image_t; @@ -56,16 +56,16 @@ void gfx_begin(graphics_t *g); * perfectly and shows up as a picture in slightly the wrong place — which is * exactly the bug class this file is for. */ typedef struct { - uint32_t pane; /* which pane it belongs to */ - uint32_t src_id; /* the id the program chose */ - uint64_t gen; /* image generation, so a changed image is re-sent */ + uint32_t pane; /* which pane it belongs to */ + uint32_t src_id; /* the id the program chose */ + uint64_t gen; /* image generation, so a changed image is re-sent */ uint32_t place_id; - uint16_t col, row; /* screen cells */ + uint16_t col, row; /* screen cells */ uint16_t cols, rows; /* cells covered */ uint16_t scale_cols, scale_rows; /* cells to scale into, 0 for natural */ - uint32_t x_off, y_off; /* offset within the first cell, in pixels */ - uint32_t sx, sy, sw, sh; /* source rectangle, in image pixels */ - uint32_t px_w, px_h; /* the image's own size */ + uint32_t x_off, y_off; /* offset within the first cell, in pixels */ + uint32_t sx, sy, sw, sh; /* source rectangle, in image pixels */ + uint32_t px_w, px_h; /* the image's own size */ int format, compression; const uint8_t *data; /* NULL once the client has it */ size_t data_len; diff --git a/src/input.c b/src/input.c index 04dc0b5..47b4737 100644 --- a/src/input.c +++ b/src/input.c @@ -54,100 +54,113 @@ static int ascii_key(uint8_t c) { if (c >= 'A' && c <= 'Z') return GHOSTTY_KEY_A + (c - 'A'); if (c >= '0' && c <= '9') return GHOSTTY_KEY_DIGIT_0 + (c - '0'); switch (c) { - case ' ': return GHOSTTY_KEY_SPACE; - case '`': case '~': return GHOSTTY_KEY_BACKQUOTE; - case '-': case '_': return GHOSTTY_KEY_MINUS; - case '=': case '+': return GHOSTTY_KEY_EQUAL; - case '[': case '{': return GHOSTTY_KEY_BRACKET_LEFT; - case ']': case '}': return GHOSTTY_KEY_BRACKET_RIGHT; - case '\\': case '|': return GHOSTTY_KEY_BACKSLASH; - case ';': case ':': return GHOSTTY_KEY_SEMICOLON; - case '\'': case '"': return GHOSTTY_KEY_QUOTE; - case ',': case '<': return GHOSTTY_KEY_COMMA; - case '.': case '>': return GHOSTTY_KEY_PERIOD; - case '/': case '?': return GHOSTTY_KEY_SLASH; - case '!': return GHOSTTY_KEY_DIGIT_1; - case '@': return GHOSTTY_KEY_DIGIT_2; - case '#': return GHOSTTY_KEY_DIGIT_3; - case '$': return GHOSTTY_KEY_DIGIT_4; - case '%': return GHOSTTY_KEY_DIGIT_5; - case '^': return GHOSTTY_KEY_DIGIT_6; - case '&': return GHOSTTY_KEY_DIGIT_7; - case '*': return GHOSTTY_KEY_DIGIT_8; - case '(': return GHOSTTY_KEY_DIGIT_9; - case ')': return GHOSTTY_KEY_DIGIT_0; - default: return GHOSTTY_KEY_UNIDENTIFIED; + case ' ': return GHOSTTY_KEY_SPACE; + case '`': + case '~': return GHOSTTY_KEY_BACKQUOTE; + case '-': + case '_': return GHOSTTY_KEY_MINUS; + case '=': + case '+': return GHOSTTY_KEY_EQUAL; + case '[': + case '{': return GHOSTTY_KEY_BRACKET_LEFT; + case ']': + case '}': return GHOSTTY_KEY_BRACKET_RIGHT; + case '\\': + case '|': return GHOSTTY_KEY_BACKSLASH; + case ';': + case ':': return GHOSTTY_KEY_SEMICOLON; + case '\'': + case '"': return GHOSTTY_KEY_QUOTE; + case ',': + case '<': return GHOSTTY_KEY_COMMA; + case '.': + case '>': return GHOSTTY_KEY_PERIOD; + case '/': + case '?': return GHOSTTY_KEY_SLASH; + case '!': return GHOSTTY_KEY_DIGIT_1; + case '@': return GHOSTTY_KEY_DIGIT_2; + case '#': return GHOSTTY_KEY_DIGIT_3; + case '$': return GHOSTTY_KEY_DIGIT_4; + case '%': return GHOSTTY_KEY_DIGIT_5; + case '^': return GHOSTTY_KEY_DIGIT_6; + case '&': return GHOSTTY_KEY_DIGIT_7; + case '*': return GHOSTTY_KEY_DIGIT_8; + case '(': return GHOSTTY_KEY_DIGIT_9; + case ')': return GHOSTTY_KEY_DIGIT_0; + default: return GHOSTTY_KEY_UNIDENTIFIED; } } /* CSI <n> ~ */ static int tilde_key(int n) { switch (n) { - case 1: case 7: return GHOSTTY_KEY_HOME; - case 2: return GHOSTTY_KEY_INSERT; - case 3: return GHOSTTY_KEY_DELETE; - case 4: case 8: return GHOSTTY_KEY_END; - case 5: return GHOSTTY_KEY_PAGE_UP; - case 6: return GHOSTTY_KEY_PAGE_DOWN; - case 11: return GHOSTTY_KEY_F1; - case 12: return GHOSTTY_KEY_F2; - case 13: return GHOSTTY_KEY_F3; - case 14: return GHOSTTY_KEY_F4; - case 15: return GHOSTTY_KEY_F5; - case 17: return GHOSTTY_KEY_F6; - case 18: return GHOSTTY_KEY_F7; - case 19: return GHOSTTY_KEY_F8; - case 20: return GHOSTTY_KEY_F9; - case 21: return GHOSTTY_KEY_F10; - case 23: return GHOSTTY_KEY_F11; - case 24: return GHOSTTY_KEY_F12; - default: return GHOSTTY_KEY_UNIDENTIFIED; + case 1: + case 7: return GHOSTTY_KEY_HOME; + case 2: return GHOSTTY_KEY_INSERT; + case 3: return GHOSTTY_KEY_DELETE; + case 4: + case 8: return GHOSTTY_KEY_END; + case 5: return GHOSTTY_KEY_PAGE_UP; + case 6: return GHOSTTY_KEY_PAGE_DOWN; + case 11: return GHOSTTY_KEY_F1; + case 12: return GHOSTTY_KEY_F2; + case 13: return GHOSTTY_KEY_F3; + case 14: return GHOSTTY_KEY_F4; + case 15: return GHOSTTY_KEY_F5; + case 17: return GHOSTTY_KEY_F6; + case 18: return GHOSTTY_KEY_F7; + case 19: return GHOSTTY_KEY_F8; + case 20: return GHOSTTY_KEY_F9; + case 21: return GHOSTTY_KEY_F10; + case 23: return GHOSTTY_KEY_F11; + case 24: return GHOSTTY_KEY_F12; + default: return GHOSTTY_KEY_UNIDENTIFIED; } } /* final byte of a CSI/SS3 cursor-ish sequence */ static int final_key(uint8_t f) { switch (f) { - case 'A': return GHOSTTY_KEY_ARROW_UP; - case 'B': return GHOSTTY_KEY_ARROW_DOWN; - case 'C': return GHOSTTY_KEY_ARROW_RIGHT; - case 'D': return GHOSTTY_KEY_ARROW_LEFT; - case 'E': return GHOSTTY_KEY_NUMPAD_BEGIN; - case 'H': return GHOSTTY_KEY_HOME; - case 'F': return GHOSTTY_KEY_END; - case 'P': return GHOSTTY_KEY_F1; - case 'Q': return GHOSTTY_KEY_F2; - case 'R': return GHOSTTY_KEY_F3; - case 'S': return GHOSTTY_KEY_F4; - default: return GHOSTTY_KEY_UNIDENTIFIED; + case 'A': return GHOSTTY_KEY_ARROW_UP; + case 'B': return GHOSTTY_KEY_ARROW_DOWN; + case 'C': return GHOSTTY_KEY_ARROW_RIGHT; + case 'D': return GHOSTTY_KEY_ARROW_LEFT; + case 'E': return GHOSTTY_KEY_NUMPAD_BEGIN; + case 'H': return GHOSTTY_KEY_HOME; + case 'F': return GHOSTTY_KEY_END; + case 'P': return GHOSTTY_KEY_F1; + case 'Q': return GHOSTTY_KEY_F2; + case 'R': return GHOSTTY_KEY_F3; + case 'S': return GHOSTTY_KEY_F4; + default: return GHOSTTY_KEY_UNIDENTIFIED; } } /* kitty functional codepoints (CSI u form) */ static int kitty_key(uint32_t cp) { switch (cp) { - case 27: return GHOSTTY_KEY_ESCAPE; - case 13: return GHOSTTY_KEY_ENTER; - case 9: return GHOSTTY_KEY_TAB; - case 127: return GHOSTTY_KEY_BACKSPACE; - case 57358: return GHOSTTY_KEY_CAPS_LOCK; - case 57359: return GHOSTTY_KEY_SCROLL_LOCK; - case 57360: return GHOSTTY_KEY_NUM_LOCK; - case 57361: return GHOSTTY_KEY_PRINT_SCREEN; - case 57362: return GHOSTTY_KEY_PAUSE; - case 57363: return GHOSTTY_KEY_CONTEXT_MENU; - case 57441: return GHOSTTY_KEY_SHIFT_LEFT; - case 57442: return GHOSTTY_KEY_CONTROL_LEFT; - case 57443: return GHOSTTY_KEY_ALT_LEFT; - case 57444: return GHOSTTY_KEY_META_LEFT; - case 57447: return GHOSTTY_KEY_SHIFT_RIGHT; - case 57448: return GHOSTTY_KEY_CONTROL_RIGHT; - case 57449: return GHOSTTY_KEY_ALT_RIGHT; - case 57450: return GHOSTTY_KEY_META_RIGHT; - default: - if (cp >= 57376 && cp <= 57398) return GHOSTTY_KEY_F13 + (int)(cp - 57376); - if (cp < 128) return ascii_key((uint8_t)cp); - return GHOSTTY_KEY_UNIDENTIFIED; + case 27: return GHOSTTY_KEY_ESCAPE; + case 13: return GHOSTTY_KEY_ENTER; + case 9: return GHOSTTY_KEY_TAB; + case 127: return GHOSTTY_KEY_BACKSPACE; + case 57358: return GHOSTTY_KEY_CAPS_LOCK; + case 57359: return GHOSTTY_KEY_SCROLL_LOCK; + case 57360: return GHOSTTY_KEY_NUM_LOCK; + case 57361: return GHOSTTY_KEY_PRINT_SCREEN; + case 57362: return GHOSTTY_KEY_PAUSE; + case 57363: return GHOSTTY_KEY_CONTEXT_MENU; + case 57441: return GHOSTTY_KEY_SHIFT_LEFT; + case 57442: return GHOSTTY_KEY_CONTROL_LEFT; + case 57443: return GHOSTTY_KEY_ALT_LEFT; + case 57444: return GHOSTTY_KEY_META_LEFT; + case 57447: return GHOSTTY_KEY_SHIFT_RIGHT; + case 57448: return GHOSTTY_KEY_CONTROL_RIGHT; + case 57449: return GHOSTTY_KEY_ALT_RIGHT; + case 57450: return GHOSTTY_KEY_META_RIGHT; + default: + if (cp >= 57376 && cp <= 57398) return GHOSTTY_KEY_F13 + (int)(cp - 57376); + if (cp < 128) return ascii_key((uint8_t)cp); + return GHOSTTY_KEY_UNIDENTIFIED; } } @@ -215,9 +228,8 @@ static size_t parse_one(input_parser_t *p, input_cb_t cb, void *ud) { static const char end[] = "\x1b[201~"; for (size_t i = 0; i + sizeof end - 1 <= n; i++) { if (memcmp(b + i, end, sizeof end - 1) == 0) { - input_event_t ev = {.kind = EV_PASTE, - .paste = (const char *)b, - .paste_len = i}; + input_event_t ev = { + .kind = EV_PASTE, .paste = (const char *)b, .paste_len = i}; cb(&ev, ud); p->in_paste = false; return i + sizeof end - 1; @@ -257,7 +269,10 @@ static size_t parse_one(input_parser_t *p, input_cb_t cb, void *ud) { if (plen >= 1 && params[0] == '<' && (final == 'M' || final == 'm')) { int f[3] = {0, 0, 0}, fi = 0; for (size_t k = 1; k < plen && fi < 3; k++) { - if (params[k] == ';') { fi++; continue; } + if (params[k] == ';') { + fi++; + continue; + } if (params[k] >= '0' && params[k] <= '9') f[fi] = f[fi] * 10 + (params[k] - '0'); } @@ -271,14 +286,15 @@ static size_t parse_one(input_parser_t *p, input_cb_t cb, void *ud) { bool motion = (btn & 32) != 0; int base = btn & 0xc3; /* strip mods+motion, keep high wheel bits */ if (base >= 64 && base <= 67) { - ev.button = (uint8_t)(MBTN_FOUR + (base - 64)); /* wheel up/down/l/r */ + ev.button = + (uint8_t)(MBTN_FOUR + (base - 64)); /* wheel up/down/l/r */ ev.maction = MOUSE_PRESS; } else { switch (base & 3) { - case 0: ev.button = MBTN_LEFT; break; - case 1: ev.button = MBTN_MIDDLE; break; - case 2: ev.button = MBTN_RIGHT; break; - default: ev.button = MBTN_UNKNOWN; break; /* 3 = release/none */ + case 0: ev.button = MBTN_LEFT; break; + case 1: ev.button = MBTN_MIDDLE; break; + case 2: ev.button = MBTN_RIGHT; break; + default: ev.button = MBTN_UNKNOWN; break; /* 3 = release/none */ } ev.maction = motion ? MOUSE_MOTION : (final == 'M' ? MOUSE_PRESS : MOUSE_RELEASE); @@ -294,8 +310,10 @@ static size_t parse_one(input_parser_t *p, input_cb_t cb, void *ud) { uint8_t ch = params[k]; if (ch >= '0' && ch <= '9') { any = true; - if (sub_i == 0) nums[ncount] = nums[ncount] * 10 + (ch - '0'); - else if (sub_i <= 2) subs[ncount][sub_i] = subs[ncount][sub_i] * 10 + (ch - '0'); + if (sub_i == 0) + nums[ncount] = nums[ncount] * 10 + (ch - '0'); + else if (sub_i <= 2) + subs[ncount][sub_i] = subs[ncount][sub_i] * 10 + (ch - '0'); } else if (ch == ':') { if (sub_i < 2) sub_i++; } else if (ch == ';') { @@ -323,11 +341,13 @@ static size_t parse_one(input_parser_t *p, input_cb_t cb, void *ud) { size_t tlen = 0; /* associated text (3rd param) wins; else derive from the codepoint */ uint32_t out_cp = 0; - if (ncount >= 3 && nums[2]) out_cp = (uint32_t)nums[2]; + if (ncount >= 3 && nums[2]) + out_cp = (uint32_t)nums[2]; else if (!(mods & (MOD_CTRL | MOD_SUPER)) && cp >= 32 && cp != 127) out_cp = (mods & MOD_SHIFT) && shifted ? shifted : cp; if (out_cp) { - if (out_cp < 0x80) text[tlen++] = (char)out_cp; + if (out_cp < 0x80) + text[tlen++] = (char)out_cp; else if (out_cp < 0x800) { text[tlen++] = (char)(0xc0 | (out_cp >> 6)); text[tlen++] = (char)(0x80 | (out_cp & 0x3f)); @@ -393,20 +413,38 @@ static size_t parse_one(input_parser_t *p, input_cb_t cb, void *ud) { } /* --- C0 controls ----------------------------------------------------- */ - if (c == '\r') { emit_key(cb, ud, GHOSTTY_KEY_ENTER, 0, KEY_PRESS, "\r", 1, 13); return 1; } - if (c == '\n') { emit_key(cb, ud, GHOSTTY_KEY_ENTER, 0, KEY_PRESS, "\r", 1, 13); return 1; } - if (c == '\t') { emit_key(cb, ud, GHOSTTY_KEY_TAB, 0, KEY_PRESS, "\t", 1, 9); return 1; } - if (c == 0x7f) { emit_key(cb, ud, GHOSTTY_KEY_BACKSPACE, 0, KEY_PRESS, NULL, 0, 127); return 1; } - if (c == 0x08) { emit_key(cb, ud, GHOSTTY_KEY_BACKSPACE, 0, KEY_PRESS, NULL, 0, 8); return 1; } - if (c == 0x00) { emit_key(cb, ud, GHOSTTY_KEY_SPACE, MOD_CTRL, KEY_PRESS, NULL, 0, ' '); return 1; } + if (c == '\r') { + emit_key(cb, ud, GHOSTTY_KEY_ENTER, 0, KEY_PRESS, "\r", 1, 13); + return 1; + } + if (c == '\n') { + emit_key(cb, ud, GHOSTTY_KEY_ENTER, 0, KEY_PRESS, "\r", 1, 13); + return 1; + } + if (c == '\t') { + emit_key(cb, ud, GHOSTTY_KEY_TAB, 0, KEY_PRESS, "\t", 1, 9); + return 1; + } + if (c == 0x7f) { + emit_key(cb, ud, GHOSTTY_KEY_BACKSPACE, 0, KEY_PRESS, NULL, 0, 127); + return 1; + } + if (c == 0x08) { + emit_key(cb, ud, GHOSTTY_KEY_BACKSPACE, 0, KEY_PRESS, NULL, 0, 8); + return 1; + } + if (c == 0x00) { + emit_key(cb, ud, GHOSTTY_KEY_SPACE, MOD_CTRL, KEY_PRESS, NULL, 0, ' '); + return 1; + } if (c < 0x20) { int key; switch (c) { - case 0x1c: key = GHOSTTY_KEY_BACKSLASH; break; - case 0x1d: key = GHOSTTY_KEY_BRACKET_RIGHT; break; - case 0x1e: key = GHOSTTY_KEY_DIGIT_6; break; - case 0x1f: key = GHOSTTY_KEY_MINUS; break; - default: key = GHOSTTY_KEY_A + (c - 1); break; + case 0x1c: key = GHOSTTY_KEY_BACKSLASH; break; + case 0x1d: key = GHOSTTY_KEY_BRACKET_RIGHT; break; + case 0x1e: key = GHOSTTY_KEY_DIGIT_6; break; + case 0x1f: key = GHOSTTY_KEY_MINUS; break; + default: key = GHOSTTY_KEY_A + (c - 1); break; } uint32_t unshifted = c < 0x1b ? (uint32_t)('a' + c - 1) : 0; emit_key(cb, ud, key, MOD_CTRL, KEY_PRESS, NULL, 0, unshifted); @@ -455,23 +493,23 @@ void input_timeout(input_parser_t *p, input_cb_t cb, void *ud) { static const char *key_name(int key) { switch (key) { - case GHOSTTY_KEY_ARROW_UP: return "ARROW_UP"; - case GHOSTTY_KEY_ARROW_DOWN: return "ARROW_DOWN"; - case GHOSTTY_KEY_ARROW_LEFT: return "ARROW_LEFT"; - case GHOSTTY_KEY_ARROW_RIGHT: return "ARROW_RIGHT"; - case GHOSTTY_KEY_ENTER: return "ENTER"; - case GHOSTTY_KEY_TAB: return "TAB"; - case GHOSTTY_KEY_ESCAPE: return "ESCAPE"; - case GHOSTTY_KEY_BACKSPACE: return "BACKSPACE"; - case GHOSTTY_KEY_SPACE: return "SPACE"; - case GHOSTTY_KEY_HOME: return "HOME"; - case GHOSTTY_KEY_END: return "END"; - case GHOSTTY_KEY_INSERT: return "INSERT"; - case GHOSTTY_KEY_DELETE: return "DELETE"; - case GHOSTTY_KEY_PAGE_UP: return "PAGE_UP"; - case GHOSTTY_KEY_PAGE_DOWN: return "PAGE_DOWN"; - case GHOSTTY_KEY_UNIDENTIFIED: return "UNIDENTIFIED"; - default: break; + case GHOSTTY_KEY_ARROW_UP: return "ARROW_UP"; + case GHOSTTY_KEY_ARROW_DOWN: return "ARROW_DOWN"; + case GHOSTTY_KEY_ARROW_LEFT: return "ARROW_LEFT"; + case GHOSTTY_KEY_ARROW_RIGHT: return "ARROW_RIGHT"; + case GHOSTTY_KEY_ENTER: return "ENTER"; + case GHOSTTY_KEY_TAB: return "TAB"; + case GHOSTTY_KEY_ESCAPE: return "ESCAPE"; + case GHOSTTY_KEY_BACKSPACE: return "BACKSPACE"; + case GHOSTTY_KEY_SPACE: return "SPACE"; + case GHOSTTY_KEY_HOME: return "HOME"; + case GHOSTTY_KEY_END: return "END"; + case GHOSTTY_KEY_INSERT: return "INSERT"; + case GHOSTTY_KEY_DELETE: return "DELETE"; + case GHOSTTY_KEY_PAGE_UP: return "PAGE_UP"; + case GHOSTTY_KEY_PAGE_DOWN: return "PAGE_DOWN"; + case GHOSTTY_KEY_UNIDENTIFIED: return "UNIDENTIFIED"; + default: break; } if (key >= GHOSTTY_KEY_A && key <= GHOSTTY_KEY_Z) { static char buf[2]; @@ -503,33 +541,33 @@ void input_event_describe(const input_event_t *ev, char *buf, size_t cap) { if (ev->mods & MOD_SUPER) strcat(mods, "M"); switch (ev->kind) { - case EV_KEY: { - const char *act = ev->action == KEY_PRESS ? "press" - : ev->action == KEY_RELEASE ? "release" : "repeat"; - char text[32] = ""; - if (ev->text_len) { - snprintf(text, sizeof text, " text=%.*s", (int)ev->text_len, ev->text); - } - snprintf(buf, cap, "key %s mods=%s %s%s", key_name(ev->key), - *mods ? mods : "-", act, text); - break; - } - case EV_MOUSE: { - const char *act = ev->maction == MOUSE_PRESS ? "press" - : ev->maction == MOUSE_RELEASE ? "release" : "motion"; - snprintf(buf, cap, "mouse btn=%u %s at=%u,%u mods=%s", ev->button, act, - ev->mx, ev->my, *mods ? mods : "-"); - break; + case EV_KEY: { + const char *act = ev->action == KEY_PRESS ? "press" + : ev->action == KEY_RELEASE ? "release" + : "repeat"; + char text[32] = ""; + if (ev->text_len) { + snprintf(text, sizeof text, " text=%.*s", (int)ev->text_len, ev->text); } - case EV_PASTE: - snprintf(buf, cap, "paste len=%zu text=%.*s", ev->paste_len, - (int)(ev->paste_len > 20 ? 20 : ev->paste_len), ev->paste); - break; - case EV_FOCUS: - snprintf(buf, cap, "focus %s", ev->focused ? "in" : "out"); - break; - default: - snprintf(buf, cap, "none"); - break; + snprintf(buf, cap, "key %s mods=%s %s%s", key_name(ev->key), + *mods ? mods : "-", act, text); + break; + } + case EV_MOUSE: { + const char *act = ev->maction == MOUSE_PRESS ? "press" + : ev->maction == MOUSE_RELEASE ? "release" + : "motion"; + snprintf(buf, cap, "mouse btn=%u %s at=%u,%u mods=%s", ev->button, act, + ev->mx, ev->my, *mods ? mods : "-"); + break; + } + case EV_PASTE: + snprintf(buf, cap, "paste len=%zu text=%.*s", ev->paste_len, + (int)(ev->paste_len > 20 ? 20 : ev->paste_len), ev->paste); + break; + case EV_FOCUS: + snprintf(buf, cap, "focus %s", ev->focused ? "in" : "out"); + break; + default: snprintf(buf, cap, "none"); break; } } diff --git a/src/input.h b/src/input.h index e2fd7fe..4329f78 100644 --- a/src/input.h +++ b/src/input.h @@ -29,23 +29,33 @@ enum { KEY_RELEASE = 0, KEY_PRESS = 1, KEY_REPEAT = 2 }; enum { MOUSE_PRESS = 0, MOUSE_RELEASE = 1, MOUSE_MOTION = 2 }; /* mirrors GhosttyMouseButton */ enum { - MBTN_UNKNOWN = 0, MBTN_LEFT = 1, MBTN_RIGHT = 2, MBTN_MIDDLE = 3, - MBTN_FOUR = 4, MBTN_FIVE = 5, MBTN_SIX = 6, MBTN_SEVEN = 7, + MBTN_UNKNOWN = 0, + MBTN_LEFT = 1, + MBTN_RIGHT = 2, + MBTN_MIDDLE = 3, + MBTN_FOUR = 4, + MBTN_FIVE = 5, + MBTN_SIX = 6, + MBTN_SEVEN = 7, }; /* mirrors GHOSTTY_MODS_* */ enum { - MOD_SHIFT = 1 << 0, MOD_CTRL = 1 << 1, MOD_ALT = 1 << 2, MOD_SUPER = 1 << 3, - MOD_CAPS = 1 << 4, MOD_NUM = 1 << 5, + MOD_SHIFT = 1 << 0, + MOD_CTRL = 1 << 1, + MOD_ALT = 1 << 2, + MOD_SUPER = 1 << 3, + MOD_CAPS = 1 << 4, + MOD_NUM = 1 << 5, }; typedef struct { ev_kind_t kind; /* EV_KEY */ - int key; /* GhosttyKey */ - uint16_t mods; /* MOD_* */ - uint8_t action; /* KEY_* */ - char text[16]; /* UTF-8 the key produced, if any */ + int key; /* GhosttyKey */ + uint16_t mods; /* MOD_* */ + uint8_t action; /* KEY_* */ + char text[16]; /* UTF-8 the key produced, if any */ uint8_t text_len; uint32_t unshifted; /* unshifted codepoint, 0 if unknown */ @@ -48,19 +48,19 @@ static void escaped(json_t *j, const char *s, size_t len) { for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)s[i]; switch (c) { - case '"': rawz(j, "\\\""); break; - case '\\': rawz(j, "\\\\"); break; - case '\n': rawz(j, "\\n"); break; - case '\r': rawz(j, "\\r"); break; - case '\t': rawz(j, "\\t"); break; - default: - if (c < 0x20) { - char b[8]; - snprintf(b, sizeof b, "\\u%04x", c); - rawz(j, b); - } else { - raw(j, (const char *)&c, 1); /* UTF-8 passes through */ - } + case '"': rawz(j, "\\\""); break; + case '\\': rawz(j, "\\\\"); break; + case '\n': rawz(j, "\\n"); break; + case '\r': rawz(j, "\\r"); break; + case '\t': rawz(j, "\\t"); break; + default: + if (c < 0x20) { + char b[8]; + snprintf(b, sizeof b, "\\u%04x", c); + rawz(j, b); + } else { + raw(j, (const char *)&c, 1); /* UTF-8 passes through */ + } } } rawz(j, "\""); @@ -15,7 +15,7 @@ typedef struct { void json_init(json_t *j); void json_free(json_t *j); -void json_obj_open(json_t *j, const char *key); /* key may be NULL in arrays */ +void json_obj_open(json_t *j, const char *key); /* key may be NULL in arrays */ void json_obj_close(json_t *j); void json_arr_open(json_t *j, const char *key); void json_arr_close(json_t *j); diff --git a/src/jsonval.c b/src/jsonval.c index acc3334..bd718b1 100644 --- a/src/jsonval.c +++ b/src/jsonval.c @@ -12,7 +12,8 @@ typedef struct { static jv_t *parse_value(P *s); static void skip_ws(P *s) { - while (*s->p == ' ' || *s->p == '\t' || *s->p == '\n' || *s->p == '\r') s->p++; + while (*s->p == ' ' || *s->p == '\t' || *s->p == '\n' || *s->p == '\r') + s->p++; } static jv_t *jv_new(jv_kind_t k) { @@ -23,7 +24,8 @@ static jv_t *jv_new(jv_kind_t k) { static void utf8_put(char **out, unsigned cp) { char *o = *out; - if (cp < 0x80) *o++ = (char)cp; + if (cp < 0x80) + *o++ = (char)cp; else if (cp < 0x800) { *o++ = (char)(0xc0 | (cp >> 6)); *o++ = (char)(0x80 | (cp & 0x3f)); @@ -45,9 +47,12 @@ static unsigned hex4(const char *p) { for (int i = 0; i < 4; i++) { char c = p[i]; v <<= 4; - if (c >= '0' && c <= '9') v |= (unsigned)(c - '0'); - else if (c >= 'a' && c <= 'f') v |= (unsigned)(c - 'a' + 10); - else if (c >= 'A' && c <= 'F') v |= (unsigned)(c - 'A' + 10); + if (c >= '0' && c <= '9') + v |= (unsigned)(c - '0'); + else if (c >= 'a' && c <= 'f') + v |= (unsigned)(c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + v |= (unsigned)(c - 'A' + 10); } return v; } @@ -66,29 +71,29 @@ static char *parse_string_raw(P *s, size_t *out_len) { if (*s->p == '\\') { s->p++; switch (*s->p) { - case 'n': *o++ = '\n'; break; - case 't': *o++ = '\t'; break; - case 'r': *o++ = '\r'; break; - case 'b': *o++ = '\b'; break; - case 'f': *o++ = '\f'; break; - case '/': *o++ = '/'; break; - case '"': *o++ = '"'; break; - case '\\': *o++ = '\\'; break; - case 'u': { - unsigned cp = hex4(s->p + 1); - s->p += 4; - if (cp >= 0xd800 && cp < 0xdc00 && s->p[1] == '\\' && s->p[2] == 'u') { - unsigned lo = hex4(s->p + 3); - s->p += 6; - cp = 0x10000 + ((cp - 0xd800) << 10) + (lo - 0xdc00); - } - utf8_put(&o, cp); - break; + case 'n': *o++ = '\n'; break; + case 't': *o++ = '\t'; break; + case 'r': *o++ = '\r'; break; + case 'b': *o++ = '\b'; break; + case 'f': *o++ = '\f'; break; + case '/': *o++ = '/'; break; + case '"': *o++ = '"'; break; + case '\\': *o++ = '\\'; break; + case 'u': { + unsigned cp = hex4(s->p + 1); + s->p += 4; + if (cp >= 0xd800 && cp < 0xdc00 && s->p[1] == '\\' && s->p[2] == 'u') { + unsigned lo = hex4(s->p + 3); + s->p += 6; + cp = 0x10000 + ((cp - 0xd800) << 10) + (lo - 0xdc00); } - default: - s->bad = true; - free(buf); - return NULL; + utf8_put(&o, cp); + break; + } + default: + s->bad = true; + free(buf); + return NULL; } s->p++; } else { @@ -121,20 +126,38 @@ static jv_t *parse_value(P *s) { for (;;) { skip_ws(s); char *key = parse_string_raw(s, NULL); - if (s->bad) { jv_free(v); return NULL; } + if (s->bad) { + jv_free(v); + return NULL; + } skip_ws(s); - if (*s->p != ':') { s->bad = true; free(key); jv_free(v); return NULL; } + if (*s->p != ':') { + s->bad = true; + free(key); + jv_free(v); + return NULL; + } s->p++; jv_t *val = parse_value(s); - if (!val) { free(key); jv_free(v); return NULL; } + if (!val) { + free(key); + jv_free(v); + return NULL; + } v->keys = realloc(v->keys, (v->len + 1) * sizeof *v->keys); v->kids = realloc(v->kids, (v->len + 1) * sizeof *v->kids); v->keys[v->len] = key; v->kids[v->len] = val; v->len++; skip_ws(s); - if (*s->p == ',') { s->p++; continue; } - if (*s->p == '}') { s->p++; return v; } + if (*s->p == ',') { + s->p++; + continue; + } + if (*s->p == '}') { + s->p++; + return v; + } s->bad = true; jv_free(v); return NULL; @@ -151,12 +174,21 @@ static jv_t *parse_value(P *s) { } for (;;) { jv_t *val = parse_value(s); - if (!val) { jv_free(v); return NULL; } + if (!val) { + jv_free(v); + return NULL; + } v->kids = realloc(v->kids, (v->len + 1) * sizeof *v->kids); v->kids[v->len++] = val; skip_ws(s); - if (*s->p == ',') { s->p++; continue; } - if (*s->p == ']') { s->p++; return v; } + if (*s->p == ',') { + s->p++; + continue; + } + if (*s->p == ']') { + s->p++; + return v; + } s->bad = true; jv_free(v); return NULL; diff --git a/src/jsonval.h b/src/jsonval.h index 53f1fe1..9d0a5d2 100644 --- a/src/jsonval.h +++ b/src/jsonval.h @@ -8,7 +8,12 @@ #include <stddef.h> typedef enum { - JV_NULL, JV_BOOL, JV_NUM, JV_STR, JV_ARR, JV_OBJ, + JV_NULL, + JV_BOOL, + JV_NUM, + JV_STR, + JV_ARR, + JV_OBJ, } jv_kind_t; typedef struct jv jv_t; @@ -85,13 +85,13 @@ static char *parse_string(K *k) { if (c == '\\') { char e = *k->p++; switch (e) { - case 'n': c = '\n'; break; - case 't': c = '\t'; break; - case 'r': c = '\r'; break; - case 'e': c = 0x1b; break; - case '\\': c = '\\'; break; - case '"': c = '"'; break; - default: c = e; break; + case 'n': c = '\n'; break; + case 't': c = '\t'; break; + case 'r': c = '\r'; break; + case 'e': c = 0x1b; break; + case '\\': c = '\\'; break; + case '"': c = '"'; break; + default: c = e; break; } } if (c == '\n') k->line++; @@ -276,7 +276,8 @@ bool kdl_arg_bool(const kdl_node_t *n, size_t i, bool fallback) { return fallback; } -const char *kdl_prop(const kdl_node_t *n, const char *key, const char *fallback) { +const char *kdl_prop(const kdl_node_t *n, const char *key, + const char *fallback) { if (!n) return fallback; for (size_t i = n->nprops; i-- > 0;) /* last wins */ if (strcmp(n->props[i].key, key) == 0) return n->props[i].val; @@ -42,7 +42,8 @@ const kdl_node_t *kdl_child(const kdl_node_t *n, const char *name); const char *kdl_arg(const kdl_node_t *n, size_t i, const char *fallback); long kdl_arg_int(const kdl_node_t *n, size_t i, long fallback); bool kdl_arg_bool(const kdl_node_t *n, size_t i, bool fallback); -const char *kdl_prop(const kdl_node_t *n, const char *key, const char *fallback); +const char *kdl_prop(const kdl_node_t *n, const char *key, + const char *fallback); long kdl_prop_int(const kdl_node_t *n, const char *key, long fallback); bool kdl_prop_bool(const kdl_node_t *n, const char *key, bool fallback); @@ -46,7 +46,8 @@ static void term_size(uint16_t *cols, uint16_t *rows) { * is a config whose "defaults would stand". */ static bool looks_like_layout(const char *path) { char buf[1024]; - kdl_node_t *root = kdl_parse_file(path_expand(path, buf, sizeof buf), NULL, 0); + kdl_node_t *root = + kdl_parse_file(path_expand(path, buf, sizeof buf), NULL, 0); if (!root) return strstr(path, ".layout.") != NULL; bool layout = false; for (size_t i = 0; i < root->nkids && !layout; i++) { @@ -113,8 +114,7 @@ static int check_config(const char *path) { char prefix[24]; config_chord_name(cfg.prefix_key, cfg.prefix_mods, prefix, sizeof prefix); printf("%s: ok\n", file); - for (size_t i = 0; i < nf; i++) - printf(" read %s\n", files[i]); + for (size_t i = 0; i < nf; i++) printf(" read %s\n", files[i]); printf(" prefix %s\n", prefix); printf(" bindings %zu\n", cfg.nbinds); } @@ -149,22 +149,28 @@ int main(int argc, char **argv) { for (int i = 1; i < argc; i++) { const char *a = argv[i]; - if (strcmp(a, "--headless") == 0) headless = true; - else if (strcmp(a, "--script") == 0) headless = script = true; - else if (strcmp(a, "--server") == 0) server = true; - else if (strcmp(a, "-s") == 0 && i + 1 < argc) name = argv[++i]; - else if (strcmp(a, "--layout") == 0 && i + 1 < argc) layout = argv[++i]; - else if (strcmp(a, "--no-reload") == 0) watch = false; + if (strcmp(a, "--headless") == 0) + headless = true; + else if (strcmp(a, "--script") == 0) + headless = script = true; + else if (strcmp(a, "--server") == 0) + server = true; + else if (strcmp(a, "-s") == 0 && i + 1 < argc) + name = argv[++i]; + else if (strcmp(a, "--layout") == 0 && i + 1 < argc) + layout = argv[++i]; + else if (strcmp(a, "--no-reload") == 0) + watch = false; else if (strcmp(a, "--check") == 0) { /* An optional path, so it lints a file you have not installed yet: * `sl0ppty --check theme.kdl`. Without one it checks the config a session * would actually read. A layout is checked as a layout: one flag, and the * file decides which schema it is held to. */ - const char *path = (i + 1 < argc && argv[i + 1][0] != '-') ? argv[++i] : NULL; + const char *path = + (i + 1 < argc && argv[i + 1][0] != '-') ? argv[++i] : NULL; return path && looks_like_layout(path) ? check_layout(path) - : check_config(path); - } - else if (strcmp(a, "--dump-config") == 0) { + : check_config(path); + } else if (strcmp(a, "--dump-config") == 0) { /* Every setting with its *default*, as a file you could have written: * `sl0ppty --dump-config > ~/.config/sl0ppty/config.kdl` is a supported * way to start one. Deliberately not the values a config in effect gives @@ -174,23 +180,27 @@ int main(int argc, char **argv) { fputs(text, stdout); free(text); return 0; - } - else if (strcmp(a, "--version") == 0) { + } else if (strcmp(a, "--version") == 0) { /* The same string the status line shows, so "which build is this" has * one answer whether you ask the binary or the session. */ printf("sl0ppty %s\n", SL0PPTY_VERSION); return 0; - } - else if (strcmp(a, "ls") == 0) list = true; - else if (strcmp(a, "cmd") == 0 && i + 1 < argc) cmd_line = argv[++i]; - else if (strcmp(a, "--cols") == 0 && i + 1 < argc) cols = (uint16_t)atoi(argv[++i]); - else if (strcmp(a, "--rows") == 0 && i + 1 < argc) rows = (uint16_t)atoi(argv[++i]); - else if (strcmp(a, "--idle-ms") == 0 && i + 1 < argc) idle_ms = atoi(argv[++i]); + } else if (strcmp(a, "ls") == 0) + list = true; + else if (strcmp(a, "cmd") == 0 && i + 1 < argc) + cmd_line = argv[++i]; + else if (strcmp(a, "--cols") == 0 && i + 1 < argc) + cols = (uint16_t)atoi(argv[++i]); + else if (strcmp(a, "--rows") == 0 && i + 1 < argc) + rows = (uint16_t)atoi(argv[++i]); + else if (strcmp(a, "--idle-ms") == 0 && i + 1 < argc) + idle_ms = atoi(argv[++i]); else if (strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) { usage(); return 0; } else if (strcmp(a, "--") == 0) { - for (int j = i + 1; j < argc && cmd_n < 63; j++) cmd_argv[cmd_n++] = argv[j]; + for (int j = i + 1; j < argc && cmd_n < 63; j++) + cmd_argv[cmd_n++] = argv[j]; break; } else { fprintf(stderr, "sl0ppty: unknown argument: %s\n", a); @@ -227,7 +237,8 @@ int main(int argc, char **argv) { return client_control(fd, cmd_line); } - if (headless) return run_headless(cmd_argv, cols, rows, idle_ms, script, layout); + if (headless) + return run_headless(cmd_argv, cols, rows, idle_ms, script, layout); if (isatty(STDOUT_FILENO)) term_size(&cols, &rows); if (server) return server_run(name, cmd_argv, cols, rows, layout, watch); diff --git a/src/osc5577.c b/src/osc5577.c index 35f23b4..dd2ec26 100644 --- a/src/osc5577.c +++ b/src/osc5577.c @@ -45,47 +45,47 @@ void osc_scan_feed(osc_scan_t *s, const uint8_t *data, size_t len, for (size_t i = 0; i < len; i++) { uint8_t c = data[i]; switch (s->state) { - case OS_GROUND: - if (c == 0x1b) s->state = OS_ESC; - break; + case OS_GROUND: + if (c == 0x1b) s->state = OS_ESC; + break; - case OS_ESC: - if (c == ']') { - s->state = OS_BODY; - s->len = 0; - s->overflow = false; - } else { - s->state = c == 0x1b ? OS_ESC : OS_GROUND; - } - break; + case OS_ESC: + if (c == ']') { + s->state = OS_BODY; + s->len = 0; + s->overflow = false; + } else { + s->state = c == 0x1b ? OS_ESC : OS_GROUND; + } + break; - case OS_BODY: - if (c == 0x07) { /* BEL terminator, as accepted for OSC 0/2 */ - if (!s->overflow) dispatch(s->buf, s->len, cb, ud); - s->state = OS_GROUND; - } else if (c == 0x1b) { - s->state = OS_BODY_ESC; - } else if (c < 0x20) { - s->state = OS_GROUND; /* a control byte ends a malformed OSC */ - } else if (s->len < sizeof s->buf) { - s->buf[s->len++] = (char)c; - } else { - s->overflow = true; /* keep scanning to the terminator, then drop */ - } - break; + case OS_BODY: + if (c == 0x07) { /* BEL terminator, as accepted for OSC 0/2 */ + if (!s->overflow) dispatch(s->buf, s->len, cb, ud); + s->state = OS_GROUND; + } else if (c == 0x1b) { + s->state = OS_BODY_ESC; + } else if (c < 0x20) { + s->state = OS_GROUND; /* a control byte ends a malformed OSC */ + } else if (s->len < sizeof s->buf) { + s->buf[s->len++] = (char)c; + } else { + s->overflow = true; /* keep scanning to the terminator, then drop */ + } + break; - case OS_BODY_ESC: - if (c == '\\') { /* ST */ - if (!s->overflow) dispatch(s->buf, s->len, cb, ud); - s->state = OS_GROUND; - } else if (c == ']') { - s->state = OS_BODY; /* a new OSC started inside one: restart */ - s->len = 0; - s->overflow = false; - } else { - s->state = OS_GROUND; - } - break; + case OS_BODY_ESC: + if (c == '\\') { /* ST */ + if (!s->overflow) dispatch(s->buf, s->len, cb, ud); + s->state = OS_GROUND; + } else if (c == ']') { + s->state = OS_BODY; /* a new OSC started inside one: restart */ + s->len = 0; + s->overflow = false; + } else { + s->state = OS_GROUND; + } + break; } } } @@ -233,9 +233,11 @@ static void on_notify(GhosttyTerminal t, void *ud, if (!p->notify_cb || !n) return; char title[96] = {0}, body[96] = {0}; if (n->title.len) - snprintf(title, sizeof title, "%.*s", (int)n->title.len, (const char *)n->title.ptr); + snprintf(title, sizeof title, "%.*s", (int)n->title.len, + (const char *)n->title.ptr); if (n->body.len) - snprintf(body, sizeof body, "%.*s", (int)n->body.len, (const char *)n->body.ptr); + snprintf(body, sizeof body, "%.*s", (int)n->body.len, + (const char *)n->body.ptr); p->notify_cb(p, title, body, p->notify_ud); } @@ -356,8 +358,8 @@ pane_t *pane_new(const char *const argv[], uint16_t cols, uint16_t rows, * clusters too; under a terminal that does not, wide emoji may misalign. */ static const uint16_t default_on[] = {25, 2027}; for (size_t i = 0; i < sizeof default_on / sizeof *default_on; i++) { - GhosttyTerminalModeConfig mc = {.mode = ghostty_mode_new(default_on[i], false), - .value = true}; + GhosttyTerminalModeConfig mc = { + .mode = ghostty_mode_new(default_on[i], false), .value = true}; ghostty_terminal_set(p->term, GHOSTTY_TERMINAL_OPT_MODE_DEFAULT, &mc); } @@ -691,8 +693,10 @@ void pane_send_mouse(pane_t *p, const input_event_t *ev) { ghostty_mouse_event_set_position(p->mev, pos); /* "no button held" is its own thing, not button zero: bare motion (hover) * must be encoded as none, or a pane in any-event tracking sees nothing. */ - if (ev->button == MBTN_UNKNOWN) ghostty_mouse_event_clear_button(p->mev); - else ghostty_mouse_event_set_button(p->mev, (GhosttyMouseButton)ev->button); + if (ev->button == MBTN_UNKNOWN) + ghostty_mouse_event_clear_button(p->mev); + else + ghostty_mouse_event_set_button(p->mev, (GhosttyMouseButton)ev->button); ghostty_mouse_event_set_action(p->mev, (GhosttyMouseAction)ev->maction); ghostty_mouse_event_set_mods(p->mev, (GhosttyMods)ev->mods); @@ -737,7 +741,8 @@ size_t pane_graphics(pane_t *p, pane_gfx_fn cb, void *ud) { if (ghostty_kitty_graphics_placement_iterator_new(NULL, &it) != GHOSTTY_SUCCESS) return 0; - if (ghostty_kitty_graphics_get(gfx, GHOSTTY_KITTY_GRAPHICS_DATA_PLACEMENT_ITERATOR, + if (ghostty_kitty_graphics_get(gfx, + GHOSTTY_KITTY_GRAPHICS_DATA_PLACEMENT_ITERATOR, &it) != GHOSTTY_SUCCESS) { ghostty_kitty_graphics_placement_iterator_free(it); return 0; @@ -838,10 +843,12 @@ size_t pane_graphics(pane_t *p, pane_gfx_fn cb, void *ud) { const uint8_t *data = NULL; size_t data_len = 0; ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_WIDTH, &w); - ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_HEIGHT, &h); - ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_FORMAT, &fmt); - ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_COMPRESSION, - &comp); + ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_HEIGHT, + &h); + ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_FORMAT, + &fmt); + ghostty_kitty_graphics_image_get( + img, GHOSTTY_KITTY_IMAGE_DATA_COMPRESSION, &comp); ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_GENERATION, &generation); ghostty_kitty_graphics_image_get(img, GHOSTTY_KITTY_IMAGE_DATA_DATA_PTR, @@ -872,7 +879,8 @@ size_t pane_graphics(pane_t *p, pane_gfx_fn cb, void *ud) { * reports the per-row range for us to highlight. */ -static bool grid_ref_at(pane_t *p, uint16_t x, uint16_t y, GhosttyGridRef *out) { +static bool grid_ref_at(pane_t *p, uint16_t x, uint16_t y, + GhosttyGridRef *out) { GhosttyPoint pt = {.tag = GHOSTTY_POINT_TAG_VIEWPORT, .value.coordinate = {.x = x, .y = y}}; return ghostty_terminal_grid_ref(p->term, pt, out) == GHOSTTY_SUCCESS; @@ -935,7 +943,8 @@ static bool word_cell(const char *utf8, size_t len, const char *seps) { * forward. Which is enough: the run containing `x` is bounded by the separator * before it and the one after it, and both are met on that single pass. */ bool pane_select_word(pane_t *p, uint16_t x, uint16_t y, const char *seps) { - if (!p || !p->term || !p->rstate || x >= p->cols || y >= p->rows_n) return false; + if (!p || !p->term || !p->rstate || x >= p->cols || y >= p->rows_n) + return false; if (ghostty_render_state_update(p->rstate, p->term) != GHOSTTY_SUCCESS) return false; if (ghostty_render_state_get(p->rstate, @@ -1012,7 +1021,8 @@ bool pane_select_word(pane_t *p, uint16_t x, uint16_t y, const char *seps) { if (!done) return false; GhosttyGridRef a, b; - if (!grid_ref_at(p, left, y, &a) || !grid_ref_at(p, right, y, &b)) return false; + if (!grid_ref_at(p, left, y, &a) || !grid_ref_at(p, right, y, &b)) + return false; GhosttySelection sel = GHOSTTY_INIT_SIZED(GhosttySelection); sel.start = a; sel.end = b; @@ -1045,8 +1055,8 @@ char *pane_selection_text(pane_t *p) { opts.trim = true; uint8_t *ptr = NULL; size_t len = 0; - if (ghostty_terminal_selection_format_alloc(p->term, NULL, opts, &ptr, &len) != - GHOSTTY_SUCCESS || + if (ghostty_terminal_selection_format_alloc(p->term, NULL, opts, &ptr, + &len) != GHOSTTY_SUCCESS || !len) return NULL; char *out = malloc(len + 1); @@ -1066,14 +1076,16 @@ void pane_scroll(pane_t *p, int delta) { } void pane_scroll_edge(pane_t *p, bool top) { - GhosttyTerminalScrollViewport b = { - .tag = top ? GHOSTTY_SCROLL_VIEWPORT_TOP : GHOSTTY_SCROLL_VIEWPORT_BOTTOM}; + GhosttyTerminalScrollViewport b = {.tag = + top ? GHOSTTY_SCROLL_VIEWPORT_TOP + : GHOSTTY_SCROLL_VIEWPORT_BOTTOM}; ghostty_terminal_scroll_viewport(p->term, b); p->dirty = true; } bool pane_scrolled(const pane_t *p) { - bool active = true; /* "the viewport is on the active area", i.e. the bottom */ + bool active = + true; /* "the viewport is on the active area", i.e. the bottom */ ghostty_terminal_get(p->term, GHOSTTY_TERMINAL_DATA_VIEWPORT_ACTIVE, &active); return !active; } @@ -1135,16 +1147,19 @@ static color_t to_color(const GhosttyColorRgb *c, bool ok) { void pane_compose(pane_t *p, screen_t *s, uint16_t x0, uint16_t y0, bool focused) { - if (ghostty_render_state_update(p->rstate, p->term) != GHOSTTY_SUCCESS) return; + if (ghostty_render_state_update(p->rstate, p->term) != GHOSTTY_SUCCESS) + return; - if (ghostty_render_state_get(p->rstate, GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR, + if (ghostty_render_state_get(p->rstate, + GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR, &p->rows) != GHOSTTY_SUCCESS) return; uint16_t y = 0; while (ghostty_render_state_row_iterator_next(p->rows)) { if (y >= p->rows_n) break; - if (ghostty_render_state_row_get(p->rows, GHOSTTY_RENDER_STATE_ROW_DATA_CELLS, + if (ghostty_render_state_row_get(p->rows, + GHOSTTY_RENDER_STATE_ROW_DATA_CELLS, &p->cells) != GHOSTTY_SUCCESS) { y++; continue; @@ -1214,8 +1229,12 @@ void pane_compose(pane_t *p, screen_t *s, uint16_t x0, uint16_t y0, size_t n = gb.len < sizeof dst->text ? gb.len : sizeof dst->text; memset(dst->text, 0, sizeof dst->text); - if (n) memcpy(dst->text, utf8, n); - else { dst->text[0] = ' '; n = 1; } + if (n) + memcpy(dst->text, utf8, n); + else { + dst->text[0] = ' '; + n = 1; + } dst->len = (uint8_t)n; dst->width = wide == GHOSTTY_CELL_WIDE_WIDE ? 2 : 1; /* Selected cells are inverted rather than recoloured, so a selection @@ -1234,15 +1253,16 @@ void pane_compose(pane_t *p, screen_t *s, uint16_t x0, uint16_t y0, if (focused) { bool vis = false, has_pos = false; uint16_t cxp = 0, cyp = 0; - ghostty_render_state_get(p->rstate, GHOSTTY_RENDER_STATE_DATA_CURSOR_VISIBLE, - &vis); + ghostty_render_state_get(p->rstate, + GHOSTTY_RENDER_STATE_DATA_CURSOR_VISIBLE, &vis); ghostty_render_state_get( - p->rstate, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE, &has_pos); + p->rstate, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE, + &has_pos); if (vis && has_pos) { - ghostty_render_state_get(p->rstate, - GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_X, &cxp); - ghostty_render_state_get(p->rstate, - GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_Y, &cyp); + ghostty_render_state_get( + p->rstate, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_X, &cxp); + ghostty_render_state_get( + p->rstate, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_Y, &cyp); s->cursor_visible = true; s->cursor_x = (uint16_t)(x0 + cxp); s->cursor_y = (uint16_t)(y0 + cyp); @@ -1252,6 +1272,7 @@ void pane_compose(pane_t *p, screen_t *s, uint16_t x0, uint16_t y0, } GhosttyRenderStateDirty clean = GHOSTTY_RENDER_STATE_DIRTY_FALSE; - ghostty_render_state_set(p->rstate, GHOSTTY_RENDER_STATE_OPTION_DIRTY, &clean); + ghostty_render_state_set(p->rstate, GHOSTTY_RENDER_STATE_OPTION_DIRTY, + &clean); p->dirty = false; } diff --git a/src/project.c b/src/project.c index 4e2ba2d..381d17f 100644 --- a/src/project.c +++ b/src/project.c @@ -70,12 +70,14 @@ static bool describe(const char *path, project_t *p) { const char *resolved = realpath(path, real) ? real : path; snprintf(p->path, sizeof p->path, "%s", resolved); basename_of(p->path, p->name, sizeof p->name); - if (!p->name[0] || p->name[0] == '.') return false; /* not `.`, `..`, dotdirs */ + if (!p->name[0] || p->name[0] == '.') + return false; /* not `.`, `..`, dotdirs */ project_slug(p->path, p->name, p->slug, sizeof p->slug); struct stat lst; if (exists(p->path, PROJECT_LAYOUT_FILE, &lst)) { - snprintf(p->layout, sizeof p->layout, "%s/%s", p->path, PROJECT_LAYOUT_FILE); + snprintf(p->layout, sizeof p->layout, "%s/%s", p->path, + PROJECT_LAYOUT_FILE); p->mtime = (int64_t)lst.st_mtime; return true; } diff --git a/src/proto.c b/src/proto.c index f714b91..100791e 100644 --- a/src/proto.c +++ b/src/proto.c @@ -94,8 +94,10 @@ int msg_send(int fd, uint8_t type, const void *data, size_t len) { static int session_dir(char *out, size_t cap) { const char *run = getenv("XDG_RUNTIME_DIR"); - if (run && *run) snprintf(out, cap, "%s/sl0ppty", run); - else snprintf(out, cap, "/tmp/sl0ppty-%u", (unsigned)getuid()); + if (run && *run) + snprintf(out, cap, "%s/sl0ppty", run); + else + snprintf(out, cap, "/tmp/sl0ppty-%u", (unsigned)getuid()); if (mkdir(out, 0700) != 0 && errno != EEXIST) return -1; return 0; } @@ -57,7 +57,10 @@ int pty_spawn(pty_t *p, const char *const argv[], uint16_t cols, uint16_t rows, if (slave > STDERR_FILENO) close(slave); close(master); - if (cwd) { if (chdir(cwd) != 0) { /* keep going in the old cwd */ } } + if (cwd) { + if (chdir(cwd) != 0) { /* keep going in the old cwd */ + } + } /* We are opinionated about the outer terminal (D11), and we present the * same contract inward. xterm-ghostty is what libghostty-vt implements. */ diff --git a/src/screen.c b/src/screen.c index aa0faa3..4642634 100644 --- a/src/screen.c +++ b/src/screen.c @@ -135,16 +135,22 @@ static size_t decode_utf8(const char *txt, size_t len, uint32_t *cps, if (l == 0 || i + l > len) break; uint32_t cp; switch (l) { - case 1: cp = (unsigned char)txt[i]; break; - case 2: cp = ((uint32_t)((unsigned char)txt[i] & 0x1f) << 6) | - ((unsigned char)txt[i + 1] & 0x3f); break; - case 3: cp = ((uint32_t)((unsigned char)txt[i] & 0x0f) << 12) | - ((uint32_t)((unsigned char)txt[i + 1] & 0x3f) << 6) | - ((unsigned char)txt[i + 2] & 0x3f); break; - default: cp = ((uint32_t)((unsigned char)txt[i] & 0x07) << 18) | - ((uint32_t)((unsigned char)txt[i + 1] & 0x3f) << 12) | - ((uint32_t)((unsigned char)txt[i + 2] & 0x3f) << 6) | - ((unsigned char)txt[i + 3] & 0x3f); break; + case 1: cp = (unsigned char)txt[i]; break; + case 2: + cp = ((uint32_t)((unsigned char)txt[i] & 0x1f) << 6) | + ((unsigned char)txt[i + 1] & 0x3f); + break; + case 3: + cp = ((uint32_t)((unsigned char)txt[i] & 0x0f) << 12) | + ((uint32_t)((unsigned char)txt[i + 1] & 0x3f) << 6) | + ((unsigned char)txt[i + 2] & 0x3f); + break; + default: + cp = ((uint32_t)((unsigned char)txt[i] & 0x07) << 18) | + ((uint32_t)((unsigned char)txt[i + 1] & 0x3f) << 12) | + ((uint32_t)((unsigned char)txt[i + 2] & 0x3f) << 6) | + ((unsigned char)txt[i + 3] & 0x3f); + break; } cps[n++] = cp; i += l; @@ -182,8 +188,8 @@ uint16_t screen_cells(const char *txt) { size_t len = strlen(txt), at = 0; while (at < len) { size_t used = 0; - size_t n = decode_utf8(txt + at, len - at, cps, sizeof cps / sizeof *cps, - &used); + size_t n = + decode_utf8(txt + at, len - at, cps, sizeof cps / sizeof *cps, &used); if (!n) break; for (size_t i = 0; i < n;) { uint8_t w = 1; @@ -231,7 +237,6 @@ void screen_put_utf8(screen_t *s, uint16_t x, uint16_t y, const char *txt, c->attrs = attrs; } - uint16_t screen_text(screen_t *s, uint16_t x, uint16_t y, const char *txt, color_t fg, color_t bg, uint16_t attrs) { uint16_t n = 0; @@ -276,7 +281,8 @@ static void emit_sgr(screen_t *s, const cell_t *c) { } static bool style_eq(const cell_t *a, const cell_t *b) { - return a->attrs == b->attrs && color_eq(a->fg, b->fg) && color_eq(a->bg, b->bg); + return a->attrs == b->attrs && color_eq(a->fg, b->fg) && + color_eq(a->bg, b->bg); } void screen_render(screen_t *s) { @@ -319,8 +325,10 @@ void screen_render(screen_t *s) { style = *c; have_style = true; } - if (c->len) out_bytes(s, c->text, c->len); - else out_str(s, " "); + if (c->len) + out_bytes(s, c->text, c->len); + else + out_str(s, " "); *p = *c; cx += c->width ? c->width : 1; @@ -399,9 +407,9 @@ static void dump_style_runs(screen_t *s, json_t *j) { uint16_t bit; const char *name; } names[] = { - {ATTR_BOLD, "bold"}, {ATTR_DIM, "dim"}, - {ATTR_ITALIC, "italic"}, {ATTR_UNDERLINE, "underline"}, - {ATTR_BLINK, "blink"}, {ATTR_INVERSE, "inverse"}, + {ATTR_BOLD, "bold"}, {ATTR_DIM, "dim"}, + {ATTR_ITALIC, "italic"}, {ATTR_UNDERLINE, "underline"}, + {ATTR_BLINK, "blink"}, {ATTR_INVERSE, "inverse"}, {ATTR_INVISIBLE, "invisible"}, {ATTR_STRIKE, "strike"}, }; for (size_t i = 0; i < sizeof names / sizeof *names; i++) diff --git a/src/server.c b/src/server.c index f728036..398d279 100644 --- a/src/server.c +++ b/src/server.c @@ -216,8 +216,8 @@ static void watch_config(watchset_t *w) { * a mistyped path should not leave a directory behind. */ if (i == 0) path_mkdirs(dir); - int wd = inotify_add_watch(w->fd, dir, - IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE); + int wd = + inotify_add_watch(w->fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE); if (wd >= 0) { bool have = false; for (size_t k = 0; k < w->nwds; k++) @@ -260,8 +260,10 @@ static bool watch_hit(const watchset_t *w, const char *name) { * and set rather than inherited so it names the binary that actually made this * pane, not one that made some pane somewhere. */ void session_env(const char *name) { - if (name) setenv("SL0PPTY_SESSION", name, 1); - else unsetenv("SL0PPTY_SESSION"); + if (name) + setenv("SL0PPTY_SESSION", name, 1); + else + unsetenv("SL0PPTY_SESSION"); char self[512]; ssize_t sn = readlink("/proc/self/exe", self, sizeof self - 1); if (sn > 0) { @@ -276,7 +278,8 @@ int server_run(const char *name, const char *const argv[], uint16_t cols, if (session_socket_path(name, path, sizeof path) != 0) return 1; int lfd = listen_socket(path); if (lfd < 0) { - fprintf(stderr, "sl0ppty: cannot listen on %s: %s\n", path, strerror(errno)); + fprintf(stderr, "sl0ppty: cannot listen on %s: %s\n", path, + strerror(errno)); return 1; } @@ -415,66 +418,65 @@ int server_run(const char *name, const char *const argv[], uint16_t cols, bool closed = false; while (!closed && msg_reader_next(&s.conns[ci].reader, &m)) { switch (m.type) { - case MSG_HELLO: - case MSG_RESIZE: - if (m.type == MSG_HELLO && !s.conns[ci].display) { - /* now it is a client: the previous display is displaced */ - for (size_t j = 0; j < s.nconns; j++) - if (j != ci && s.conns[j].display) { - conn_drop(&s, j, EXIT_REPLACED); - if (j < ci) ci--; /* the array compacted under us */ - break; - } - s.conns[ci].display = true; - } - if (m.len >= 4 && s.conns[ci].display) { - uint16_t c = (uint16_t)(m.data[0] << 8 | m.data[1]); - uint16_t rr = (uint16_t)(m.data[2] << 8 | m.data[3]); - if (c && rr) { - screen_resize(&s.screen, c, rr); - app_resize(s.app, c, rr); + case MSG_HELLO: + case MSG_RESIZE: + if (m.type == MSG_HELLO && !s.conns[ci].display) { + /* now it is a client: the previous display is displaced */ + for (size_t j = 0; j < s.nconns; j++) + if (j != ci && s.conns[j].display) { + conn_drop(&s, j, EXIT_REPLACED); + if (j < ci) ci--; /* the array compacted under us */ + break; } - /* The cell size arrived with it, from a client new enough to + s.conns[ci].display = true; + } + if (m.len >= 4 && s.conns[ci].display) { + uint16_t c = (uint16_t)(m.data[0] << 8 | m.data[1]); + uint16_t rr = (uint16_t)(m.data[2] << 8 | m.data[3]); + if (c && rr) { + screen_resize(&s.screen, c, rr); + app_resize(s.app, c, rr); + } + /* The cell size arrived with it, from a client new enough to * send one and a terminal willing to say. Zero means neither, * and app_set_cell_px leaves the default standing. */ - if (m.len >= 8) { - uint16_t cw = (uint16_t)(m.data[4] << 8 | m.data[5]); - uint16_t ch = (uint16_t)(m.data[6] << 8 | m.data[7]); - app_set_cell_px(s.app, cw, ch); - } + if (m.len >= 8) { + uint16_t cw = (uint16_t)(m.data[4] << 8 | m.data[5]); + uint16_t ch = (uint16_t)(m.data[6] << 8 | m.data[7]); + app_set_cell_px(s.app, cw, ch); } - s.screen.force_full = true; /* a fresh client knows nothing */ - app_graphics_reset(s.app); /* including any image we had sent */ - pending_paint = true; - next_frame = now_ms(); - break; - case MSG_INPUT: - if (!s.conns[ci].display) break; /* only the client types */ - input_feed(s.in, m.data, m.len, srv_event, &s); - esc_due = now_ms() + ESC_MS; - pending_paint = true; - break; - case MSG_DETACH: - conn_drop(&s, ci, EXIT_DETACHED); - closed = true; - break; - case MSG_CMD: { - /* The same vocabulary the headless driver speaks (cmd.c), so a - * script written against one works against the other. */ - bool q = false; - char *reply = - cmd_exec(s.app, &s.screen, s.in, (const char *)m.data, &q); - const char *body = reply ? reply : "{\"error\":\"unknown command\"}"; - msg_send(s.conns[ci].fd, MSG_REPLY, body, strlen(body)); - free(reply); - if (q) g_stop = 1; - s.screen.force_full = true; /* it may have changed the layout */ - pending_paint = true; - next_frame = now_ms(); - break; } - default: - break; + s.screen.force_full = true; /* a fresh client knows nothing */ + app_graphics_reset(s.app); /* including any image we had sent */ + pending_paint = true; + next_frame = now_ms(); + break; + case MSG_INPUT: + if (!s.conns[ci].display) break; /* only the client types */ + input_feed(s.in, m.data, m.len, srv_event, &s); + esc_due = now_ms() + ESC_MS; + pending_paint = true; + break; + case MSG_DETACH: + conn_drop(&s, ci, EXIT_DETACHED); + closed = true; + break; + case MSG_CMD: { + /* The same vocabulary the headless driver speaks (cmd.c), so a + * script written against one works against the other. */ + bool q = false; + char *reply = + cmd_exec(s.app, &s.screen, s.in, (const char *)m.data, &q); + const char *body = reply ? reply : "{\"error\":\"unknown command\"}"; + msg_send(s.conns[ci].fd, MSG_REPLY, body, strlen(body)); + free(reply); + if (q) g_stop = 1; + s.screen.force_full = true; /* it may have changed the layout */ + pending_paint = true; + next_frame = now_ms(); + break; + } + default: break; } } if (!closed) ci++; diff --git a/src/shader.c b/src/shader.c index 56a0a2b..6f24564 100644 --- a/src/shader.c +++ b/src/shader.c @@ -20,8 +20,8 @@ static uint8_t mix8(uint8_t from, uint8_t to, uint8_t amount) { } 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)}; + return (color_t){true, mix8(from.r, to.r, amount), mix8(from.g, to.g, amount), + mix8(from.b, to.b, amount)}; } /* Rec. 601 luma. The green weight is not a rounding error: the eye is far more @@ -84,10 +84,22 @@ static void sh_vignette(const shader_t *sh, const shade_ctx_t *ctx, cell_t *c) { static void sh_gradient(const shader_t *sh, const shade_ctx_t *ctx, cell_t *c) { int span, pos; switch (sh->param) { - case 1: span = ctx->rows; pos = ctx->rows - 1 - ctx->y; break; - case 2: span = ctx->cols; pos = ctx->x; break; - case 3: span = ctx->cols; pos = ctx->cols - 1 - ctx->x; break; - default: span = ctx->rows; pos = ctx->y; break; + case 1: + span = ctx->rows; + pos = ctx->rows - 1 - ctx->y; + break; + case 2: + span = ctx->cols; + pos = ctx->x; + break; + case 3: + span = ctx->cols; + pos = ctx->cols - 1 - ctx->x; + break; + default: + span = ctx->rows; + pos = ctx->y; + break; } if (span <= 1) return; uint8_t a = (uint8_t)((int)sh->amount * pos / (span - 1)); @@ -126,8 +138,8 @@ static void sh_spotlight(const shader_t *sh, const shade_ctx_t *ctx, if (!ctx->has_cursor) return; int r = sh->param ? sh->param : 10; int inner = r * r; - int d = dist2((int)ctx->x - (int)ctx->cursor_x, - (int)ctx->y - (int)ctx->cursor_y); + int d = + dist2((int)ctx->x - (int)ctx->cursor_x, (int)ctx->y - (int)ctx->cursor_y); if (d <= inner) return; int ramp = inner * 3; int over = d - inner; @@ -188,8 +200,11 @@ bool shader_make_p(shader_t *out, const char *kind, color_t color, if (!d) return false; /* channels left 0, which the pass reads as both: a shader made here has no * opinion about that, and the config sets it when the config has one. */ - *out = (shader_t){.kind = d->name, .fn = d->fn, .color = color, - .amount = amount, .param = param}; + *out = (shader_t){.kind = d->name, + .fn = d->fn, + .color = color, + .amount = amount, + .param = param}; return true; } @@ -403,8 +418,10 @@ void shade_apply(screen_t *s, const shader_t *shaders, size_t n, rect_t r, * reused for as long as nothing it reads has changed. One that does * read the clock is evaluated per cell, per frame, which is what * asking for animation costs. */ - if (expr_amount_map(sh->amount_expr, &env, &m)) map = m; - else per_cell = true; + if (expr_amount_map(sh->amount_expr, &env, &m)) + map = m; + else + per_cell = true; } } diff --git a/src/shader_abi.h b/src/shader_abi.h index 96b7069..5cfb560 100644 --- a/src/shader_abi.h +++ b/src/shader_abi.h @@ -110,7 +110,7 @@ struct shader { const char *kind; /* registry name, NULL for an empty slot */ shade_fn fn; expr_prog_t *amount_expr; /* NULL when `amount` is just a number */ - color_t color; /* the target colour, for shaders that have one */ + color_t color; /* the target colour, for shaders that have one */ /* Strength, 0..255; 0 is identity, 255 is fully applied. A cell whose * strength works out to 0 is skipped by the pass rather than handed to the * shader, so a plugin is never called to do nothing -- and a cell the @@ -158,19 +158,19 @@ typedef struct { #define SL0PPTY_SHADER_PLUGIN_SYM "sl0ppty_shader_plugin" /* Define the entry point. `defs` is a static array of shader_def_t. */ -#define SL0PPTY_SHADER_PLUGIN(bundle_name, defs) \ - const shader_plugin_t *sl0ppty_shader_plugin(void); \ - const shader_plugin_t *sl0ppty_shader_plugin(void) { \ - static const shader_plugin_t table = { \ - .abi = SL0PPTY_SHADER_ABI, \ - .cell_size = sizeof(cell_t), \ - .ctx_size = sizeof(shade_ctx_t), \ - .shader_size = sizeof(shader_t), \ - .name = (bundle_name), \ - .count = sizeof(defs) / sizeof((defs)[0]), \ - .shaders = (defs), \ - }; \ - return &table; \ +#define SL0PPTY_SHADER_PLUGIN(bundle_name, defs) \ + const shader_plugin_t *sl0ppty_shader_plugin(void); \ + const shader_plugin_t *sl0ppty_shader_plugin(void) { \ + static const shader_plugin_t table = { \ + .abi = SL0PPTY_SHADER_ABI, \ + .cell_size = sizeof(cell_t), \ + .ctx_size = sizeof(shade_ctx_t), \ + .shader_size = sizeof(shader_t), \ + .name = (bundle_name), \ + .count = sizeof(defs) / sizeof((defs)[0]), \ + .shaders = (defs), \ + }; \ + return &table; \ } #endif /* SL0PPTY_SHADER_ABI_H */ diff --git a/src/sl0ppty.h b/src/sl0ppty.h index 7e9e11d..09e373e 100644 --- a/src/sl0ppty.h +++ b/src/sl0ppty.h @@ -245,8 +245,8 @@ typedef struct { * placement happens to cover changes by one as it slides across a cell * boundary — so passing it on makes a moving image change size. */ uint16_t req_cols, req_rows; - uint32_t px_w, px_h; /* rendered size */ - uint32_t src_w, src_h; /* the image's own size */ + uint32_t px_w, px_h; /* rendered size */ + uint32_t src_w, src_h; /* the image's own size */ /* The part of the image to draw, in pixels. Clipping has to move this: * asking for fewer columns alone *scales* the image into them, it does not * crop it. */ |
