diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2022-06-21 02:25:07 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2022-06-21 02:25:07 +0200 |
commit | 9586a037685417a2079e79b2895b61d8f443ca91 (patch) | |
tree | 873d0962737795571bfe7ae4aabd6ef42709c1d4 /src | |
parent | f15bc53a011bcfbcb6848c19cae2d5ae4ff1fc64 (diff) | |
download | sciteco-9586a037685417a2079e79b2895b61d8f443ca91.tar.gz |
PDCurses: fixed CTRL+C interruptions on WinCON and WinGUI
* Due to regressions, the Control handler needs to be installed later
(PDCursesMod installs its own control handler).
* We no longer have to manually set the control mode - at least on PDCursesMod/WinCON.
It's not worth keeping the workaround for the original PDCurses.
* For WinGUI neither the control handler, nor the polling-fallback will work,
therefore we introduced yet another version based on keyboard hooks.
See https://github.com/Bill-Gray/PDCursesMod/issues/197
This version may even become the default on all Win32-ports but I
need to think this through more thorougly.
Diffstat (limited to 'src')
-rw-r--r-- | src/interface-curses/interface.c | 115 |
1 files changed, 87 insertions, 28 deletions
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index 1a94280..993b4f9 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -110,6 +110,7 @@ * handler. MinGW provides signal(), but it's not * reliable. * This may also be used to handle CTRL_CLOSE_EVENTs. + * * NOTE: Unlike signal handlers, this is executed in a * separate thread. */ @@ -127,6 +128,55 @@ teco_console_ctrl_handler(DWORD type) #endif +#if defined(PDCURSES_GUI) && defined(G_OS_WIN32) + +/** + * Hooks into the key processing to catch CTRL+C. + * + * FIXME: This might be used as the default way to catch CTRL+C on + * all Windows ports, even including Gtk+. + */ +static LRESULT CALLBACK +teco_keyboard_hook(int nCode, WPARAM wParam, LPARAM lParam) +{ + PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam; + static gboolean ctrl_pressed = FALSE; + + if (nCode == HC_ACTION) { + switch (wParam) { + case WM_KEYDOWN: + switch (p->vkCode) { + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + ctrl_pressed = TRUE; + break; + case 0x43: /* C */ + if (ctrl_pressed) { + teco_sigint_occurred = TRUE; + return 1; + } + break; + } + break; + + case WM_KEYUP: + switch (p->vkCode) { + case VK_LCONTROL: + case VK_RCONTROL: + case VK_CONTROL: + ctrl_pressed = FALSE; + break; + } + break; + } + } + + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +#endif + static gint teco_xterm_version(void) G_GNUC_UNUSED; #define UNNAMED_FILE "(Unnamed)" @@ -373,16 +423,6 @@ teco_interface_init(void) teco_curses_info_popup_init(&teco_interface.popup); /* - * We must register this handler to handle - * asynchronous interruptions via CTRL+C - * reliably. The signal handler we already - * have won't do. - */ -#if defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) - SetConsoleCtrlHandler(teco_console_ctrl_handler, TRUE); -#endif - - /* * Make sure we have a string for the info line * even if teco_interface_info_update() is never called. */ @@ -672,6 +712,18 @@ teco_interface_init_interactive(GError **error) teco_interface_init_screen(); + /* + * We must register this handler for + * asynchronous interruptions via CTRL+C + * reliably. The signal handler we already + * have won't do. + * Doing this here ensures that we have a higher + * precedence than the handler installed by PDCursesMod. + */ +#if defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) + SetConsoleCtrlHandler(teco_console_ctrl_handler, TRUE); +#endif + cbreak(); noecho(); /* Scintilla draws its own cursor */ @@ -1454,7 +1506,21 @@ teco_interface_is_interrupted(void) return teco_sigint_occurred != FALSE; } -#else /* !CURSES_TTY && !PDCURSES_WINCON && !NCURSES_WIN32 */ +#elif defined(PDCURSES_GUI) && defined(G_OS_WIN32) + +gboolean +teco_interface_is_interrupted(void) +{ + /* + * NOTE: The teco_keyboard_hook() is only called from event loops. + */ + MSG msg; + PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE); + + return teco_sigint_occurred != FALSE; +} + +#else /* !CURSES_TTY && !PDCURSES_WINCON && !NCURSES_WIN32 && (!PDCURSES_GUI || !G_OS_WIN32) */ /* * This function is called repeatedly, so we can poll the keyboard input queue, @@ -1487,18 +1553,11 @@ teco_interface_is_interrupted(void) static gint teco_interface_blocking_getch(void) { - /* - * On PDCurses/WinCon, raw() and cbreak() does - * not disable and enable CTRL+C handling properly. - * Since I don't want to patch PDCurses/win32, - * we do this manually here. - * NOTE: This exploits the fact that PDCurses uses - * STD_INPUT_HANDLE internally! - */ -#ifdef PDCURSES_WINCON - HANDLE console_hnd = GetStdHandle(STD_INPUT_HANDLE); - DWORD console_mode; - GetConsoleMode(console_hnd, &console_mode); +#if defined(PDCURSES_GUI) && defined(G_OS_WIN32) + static HHOOK keyboard_hook = NULL; + + if (G_LIKELY(keyboard_hook != NULL)) + UnhookWindowsHookEx(keyboard_hook); #endif /* @@ -1520,9 +1579,6 @@ teco_interface_blocking_getch(void) /* no special <CTRL/C> handling */ raw(); nodelay(teco_interface.cmdline_window, FALSE); -#ifdef PDCURSES_WINCON - SetConsoleMode(console_hnd, console_mode & ~ENABLE_PROCESSED_INPUT); -#endif /* * Memory limiting is stopped temporarily, since it might otherwise * constantly place 100% load on the CPU. @@ -1533,10 +1589,13 @@ teco_interface_blocking_getch(void) /* allow asynchronous interruptions on <CTRL/C> */ teco_sigint_occurred = FALSE; nodelay(teco_interface.cmdline_window, TRUE); +#if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */ cbreak(); -#ifdef PDCURSES_WINCON - SetConsoleMode(console_hnd, console_mode | ENABLE_PROCESSED_INPUT); +#endif + +#if defined(PDCURSES_GUI) && defined(G_OS_WIN32) + keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, teco_keyboard_hook, NULL, 0); #endif return key; |