From f18ba006fbe970e461b4f5dc00ec161fc1e79f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 18 Sep 2026 00:26:56 -0300 Subject: [PATCH] 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. --- .../src/eterm/terminal/terminalemulator.cpp | 12 ++++++++---- src/tests/unit_tests/eterm_tests.cpp | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp b/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp index d5f0a183b..f07b83472 100644 --- a/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp @@ -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 ); diff --git a/src/tests/unit_tests/eterm_tests.cpp b/src/tests/unit_tests/eterm_tests.cpp index 9a641c47f..e18d4622e 100644 --- a/src/tests/unit_tests/eterm_tests.cpp +++ b/src/tests/unit_tests/eterm_tests.cpp @@ -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(); + pty->mBuffer = "\033[?1h"; + pty->mLoopWrites = false; + MockPty* ptyPtr = pty.get(); + auto process = std::make_unique(); + auto display = std::make_shared(); + 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(); pty->mBuffer = "\033[>15u";