From 69428fe6e8793fb57403491a5b6897bb2e2e8b89 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 8 Mar 2018 17:24:54 +0100 Subject: [PATCH] Handle Ctrl+AltGr on Windows Improve AltGr detection even further on Windows so we can detect the case when Ctrl and AltGr are pressed together. --- doc/keyboard-test.txt | 4 +-- vncviewer/Viewport.cxx | 34 ++++++++++++------------- vncviewer/win32.c | 58 ++++++++++++++++++++++++++++++++++++++++++ vncviewer/win32.h | 2 ++ 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/doc/keyboard-test.txt b/doc/keyboard-test.txt index 6375a9e5..d66e8533 100644 --- a/doc/keyboard-test.txt +++ b/doc/keyboard-test.txt @@ -66,7 +66,8 @@ Client (classic, non-raw, mode) - CapsLock, NumLock - Shift, Ctrl - - Alt, AltGr, Super [Win, X11] (FIXME: Ctrl+AltGr broken on Win) + - Alt, AltGr, Super [Win, X11] + - Ctrl+AltGr [Win] - Meta [X11] - Left/right identification (FIXME: broken for Shift on Win) - CmdL => AltL, CmdR => SuperL, AltL => ModeSwitch, AltR => Level3Shift [OS X] @@ -75,7 +76,6 @@ Client (classic, non-raw, mode) - CapsLock, Shift and AltGr affect symbol lookup - Ctrl does not affect symbol lookup - Ctrl+Alt+ sends the same symbol as - - Ctrl+AltGr+ sends the same symbol as AltGr+ (FIXME: broken on Win) - "Shift press, A press, Shift release, A release" should not send "a release" - Numpad: diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index ba0b0e93..1c0503c5 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -838,24 +838,18 @@ int Viewport::handleSystemEvent(void *event, void *data) // Ctrl+Alt. However the remote end might not be Windows, so we need // to merge those in to a single AltGr event. We detect this case // by seeing the two key events directly after each other with a very - // short time between them (<50ms). + // short time between them (<50ms) and supress the Ctrl event. if (self->altGrArmed) { self->altGrArmed = false; Fl::remove_timeout(handleAltGrTimeout); if (isExtended && (keyCode == 0x38) && (vKey == VK_MENU) && ((msg->time - self->altGrCtrlTime) < 50)) { - // FIXME: We fail to detect this if either Ctrl key is - // first manually pressed as Windows then no longer - // sends the fake Ctrl down event. It does however - // happily send real Ctrl events even when AltGr - // is already down. - vlog.debug("Detected AltGr combination"); - self->handleKeyPress(0xb8, XK_ISO_Level3_Shift); - return 1; + // Alt seen, so this is an AltGr sequence + } else { + // Not Alt, so fire the queued up Ctrl event + self->handleKeyPress(0x1d, XK_Control_L); } - - self->handleKeyPress(0x1d, XK_Control_L); } if (keyCode == SCAN_FAKE) { @@ -917,12 +911,18 @@ int Viewport::handleSystemEvent(void *event, void *data) if ((keySym == XK_Shift_L) && (keyCode == 0x36)) keySym = XK_Shift_R; - // Possible start of AltGr sequence? (see above) - if ((keyCode == 0x1d) && (keySym == XK_Control_L)) { - self->altGrArmed = true; - self->altGrCtrlTime = msg->time; - Fl::add_timeout(0.1, handleAltGrTimeout, self); - return 1; + // AltGr handling (see above) + if (win32_has_altgr()) { + if ((keyCode == 0xb8) && (keySym == XK_Alt_R)) + keySym = XK_ISO_Level3_Shift; + + // Possible start of AltGr sequence? + if ((keyCode == 0x1d) && (keySym == XK_Control_L)) { + self->altGrArmed = true; + self->altGrCtrlTime = msg->time; + Fl::add_timeout(0.1, handleAltGrTimeout, self); + return 1; + } } self->handleKeyPress(keyCode, keySym); diff --git a/vncviewer/win32.c b/vncviewer/win32.c index 30c87f18..537ff435 100644 --- a/vncviewer/win32.c +++ b/vncviewer/win32.c @@ -33,6 +33,9 @@ #define MAPVK_VK_TO_CHAR 2 #endif +int has_altgr; +HKL current_layout = 0; + static HANDLE thread; static DWORD thread_id; @@ -300,3 +303,58 @@ int win32_vkey_to_keysym(UINT vkey, int extended) return NoSymbol; } + +int win32_has_altgr(void) +{ + BYTE orig_state[256]; + BYTE altgr_state[256]; + + if (current_layout == GetKeyboardLayout(0)) + return has_altgr; + + // Save current keyboard state so we can get things sane again after + // we're done + if (!GetKeyboardState(orig_state)) + return 0; + + // We press Ctrl+Alt (Windows fake AltGr) and then test every key + // to see if it produces a printable character. If so then we assume + // AltGr is used in the current layout. + + has_altgr = 0; + + memset(altgr_state, 0, sizeof(altgr_state)); + altgr_state[VK_CONTROL] = 0x80; + altgr_state[VK_MENU] = 0x80; + + for (UINT vkey = 0;vkey <= 0xff;vkey++) { + int ret; + WCHAR wstr[10]; + + // Need to skip this one as it is a bit magical and will trigger + // a false positive + if (vkey == VK_PACKET) + continue; + + ret = ToUnicode(vkey, 0, altgr_state, wstr, + sizeof(wstr)/sizeof(wstr[0]), 0); + if (ret == 1) { + has_altgr = 1; + break; + } + + if (ret == -1) { + // Dead key, need to clear out state before we proceed + do { + ret = ToUnicode(vkey, 0, altgr_state, wstr, + sizeof(wstr)/sizeof(wstr[0]), 0); + } while (ret < 0); + } + } + + SetKeyboardState(orig_state); + + current_layout = GetKeyboardLayout(0); + + return has_altgr; +} diff --git a/vncviewer/win32.h b/vncviewer/win32.h index 01487085..9d4704bc 100644 --- a/vncviewer/win32.h +++ b/vncviewer/win32.h @@ -32,6 +32,8 @@ void win32_disable_lowlevel_keyboard(HWND hwnd); int win32_vkey_to_keysym(UINT vkey, int extended); +int win32_has_altgr(void); + }; #endif -- 2.39.5