1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 16:37:35 +00:00

LibVT+LibLine: Use 1;mods CSI parameters for ctrl/alt/shift-arrow keys

xterms send a bitmask (+ 1) in the 2nd CSI parameter if "special"
keys (arrow keys, pgup/down, etc) are sent with modifiers held down.

Serenity's Terminal used to send ^[[O, which is a nonexistent
escape sequence and a misread of VT100's ^[O (ie the '[' is
replaced by 'O'). Since the xterm scheme also supports shift
and alt modifiers, switch to that.

More flexible, and makes ctrl-left/right and alt-left/right work
in SerenityOS's bash port.

Also do this for page up/down.

No behavior change for SerenityOS's Shell.
This commit is contained in:
Nico Weber 2020-09-13 21:08:19 -04:00 committed by Andreas Kling
parent 2fe127d96f
commit 83c07be794
2 changed files with 32 additions and 28 deletions

View file

@ -616,7 +616,6 @@ void Editor::handle_read_event()
} }
auto reverse_tab = false; auto reverse_tab = false;
auto ctrl_held = false;
// Discard starting bytes until they make sense as utf-8. // Discard starting bytes until they make sense as utf-8.
size_t valid_bytes = 0; size_t valid_bytes = 0;
@ -635,6 +634,11 @@ void Editor::handle_read_event()
Vector<unsigned, 4> csi_parameters; Vector<unsigned, 4> csi_parameters;
Vector<u8> csi_intermediate_bytes; Vector<u8> csi_intermediate_bytes;
u8 csi_final; u8 csi_final;
enum CSIMod {
Shift = 1,
Alt = 2,
Ctrl = 4,
};
for (auto code_point : input_view) { for (auto code_point : input_view) {
if (m_finish) if (m_finish)
@ -677,11 +681,10 @@ void Editor::handle_read_event()
} }
m_state = InputState::CSIExpectFinal; m_state = InputState::CSIExpectFinal;
[[fallthrough]]; [[fallthrough]];
case InputState::CSIExpectFinal: case InputState::CSIExpectFinal: {
m_state = InputState::Free; m_state = InputState::Free;
if (!(code_point >= 0x40 && code_point <= 0x7f)) { if (!(code_point >= 0x40 && code_point <= 0x7f)) {
dbgprintf("LibLine: Invalid CSI: %02x (%c)\r\n", code_point, code_point); dbgprintf("LibLine: Invalid CSI: %02x (%c)\r\n", code_point, code_point);
ctrl_held = false;
continue; continue;
} }
csi_final = code_point; csi_final = code_point;
@ -692,68 +695,61 @@ void Editor::handle_read_event()
else else
csi_parameters.append(0); csi_parameters.append(0);
} }
unsigned param1 = 0, param2 = 0;
if (csi_parameters.size() >= 1)
param1 = csi_parameters[0];
if (csi_parameters.size() >= 2)
param2 = csi_parameters[1];
unsigned modifiers = param2 ? param2 - 1 : 0;
if (csi_final == 'O') {
// mod_ctrl
ctrl_held = true;
continue;
}
if (csi_final == 'Z') { if (csi_final == 'Z') {
// 'reverse tab' // 'reverse tab'
reverse_tab = true; reverse_tab = true;
ctrl_held = false;
break; break;
} }
cleanup_suggestions(); cleanup_suggestions();
switch (csi_final) { switch (csi_final) {
case 'A': // ^[[A: arrow up case 'A': // ^[[A: arrow up
search_backwards(); search_backwards();
ctrl_held = false;
continue; continue;
case 'B': // ^[[B: arrow down case 'B': // ^[[B: arrow down
search_forwards(); search_forwards();
ctrl_held = false;
continue; continue;
case 'D': // ^[[D: arrow left case 'D': // ^[[D: arrow left
if (ctrl_held) if (modifiers == CSIMod::Ctrl)
cursor_left_word(); cursor_left_word();
else else
cursor_left_character(); cursor_left_character();
ctrl_held = false;
continue; continue;
case 'C': // ^[[C: arrow right case 'C': // ^[[C: arrow right
if (ctrl_held) if (modifiers == CSIMod::Ctrl)
cursor_right_word(); cursor_right_word();
else else
cursor_right_character(); cursor_right_character();
ctrl_held = false;
continue; continue;
case 'H': // ^[[H: home case 'H': // ^[[H: home
go_home(); go_home();
ctrl_held = false;
continue; continue;
case 'F': // ^[[F: end case 'F': // ^[[F: end
go_end(); go_end();
ctrl_held = false;
continue; continue;
case '~': case '~':
if (csi_parameters.size() == 1 && csi_parameters[0] == 3) { // ^[[3~: delete if (param1 == 3) { // ^[[3~: delete
erase_character_forwards(); erase_character_forwards();
m_search_offset = 0; m_search_offset = 0;
ctrl_held = false;
continue; continue;
} }
// ^[[5~: page up // ^[[5~: page up
// ^[[6~: page down // ^[[6~: page down
dbgprintf("LibLine: Unhandled '~'\r\n"); dbgprintf("LibLine: Unhandled '~': %d\r\n", param1);
ctrl_held = false;
continue; continue;
default: default:
dbgprintf("LibLine: Unhandled final: %02x (%c)\r\n", code_point, code_point); dbgprintf("LibLine: Unhandled final: %02x (%c)\r\n", code_point, code_point);
ctrl_held = false;
continue; continue;
} }
break; break;
}
case InputState::Free: case InputState::Free:
if (code_point == 27) { if (code_point == 27) {
m_state = InputState::GotEscape; m_state = InputState::GotEscape;

View file

@ -1026,19 +1026,27 @@ void Terminal::handle_key_press(KeyCode key, u32 code_point, u8 flags)
bool ctrl = flags & Mod_Ctrl; bool ctrl = flags & Mod_Ctrl;
bool alt = flags & Mod_Alt; bool alt = flags & Mod_Alt;
bool shift = flags & Mod_Shift; bool shift = flags & Mod_Shift;
unsigned modifier_mask = int(shift) + (int(alt) << 1) + (int(ctrl) << 2);
auto emit_final_with_modifier = [this, modifier_mask](char final) {
if (modifier_mask)
emit_string(String::format("\e[1;%d%c", modifier_mask + 1, final));
else
emit_string(String::format("\e[%c", final));
};
switch (key) { switch (key) {
case KeyCode::Key_Up: case KeyCode::Key_Up:
emit_string(ctrl ? "\033[OA" : "\033[A"); emit_final_with_modifier('A');
return; return;
case KeyCode::Key_Down: case KeyCode::Key_Down:
emit_string(ctrl ? "\033[OB" : "\033[B"); emit_final_with_modifier('B');
return; return;
case KeyCode::Key_Right: case KeyCode::Key_Right:
emit_string(ctrl ? "\033[OC" : "\033[C"); emit_final_with_modifier('C');
return; return;
case KeyCode::Key_Left: case KeyCode::Key_Left:
emit_string(ctrl ? "\033[OD" : "\033[D"); emit_final_with_modifier('D');
return; return;
case KeyCode::Key_Insert: case KeyCode::Key_Insert:
emit_string("\033[2~"); emit_string("\033[2~");
@ -1047,10 +1055,10 @@ void Terminal::handle_key_press(KeyCode key, u32 code_point, u8 flags)
emit_string("\033[3~"); emit_string("\033[3~");
return; return;
case KeyCode::Key_Home: case KeyCode::Key_Home:
emit_string("\033[H"); emit_final_with_modifier('H');
return; return;
case KeyCode::Key_End: case KeyCode::Key_End:
emit_string("\033[F"); emit_final_with_modifier('F');
return; return;
case KeyCode::Key_PageUp: case KeyCode::Key_PageUp:
emit_string("\033[5~"); emit_string("\033[5~");