Files
ETTerms/src/ETTerms/Terminal/TerminalInput.cs
T
etwenandClaude Fable 5 572e6589ec feat: v0.4.0 performance & stability overhaul
Terminal:
- cache font variants + single reusable brush in render hot path;
  resolve cell colors once per cell; dispose GDI resources
- scrollback: List+RemoveRange -> O(1) ring buffer
- follow new output only when already at bottom (no more yank-to-bottom
  while reading history)
- alt-screen mouse wheel -> arrow keys (vim/htop scroll)
- answer DSR (ESC[5n/6n) and DA (ESC[c) queries so TUIs no longer hang
- remove dead code in OnKeyPress

Encoding correctness (garbled CJK across chunk boundaries):
- stateful UTF-8 Decoder in TTLInterpreter.OnData, SerialBridgeServer
  rx forwarding, and SessionLogger.Write

Sessions:
- ShellChannel: free proc-thread attribute list, close hProcess,
  notify '[ETTerms] shell process exited' in the tab
- SshChannel: surface ErrorOccurred / ShellStream.Closed in the tab;
  Write no longer throws into the UI thread on a dead connection

PDU:
- new shared ETTerms.PduCore project replaces the two drifted copies of
  PduController (GUI + PduMcp); logging via injected delegates
- batched SNMP GET (GetAllPortsStatus): 12-port poll is 3 UDP
  round-trips instead of 36 (StatusView polling + pdu_status tool)

Scripting:
- cap TTL receive buffer at 1MB; skip re-scan in wait when buffer
  length unchanged
- merge ScriptRunner.RunAsync/RunGroupAsync; new TtlScript helper
  dedups script picking + group-command checks (3 copies -> 1)

Misc:
- ConnectionStore: parse LastUsedUtc with InvariantCulture/RoundtripKind
- version 0.4.0; About changelog; CLAUDE.md notes (intentional group
  barrier behavior, SSH.NET reflection resize caveat)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 09:17:19 +08:00

62 lines
2.3 KiB
C#

using System.Text;
using System.Windows.Forms;
namespace ETTerms.Terminal;
/// <summary>把特殊鍵(方向鍵 / Fn / Home/End… / Enter/Tab/Backspace/Esc)對應成終端機 byte 序列。
/// 一般可列印字元回傳 null,交給 KeyPress 處理。</summary>
public static class TerminalInput
{
public static byte[]? Map(KeyEventArgs e, bool appCursor)
{
// Enter:一般送 CR(\r) 視為「送出」;Shift+Enter 送 LF(\n),讓 PSReadLine / Kiro 等
// 視為「插入換行」以便輸入多行指令,而不是直接送出。
if (e.KeyCode == Keys.Enter)
return new[] { (byte)(e.Shift ? '\n' : '\r') };
string? seq = e.KeyCode switch
{
Keys.Up => appCursor ? "\x1bOA" : "\x1b[A",
Keys.Down => appCursor ? "\x1bOB" : "\x1b[B",
Keys.Right => appCursor ? "\x1bOC" : "\x1b[C",
Keys.Left => appCursor ? "\x1bOD" : "\x1b[D",
Keys.Home => "\x1b[H",
Keys.End => "\x1b[F",
Keys.Insert => "\x1b[2~",
Keys.Delete => "\x1b[3~",
Keys.PageUp => "\x1b[5~",
Keys.PageDown => "\x1b[6~",
Keys.Tab => "\t",
Keys.Escape => "\x1b",
Keys.Back => "\x7f",
Keys.F1 => "\x1bOP",
Keys.F2 => "\x1bOQ",
Keys.F3 => "\x1bOR",
Keys.F4 => "\x1bOS",
Keys.F5 => "\x1b[15~",
Keys.F6 => "\x1b[17~",
Keys.F7 => "\x1b[18~",
Keys.F8 => "\x1b[19~",
Keys.F9 => "\x1b[20~",
Keys.F10 => "\x1b[21~",
Keys.F11 => "\x1b[23~",
Keys.F12 => "\x1b[24~",
_ => null
};
return seq is null ? null : Encoding.ASCII.GetBytes(seq);
}
/// <summary>滾輪 → 上/下方向鍵序列(alt screen 用,每格 3 行)。notches 正值往上。</summary>
public static byte[]? WheelArrows(int notches, bool appCursor)
{
if (notches == 0) return null;
string one = notches > 0
? (appCursor ? "\x1bOA" : "\x1b[A")
: (appCursor ? "\x1bOB" : "\x1b[B");
int count = Math.Abs(notches) * 3;
var sb = new StringBuilder(one.Length * count);
for (int i = 0; i < count; i++) sb.Append(one);
return Encoding.ASCII.GetBytes(sb.ToString());
}
}