This is an automated email from the git hooks/post-receive script.
git pushed a commit to reference refs/pull/36/head
in repository terminology.
View the commit online.
commit 44b2c8860917dbbd6d1bb98ac9add8e990126cc0
Author: [email protected] <[email protected]>
AuthorDate: Mon Mar 23 22:42:20 2026 -0600
feat(tymux): encode attach mode in escape sequences
Extend the Terminology escape protocol so --shared and --readonly
work through native attach paths (including --new-tab):
at;<name> → hijack, current tab (backward compat)
ats;<name> → shared, current tab
atr;<name> → readonly, current tab
atn;<name> → hijack, new tab
atns;<name> → shared, new tab
atnr;<name> → readonly, new tab
termtymux_attach() now accepts a TermSrvAttachMode parameter
instead of hardcoding HIJACK.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
src/bin/termio.c | 66 ++++++++++++++++++++++++++++++---------------
src/bin/termiointernals.h | 1 +
src/bin/termtymux.c | 3 ++-
src/bin/termtymux.h | 1 +
src/bin/tymux.c | 68 ++++++++++++++++++++++++++---------------------
5 files changed, 86 insertions(+), 53 deletions(-)
diff --git a/src/bin/termio.c b/src/bin/termio.c
index cda79762..c978be6a 100644
--- a/src/bin/termio.c
+++ b/src/bin/termio.c
@@ -3883,18 +3883,21 @@ _termio_tymux_newtab_job(void *data)
/* Schedule a tymux switch in the new tab on the next iteration. */
eina_stringshare_replace(&new_sd->tymux_pending, tymux_name);
+ new_sd->tymux_pending_mode = sd->tymux_pending_mode;
ecore_job_add(_termio_tymux_switch_job, new_termio);
eina_stringshare_del(tymux_name);
}
static void
-_termio_tymux_newtab(Evas_Object *obj, Termio *sd, const char *tymux_name)
+_termio_tymux_newtab(Evas_Object *obj, Termio *sd, const char *tymux_name,
+ TermSrvAttachMode mode)
{
EINA_SAFETY_ON_NULL_RETURN(tymux_name);
if (!_tymux_name_valid(tymux_name)) return;
eina_stringshare_replace(&sd->tymux_newtab_pending, tymux_name);
+ sd->tymux_pending_mode = mode;
ecore_job_add(_termio_tymux_newtab_job, obj);
}
@@ -3934,7 +3937,9 @@ _termio_tymux_switch_job(void *data)
tymux = termtymux_attach(tymux_name,
sd->grid.w,
sd->grid.h,
- sd->config, &spty);
+ sd->config,
+ sd->tymux_pending_mode,
+ &spty);
if (!tymux)
{
ERR("tymux switch: could not attach to '%s'", tymux_name);
@@ -3972,7 +3977,8 @@ _termio_tymux_switch_job(void *data)
}
static void
-_termio_tymux_switch(Evas_Object *obj, Termio *sd, const char *tymux_name)
+_termio_tymux_switch(Evas_Object *obj, Termio *sd, const char *tymux_name,
+ TermSrvAttachMode mode)
{
EINA_SAFETY_ON_NULL_RETURN(tymux_name);
if (!_tymux_name_valid(tymux_name)) return;
@@ -3983,6 +3989,7 @@ _termio_tymux_switch(Evas_Object *obj, Termio *sd, const char *tymux_name)
* → _handle_esc → _smart_pty_command). Freeing it now would leave
* dangling pointers up the call stack. */
eina_stringshare_replace(&sd->tymux_pending, tymux_name);
+ sd->tymux_pending_mode = mode;
ecore_job_add(_termio_tymux_switch_job, obj);
}
#endif /* HAVE_TYMUX */
@@ -4008,25 +4015,39 @@ _smart_pty_command(void *data)
}
#ifdef HAVE_TYMUX
- /* at;<session_name> — escape emitted by 'tymux attach' to request a
- * native session hand-off. Handled before the ty_escapes gate so that
- * it works regardless of whether Terminology escapes are enabled. */
- if (ty->cur_cmd[0] == 'a' && ty->cur_cmd[1] == 't' && ty->cur_cmd[2] == ';')
+ /* tymux attach escapes — handled before the ty_escapes gate so they
+ * work regardless of whether Terminology escapes are enabled.
+ *
+ * Escape prefixes encode tab behavior and attach mode:
+ * at;<name> — hijack, current tab (backward compat)
+ * ats;<name> — shared, current tab
+ * atr;<name> — readonly, current tab
+ * atn;<name> — hijack, new tab
+ * atns;<name> — shared, new tab
+ * atnr;<name> — readonly, new tab
+ */
+ if (ty->cur_cmd[0] == 'a' && ty->cur_cmd[1] == 't')
{
- const char *sess = &ty->cur_cmd[3];
- if (sess[0] && _tymux_name_valid(sess))
- _termio_tymux_switch(obj, sd, sess);
- return;
- }
- /* atn;<session_name> — open a NEW tab attached to the named tymux session.
- * Emitted by the tymux client when the user requests a new window. */
- if (ty->cur_cmd[0] == 'a' && ty->cur_cmd[1] == 't' &&
- ty->cur_cmd[2] == 'n' && ty->cur_cmd[3] == ';')
- {
- const char *sess = &ty->cur_cmd[4];
- if (sess[0] && _tymux_name_valid(sess))
- _termio_tymux_newtab(obj, sd, sess);
- return;
+ const char *p = &ty->cur_cmd[2];
+ Eina_Bool newtab = EINA_FALSE;
+ TermSrvAttachMode mode = TSRV_ATTACH_HIJACK;
+
+ if (*p == 'n') { newtab = EINA_TRUE; p++; }
+ if (*p == 's') { mode = TSRV_ATTACH_SHARED; p++; }
+ else if (*p == 'r') { mode = TSRV_ATTACH_READONLY; p++; }
+
+ if (*p == ';')
+ {
+ const char *sess = p + 1;
+ if (sess[0] && _tymux_name_valid(sess))
+ {
+ if (newtab)
+ _termio_tymux_newtab(obj, sd, sess, mode);
+ else
+ _termio_tymux_switch(obj, sd, sess, mode);
+ }
+ return;
+ }
}
#endif
@@ -4548,7 +4569,8 @@ termio_add(Evas_Object *win, Config *config,
{
sd->tymux_name = eina_stringshare_add(tymux_name);
Termpty *spty = NULL;
- sd->tymux = termtymux_attach(tymux_name, w, h, config, &spty);
+ sd->tymux = termtymux_attach(tymux_name, w, h, config,
+ TSRV_ATTACH_HIJACK, &spty);
if (!sd->tymux)
{
ERR("Could not attach to tymux '%s'", tymux_name);
diff --git a/src/bin/termiointernals.h b/src/bin/termiointernals.h
index 4c4292d0..be6bb120 100644
--- a/src/bin/termiointernals.h
+++ b/src/bin/termiointernals.h
@@ -83,6 +83,7 @@ struct tag_Termio
const char *tymux_name; /* eina_stringshare */
const char *tymux_pending; /* eina_stringshare; deferred switch target */
const char *tymux_newtab_pending; /* eina_stringshare; deferred new-tab session */
+ TermSrvAttachMode tymux_pending_mode; /* mode for pending switch/newtab */
#endif
Ecore_Animator *anim;
Ecore_Timer *delayed_size_timer;
diff --git a/src/bin/termtymux.c b/src/bin/termtymux.c
index e007b186..cca22ea8 100644
--- a/src/bin/termtymux.c
+++ b/src/bin/termtymux.c
@@ -873,6 +873,7 @@ TermTymux *
termtymux_attach(const char *tymux_name,
int cols, int rows,
Config *config,
+ TermSrvAttachMode mode,
Termpty **shadow_pty_out)
{
char sock_path[512];
@@ -938,7 +939,7 @@ termtymux_attach(const char *tymux_name,
size_t pay_len = 1 + name_len;
uint8_t *pay = alloca(pay_len);
- pay[0] = TSRV_ATTACH_HIJACK;
+ pay[0] = (uint8_t)mode;
memcpy(pay + 1, tymux_name, name_len);
if (termsrv_msg_send(sock_fd, TSRV_MSG_ATTACH,
diff --git a/src/bin/termtymux.h b/src/bin/termtymux.h
index 2457be88..70b7a18d 100644
--- a/src/bin/termtymux.h
+++ b/src/bin/termtymux.h
@@ -63,6 +63,7 @@ struct _TermTymux {
TermTymux *termtymux_attach(const char *tymux_name,
int cols, int rows,
Config *config,
+ TermSrvAttachMode mode,
Termpty **shadow_pty_out);
/* Send DETACH, munmap, close socket, free shadow Termpty. */
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index e24d5575..5086c86a 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -1106,36 +1106,44 @@ cmd_attach(int argc, char **argv)
*
* The liveness check is performed first so we never send the escape
* for a session that does not exist. */
- if (new_tab)
- {
- if (getenv("TERMINOLOGY"))
- {
- char esc[128];
- int n = snprintf(esc, sizeof(esc), "\033}atn;%s", name);
- if (n > 0 && (size_t)n < sizeof(esc))
- {
- ssize_t written = write(STDOUT_FILENO, esc, (size_t)(n + 1));
- if (written < 0 || written != (ssize_t)(n + 1))
- fprintf(stderr, "tymux: warning: escape delivery failed\n");
- }
- return 0;
- }
- else
- fprintf(stderr,
- "tymux: --new-tab requires Terminology; "
- "falling back to inline attach\n");
- }
- else
- {
- char esc[128];
- int n = snprintf(esc, sizeof(esc), "\033}at;%s", name);
- if (n > 0 && (size_t)n < sizeof(esc))
- {
- ssize_t written = write(STDOUT_FILENO, esc, (size_t)(n + 1)); /* +1 for NUL */
- if (written < 0 || written != (ssize_t)(n + 1))
- fprintf(stderr, "tymux: warning: escape delivery failed\n");
- }
- }
+ /* Build escape prefix: at[n][s|r];<name>
+ * at; = hijack, current tab ats; = shared atr; = readonly
+ * atn; = hijack, new tab atns; = shared atnr; = readonly */
+ {
+ const char *prefix;
+ if (new_tab)
+ {
+ if (mode == 1) prefix = "\033}atns;";
+ else if (mode == 2) prefix = "\033}atnr;";
+ else prefix = "\033}atn;";
+ }
+ else
+ {
+ if (mode == 1) prefix = "\033}ats;";
+ else if (mode == 2) prefix = "\033}atr;";
+ else prefix = "\033}at;";
+ }
+
+ if (new_tab && !getenv("TERMINOLOGY"))
+ {
+ fprintf(stderr,
+ "tymux: --new-tab requires Terminology; "
+ "falling back to inline attach\n");
+ }
+ else
+ {
+ char esc[128];
+ int n = snprintf(esc, sizeof(esc), "%s%s", prefix, name);
+ if (n > 0 && (size_t)n < sizeof(esc))
+ {
+ ssize_t written = write(STDOUT_FILENO, esc, (size_t)(n + 1)); /* +1 for NUL */
+ if (written < 0 || written != (ssize_t)(n + 1))
+ fprintf(stderr, "tymux: warning: escape delivery failed\n");
+ }
+ if (new_tab)
+ return 0; /* exit cleanly — Terminology opens new tab */
+ }
+ }
/* ── Connect ── */
int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.