diff --git a/WinPort/src/Backend/TTY/TTYBackend.cpp b/WinPort/src/Backend/TTY/TTYBackend.cpp index f2e172e63..2117f3acb 100644 --- a/WinPort/src/Backend/TTY/TTYBackend.cpp +++ b/WinPort/src/Backend/TTY/TTYBackend.cpp @@ -369,6 +369,7 @@ void TTYBackend::WriterThread() { bool gone_background = false; try { + _focused = !_far2l_tty; // assume starting focused unless far2l_tty, this trick allows notification to work in best effort under old far2l that didnt support focus change notifications TTYOutput tty_out(_stdout, _far2l_tty, _norgb, _nodetect); DispatchPalette(tty_out); // DispatchTermResized(tty_out); @@ -959,7 +960,7 @@ void TTYBackend::OnConsoleExit() bool TTYBackend::OnConsoleIsActive() { - return false;//true; + return _focused; } void TTYBackend_OnTerminalDamaged(bool flush_input_queue) @@ -1082,6 +1083,16 @@ void TTYBackend::OnInspectKeyEvent(KEY_EVENT_RECORD &event) } } +void TTYBackend::OnFocusChange(bool focused) +{ + fprintf(stderr, "OnFocusChange: %u\n", (unsigned)!!focused); + _focused = focused; + INPUT_RECORD ir = {}; + ir.EventType = FOCUS_EVENT; + ir.Event.FocusEvent.bSetFocus = focused ? TRUE : FALSE; + g_winport_con_in->Enqueue(&ir, 1); +} + void TTYBackend::OnFar2lEvent(StackSerializer &stk_ser) { if (!_far2l_tty) { diff --git a/WinPort/src/Backend/TTY/TTYBackend.h b/WinPort/src/Backend/TTY/TTYBackend.h index 77478b576..d4d6c46a1 100644 --- a/WinPort/src/Backend/TTY/TTYBackend.h +++ b/WinPort/src/Backend/TTY/TTYBackend.h @@ -66,6 +66,7 @@ class TTYBackend : IConsoleOutputBackend, ITTYInputSpecialSequenceHandler, IFar2 COORD _largest_window_size{}; std::atomic _largest_window_size_ready{false}; std::atomic _flush_input_queue{false}; + std::atomic _focused{true}; // assume starting focused struct BI : std::mutex { std::string flavor; } _backend_info; @@ -145,6 +146,7 @@ class TTYBackend : IConsoleOutputBackend, ITTYInputSpecialSequenceHandler, IFar2 // ITTYInputSpecialSequenceHandler virtual void OnUsingExtension(char extension); virtual void OnInspectKeyEvent(KEY_EVENT_RECORD &event); + virtual void OnFocusChange(bool focused); virtual void OnFar2lEvent(StackSerializer &stk_ser); virtual void OnFar2lReply(StackSerializer &stk_ser); virtual void OnInputBroken(); diff --git a/WinPort/src/Backend/TTY/TTYInputSequenceParser.cpp b/WinPort/src/Backend/TTY/TTYInputSequenceParser.cpp index 0aef72899..525f59d1a 100644 --- a/WinPort/src/Backend/TTY/TTYInputSequenceParser.cpp +++ b/WinPort/src/Backend/TTY/TTYInputSequenceParser.cpp @@ -314,6 +314,11 @@ size_t TTYInputSequenceParser::ParseEscapeSequence(const char *s, size_t l) fprintf(stderr, "\n"); */ + if (l > 1 && s[0] == '[' && (s[1] == 'I' || s[1] == 'O')) { // focus + _handler->OnFocusChange(s[1] == 'I'); + return 2; + } + if (l > 2 && s[0] == '[' && s[2] == 'n') { return 3; } diff --git a/WinPort/src/Backend/TTY/TTYInputSequenceParser.h b/WinPort/src/Backend/TTY/TTYInputSequenceParser.h index 184390318..10bca76aa 100644 --- a/WinPort/src/Backend/TTY/TTYInputSequenceParser.h +++ b/WinPort/src/Backend/TTY/TTYInputSequenceParser.h @@ -67,6 +67,7 @@ struct ITTYInputSpecialSequenceHandler { virtual void OnUsingExtension(char extension) = 0; virtual void OnInspectKeyEvent(KEY_EVENT_RECORD &event) = 0; + virtual void OnFocusChange(bool focused) = 0; virtual void OnFar2lEvent(StackSerializer &stk_ser) = 0; virtual void OnFar2lReply(StackSerializer &stk_ser) = 0; virtual void OnInputBroken() = 0; diff --git a/WinPort/src/Backend/TTY/TTYOutput.cpp b/WinPort/src/Backend/TTY/TTYOutput.cpp index 4ea0b0e7b..20ecf45ec 100644 --- a/WinPort/src/Backend/TTY/TTYOutput.cpp +++ b/WinPort/src/Backend/TTY/TTYOutput.cpp @@ -174,7 +174,8 @@ TTYOutput::TTYOutput(int out, bool far2l_tty, bool norgb, DWORD nodetect) } #endif - Format(ESC "7" ESC "[?47h" ESC "[?1049h" ESC "[?2004h"); + // enable mouse and focus notifications + Format(ESC "7" ESC "[?47h" ESC "[?1049h" ESC "[?2004h" ESC "[?1004h"); if ((_nodetect & NODETECT_W) == 0) { Format(ESC "[?9001h"); // win32-input-mode on @@ -185,6 +186,7 @@ TTYOutput::TTYOutput(int out, bool far2l_tty, bool norgb, DWORD nodetect) if ((_nodetect & NODETECT_K) == 0) { Format(ESC "[=15;1u"); // kovidgoyal's kitty mode on } + ChangeKeypad(true); ChangeMouse(true); @@ -215,7 +217,7 @@ TTYOutput::~TTYOutput() if ((_nodetect & NODETECT_K) == 0) { Format(ESC "[=0;1u" "\r"); // kovidgoyal's kitty mode off } - Format(ESC "[0m" ESC "[?1049l" ESC "[?47l" ESC "8" ESC "[?2004l" "\r\n"); + Format(ESC "[0m" ESC "[?1049l" ESC "[?47l" ESC "8" ESC "[?2004l" ESC "[?1004l" "\r\n"); if ((_nodetect & NODETECT_W) == 0) { Format(ESC "[?9001l"); // win32-input-mode off } diff --git a/WinPort/src/Backend/WX/wxMain.cpp b/WinPort/src/Backend/WX/wxMain.cpp index 62155402d..1ba88dbfe 100644 --- a/WinPort/src/Backend/WX/wxMain.cpp +++ b/WinPort/src/Backend/WX/wxMain.cpp @@ -2052,15 +2052,28 @@ void WinPortPanel::CheckPutText2CLip() void WinPortPanel::OnSetFocus( wxFocusEvent &event ) { //fprintf(stderr, "OnSetFocus\n"); + const bool was_focused = (_focused_ts != 0); const DWORD ts = WINPORT(GetTickCount)(); _focused_ts = ts ? ts : 1; ResetTimerIdling(); + if (!was_focused) { + INPUT_RECORD ir = {}; + ir.EventType = FOCUS_EVENT; + ir.Event.FocusEvent.bSetFocus = TRUE; + g_winport_con_in->Enqueue(&ir, 1); + } } void WinPortPanel::OnKillFocus( wxFocusEvent &event ) { fprintf(stderr, "OnKillFocus\n"); - _focused_ts = 0; + if (_focused_ts) { + _focused_ts = 0; + INPUT_RECORD ir = {}; + ir.EventType = FOCUS_EVENT; + ir.Event.FocusEvent.bSetFocus = FALSE; + g_winport_con_in->Enqueue(&ir, 1); + } ResetInputState(); } diff --git a/far2l/src/vt/IVTShell.h b/far2l/src/vt/IVTShell.h index c6446a1cf..6257a9e9b 100644 --- a/far2l/src/vt/IVTShell.h +++ b/far2l/src/vt/IVTShell.h @@ -15,6 +15,7 @@ struct IVTShell { virtual void OnMouseExpectation(MouseExpectation mex, bool enabled) = 0; virtual void OnBracketedPasteExpectation(bool enabled) = 0; + virtual void OnFocusChangeExpectation(bool enabled) = 0; virtual void OnWin32InputMode(bool enabled) = 0; virtual void SetKittyFlags(int flags) = 0; virtual int GetKittyFlags() = 0; diff --git a/far2l/src/vt/vtansi.cpp b/far2l/src/vt/vtansi.cpp index c04aa1ac1..3617ff2be 100644 --- a/far2l/src/vt/vtansi.cpp +++ b/far2l/src/vt/vtansi.cpp @@ -800,6 +800,9 @@ struct VTAnsiContext // alternative_screen_buffer.Toggle(suffix == 'h'); // break; + case 1004: + vt_shell->OnFocusChangeExpectation(suffix == 'h'); + break; case 2004: vt_shell->OnBracketedPasteExpectation(suffix == 'h'); break; diff --git a/far2l/src/vt/vtshell.cpp b/far2l/src/vt/vtshell.cpp index 35d7ba21c..7bc304334 100644 --- a/far2l/src/vt/vtshell.cpp +++ b/far2l/src/vt/vtshell.cpp @@ -40,8 +40,10 @@ #include "AnsiEsc.hpp" #include "TestPath.h" -#define BRACKETED_PASTE_SEQ_START "\x1b[200~" -#define BRACKETED_PASTE_SEQ_STOP "\x1b[201~" +#define BRACKETED_PASTE_SEQ_START "\x1b[200~" +#define BRACKETED_PASTE_SEQ_STOP "\x1b[201~" +#define FOCUS_CHANGED_SEQ_ACTIVE "\x1b[I" +#define FOCUS_CHANGED_SEQ_INACTIVE "\x1b[O" const char *VT_TranslateSpecialKey(const WORD key, bool ctrl, bool alt, bool shift, unsigned char keypad = 0, WCHAR uc = 0); std::string VT_TranslateKeyToKitty(const KEY_EVENT_RECORD &KeyEvent, int kitty_kb_flags); @@ -118,6 +120,7 @@ class VTShell : VTOutputReader::IProcessor, VTInputReader::IProcessor, IVTShell std::atomic _keypad{0}; std::atomic _bracketed_paste_expected{false}; std::atomic _win32_input_mode_expected{false}; + std::atomic _focus_change_expected{false}; std::atomic _kitty_kb_flags{0}; INPUT_RECORD _last_window_info_ir; std::unique_ptr _far2l_exts; @@ -456,6 +459,21 @@ class VTShell : VTOutputReader::IProcessor, VTInputReader::IProcessor, IVTShell } } + virtual void OnFocusChanged() // called from worker thread + { + if (_focus_change_expected) { + bool active = WINPORT(IsConsoleActive)() != FALSE; + const char *seq = active ? FOCUS_CHANGED_SEQ_ACTIVE : FOCUS_CHANGED_SEQ_INACTIVE; + if (!WriteTerm(seq, strlen(seq))) { + fprintf(stderr, "VT: OnFocusChanged - write error %d\n", errno); + } else { + fprintf(stderr, "VT: OnFocusChanged - %s\n", active ? "active" : "inactive"); + } + } else { + fprintf(stderr, "VT: OnFocusChanged - SKIPPED\n"); + } + } + void OnCtrlC(bool alt) { if (alt) { @@ -554,6 +572,14 @@ class VTShell : VTOutputReader::IProcessor, VTInputReader::IProcessor, IVTShell _bracketed_paste_expected = enabled; } + virtual void OnFocusChangeExpectation(bool enabled) + { + bool was_enabled = _focus_change_expected.exchange(enabled); + if (!was_enabled) { + OnFocusChanged(); + } + } + virtual void OnWin32InputMode(bool enabled) { _win32_input_mode_expected = enabled; diff --git a/far2l/src/vt/vtshell_ioreaders.cpp b/far2l/src/vt/vtshell_ioreaders.cpp index 19c1e8a2a..fe01142e5 100644 --- a/far2l/src/vt/vtshell_ioreaders.cpp +++ b/far2l/src/vt/vtshell_ioreaders.cpp @@ -213,6 +213,9 @@ void *VTInputReader::ThreadProc() } else if (ir.EventType == KEY_EVENT) { _processor->OnInputKey(ir.Event.KeyEvent); + } else if (ir.EventType == FOCUS_EVENT) { + _processor->OnFocusChanged(); + } else if (ir.EventType == BRACKETED_PASTE_EVENT) { _processor->OnBracketedPaste(ir.Event.BracketedPaste.bStartPaste != FALSE); diff --git a/far2l/src/vt/vtshell_ioreaders.h b/far2l/src/vt/vtshell_ioreaders.h index 3735cac84..3855c9573 100644 --- a/far2l/src/vt/vtshell_ioreaders.h +++ b/far2l/src/vt/vtshell_ioreaders.h @@ -75,6 +75,7 @@ class VTInputReader : protected WithThread { virtual void OnInputMouse(const MOUSE_EVENT_RECORD &MouseEvent) = 0; virtual void OnInputKey(const KEY_EVENT_RECORD &KeyEvent) = 0; + virtual void OnFocusChanged() = 0; virtual void OnInputResized(const INPUT_RECORD &ir) = 0; virtual void OnInputInjected(const std::string &str) = 0; virtual void OnBracketedPaste(bool start) = 0;