Skip to content
This repository has been archived by the owner on Sep 4, 2022. It is now read-only.

Special keys and modifiers

Gustav Dersjö edited this page May 13, 2021 · 20 revisions

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.

VT format

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)> ] ~

Examples

  • Example with no modifier: $CSI 4~
  • Example with modifier: $CSI 4;2~

Value tables

$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~   -       |

Xterm format

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)

Non-standard sequence variations

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.

Examples

  • Example with no modifier: $CSI F
  • Example with modifier, in standard format: $CSI 5F
  • Example with modifier, in non-standard format: $CSI 1;5F

Value table

$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    -      |

Modifier values

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

Examples

  • $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)

Value table

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

Example code (java)

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]
    );
}