aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorIngmar Steen <iksteen@gmail.com>2026-09-06 20:34:26 +0200
committerIngmar Steen <iksteen@gmail.com>2026-09-06 20:34:26 +0200
commit79a2812808061129bf1449d9f4c3356b36bf0061 (patch)
tree773ca521e7432af6e271bfb7f69ff2ab5047f1c9 /src
parentgraphics: place what a float leaves of an image, not nothing (diff)
downloadslosh-79a2812808061129bf1449d9f4c3356b36bf0061.tar.gz
slosh-79a2812808061129bf1449d9f4c3356b36bf0061.tar.xz
slosh-79a2812808061129bf1449d9f4c3356b36bf0061.zip
app: cut the modals, toasts and splash out of the images under them
Overlays are painted over the panes and the compositor needs no more than that: painted later is on top. A placement is not painted, though -- the client's terminal draws the image over the cells, in whatever order they arrived -- so an overlay that is only drawn is an overlay an image sits on top of. The cheatsheet came up under a picture and was unreadable through it, in the terminal and in the mirror both. So a frame now records where its overlays landed, and the graphics pass takes them out of a placement exactly as it takes out a float: the modal's box (the cheatsheet and the picker wear the same frame), each toast, and the splash. The list is rebuilt every compose, so a rect that is not registered again is one that is no longer on screen. Only the opaque things are registered. The scrim behind a modal tints what is behind it rather than hiding it, and an image cannot be tinted by us at all -- so a picture beside the cheatsheet stays bright while the text around it is pushed back, which is wrong-looking but a great deal better than the image disappearing while a modal is up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRz3zwZJvBvzsR9k4cj6dd
Diffstat (limited to 'src')
-rw-r--r--src/app.c10
-rw-r--r--src/app_draw.c13
-rw-r--r--src/app_ui.c7
3 files changed, 27 insertions, 3 deletions
diff --git a/src/app.c b/src/app.c
index cc06924..7ff584b 100644
--- a/src/app.c
+++ b/src/app.c
@@ -668,6 +668,15 @@ void draw_status_line(app_t *a, screen_t *s) {
screen_text(s, (uint16_t)from, y, middle, middle_fg, NO_COLOR, 0);
}
+/* Register something painted over the panes, so the graphics pass can cut it
+ * out of any image underneath it. Opaque things only: a scrim tints what is
+ * behind it and an image under one should stay on screen, undimmed and wrong
+ * rather than gone. */
+void app_overlay(app_t *a, rect_t r) {
+ if (!a || !r.w || !r.h || a->noverlays >= APP_MAX_OVERLAYS) return;
+ a->overlays[a->noverlays++] = r;
+}
+
void draw_toasts(app_t *a, screen_t *s) {
toasts_expire(a);
if (!a->ntoasts) return;
@@ -706,6 +715,7 @@ void draw_toasts(app_t *a, screen_t *s) {
char clipped[160];
snprintf(clipped, sizeof clipped, "%.*s", (int)w, line);
screen_text(s, x, y, clipped, TOAST_FG, TOAST_BG, ATTR_BOLD);
+ app_overlay(a, (rect_t){x, y, (uint16_t)cells(clipped), 1});
}
}
diff --git a/src/app_draw.c b/src/app_draw.c
index c47a253..495add6 100644
--- a/src/app_draw.c
+++ b/src/app_draw.c
@@ -2520,11 +2520,16 @@ static void gfx_leaf_cb(node_t *n, void *ud) {
if (n->hidden || n->collapsed) return; /* not drawn: images included */
/* Above this leaf: every float for a tiled pane, the higher-raised for a
* float — the same order draw_floats paints in, so an image is clipped by
- * exactly what its cells are covered by. */
- rect_t occ[64];
+ * exactly what its cells are covered by. Then the frame's overlays, which
+ * are above the floats too: the cheatsheet, a toast, the splash. They are
+ * drawn as cells and a placement is not, so without this the terminal puts
+ * the picture back over the top of them -- the cheatsheet appearing under
+ * an image is what asked for this. */
+ rect_t occ[64 + APP_MAX_OVERLAYS];
size_t k = 0;
for (size_t i = 0; i < w->n; i++)
if (!n->floating || w->raised[i] > n->raised) occ[k++] = w->occ[i];
+ for (size_t i = 0; i < w->a->noverlays; i++) occ[k++] = w->a->overlays[i];
struct gfx_ctx ctx = {.a = w->a,
.out = w->out,
.leaf = n,
@@ -2817,6 +2822,10 @@ void draw_splash(app_t *a, screen_t *s) {
uint16_t x0 = (uint16_t)((s->cols - bw) / 2);
uint16_t y0 = (uint16_t)((s->rows - bh) / 2);
+ /* A cleared backdrop, so the logo reads over whatever a pane put there --
+ * including an image, which is cleared by being cut out of its placement
+ * rather than by writing cells over it. */
+ app_overlay(a, (rect_t){x0, y0, bw, bh});
/* A cleared backdrop, so the logo reads over whatever a pane put there. */
char blank[512];
size_t nb = bw < sizeof blank - 1 ? bw : sizeof blank - 1;
diff --git a/src/app_ui.c b/src/app_ui.c
index 8daf8f1..21b3529 100644
--- a/src/app_ui.c
+++ b/src/app_ui.c
@@ -189,10 +189,14 @@ static rect_t modal_frame(app_t *a, screen_t *s, uint16_t w, uint16_t h,
uint16_t x1 = (uint16_t)(x + w - 1), y1 = (uint16_t)(y + h - 1);
/* Opaque, or the panes underneath read through it: a modal you can see a
- * shell prompt through is a modal nobody trusts. */
+ * shell prompt through is a modal nobody trusts. Opaque for the cells is
+ * this loop; opaque for an image under it is the registration, without
+ * which the terminal draws the picture over the modal and the cheatsheet
+ * reads through a screenshot. */
for (uint16_t yy = y; yy < y + h; yy++)
for (uint16_t xx = x; xx < x + w; xx++)
screen_text(s, xx, yy, " ", NO_COLOR, MODAL_BG, 0);
+ app_overlay(a, (rect_t){x, y, w, h});
const char *tl = CFG.rounded ? "\u256d" : "\u250c",
*tr = CFG.rounded ? "\u256e" : "\u2510";
@@ -993,6 +997,7 @@ static void drop_render_cb(node_t *n, void *ud) {
void app_compose(app_t *a, screen_t *s) {
screen_clear(s); /* every frame starts blank: no ghosts in the gap ring */
hit_reset(&s->hits);
+ a->noverlays = 0; /* re-registered by whatever draws over the panes below */
s->cursor_visible = false;
a->painted = s;
/* Re-derived by the shader passes below, every frame, from what they ran. */