Fix resetting keyboard hook state on MS-Windows

Register session notifications so Emacs is notified when the
computer is being locked, as required to reset the low level
keyboard hook state.  (Bug#69083).
* src/w32term.h:
* src/w32fns.c (setup_w32_kbdhook, remove_w32_kbdhook)
(w32_wnd_proc, globals_of_w32fns, maybe_pass_notification):
Register and manage session notifications in GUI Emacs.
* src/w32console.c (initialize_w32_display, find_ime_window):
* src/w32xfns.c (drain_message_queue): Register notifications
and reset keyboard hook state in console Emacs.
* src/w32.c (term_ntproc): Un-register session notifications
when terminating.
This commit is contained in:
Raffael Stocker 2024-03-04 19:06:07 +01:00 committed by Eli Zaretskii
parent 1b94f800ae
commit e7b1743b79
5 changed files with 120 additions and 9 deletions

View file

@ -10392,11 +10392,16 @@ check_windows_init_file (void)
}
}
/* from w32fns.c */
extern void remove_w32_kbdhook (void);
void
term_ntproc (int ignored)
{
(void)ignored;
remove_w32_kbdhook ();
term_timers ();
/* shutdown the socket interface if necessary */

View file

@ -659,6 +659,24 @@ w32_face_attributes (struct frame *f, int face_id)
return char_attr;
}
/* The IME window is needed to receive the session notifications
required to reset the low level keyboard hook state. */
static BOOL CALLBACK
find_ime_window (HWND hwnd, LPARAM arg)
{
char window_class[32];
GetClassName (hwnd, window_class, sizeof (window_class));
if (strcmp (window_class, "IME") == 0)
{
*(HWND *) arg = hwnd;
return FALSE;
}
/* keep looking */
return TRUE;
}
void
initialize_w32_display (struct terminal *term, int *width, int *height)
{
@ -818,11 +836,14 @@ initialize_w32_display (struct terminal *term, int *width, int *height)
else
w32_console_unicode_input = 0;
/* Setup w32_display_info structure for this frame. */
/* Setup w32_display_info structure for this frame. */
w32_initialize_display_info (build_string ("Console"));
HWND hwnd = NULL;
EnumThreadWindows (GetCurrentThreadId (), find_ime_window, (LPARAM) &hwnd);
/* Set up the keyboard hook. */
setup_w32_kbdhook ();
setup_w32_kbdhook (hwnd);
}

View file

@ -49,6 +49,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#ifdef WINDOWSNT
#include <mbstring.h>
#include <mbctype.h> /* for _getmbcp */
#include <wtsapi32.h> /* for WTS(Un)RegisterSessionNotification */
#endif /* WINDOWSNT */
#if CYGWIN
@ -204,6 +205,10 @@ typedef HRESULT (WINAPI * SetWindowTheme_Proc)
typedef HRESULT (WINAPI * DwmSetWindowAttribute_Proc)
(HWND hwnd, DWORD dwAttribute, IN LPCVOID pvAttribute, DWORD cbAttribute);
typedef BOOL (WINAPI * WTSRegisterSessionNotification_Proc)
(HWND hwnd, DWORD dwFlags);
typedef BOOL (WINAPI * WTSUnRegisterSessionNotification_Proc) (HWND hwnd);
TrackMouseEvent_Proc track_mouse_event_fn = NULL;
ImmGetCompositionString_Proc get_composition_string_fn = NULL;
ImmGetContext_Proc get_ime_context_fn = NULL;
@ -220,6 +225,8 @@ IsDebuggerPresent_Proc is_debugger_present = NULL;
SetThreadDescription_Proc set_thread_description = NULL;
SetWindowTheme_Proc SetWindowTheme_fn = NULL;
DwmSetWindowAttribute_Proc DwmSetWindowAttribute_fn = NULL;
WTSUnRegisterSessionNotification_Proc WTSUnRegisterSessionNotification_fn = NULL;
WTSRegisterSessionNotification_Proc WTSRegisterSessionNotification_fn = NULL;
extern AppendMenuW_Proc unicode_append_menu;
@ -307,6 +314,7 @@ static struct
int hook_count; /* counter, if several windows are created */
HHOOK hook; /* hook handle */
HWND console; /* console window handle */
HWND notified_wnd; /* window that receives session notifications */
int lwindown; /* Left Windows key currently pressed (and hooked) */
int rwindown; /* Right Windows key currently pressed (and hooked) */
@ -2744,7 +2752,7 @@ funhook (int code, WPARAM w, LPARAM l)
/* Set up the hook; can be called several times, with matching
remove_w32_kbdhook calls. */
void
setup_w32_kbdhook (void)
setup_w32_kbdhook (HWND hwnd)
{
kbdhook.hook_count++;
@ -2800,6 +2808,15 @@ setup_w32_kbdhook (void)
/* Set the hook. */
kbdhook.hook = SetWindowsHookEx (WH_KEYBOARD_LL, funhook,
GetModuleHandle (NULL), 0);
/* Register session notifications so we get notified about the
computer being locked. */
kbdhook.notified_wnd = NULL;
if (hwnd != NULL && WTSRegisterSessionNotification_fn != NULL)
{
WTSRegisterSessionNotification_fn (hwnd, NOTIFY_FOR_THIS_SESSION);
kbdhook.notified_wnd = hwnd;
}
}
}
@ -2811,7 +2828,11 @@ remove_w32_kbdhook (void)
if (kbdhook.hook_count == 0 && w32_kbdhook_active)
{
UnhookWindowsHookEx (kbdhook.hook);
if (kbdhook.notified_wnd != NULL
&& WTSUnRegisterSessionNotification_fn != NULL)
WTSUnRegisterSessionNotification_fn (kbdhook.notified_wnd);
kbdhook.hook = NULL;
kbdhook.notified_wnd = NULL;
}
}
#endif /* WINDOWSNT */
@ -2884,13 +2905,12 @@ check_w32_winkey_state (int vkey)
}
return 0;
}
#endif /* WINDOWSNT */
/* Reset the keyboard hook state. Locking the workstation with Win-L
leaves the Win key(s) "down" from the hook's point of view - the
keyup event is never seen. Thus, this function must be called when
the system is locked. */
static void
void
reset_w32_kbdhook_state (void)
{
kbdhook.lwindown = 0;
@ -2900,6 +2920,7 @@ reset_w32_kbdhook_state (void)
kbdhook.suppress_lone = 0;
kbdhook.winseen = 0;
}
#endif /* WINDOWSNT */
/* GetKeyState and MapVirtualKey on Windows 95 do not actually distinguish
between left and right keys as advertised. We test for this
@ -4129,6 +4150,47 @@ deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam,
return 0;
}
/* Maybe pass session notification registration to another frame. If
the frame with window handle HWND is deleted, we must pass the
notifications to some other frame, if they have been sent to this
frame before and have not already been passed on. If there is no
other frame, do nothing. */
#ifdef WINDOWSNT
static void
maybe_pass_notification (HWND hwnd)
{
if (hwnd == kbdhook.notified_wnd
&& kbdhook.hook_count > 0 && w32_kbdhook_active)
{
Lisp_Object tail, frame;
struct frame *f;
bool found_frame = false;
FOR_EACH_FRAME (tail, frame)
{
f = XFRAME (frame);
if (FRAME_W32_P (f) && FRAME_OUTPUT_DATA (f) != NULL
&& FRAME_W32_WINDOW (f) != hwnd)
{
found_frame = true;
break;
}
}
if (found_frame && WTSUnRegisterSessionNotification_fn != NULL
&& WTSRegisterSessionNotification_fn != NULL)
{
/* There is another frame, pass on the session notification. */
HWND next_wnd = FRAME_W32_WINDOW (f);
WTSUnRegisterSessionNotification_fn (hwnd);
WTSRegisterSessionNotification_fn (next_wnd, NOTIFY_FOR_THIS_SESSION);
kbdhook.notified_wnd = next_wnd;
}
}
}
#endif /* WINDOWSNT */
/* Main window procedure */
static LRESULT CALLBACK
@ -5301,23 +5363,29 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
#ifdef WINDOWSNT
case WM_CREATE:
setup_w32_kbdhook ();
setup_w32_kbdhook (hwnd);
goto dflt;
#endif
case WM_DESTROY:
#ifdef WINDOWSNT
maybe_pass_notification (hwnd);
remove_w32_kbdhook ();
#endif
CoUninitialize ();
return 0;
#ifdef WINDOWSNT
case WM_WTSSESSION_CHANGE:
if (wParam == WTS_SESSION_LOCK)
reset_w32_kbdhook_state ();
goto dflt;
#endif
case WM_CLOSE:
#ifdef WINDOWSNT
maybe_pass_notification (hwnd);
#endif
wmsg.dwModifiers = w32_get_modifiers ();
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
return 0;
@ -11335,6 +11403,14 @@ globals_of_w32fns (void)
set_thread_description = (SetThreadDescription_Proc)
get_proc_addr (hm_kernel32, "SetThreadDescription");
#ifdef WINDOWSNT
HMODULE wtsapi32_lib = LoadLibrary ("wtsapi32.dll");
WTSRegisterSessionNotification_fn = (WTSRegisterSessionNotification_Proc)
get_proc_addr (wtsapi32_lib, "WTSRegisterSessionNotification");
WTSUnRegisterSessionNotification_fn = (WTSUnRegisterSessionNotification_Proc)
get_proc_addr (wtsapi32_lib, "WTSUnRegisterSessionNotification");
#endif
/* Support OS dark mode on Windows 10 version 1809 and higher.
See `w32_applytheme' which uses appropriate APIs per version of Windows.
For future wretches who may need to understand Windows build numbers:

View file

@ -779,8 +779,9 @@ extern bool w32_image_rotations_p (void);
#ifdef WINDOWSNT
/* Keyboard hooks. */
extern void setup_w32_kbdhook (void);
extern void setup_w32_kbdhook (HWND);
extern void remove_w32_kbdhook (void);
extern void reset_w32_kbdhook_state (void);
extern int check_w32_winkey_state (int);
#define w32_kbdhook_active (os_subtype != OS_SUBTYPE_9X)
#else

View file

@ -413,8 +413,16 @@ drain_message_queue (void)
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_EMACS_FILENOTIFY)
retval = 1;
switch (msg.message)
{
case WM_WTSSESSION_CHANGE:
if (msg.wParam == WTS_SESSION_LOCK)
reset_w32_kbdhook_state ();
break;
case WM_EMACS_FILENOTIFY:
retval = 1;
break;
}
TranslateMessage (&msg);
DispatchMessage (&msg);
}