-
Notifications
You must be signed in to change notification settings - Fork 0
Special keys and modifiers
In addition to reading which key was pressed during input, you can also read which modifier keys which were held down during the keypress. There are two formats for this - VT and Xterm input sequences.
If the terminating character is '~', the first number must be present and is a keycode number, the second number is an optional modifier value.
$CSI <key (%s)> [; <mod (%d)> ] ~
- Example with no modifier:
$CSI 4~
- Example with modifier:
$CSI 4;2~
$CSI 1~ - Home | $CSI 16~ - | $CSI 31~ - F17
$CSI 2~ - Insert | $CSI 17~ - F6 | $CSI 32~ - F18
$CSI 3~ - Delete | $CSI 18~ - F7 | $CSI 33~ - F19
$CSI 4~ - End | $CSI 19~ - F8 | $CSI 34~ - F20
$CSI 5~ - PgUp | $CSI 20~ - F9 | $CSI 35~ -
$CSI 6~ - PgDn | $CSI 21~ - F10 |
$CSI 7~ - Home | $CSI 22~ - |
$CSI 8~ - End | $CSI 23~ - F11 |
$CSI 9~ - | $CSI 24~ - F12 |
$CSI 10~ - F0 | $CSI 25~ - F13 |
$CSI 11~ - F1 | $CSI 26~ - F14 |
$CSI 12~ - F2 | $CSI 27~ - |
$CSI 13~ - F3 | $CSI 28~ - F15 |
$CSI 14~ - F4 | $CSI 29~ - F16 |
$CSI 15~ - F5 | $CSI 30~ - |
If the terminating character is a letter, the letter is the keycode value, and the optional number is the modifier value.
-
$CSI [mod (%d)] <key (%l)>
(Standard) -
$CSI [1; <mod (%d)>] <key (%l)>
(Non-standard)
During testing I've found that certain terminals (such as "Kitty") append 1;
to the modifier.
I have yet to find this mentioned anywhere in xterm documentation, and I'm uncertain as to why it does this.
For TUI, BBS or MUD developers, I recommend implementing support for this additional format to ensure compatibility across terminals.
- Example with no modifier:
$CSI F
- Example with modifier, in standard format:
$CSI 5F
- Example with modifier, in non-standard format:
$CSI 1;5F
$CSI A - Up | $CSI K - | $CSI U -
$CSI B - Down | $CSI L - | $CSI V -
$CSI C - Right | $CSI M - | $CSI W -
$CSI D - Left | $CSI N - | $CSI X -
$CSI E - | $CSI O - | $CSI Y -
$CSI F - End | $CSI 1P - F1 | $CSI Z -
$CSI G - Keypad 5 | $CSI 1Q - F2 |
$CSI H - Home | $CSI 1R - F3 |
$CSI I - | $CSI 1S - F4 |
$CSI J - | $CSI T - |
The modifier is a digit, which defaults to 1 when no modifier keys are pressed. After subtracting 1 from the modifier, the byte representation of it becomes a so-called "bitmap", which lets us figure out which keys were pressed.
The bitmap is laid out like this (0 + %d...
):
-
Meta-Ctrl-Alt-Shift
-
Meta
: + 8 -
Ctrl
: + 4 -
Alt
: + 2 -
Shift
: + 1
-
-
$CSI 4 ; 2 ~
- Key: End
- Modifier(s): SHIFT. (
2 - 1 = 1
,1 = SHIFT
)
-
$CSI 6C
- Key: Arrow right
- Modifier(s): SHIFT, CTRL. (
6 - 1 = 5
,4 = CTRL
,1 = SHIFT
,CTRL + SHIFT = 4 + 1 = 5
)
This is only meant to be used as a bug/sanity check, don't hardcode it please. I recommend using something akin to this example if you want to calculate the value.
Modifier keys | Meta | Ctrl | Alt | Shift | Value (raw) | Value (valid bitmap) |
---|---|---|---|---|---|---|
None | 1 | 0 | ||||
Shift | X | 2 | 1 | |||
Alt | X | 3 | 2 | |||
Shift + Alt | X | X | 4 | 3 | ||
Control | X | 5 | 4 | |||
Shift + Control | X | X | 6 | 5 | ||
Alt + Control | X | X | 7 | 6 | ||
Shift + Alt + Control | X | X | X | 8 | 7 | |
Meta | X | 9 | 8 | |||
Meta + Shift | X | X | 10 | 9 | ||
Meta + Alt | X | X | 11 | 10 | ||
Meta + Alt + Shift | X | X | X | 12 | 11 | |
Meta + Ctrl | X | X | 13 | 12 | ||
Meta + Ctrl + Shift | X | X | X | 14 | 13 | |
Meta + Ctrl + Alt | X | X | X | 15 | 14 | |
Meta + Ctrl + Alt + Shift | X | X | X | X | 16 | 15 |
This is an exert from my input event parser, feel free to modify it according to your own needs.
SpecialInputEvent parseSpecialKey(@NotNull String keycode, int modifier) {
SpecialInputEvent.SpecialKey specialKey = sequenceMap.get(keycode);
// If there's no special key we don't need to continue
if (specialKey == null) return null;
// Order: { META, CTRL, ALT, SHIFT }.
boolean[] modifierBits = new boolean[]{false, false, false, false};
// Subtract 1 to create a valid bitmap
modifier--;
// Calculate which modifier keys were active
for (int i = 3; i >= 0; i--) {
modifierBits[3 - i] = ((modifier >> i) & 1) == 1;
}
// Return the resulting values
return new SpecialInputEvent(
plugin,
specialKey,
modifierBits[0],
modifierBits[1],
modifierBits[2],
modifierBits[3]
);
}
The standards outlined here shall be used in all wiki pages for this project. They exist to improve readability and make it easier to understand the requirements for certain sequences, commands and arguments.
NOTE: Unless marked as literal, spaces are not to be taken literally, and are only there to improve readability.
- Escaping:
\arg
(read asarg
) - Optional argument:
[arg]
- Required argument:
<arg>
- Default value:
{arg}
- Miscellaneous info:
(arg)
- Multiple arguments can be supplied:
arg...
- Literal
"arg"
orarg
- Acronym or predefined value
$arg
- For example
$ESC
refers to character 27, "escape". - See predefined values.
- For example
-
%d
: Single numeric only. -
%a
: Single alphanumeric character only. -
%l
: Single letter only. -
%s
: String (any length unless specified otherwise in a custom condition). -
%(<custom condition>)
: Custom condition(s). -
...
can be suffixed to indicate that any amount greater than 0 is accepted
- Specify a custom length:
%a(length 1-5)
- Specify a custom range:
%a(range 9-1005)
More values can be defined in each wiki page, these are just some common ones. Additional values are usually at the bottom of said pages, or explained in tables with acronyms. Uppercase letters are not required, but strongly recommended. If uppercase is not suitable then CamelCase is another option.
-
$ESC
: "Escape", character 27. -
$CSI
: Control Sequence Inducer, AKA$ESC\[
-
$SP
: "Space", character 32.