namespace DodoSSH.Client.Shell.Keyboard;
///
/// What a latched Ctrl or Alt does to the key pressed after it.
///
///
///
/// The encodings xterm uses, and nothing cleverer. Ctrl with a character is that character's
/// control form — c & 0x1F, the rule caret notation names. Alt is an Escape in front, which is
/// what readline's M-b and M-f and every Emacs binding expect from a terminal. Ctrl or Alt with
/// a cursor key is the modified CSI form, CSI 1 ; m X, which is how Ctrl+← jumps a word in bash and
/// zsh.
///
///
/// Anything else goes through untouched. 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.
///
///
internal static class TerminalModifiers
{
/// Applies whichever modifiers are latched to one key's worth of bytes.
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;
}
/// The control character for one byte, or the byte itself where there is none.
/// Space as NUL is the one addition to caret notation, and every terminal makes it.
private static byte ControlForm(byte c) =>
c == (byte)' ' ? (byte)0x00
: c < 0x80 && KeySequence.TryControl((char)c, out var control) ? control
: c;
}