Public Access
The key bar above the software keyboard is now the user's own: which keys, in what order, up to sixteen in two rows that never scroll. Ctrl and Alt latch across the bar and the phone's keyboard, and Ctrl or Alt with a cursor key sends the modified CSI form. Seven predefined groups (essentials, cursor, control, symbols, tmux, vim, function) and keys of one's own, written in \e / ^C notation, are arranged on a customise sheet that saves as it goes. A grid button swaps the software keyboard for a quick-access keyboard of the same height, with KEYS, SNIPPETS, HISTORY and APPEARANCE tabs; it reopens to the tab last used. Snippets go through the shell's existing InsertSnippet. History keeps a typed command only once the remote has echoed it, so nothing typed at a password prompt is ever recorded; it is memory-only and LOCK clears it. Each session pump now keeps its last kilobyte of output and the alternate-screen state for that test (OutputTail). Long-press and drag on the terminal sends arrow keys, as a joystick with three speeds by distance; scrolling is unchanged and there is no mode switch. It takes the touch before Chromium's own long press, so a held finger no longer pastes; paste is on the bar by default instead. A switch in APPEARANCE turns the gesture off. The text-size buttons moved from the bar to APPEARANCE. On wide screens the same tabs open as a side panel beside the terminal, toggled from the top bar, with the bar's own keys repeated at the top of KEYS while the bar stands down. The model, gesture arithmetic, echo reading and history are in DodoSSH.Client.Shell and covered by TerminalKeyboardTests and OutputTailTests. Builds clean; not yet exercised on a device, because the emulator's install is locked behind a passphrase that was not recorded.
58 lines
2.3 KiB
C#
58 lines
2.3 KiB
C#
namespace DodoSSH.Client.Shell.Keyboard;
|
|
|
|
/// <summary>
|
|
/// What a latched Ctrl or Alt does to the key pressed after it.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <b>The encodings xterm uses, and nothing cleverer.</b> Ctrl with a character is that character's
|
|
/// control form — <c>c & 0x1F</c>, the rule caret notation names. Alt is an Escape in front, which is
|
|
/// what readline's <c>M-b</c> and <c>M-f</c> and every Emacs binding expect from a terminal. Ctrl or Alt with
|
|
/// a cursor key is the modified CSI form, <c>CSI 1 ; m X</c>, which is how Ctrl+← jumps a word in bash and
|
|
/// zsh.
|
|
/// </para>
|
|
/// <para>
|
|
/// <b>Anything else goes through untouched.</b> Ctrl with a pasted line or with a function key has no
|
|
/// encoding a shell agrees on, and mangling one into something else would be worse than ignoring the
|
|
/// latch. The latch still releases, because the person pressed the key it was latched for.
|
|
/// </para>
|
|
/// </remarks>
|
|
internal static class TerminalModifiers
|
|
{
|
|
/// <summary>Applies whichever modifiers are latched to one key's worth of bytes.</summary>
|
|
internal static byte[] Apply(byte[] payload, bool control, bool alt)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(payload);
|
|
|
|
if (!control && !alt)
|
|
{
|
|
return payload;
|
|
}
|
|
|
|
// ESC [ X for the six cursor keys that have a modified form: the arrows, Home and End.
|
|
if (payload is [0x1B, (byte)'[', var final] && final is (byte)'A' or (byte)'B' or (byte)'C'
|
|
or (byte)'D' or (byte)'H' or (byte)'F')
|
|
{
|
|
var modifier = 1 + (alt ? 2 : 0) + (control ? 4 : 0);
|
|
|
|
return [0x1B, (byte)'[', (byte)'1', (byte)';', (byte)('0' + modifier), final];
|
|
}
|
|
|
|
var result = payload;
|
|
|
|
if (control && payload is [var single])
|
|
{
|
|
result = [ControlForm(single)];
|
|
}
|
|
|
|
return alt ? [0x1B, .. result] : result;
|
|
}
|
|
|
|
/// <summary>The control character for one byte, or the byte itself where there is none.</summary>
|
|
/// <remarks>Space as NUL is the one addition to caret notation, and every terminal makes it.</remarks>
|
|
private static byte ControlForm(byte c) =>
|
|
c == (byte)' ' ? (byte)0x00
|
|
: c < 0x80 && KeySequence.TryControl((char)c, out var control) ? control
|
|
: c;
|
|
}
|