aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-02 14:57:46 +0200
committeruser <user@clank>2026-09-02 14:57:46 +0200
commitbac4b796f3f729d8d76b9c7c973126eac49872f2 (patch)
tree0d040eb64e433122f982ea45912eba37ab54822a /include
parentwebdemo: version from contrib/version, published by ship (diff)
parentmulti-attach: the config knobs (diff)
downloadslosh-bac4b796f3f729d8d76b9c7c973126eac49872f2.tar.gz
slosh-bac4b796f3f729d8d76b9c7c973126eac49872f2.tar.xz
slosh-bac4b796f3f729d8d76b9c7c973126eac49872f2.zip
Merge branch 'multiplayer': shared multi-client attach
Several terminals can attach to one session at once: the canonical screen becomes a model projected into every client's own size, with per-client diff history, input parsing and kitty graphics state. The active client (newest attach, then last input) sizes the canvas by default; multi_attach and size_follows in the config control both halves of the policy.
Diffstat (limited to 'include')
-rw-r--r--include/app.h18
-rw-r--r--include/config.h19
-rw-r--r--include/graphics.h1
-rw-r--r--include/proto.h2
-rw-r--r--include/slosh.h8
5 files changed, 47 insertions, 1 deletions
diff --git a/include/app.h b/include/app.h
index a203858..b3dd57c 100644
--- a/include/app.h
+++ b/include/app.h
@@ -5,6 +5,7 @@
#ifndef SLOSH_APP_H
#define SLOSH_APP_H
+#include "graphics.h"
#include "kdl.h"
#include "project.h"
#include "shader.h"
@@ -22,10 +23,16 @@ void app_event(app_t *a, const input_event_t *ev);
/* Kitty graphics for this frame: the bytes the client's terminal needs, and
* the same thing as JSON for tests. Bytes are borrowed until the next call. */
const char *app_graphics(app_t *a, size_t *len);
+/* The same frame for one attached terminal. `state` is that terminal's own
+ * transmission/deletion history; the viewport is in canonical screen cells. */
+const char *app_graphics_view(app_t *a, graphics_t *state, uint16_t x,
+ uint16_t y, uint16_t cols, uint16_t rows,
+ size_t *len);
void app_graphics_reset(app_t *a);
/* Did the last app_graphics() stream reach the client? Undelivered frames
* are regenerated: transmissions repeat and deletions stay owed. */
void app_graphics_commit(app_t *a, bool delivered);
+void app_graphics_view_commit(graphics_t *state, bool delivered);
char *app_graphics_json(app_t *a);
/* Transient announcements, drawn bottom-right and expiring on their own. */
@@ -52,6 +59,11 @@ bool app_reload_config(char *err, size_t errcap);
/* The files the config in force was read from: the one that was loaded and
* everything it included, existing or not. What the watcher watches. */
size_t app_config_files(const char **out, size_t max);
+/* The attach-sharing knobs, read fresh so a config reload changes policy on
+ * a running session: may several clients attach at once, and whose size the
+ * shared screen takes (SIZE_FOLLOWS_* in config.h). */
+bool app_cfg_multi_attach(void);
+int app_cfg_size_follows(void);
/* What the config in force complained about while loading, or "". A complaint is
* not a failure -- an include that is not there, a shader nobody has heard of --
* so the session is running either way; this is for saying so. */
@@ -87,6 +99,12 @@ bool app_should_quit(const app_t *a);
/* C-a d asks the client to leave while the session keeps running. */
bool app_detach_requested(const app_t *a);
void app_clear_detach(app_t *a);
+/* A different display took over the shared input stream. End state that cannot
+ * safely span clients (leader chord, pointer drag and hover position). */
+void app_cancel_client_interaction(app_t *a);
+/* A release over client-side filler has no canonical target, but still ends a
+ * pointer gesture without disturbing that client's keyboard leader state. */
+void app_cancel_client_pointer(app_t *a);
size_t app_pane_count(const app_t *a);
/* Tabs. A tab is a layout tree; panes in every tab keep running. */
diff --git a/include/config.h b/include/config.h
index fee6b2e..9db9c53 100644
--- a/include/config.h
+++ b/include/config.h
@@ -112,6 +112,13 @@ typedef struct {
typedef enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT } align_t;
+/* size_follows: which attached client sizes the shared screen. */
+enum {
+ SIZE_FOLLOWS_ACTIVE = 0, /* newest attach, then last input */
+ SIZE_FOLLOWS_SMALLEST, /* min over clients: filler, never a crop */
+ SIZE_FOLLOWS_LARGEST, /* max over clients: crops, never filler */
+};
+
/* How many files one config may be built from: itself plus its includes. Small
* and fixed, because a config assembled from more than this many pieces is a
* thing nobody can read. */
@@ -306,6 +313,18 @@ typedef struct {
uint16_t status_pad;
bool focus_follows_mouse;
+ /* Several terminals may attach to one session at once (the default).
+ * false restores the classic rule: a new client displaces the one before
+ * it, which is what anyone using attach-from-elsewhere as "move my session
+ * here and *close it there*" actually wants. */
+ bool multi_attach;
+ /* Whose size the shared screen takes when several clients are attached:
+ * the active client (newest attach, then last input -- the default), or
+ * the smallest / largest attached one. Smallest is the tmux rule: nobody
+ * is cropped and the roomiest terminal pays in filler; largest gives the
+ * big display everything and small ones a cursor-following crop. */
+ int size_follows;
+
/* Whether Ctrl-D at an idle shell prompt exits that shell, the way the tty
* line discipline would do it on POSIX.
*
diff --git a/include/graphics.h b/include/graphics.h
index 6a338df..13cc5dc 100644
--- a/include/graphics.h
+++ b/include/graphics.h
@@ -66,6 +66,7 @@ typedef struct {
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 cell_px_w, cell_px_h; /* canonical pixels represented by a cell */
int format, compression;
const uint8_t *data; /* NULL once the client has it */
size_t data_len;
diff --git a/include/proto.h b/include/proto.h
index 67b45f0..4df52a6 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -30,7 +30,7 @@ enum {
enum {
EXIT_SESSION_ENDED = 0, /* the last pane closed */
EXIT_DETACHED = 1, /* the user detached */
- EXIT_REPLACED = 2, /* another client attached */
+ EXIT_REPLACED = 2, /* legacy: retained so new clients speak to old servers */
};
typedef struct {
diff --git a/include/slosh.h b/include/slosh.h
index e624bda..75aa658 100644
--- a/include/slosh.h
+++ b/include/slosh.h
@@ -76,6 +76,14 @@ void screen_init(screen_t *s, uint16_t cols, uint16_t rows);
void screen_free(screen_t *s);
void screen_resize(screen_t *s, uint16_t cols, uint16_t rows);
void screen_clear(screen_t *s);
+/* Project a canonical screen into a client-sized one. The source starts at
+ * (x,y); anything beyond its edges is blank filler. Diff history in `dst` is
+ * deliberately left alone, so every attached terminal advances independently. */
+void screen_project(screen_t *dst, const screen_t *src, uint16_t x, uint16_t y);
+/* Move an existing client viewport only as far as needed to keep the canonical
+ * cursor visible, then clamp it to the canonical screen. */
+void screen_follow_cursor(const screen_t *src, uint16_t cols, uint16_t rows,
+ uint16_t *x, uint16_t *y);
cell_t *screen_at(screen_t *s, uint16_t x, uint16_t y);
void screen_put_utf8(screen_t *s, uint16_t x, uint16_t y, const char *txt,
size_t len, color_t fg, color_t bg, uint16_t attrs);