aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-02 15:03:53 +0200
committeruser <user@clank>2026-09-02 15:03:53 +0200
commit1a3ba183ae8f2613cae847c8f6460782512acc40 (patch)
tree68bdc1e82b25e40452e2400ddcfcd2e6dd05b36e /src
parentMerge branch 'multiplayer': shared multi-client attach (diff)
downloadslosh-1a3ba183ae8f2613cae847c8f6460782512acc40.tar.gz
slosh-1a3ba183ae8f2613cae847c8f6460782512acc40.tar.xz
slosh-1a3ba183ae8f2613cae847c8f6460782512acc40.zip
multi-attach: say who else is watching, and where you are
A small tag in each client's own top-right corner when several terminals share the session: the viewer count, and on a view that is a crop being panned by somebody else's cursor, the crop's offset into the shared screen ("2 clients +12+4") -- which is exactly the moment a screen moving on its own needs explaining. Stamped per client after projection, because every terminal has its own count of others and its own offset; the shared status bar is composed once and can be cropped out of a panned view anyway. attach_indicator false hides it.
Diffstat (limited to 'src')
-rw-r--r--src/app.c5
-rw-r--r--src/config.c7
-rw-r--r--src/server.c29
3 files changed, 41 insertions, 0 deletions
diff --git a/src/app.c b/src/app.c
index 9c9829b..b7af3d1 100644
--- a/src/app.c
+++ b/src/app.c
@@ -93,6 +93,11 @@ bool app_cfg_multi_attach(void) {
return CFG.multi_attach;
}
+bool app_cfg_attach_indicator(void) {
+ ensure_config();
+ return CFG.attach_indicator;
+}
+
int app_cfg_size_follows(void) {
ensure_config();
return CFG.size_follows;
diff --git a/src/config.c b/src/config.c
index bfbfdf7..9c06983 100644
--- a/src/config.c
+++ b/src/config.c
@@ -919,6 +919,7 @@ void config_defaults(config_t *c) {
c->status_pad = 4;
c->focus_follows_mouse = true;
c->multi_attach = true;
+ c->attach_indicator = true;
c->size_follows = SIZE_FOLLOWS_ACTIVE;
c->in_band_shaders = false;
/* Windows has no VEOF for the line discipline to act on, so ^D there is a
@@ -1613,6 +1614,9 @@ char *config_render(const config_t *c) {
"multi_attach %s // several terminals at once; off: a new "
"client displaces the old\n",
yesno(c->multi_attach));
+ cb_add(&b,
+ "attach_indicator %s // top-right tag when others are watching\n",
+ yesno(c->attach_indicator));
cb_add(&b, "size_follows \"%s\" // or \"smallest\" / \"largest\"\n",
c->size_follows == SIZE_FOLLOWS_SMALLEST ? "smallest"
: c->size_follows == SIZE_FOLLOWS_LARGEST ? "largest"
@@ -1851,6 +1855,7 @@ const char *config_default_path(void) {
static const char *const KNOWN_TOP[] = {
"include", /* read before the rest, but a name this file understands */
"anim_ms",
+ "attach_indicator",
"bell_indicator",
"bell_mark",
"close_mark",
@@ -2095,6 +2100,8 @@ static bool load_into(config_t *c, const char *path, int depth, char *err,
0, c->focus_follows_mouse);
c->multi_attach =
kdl_arg_bool(kdl_child(root, "multi_attach"), 0, c->multi_attach);
+ c->attach_indicator =
+ kdl_arg_bool(kdl_child(root, "attach_indicator"), 0, c->attach_indicator);
const char *sizef = kdl_arg(kdl_child(root, "size_follows"), 0, NULL);
if (sizef) {
if (strcmp(sizef, "active") == 0)
diff --git a/src/server.c b/src/server.c
index 7443648..472fcdd 100644
--- a/src/server.c
+++ b/src/server.c
@@ -308,11 +308,40 @@ static void update_viewport(conn_t *c, const screen_t *src) {
if (c->shown_rows > c->rows) c->shown_rows = c->rows;
}
+/* The multi-client tag, stamped into one client's projected view: this is
+ * per-client chrome (every terminal has its own count of "others", its own
+ * crop offset), so it cannot live on the shared status bar, which is
+ * composed once and might be cropped out of a panned view anyway. Top-right
+ * of the client's own screen, which is always visible; toasts keep the
+ * bottom-right. Written after projection and before the diff, so it costs
+ * bytes only when it changes -- and when it disappears, the diff restores
+ * the shared cells beneath it on its own. */
+static void stamp_indicator(server_t *s, conn_t *c) {
+ if (!app_cfg_attach_indicator()) return;
+ size_t viewers = display_count(s);
+ if (viewers < 2) return;
+ char tag[48];
+ if (c->view_x || c->view_y)
+ /* The crop's origin in the shared screen: "you are at +col+row of
+ * something bigger", which is the disorienting case the tag exists for. */
+ snprintf(tag, sizeof tag, " %zu clients +%u+%u ", viewers,
+ (unsigned)c->view_x, (unsigned)c->view_y);
+ else
+ snprintf(tag, sizeof tag, " %zu clients ", viewers);
+ size_t len = strlen(tag);
+ if (len >= c->view.cols) return; /* a view too narrow to say it in */
+ uint16_t x0 = (uint16_t)(c->view.cols - len);
+ for (size_t i = 0; i < len; i++)
+ screen_put_utf8(&c->view, (uint16_t)(x0 + i), 0, &tag[i], 1, (color_t){0},
+ (color_t){0}, ATTR_DIM | ATTR_INVERSE);
+}
+
static bool push_frame(server_t *s, conn_t *c) {
uint16_t old_x = c->view_x, old_y = c->view_y;
uint16_t old_cols = c->shown_cols, old_rows = c->shown_rows;
update_viewport(c, &s->screen);
screen_project(&c->view, &s->screen, c->view_x, c->view_y);
+ stamp_indicator(s, c);
screen_render(&c->view);
if (!c->view.out_len) return true;
if (!conn_send(c, MSG_OUTPUT, c->view.out, c->view.out_len)) {