Fix application cursor keys in terminal sessions

Read application cursor, keypad, and NumLock state from the worker display
when encoding mapped keys. These are display modes rather than terminal text
modes, so checking mTerm.mode caused full-screen applications such as htop
and nvtop to receive normal cursor sequences instead of application-mode
sequences.

Add regression coverage for application-mode Up and Down keys.
This commit is contained in:
Martín Lucas Golini
2026-09-18 00:26:56 -03:00
parent 2edf4cfd43
commit f18ba006fb
2 changed files with 24 additions and 4 deletions

View File

@@ -1228,15 +1228,19 @@ void TerminalEmulator::keyEvent( const KittyKeyEvent& event ) {
}
const Uint32 modifiers = keyboardSanitizeMod( event.modifiers );
auto writeMapped = [this, modifiers]( const auto& entries ) {
const auto dpy = mDpy.lock();
const bool appKeypad = dpy && dpy->getMode( MODE_APPKEYPAD );
const bool numLock = dpy && dpy->getMode( MODE_NUMLOCK );
const bool appCursor = dpy && dpy->getMode( MODE_APPCURSOR );
auto writeMapped = [this, modifiers, appKeypad, numLock, appCursor]( const auto& entries ) {
for ( const auto& entry : entries ) {
if ( entry.mask != KEYMOD_CTRL_SHIFT_ALT_META && entry.mask != modifiers )
continue;
if ( IS_SET( MODE_APPKEYPAD ) ? entry.appkey < 0 : entry.appkey > 0 )
if ( appKeypad ? entry.appkey < 0 : entry.appkey > 0 )
continue;
if ( IS_SET( MODE_NUMLOCK ) && entry.appkey == 2 )
if ( numLock && entry.appkey == 2 )
continue;
if ( IS_SET( MODE_APPCURSOR ) ? entry.appcursor < 0 : entry.appcursor > 0 )
if ( appCursor ? entry.appcursor < 0 : entry.appcursor > 0 )
continue;
if ( !entry.string.empty() ) {
ttywrite( entry.string.data(), entry.string.size(), 1 );

View File

@@ -1103,6 +1103,22 @@ UTEST( eterm, kitty_modifier_key_does_not_scroll_to_bottom ) {
EXPECT_EQ( 0, term->scrollPos() );
}
UTEST( eterm, application_cursor_keys_are_written_to_pty ) {
auto pty = std::make_unique<MockPty>();
pty->mBuffer = "\033[?1h";
pty->mLoopWrites = false;
MockPty* ptyPtr = pty.get();
auto process = std::make_unique<MockProcess>();
auto display = std::make_shared<MockDisplay>();
auto term = TerminalEmulator::create( std::move( pty ), std::move( process ), display, 100 );
term->update();
term->keyEvent( { KEY_UP, SCANCODE_UP, 0, KEYMOD_NONE, KittyKeyEventType::Press } );
term->keyEvent( { KEY_DOWN, SCANCODE_DOWN, 0, KEYMOD_NONE, KittyKeyEventType::Press } );
EXPECT_STDSTREQ( "\033OA\033OB", ptyPtr->mWrites );
}
UTEST( eterm, kitty_keyboard_protocol_preserves_altgr_text ) {
auto pty = std::make_unique<MockPty>();
pty->mBuffer = "\033[>15u";