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 c194c5ccc9a908e96e0b081e3aefd8f3b16e3354
Author: [email protected] <[email protected]>
AuthorDate: Thu May 14 18:35:14 2026 -0600
fix(tymux): mirror input-mode state to shadow pty for inner app key/mouse modes
The daemon's real pty parses escape sequences and holds the true input mode
bits (DECCKM, application keypad, mouse modes, etc). The client's shadow
pty never received these bits, so inner applications that set their own modes
(e.g. htop enabling mouse) appeared to fail. terminology's keyin encoder
reads appcursor from the active pty to choose between \eOA (app) and \e[A
(normal) arrow keys; without the shadow bits in sync, applications saw
"normal cursor keys" despite requesting application mode, breaking navigation.
Mirror six mode bits via a v2 extension to TermSrvMsgNotify: appcursor,
appkeypad, mouse_mode, mouse_encoding, paste_in_bracketed, focus_in. Gate
on offsetof for v1/v2 interop. Update termptyesc to fire cb.change on DECSET/
DECRST and DECPAM/DECPNM so the daemon flushes changed modes even without
an immediate repaint.
User-visible: arrow keys and mouse now work in htop and other inner apps.
---
src/bin/termd_tymux.c | 11 +++++++++++
src/bin/termptyesc.c | 21 +++++++++++++++++++++
src/bin/termsrv.h | 12 ++++++++++++
src/bin/termtymux.c | 19 +++++++++++++++++--
4 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/src/bin/termd_tymux.c b/src/bin/termd_tymux.c
index f0c06ea6..b89bd8be 100644
--- a/src/bin/termd_tymux.c
+++ b/src/bin/termd_tymux.c
@@ -383,6 +383,17 @@ _tymux_flush_and_notify(TermDTymux *sess)
else
pay.backlog_head_seq = 0;
+ /* v2: snapshot input-mode bits so attached clients can mirror them onto
+ * their shadow pty — enabling correct arrow-key encoding and mouse reports. */
+ pay.appcursor = (uint8_t)sess->pty->termstate.appcursor;
+ pay.appkeypad = (uint8_t)sess->pty->termstate.alt_kp;
+ pay.mouse_mode = (uint8_t)sess->pty->mouse_mode;
+ pay.mouse_ext = (uint8_t)sess->pty->mouse_ext;
+ pay.bracketed_paste = (uint8_t)sess->pty->bracketed_paste;
+ pay.focus_reporting = (uint8_t)sess->pty->focus_reporting;
+ pay.reserved[0] = 0;
+ pay.reserved[1] = 0;
+
EINA_LIST_FOREACH_SAFE(sess->clients, l, ln, c)
{
if (termsrv_msg_send(c->fd, TSRV_MSG_NOTIFY, &pay, sizeof(pay)) < 0)
diff --git a/src/bin/termptyesc.c b/src/bin/termptyesc.c
index 8a9b3f92..7f56f00e 100644
--- a/src/bin/termptyesc.c
+++ b/src/bin/termptyesc.c
@@ -384,6 +384,8 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
}
if (priv) /* DEC Private Mode Reset (DECRST) */
{
+ Eina_Bool mode_changed = EINA_FALSE;
+
while (b && b <= end)
{
arg = _csi_arg_get(ty, &b);
@@ -392,10 +394,13 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
switch (arg)
{
case -ESC_ARG_ERROR:
+ if (mode_changed && ty->cb.change.func)
+ ty->cb.change.func(ty->cb.change.data);
return;
/* TODO: -ESC_ARG_NO_VALUE */
case 1:
ty->termstate.appcursor = mode;
+ mode_changed = EINA_TRUE;
break;
case 2:
DBG("DECANM - ANSI MODE - VT52");
@@ -458,6 +463,7 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
DBG("set mouse (X10) %i", mode);
if (mode) ty->mouse_mode = MOUSE_X10;
else ty->mouse_mode = MOUSE_OFF;
+ mode_changed = EINA_TRUE;
break;
case 12: // ignore
WRN("TODO: set blinking cursor to (stop?) %i or local echo (ignored)", mode);
@@ -541,6 +547,7 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
if (mode) ty->mouse_mode = MOUSE_NORMAL;
else ty->mouse_mode = MOUSE_OFF;
DBG("set mouse (press+release only) to %i", mode);
+ mode_changed = EINA_TRUE;
break;
case 1001:
WRN("TODO: x11 mouse highlighting %i", mode);
@@ -550,11 +557,13 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
if (mode) ty->mouse_mode = MOUSE_NORMAL_BTN_MOVE;
else ty->mouse_mode = MOUSE_OFF;
DBG("set mouse (press+release+motion while pressed) %i", mode);
+ mode_changed = EINA_TRUE;
break;
case 1003:
if (mode) ty->mouse_mode = MOUSE_NORMAL_ALL_MOVE;
else ty->mouse_mode = MOUSE_OFF;
DBG("set mouse (press+release+all motion) %i", mode);
+ mode_changed = EINA_TRUE;
break;
case 1004:
DBG("%s focus reporting", mode ? "enable" : "disable");
@@ -567,16 +576,19 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
{
ty->focus_reporting = EINA_FALSE;
}
+ mode_changed = EINA_TRUE;
break;
case 1005:
if (mode) ty->mouse_ext = MOUSE_EXT_UTF8;
else ty->mouse_ext = MOUSE_EXT_NONE;
DBG("set mouse (xterm utf8 style) %i", mode);
+ mode_changed = EINA_TRUE;
break;
case 1006:
if (mode) ty->mouse_ext = MOUSE_EXT_SGR;
else ty->mouse_ext = MOUSE_EXT_NONE;
DBG("set mouse (xterm sgr style) %i", mode);
+ mode_changed = EINA_TRUE;
break;
case 1010: // ignore
WRN("TODO: set home on tty output %i", mode);
@@ -590,6 +602,7 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
if (mode) ty->mouse_ext = MOUSE_EXT_URXVT;
else ty->mouse_ext = MOUSE_EXT_NONE;
DBG("set mouse (rxvt-unicode style) %i", mode);
+ mode_changed = EINA_TRUE;
break;
case 1034: // ignore
/* libreadline6 emits it but it shouldn't.
@@ -627,6 +640,7 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
break;
case 2004:
ty->bracketed_paste = mode;
+ mode_changed = EINA_TRUE;
break;
case 2026: /* Synchronized Output (BSU/ESU) */
if (mode)
@@ -666,6 +680,11 @@ _handle_esc_csi_reset_mode(Termpty *ty, Eina_Unicode cc, Eina_Unicode *b,
break;
}
}
+ /* Fire change callback when a mode that affects key/mouse encoding
+ * changed without any accompanying screen write — this ensures the
+ * tymux daemon ships a NOTIFY carrying the updated input-mode bits. */
+ if (mode_changed && ty->cb.change.func)
+ ty->cb.change.func(ty->cb.change.data);
}
else /* Reset Mode (RM) */
{
@@ -5090,9 +5109,11 @@ _handle_esc(Termpty *ty, const Eina_Unicode *c, const Eina_Unicode *ce)
return 1 + len;
case '=': // set alternate keypad mode
ty->termstate.alt_kp = 1;
+ if (ty->cb.change.func) ty->cb.change.func(ty->cb.change.data);
return 1;
case '>': // set numeric keypad mode
ty->termstate.alt_kp = 0;
+ if (ty->cb.change.func) ty->cb.change.func(ty->cb.change.data);
return 1;
case 'M': // move to prev line
DBG("move to prev line");
diff --git a/src/bin/termsrv.h b/src/bin/termsrv.h
index 85c866a8..086a2b17 100644
--- a/src/bin/termsrv.h
+++ b/src/bin/termsrv.h
@@ -143,6 +143,18 @@ typedef struct {
uint32_t backlog_buf_count; /* total backlog buffers available */
uint32_t backlog_head_seq; /* write_seq of head buffer (low 32) */
uint32_t oldest_buf_id; /* first valid buffer index */
+ /* v2 extension: input-mode mirror.
+ * Old daemons send a payload smaller than sizeof(TermSrvMsgNotify);
+ * clients gate access on size >= offsetof(TermSrvMsgNotify, appcursor)
+ * for the v1 fields and size >= sizeof(TermSrvMsgNotify) for the v2
+ * fields below, so new/old endpoints interoperate without a version bump. */
+ uint8_t appcursor; /* DECCKM (CSI ?1h) */
+ uint8_t appkeypad; /* alt_kp (ESC = / ESC >) */
+ uint8_t mouse_mode; /* MOUSE_OFF..MOUSE_NORMAL_ALL_MOVE */
+ uint8_t mouse_ext; /* MOUSE_EXT_NONE..MOUSE_EXT_URXVT */
+ uint8_t bracketed_paste; /* DECSET 2004 */
+ uint8_t focus_reporting; /* DECSET 1004 */
+ uint8_t reserved[2];
} TermSrvMsgNotify;
/* EXIT payload */
diff --git a/src/bin/termtymux.c b/src/bin/termtymux.c
index d91e37b0..eb44ec9b 100644
--- a/src/bin/termtymux.c
+++ b/src/bin/termtymux.c
@@ -634,8 +634,10 @@ _cb_tymux_data(void *data, Ecore_Fd_Handler *handler EINA_UNUSED)
{
uint64_t seq;
- /* Parse extended backlog fields if the payload is large enough */
- if (payload && size >= sizeof(TermSrvMsgNotify))
+ /* Parse v1 backlog fields. Use offsetof(TermSrvMsgNotify, appcursor)
+ * as the gate so old daemons (which send the smaller v1 struct) still
+ * satisfy the check even though sizeof(TermSrvMsgNotify) is now larger. */
+ if (payload && size >= offsetof(TermSrvMsgNotify, appcursor))
{
TermSrvMsgNotify *n = payload;
uint32_t new_count = n->backlog_buf_count;
@@ -713,6 +715,19 @@ _cb_tymux_data(void *data, Ecore_Fd_Handler *handler EINA_UNUSED)
/* Recalculate total scrollback lines visible to renderer */
_backlog_update_backsize(ts);
+
+ /* v2: input-mode mirror — propagate terminal mode bits set by
+ * inner applications onto the shadow pty so keyin.c and the
+ * mouse handler consult the correct flags. */
+ if (size >= sizeof(TermSrvMsgNotify))
+ {
+ ts->shadow_pty->termstate.appcursor = n->appcursor;
+ ts->shadow_pty->termstate.alt_kp = n->appkeypad;
+ ts->shadow_pty->mouse_mode = n->mouse_mode & 0x07u;
+ ts->shadow_pty->mouse_ext = n->mouse_ext & 0x03u;
+ ts->shadow_pty->bracketed_paste = n->bracketed_paste;
+ ts->shadow_pty->focus_reporting = n->focus_reporting;
+ }
}
/* Read write_seq from shm with acquire semantics */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.