aboutsummaryrefslogtreecommitdiffstats
path: root/src/graphics.c
diff options
context:
space:
mode:
authorIngmar Steen <iksteen@gmail.com>2026-09-06 19:59:13 +0200
committerIngmar Steen <iksteen@gmail.com>2026-09-06 19:59:13 +0200
commitaf82aff3cac5370cc08b2e0d7358097bfb2d9e45 (patch)
treef13ad9272e021d039a462a96ce49f9c6b1929d68 /src/graphics.c
parentgraphics: state z=0 on a placement, so a mirror paints it over the text (diff)
downloadslosh-af82aff3cac5370cc08b2e0d7358097bfb2d9e45.tar.gz
slosh-af82aff3cac5370cc08b2e0d7358097bfb2d9e45.tar.xz
slosh-af82aff3cac5370cc08b2e0d7358097bfb2d9e45.zip
graphics: place what a float leaves of an image, not nothing
Occlusion for placements is clipping: the cell compositor gets it free from paint order, but images are sent after the diff and drawn over it, so what a float covers has to be taken out of the placement itself. A clean edge came out as a smaller rectangle; anything else -- a float across the middle, a float on a corner, a float wholly inside -- was refused, on the grounds that the remainder is an L and a placement is a rectangle. The image vanished, all of it, the moment a floating pane crossed it, on the terminal and in the mirror alike, and came back when the float moved off. The remainder is not one rectangle, but it is always a few: the strips above and below what is covered, and what is left beside it between them. So rect_subtract() cuts the visible remainder into rectangles and each is placed in its own right, with the source rectangle the same arithmetic the pane edge already used -- generalised to a cut on any side, and to a first piece that keeps the sub-cell offset while the others start on a cell boundary. Eight pieces is the ceiling (two floats' worth); past it the old refusal stands. The pieces of one placement need an id each, and the program's own placement id is one number that cannot be several, so the model now hands out the placement ids the client is told and keeps the program's as the identity it was. `graphics` reports the piece alongside it. Checked against a float parked over an image: the picture is cut around the float, the strips tile exactly the image minus what the float covers, and their source rectangles land where their cells do (a float inside a 40x10-cell natural-size image gives 320x32 above, 320x32 below, 56x96 left and 72x96 right). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRz3zwZJvBvzsR9k4cj6dd
Diffstat (limited to 'src/graphics.c')
-rw-r--r--src/graphics.c17
1 files changed, 11 insertions, 6 deletions
diff --git a/src/graphics.c b/src/graphics.c
index 3330ece..77b3b7f 100644
--- a/src/graphics.c
+++ b/src/graphics.c
@@ -18,7 +18,7 @@
* pinned to a spot on the screen no scrolling will move. The queue survives
* until gfx_commit() says the client actually heard it. */
typedef struct {
- uint32_t out_id, place_id;
+ uint32_t out_id, wire_id;
} gfx_dead_t;
struct graphics {
@@ -27,6 +27,7 @@ struct graphics {
gfx_place_t *places;
size_t nplaces, placecap;
uint32_t next_out_id;
+ uint32_t next_wire_id; /* placement ids we hand out; see gfx_place_t */
gfx_dead_t *dead; /* deletions owed to the client */
size_t ndead, deadcap;
@@ -85,6 +86,7 @@ static void out_b64(graphics_t *g, const uint8_t *in, size_t len) {
graphics_t *gfx_new(void) {
graphics_t *g = calloc(1, sizeof *g);
g->next_out_id = OUT_ID_BASE;
+ g->next_wire_id = 1;
return g;
}
@@ -223,7 +225,8 @@ void gfx_place(graphics_t *g, const gfx_req_t *req) {
uint32_t pid = req->place_id ? req->place_id : 1;
gfx_place_t *slot = NULL;
for (size_t i = 0; i < g->nplaces; i++)
- if (g->places[i].out_id == img->out_id && g->places[i].place_id == pid)
+ if (g->places[i].out_id == img->out_id && g->places[i].place_id == pid &&
+ g->places[i].piece == req->piece)
slot = &g->places[i];
if (!slot) {
if (g->nplaces == g->placecap) {
@@ -234,6 +237,8 @@ void gfx_place(graphics_t *g, const gfx_req_t *req) {
memset(slot, 0, sizeof *slot);
slot->out_id = img->out_id;
slot->place_id = pid;
+ slot->piece = req->piece;
+ slot->wire_id = g->next_wire_id++;
}
/* What the client is holding, against what it should be holding. A
* placement that has not moved, resized or re-cropped is left alone: see
@@ -299,14 +304,14 @@ char *gfx_flush(graphics_t *g, size_t *out_len) {
}
bool queued = false;
for (size_t j = 0; j < g->ndead; j++)
- if (g->dead[j].out_id == p->out_id && g->dead[j].place_id == p->place_id)
+ if (g->dead[j].out_id == p->out_id && g->dead[j].wire_id == p->wire_id)
queued = true;
if (!queued) {
if (g->ndead == g->deadcap) {
g->deadcap = g->deadcap ? g->deadcap * 2 : 8;
g->dead = realloc(g->dead, g->deadcap * sizeof *g->dead);
}
- g->dead[g->ndead++] = (gfx_dead_t){p->out_id, p->place_id};
+ g->dead[g->ndead++] = (gfx_dead_t){p->out_id, p->wire_id};
}
}
g->nplaces = keep;
@@ -315,7 +320,7 @@ char *gfx_flush(graphics_t *g, size_t *out_len) {
* came back is deleted and re-placed in the same stream, which nets out. */
for (size_t i = 0; i < g->ndead; i++)
out_fmt(g, "\x1b_Ga=d,d=i,q=2,i=%u,p=%u\x1b\\", g->dead[i].out_id,
- g->dead[i].place_id);
+ g->dead[i].wire_id);
/* Then place what the client is not already holding. C=1 keeps the cursor
* where the text renderer left it, which matters because we are
@@ -350,7 +355,7 @@ char *gfx_flush(graphics_t *g, size_t *out_len) {
* cells (dim_unfocused paints one over every cell it touches) hides the
* picture completely -- on the mirrored copy only, while the terminal,
* which took the default, still shows it. */
- out_fmt(g, "\x1b_Ga=p,q=2,C=1,z=0,i=%u,p=%u", p->out_id, p->place_id);
+ out_fmt(g, "\x1b_Ga=p,q=2,C=1,z=0,i=%u,p=%u", p->out_id, p->wire_id);
/* c=/r= mean *scale into this many cells*, so they are passed on only
* when the program asked for them. Sending the cell count a natural-size
* image happens to cover looks identical in a still picture and makes a