This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch tymux
in repository terminology.
View the commit online.
commit 9110226c14af5c9044f3abf945b0bf5ee2762a36
Author: [email protected] <[email protected]>
AuthorDate: Fri Mar 20 22:27:26 2026 -0600
feat: tymux attach switches Terminology to native session widget
When tymux attach runs inside Terminology, it writes the escape
sequence \033}at;<name>\0 to the PTY. Terminology's command handler
recognizes the "at;" opcode and calls _termio_tymux_switch(), which:
1. Connects to the tymuxd daemon via termsession_attach()
2. Kills the old PTY (shell + tymux process get SIGHUP)
3. Installs the shadow Termpty with write_cb forwarding
4. Wires session callbacks (change/title/bell/exit)
5. Enables the green tymux indicator
If tymux attach is running in a non-Terminology terminal, the
escape sequence is silently consumed and it falls through to the
inline proxy path as before.
This means session save/restore works correctly for terminals that
were switched to tymux via "tymux attach" — the widget has native
session_name set, so it gets saved as mux_type="tymux" and restored
via termsession_attach on reload.
New public API: term_tymux_indicator_set() in win.h allows termio.c
to toggle the green indicator without accessing Term internals.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
src/bin/termio.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/bin/termptyext.c | 3 ++
src/bin/tymux.c | 21 +++++++++++
src/bin/win.c | 16 ++++++---
src/bin/win.h | 4 +++
5 files changed, 139 insertions(+), 4 deletions(-)
diff --git a/src/bin/termio.c b/src/bin/termio.c
index f91fd4b8..16568308 100644
--- a/src/bin/termio.c
+++ b/src/bin/termio.c
@@ -23,6 +23,7 @@
#include "gravatar.h"
#include "sb.h"
#include "utils.h"
+#include "win.h"
#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
# include <sys/proc_info.h>
@@ -3793,6 +3794,91 @@ _handle_query_esc(Termio *sd)
}
}
+#ifdef HAVE_TYMUX
+/*
+ * _termio_tymux_switch - replace the current plain PTY with a native tymux session.
+ *
+ * Called from _smart_pty_command() when the running 'tymux attach' tool emits
+ * the \033}at;<name>\0 escape sequence. On success:
+ * - the old PTY (and its shell) is freed/killed
+ * - a shadow Termpty backed by the tymux daemon is installed in its place
+ * - the Term's tymux indicator is lit
+ *
+ * If attaching fails the current PTY is left untouched.
+ */
+static Eina_Bool
+_tymux_name_valid(const char *name)
+{
+ if (!name || name[0] == '\0' || strchr(name, '/')) return EINA_FALSE;
+ return EINA_TRUE;
+}
+
+static void
+_termio_tymux_switch(Evas_Object *obj, Termio *sd, const char *tymux_name)
+{
+ Termpty *spty = NULL;
+ TermTymux *tymux = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN(tymux_name);
+ if (!_tymux_name_valid(tymux_name)) return;
+
+ /* Clean up any previous tymux or plain PTY state before attaching. */
+ if (sd->tymux)
+ {
+ /* Already in a tymux session — tear it down first.
+ * termtymux_free() also frees the shadow Termpty. */
+ termtymux_free(sd->tymux);
+ sd->tymux = NULL;
+ sd->pty = NULL; /* freed by termtymux_free */
+ eina_stringshare_del(sd->tymux_name);
+ sd->tymux_name = NULL;
+ }
+ else if (sd->pty)
+ {
+ /* Plain PTY — free it. The shell and 'tymux attach' helper both
+ * receive SIGHUP when the PTY master is closed. */
+ termpty_free(sd->pty);
+ sd->pty = NULL;
+ }
+
+ tymux = termtymux_attach(tymux_name,
+ sd->grid.w,
+ sd->grid.h,
+ sd->config, &spty);
+ if (!tymux)
+ {
+ ERR("tymux switch: could not attach to '%s'", tymux_name);
+ return;
+ }
+
+ /* Install the shadow PTY that renders the tymux session. */
+ sd->pty = spty;
+ sd->pty->obj = obj;
+
+ /* Replace tymux tracking fields. */
+ eina_stringshare_del(sd->tymux_name);
+ sd->tymux_name = eina_stringshare_add(tymux_name);
+ sd->tymux = tymux;
+
+ /* Wire callbacks — same pattern as the tymux branch in termio_add(). */
+ sd->tymux->cb_change.func = _smart_pty_change;
+ sd->tymux->cb_change.data = ""
+ sd->tymux->cb_title.func = _smart_pty_title;
+ sd->tymux->cb_title.data = ""
+ sd->tymux->cb_bell.func = _smart_pty_bell;
+ sd->tymux->cb_bell.data = ""
+ sd->tymux->cb_exit.func = _smart_pty_exited;
+ sd->tymux->cb_exit.data = ""
+
+ /* Light up the tymux indicator in the containing Term widget. */
+ if (sd->term)
+ term_tymux_indicator_set(sd->term, EINA_TRUE);
+
+ /* Force an immediate redraw so the session content appears. */
+ _smart_apply(obj);
+}
+#endif /* HAVE_TYMUX */
+
static void
_smart_pty_command(void *data)
{
@@ -3813,6 +3899,19 @@ _smart_pty_command(void *data)
return;
}
+#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] == ';')
+ {
+ const char *sess = &ty->cur_cmd[3];
+ if (sess[0] && _tymux_name_valid(sess))
+ _termio_tymux_switch(obj, sd, sess);
+ return;
+ }
+#endif
+
if (!config->ty_escapes)
return;
diff --git a/src/bin/termptyext.c b/src/bin/termptyext.c
index b0c9627c..863f9097 100644
--- a/src/bin/termptyext.c
+++ b/src/bin/termptyext.c
@@ -541,6 +541,9 @@ termpty_ext_handle(Termpty *ty ARG_USED_FOR_TESTS,
{
switch (buf[0]) // major opcode
{
+ /* 'a' is reserved for tymux attach — handled in termio.c
+ * _smart_pty_command() via the "at;<name>" opcode. Do NOT add a
+ * case 'a' here or the native attach hand-off will break. */
#if defined(BINARY_TYTEST) || defined(ENABLE_TEST_UI)
case 't':
tytest_handle_escape_codes(ty, buf + 1);
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index caa87655..985cc6b4 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -1035,6 +1035,27 @@ cmd_attach(int argc, char **argv)
return 1;
}
+ /* If running inside Terminology, ask it to take over natively.
+ * Write \033}at;<name>\0 — Terminology's escape parser catches \033}
+ * sequences and the 'at' opcode triggers _termio_tymux_switch().
+ * If Terminology handles it, our PTY is killed and we exit below via
+ * the proxy loop's EOF path. If we are in a different terminal the
+ * escape is silently consumed and we fall through to the proxy path.
+ *
+ * The liveness check is performed first so we never send the escape
+ * for a session that does not exist — Terminology would then attempt
+ * to attach to a dead socket and log a spurious error. */
+ {
+ 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");
+ }
+ }
+
/* ── Connect ── */
int sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (sock < 0) { perror("tymux: socket"); return 1; }
diff --git a/src/bin/win.c b/src/bin/win.c
index 73271887..9870ac0c 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -456,10 +456,18 @@ static void
_tymux_indicator_update(Term *term)
{
if (term->is_tymux)
- {
- elm_layout_signal_emit(term->bg, "tymux,on", "terminology");
- edje_object_message_signal_process(term->bg_edj);
- }
+ elm_layout_signal_emit(term->bg, "tymux,on", "terminology");
+ else
+ elm_layout_signal_emit(term->bg, "tymux,off", "terminology");
+ edje_object_message_signal_process(term->bg_edj);
+}
+
+void
+term_tymux_indicator_set(Term *term, Eina_Bool on)
+{
+ EINA_SAFETY_ON_NULL_RETURN(term);
+ term->is_tymux = on ? 1 : 0;
+ _tymux_indicator_update(term);
}
#endif
diff --git a/src/bin/win.h b/src/bin/win.h
index 58926be1..f9636b5c 100644
--- a/src/bin/win.h
+++ b/src/bin/win.h
@@ -91,4 +91,8 @@ for_each_term_do(Win *wn, For_Each_Term cb, void *data);
void main_trans_update(void);
+#ifdef HAVE_TYMUX
+void term_tymux_indicator_set(Term *term, Eina_Bool on);
+#endif
+
#endif
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.