From 71ea2eb9e5c71c90233281769e74815798e9cc00 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 31 Oct 2026 23:05:54 +0000 Subject: [PATCH] Send the terminal resize off the main thread onResize runs on the main thread, and the window-change it sent is network I/O, which Android refuses there. The SSH library had already encrypted the packet and advanced its sequence number before the write failed, so every later packet was out of step with the server, and the server closed the connection at the client's next packet: the reply to the C3X's 10-second keepalive probe, or the 5-second client keepalive on Proxmox. The resize now goes through the bridge's writer thread, the same one keystrokes use, so it is sent in order with them. Claude-Session: https://claude.ai/code/session_01CiTeuL5wrBa86ELAzASv4B --- .../main/kotlin/com/secrm/core/terminal/TerminalBridge.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/terminal/src/main/kotlin/com/secrm/core/terminal/TerminalBridge.kt b/core/terminal/src/main/kotlin/com/secrm/core/terminal/TerminalBridge.kt index 9ce08b9..b79a803 100644 --- a/core/terminal/src/main/kotlin/com/secrm/core/terminal/TerminalBridge.kt +++ b/core/terminal/src/main/kotlin/com/secrm/core/terminal/TerminalBridge.kt @@ -72,7 +72,10 @@ class TerminalBridge private constructor( initialRows = 40, initialCols = 120, onKeyboardInput = ::send, - onResize = { ssh?.resizePty(it.columns, it.rows, it.widthPixels, it.heightPixels) }, + // On the writer thread, as keystrokes are: onResize runs on the main thread, where Android refuses network + // I/O. The refused write left the SSH cipher and sequence number one packet ahead of what was sent, so the + // server dropped the session at the next packet. + onResize = { d -> writer.execute { ssh?.resizePty(d.columns, d.rows, d.widthPixels, d.heightPixels) } }, ) private var started: Boolean? = null