Fixed keyboard handling on windows 95 family.
This commit is contained in:
parent
e50146119f
commit
97046541b9
|
@ -13,7 +13,6 @@
|
|||
*/
|
||||
|
||||
#include "CMSWindowsDesks.h"
|
||||
#include "CMSWindowsDesktop.h"
|
||||
#include "CMSWindowsScreen.h"
|
||||
#include "IScreenSaver.h"
|
||||
#include "XScreen.h"
|
||||
|
@ -200,6 +199,27 @@ CMSWindowsDesks::fakeKeyEvent(
|
|||
KeyButton button, UINT virtualKey,
|
||||
bool press, bool /*isAutoRepeat*/) const
|
||||
{
|
||||
// win 95 family doesn't understand handed modifier virtual keys
|
||||
if (m_is95Family) {
|
||||
switch (virtualKey) {
|
||||
case VK_LSHIFT:
|
||||
case VK_RSHIFT:
|
||||
virtualKey = VK_SHIFT;
|
||||
break;
|
||||
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
virtualKey = VK_CONTROL;
|
||||
break;
|
||||
|
||||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
virtualKey = VK_MENU;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// synthesize event
|
||||
DWORD flags = 0;
|
||||
if (((button & 0x100u) != 0)) {
|
||||
flags |= KEYEVENTF_EXTENDEDKEY;
|
||||
|
@ -552,6 +572,14 @@ CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout)
|
|||
SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
|
||||
// if not using low-level hooks we have to also activate the
|
||||
// window to ensure we don't lose keyboard focus.
|
||||
// FIXME -- see if this can be avoided. if so then always
|
||||
// disable the window (see handling of SYNERGY_MSG_SWITCH).
|
||||
if (!desk->m_lowLevel) {
|
||||
SetActiveWindow(desk->m_window);
|
||||
}
|
||||
|
||||
// switch to requested keyboard layout
|
||||
ActivateKeyboardLayout(keyLayout, 0);
|
||||
}
|
||||
|
@ -594,11 +622,6 @@ CMSWindowsDesks::deskThread(void* vdesk)
|
|||
// ignore
|
||||
LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str()));
|
||||
}
|
||||
|
||||
// a window on the primary screen should never activate
|
||||
if (m_isPrimary && desk->m_window != NULL) {
|
||||
EnableWindow(desk->m_window, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// tell main thread that we're ready
|
||||
|
@ -636,6 +659,10 @@ CMSWindowsDesks::deskThread(void* vdesk)
|
|||
desk->m_lowLevel = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// a window on the primary screen with low-level hooks
|
||||
// should never activate.
|
||||
EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -573,7 +573,8 @@ const KeyID CMSWindowsKeyState::s_virtualKey[][2] =
|
|||
/* 0xff */ kKeyNone, kKeyNone // reserved
|
||||
};
|
||||
|
||||
// map special KeyID keys to virtual key codes
|
||||
// map special KeyID keys to virtual key codes plus whether or not
|
||||
// the key maps to an extended scan code
|
||||
const UINT CMSWindowsKeyState::s_mapE000[] =
|
||||
{
|
||||
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
@ -597,15 +598,15 @@ const UINT CMSWindowsKeyState::s_mapE000[] =
|
|||
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xa0 */ 0, 0, 0, 0,
|
||||
/* 0xa4 */ 0, 0, VK_BROWSER_BACK, VK_BROWSER_FORWARD,
|
||||
/* 0xa8 */ VK_BROWSER_REFRESH, VK_BROWSER_STOP,
|
||||
/* 0xaa */ VK_BROWSER_SEARCH, VK_BROWSER_FAVORITES,
|
||||
/* 0xac */ VK_BROWSER_HOME, VK_VOLUME_MUTE,
|
||||
/* 0xae */ VK_VOLUME_DOWN, VK_VOLUME_UP,
|
||||
/* 0xb0 */ VK_MEDIA_NEXT_TRACK, VK_MEDIA_PREV_TRACK,
|
||||
/* 0xb2 */ VK_MEDIA_STOP, VK_MEDIA_PLAY_PAUSE,
|
||||
/* 0xb4 */ VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT,
|
||||
/* 0xb6 */ VK_LAUNCH_APP1, VK_LAUNCH_APP2,
|
||||
/* 0xa4 */ 0, 0, VK_BROWSER_BACK | 0x100u, VK_BROWSER_FORWARD | 0x100u,
|
||||
/* 0xa8 */ VK_BROWSER_REFRESH | 0x100u, VK_BROWSER_STOP | 0x100u,
|
||||
/* 0xaa */ VK_BROWSER_SEARCH | 0x100u, VK_BROWSER_FAVORITES | 0x100u,
|
||||
/* 0xac */ VK_BROWSER_HOME | 0x100u, VK_VOLUME_MUTE | 0x100u,
|
||||
/* 0xae */ VK_VOLUME_DOWN | 0x100u, VK_VOLUME_UP | 0x100u,
|
||||
/* 0xb0 */ VK_MEDIA_NEXT_TRACK | 0x100u, VK_MEDIA_PREV_TRACK | 0x100u,
|
||||
/* 0xb2 */ VK_MEDIA_STOP | 0x100u, VK_MEDIA_PLAY_PAUSE | 0x100u,
|
||||
/* 0xb4 */ VK_LAUNCH_MAIL | 0x100u, VK_LAUNCH_MEDIA_SELECT | 0x100u,
|
||||
/* 0xb6 */ VK_LAUNCH_APP1 | 0x100u, VK_LAUNCH_APP2 | 0x100u,
|
||||
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
@ -666,35 +667,41 @@ const UINT CMSWindowsKeyState::s_mapEF00[] =
|
|||
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT,
|
||||
/* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END,
|
||||
/* 0x50 */ VK_HOME | 0x100u, VK_LEFT | 0x100u,
|
||||
/* 0x52 */ VK_UP | 0x100u, VK_RIGHT | 0x100u,
|
||||
/* 0x54 */ VK_DOWN | 0x100u, VK_PRIOR | 0x100u,
|
||||
/* 0x56 */ VK_NEXT | 0x100u, VK_END | 0x100u,
|
||||
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT,
|
||||
/* 0x64 */ 0, 0, 0, VK_APPS,
|
||||
/* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0,
|
||||
/* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT | 0x100u,
|
||||
/* 0x64 */ 0, 0, 0, VK_APPS | 0x100u,
|
||||
/* 0x68 */ 0, 0, VK_HELP, VK_CANCEL | 0x100u, 0, 0, 0, 0,
|
||||
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK,
|
||||
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, VK_NUMLOCK | 0x100u,
|
||||
/* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN, 0, 0,
|
||||
/* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN | 0x100u, 0, 0,
|
||||
/* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP,
|
||||
/* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT,
|
||||
/* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE,
|
||||
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD,
|
||||
/* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE,
|
||||
/* 0xac */ VK_DECIMAL, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE | 0x100u,
|
||||
/* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3,
|
||||
/* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
||||
/* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2,
|
||||
/* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10,
|
||||
/* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18,
|
||||
/* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0,
|
||||
/* 0xc8 */ VK_F11, VK_F12, VK_F13 | 0x100u, VK_F14 | 0x100u,
|
||||
/* 0xcc */ VK_F15 | 0x100u, VK_F16 | 0x100u,
|
||||
/* 0xce */ VK_F17 | 0x100u, VK_F18 | 0x100u,
|
||||
/* 0xd0 */ VK_F19 | 0x100u, VK_F20 | 0x100u,
|
||||
/* 0xd2 */ VK_F21 | 0x100u, VK_F22 | 0x100u,
|
||||
/* 0xd4 */ VK_F23 | 0x100u, VK_F24 | 0x100u, 0, 0,
|
||||
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL,
|
||||
/* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, 0,
|
||||
/* 0xe8 */ 0, VK_LMENU, VK_RMENU, VK_LWIN,
|
||||
/* 0xec */ VK_RWIN, 0, 0, 0,
|
||||
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT | 0x100u, VK_LCONTROL,
|
||||
/* 0xe4 */ VK_RCONTROL | 0x100u, VK_CAPITAL, 0, 0,
|
||||
/* 0xe8 */ 0, VK_LMENU, VK_RMENU | 0x100u, VK_LWIN | 0x100u,
|
||||
/* 0xec */ VK_RWIN | 0x100u, 0, 0, 0,
|
||||
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE
|
||||
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE | 0x100u
|
||||
};
|
||||
|
||||
CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks) :
|
||||
|
@ -733,7 +740,7 @@ CMSWindowsKeyState::fixKey(void* target, UINT virtualKey)
|
|||
KeyID key = mapKeyFromEvent(virtualKey, lParam, &mask);
|
||||
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
||||
CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
|
||||
CKeyState::setKeyDown(button, false);
|
||||
setKeyDown(button, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -741,6 +748,7 @@ KeyID
|
|||
CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey,
|
||||
LPARAM info, KeyModifierMask* maskOut) const
|
||||
{
|
||||
// FIXME -- look into this
|
||||
// note: known microsoft bugs
|
||||
// Q72583 -- MapVirtualKey() maps keypad keys incorrectly
|
||||
// 95,98: num pad vk code -> invalid scan code
|
||||
|
@ -837,27 +845,6 @@ CMSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const
|
|||
return m_virtKeyToScanCode[virtualKey & 0xffu];
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsKeyState::setKeyDown(KeyButton button, bool down)
|
||||
{
|
||||
CKeyState::setKeyDown(button, down);
|
||||
|
||||
// special case: we detect ctrl+alt+del being pressed on some
|
||||
// systems but we don't detect the release of those keys. so
|
||||
// if ctrl, alt, and del are down then mark them up.
|
||||
if (down && isKeyDown(m_virtKeyToScanCode[VK_DELETE])) {
|
||||
KeyModifierMask mask = getActiveModifiers();
|
||||
if ((mask & (KeyModifierControl | KeyModifierAlt)) ==
|
||||
(KeyModifierControl | KeyModifierAlt)) {
|
||||
CKeyState::setKeyDown(m_virtKeyToScanCode[VK_LCONTROL], false);
|
||||
CKeyState::setKeyDown(m_virtKeyToScanCode[VK_RCONTROL], false);
|
||||
CKeyState::setKeyDown(m_virtKeyToScanCode[VK_LMENU], false);
|
||||
CKeyState::setKeyDown(m_virtKeyToScanCode[VK_RMENU], false);
|
||||
CKeyState::setKeyDown(m_virtKeyToScanCode[VK_DELETE], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsKeyState::sendKeyEvent(void* target,
|
||||
bool press, bool isAutoRepeat,
|
||||
|
@ -865,7 +852,7 @@ CMSWindowsKeyState::sendKeyEvent(void* target,
|
|||
SInt32 count, KeyButton button)
|
||||
{
|
||||
if (press || isAutoRepeat) {
|
||||
// if AltGr required for this key then make sure
|
||||
// if AltGr is required for this key then make sure
|
||||
// the ctrl and alt keys are *not* down on the
|
||||
// client. windows simulates AltGr with ctrl and
|
||||
// alt for some inexplicable reason and clients
|
||||
|
@ -950,37 +937,6 @@ CMSWindowsKeyState::sendKeyEvent(void* target,
|
|||
}
|
||||
}
|
||||
else {
|
||||
// key release. if the key isn't down according to
|
||||
// our table then we never got the key press event
|
||||
// for it. if it's not a modifier key then we'll
|
||||
// synthesize the press first. only do this on
|
||||
// the windows 95 family, which eats certain special
|
||||
// keys like alt+tab, ctrl+esc, etc.
|
||||
if (m_is95Family && isKeyDown(button)) {
|
||||
switch (m_scanCodeToVirtKey[button]) {
|
||||
case VK_LSHIFT:
|
||||
case VK_RSHIFT:
|
||||
case VK_SHIFT:
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
case VK_CONTROL:
|
||||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
case VK_MENU:
|
||||
case VK_CAPITAL:
|
||||
case VK_NUMLOCK:
|
||||
case VK_SCROLL:
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
break;
|
||||
|
||||
default:
|
||||
CKeyState::sendKeyEvent(target,
|
||||
true, false, key, mask, 1, button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// do key up
|
||||
CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
|
||||
}
|
||||
|
@ -1065,47 +1021,35 @@ CMSWindowsKeyState::doUpdateKeys()
|
|||
// KeyModifierModeSwitch mask can be converted to keystrokes. it
|
||||
// must be mapped before the Alt modifier so that the Alt modifier
|
||||
// takes precedence when mapping keystrokes to modifier masks.
|
||||
//
|
||||
// we have to explicitly set the extended key flag for some
|
||||
// modifiers because the win32 API is inadequate.
|
||||
KeyButtons keys;
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100));
|
||||
keys.push_back(mapVirtKeyToButton(VK_RMENU));
|
||||
addModifier(KeyModifierModeSwitch, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)MapVirtualKey(VK_LSHIFT, 0));
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_RSHIFT, 0) | 0x100));
|
||||
keys.push_back(mapVirtKeyToButton(VK_LSHIFT));
|
||||
keys.push_back(mapVirtKeyToButton(VK_RSHIFT));
|
||||
addModifier(KeyModifierShift, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)MapVirtualKey(VK_LCONTROL, 0));
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_RCONTROL, 0) | 0x100));
|
||||
keys.push_back(mapVirtKeyToButton(VK_LCONTROL));
|
||||
keys.push_back(mapVirtKeyToButton(VK_RCONTROL));
|
||||
addModifier(KeyModifierControl, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)MapVirtualKey(VK_LMENU, 0));
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_RMENU, 0) | 0x100));
|
||||
keys.push_back(mapVirtKeyToButton(VK_LMENU));
|
||||
keys.push_back(mapVirtKeyToButton(VK_RMENU));
|
||||
addModifier(KeyModifierAlt, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_LWIN, 0) | 0x100));
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_RWIN, 0) | 0x100));
|
||||
keys.push_back(mapVirtKeyToButton(VK_LWIN));
|
||||
keys.push_back(mapVirtKeyToButton(VK_RWIN));
|
||||
addModifier(KeyModifierSuper, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)MapVirtualKey(VK_CAPITAL, 0));
|
||||
keys.push_back(mapVirtKeyToButton(VK_CAPITAL));
|
||||
addModifier(KeyModifierCapsLock, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)(MapVirtualKey(VK_NUMLOCK, 0) | 0x100));
|
||||
keys.push_back(mapVirtKeyToButton(VK_NUMLOCK));
|
||||
addModifier(KeyModifierNumLock, keys);
|
||||
keys.clear();
|
||||
keys.push_back((KeyButton)MapVirtualKey(VK_SCROLL, 0));
|
||||
keys.push_back(mapVirtKeyToButton(VK_SCROLL));
|
||||
addModifier(KeyModifierScrollLock, keys);
|
||||
|
||||
/* FIXME -- potential problem here on win me
|
||||
// win me (sony vaio laptop):
|
||||
// MapVirtualKey(vk, 0):
|
||||
// VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped
|
||||
// VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped
|
||||
// VK_MENU mapped; VK_LMENU, VK_RMENU not mapped
|
||||
// MapVirtualKey(sc, 3):
|
||||
// all scan codes unmapped (function apparently unimplemented)
|
||||
*/
|
||||
BYTE keyState[256];
|
||||
GetKeyboardState(keyState);
|
||||
for (UINT i = 1; i < 256; ++i) {
|
||||
|
@ -1123,63 +1067,20 @@ CMSWindowsKeyState::doUpdateKeys()
|
|||
}
|
||||
|
||||
// map to a scancode and back to a virtual key
|
||||
UINT scancode = MapVirtualKey(i, 0);
|
||||
UINT virtKey = MapVirtualKey(scancode, 3);
|
||||
if (virtKey == 0) {
|
||||
// assume MapVirtualKey(xxx, 3) is unimplemented
|
||||
virtKey = i;
|
||||
}
|
||||
else if (scancode == 0) {
|
||||
// the VK_PAUSE virtual key doesn't map properly
|
||||
if (i == VK_PAUSE) {
|
||||
// i hope this works on all keyboards
|
||||
scancode = 0x45;
|
||||
virtKey = i;
|
||||
}
|
||||
else {
|
||||
KeyButton button2;
|
||||
KeyButton button = mapVirtKeyToButton(i, button2);
|
||||
if (button == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// we need some adjustments due to inadequacies in the API.
|
||||
// the API provides no means to query the keyboard by scan
|
||||
// code that i can see. so we're doing it by virtual key.
|
||||
// but a single virtual key can map to multiple physical
|
||||
// keys. for example, VK_HOME maps to NumPad 7 and to the
|
||||
// (extended key) Home key. this means we can never tell
|
||||
// which of the two keys is pressed.
|
||||
//
|
||||
// this is a problem if a key is down when this method is
|
||||
// called. if the extended key is down we'll record the
|
||||
// non-extended key as being down. when the extended key
|
||||
// goes up, we'll record that correctly and leave the
|
||||
// non-extended key as being down. to deal with that we
|
||||
// always re-check the keyboard state if we think we're
|
||||
// locked to a screen because a key is down. the re-check
|
||||
// should clear it up.
|
||||
//
|
||||
// the win32 functions that take scan codes are:
|
||||
|
||||
//
|
||||
// if the mapped virtual key doesn't match the starting
|
||||
// point then there's a really good chance that that virtual
|
||||
// key is mapped to an extended key. however, this is not
|
||||
// the case for modifiers that don't distinguish between left
|
||||
// and right. also VK_NUMLOCK gets mapped to a non-extended
|
||||
// key but it should be.
|
||||
if (virtKey != i || i == VK_NUMLOCK) {
|
||||
if (i != VK_SHIFT && i != VK_CONTROL && i != VK_MENU) {
|
||||
scancode |= 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
// okay, now we have the scan code for the virtual key.
|
||||
m_scanCodeToVirtKey[scancode] = i;
|
||||
m_virtKeyToScanCode[i] = (KeyButton)scancode;
|
||||
m_scanCodeToVirtKey[button] = i;
|
||||
m_scanCodeToVirtKey[button2] = i;
|
||||
m_virtKeyToScanCode[i] = button;
|
||||
|
||||
// save the key state
|
||||
if ((keyState[i] & 0x80) != 0) {
|
||||
setKeyDown((KeyButton)scancode, true);
|
||||
setKeyDown(button, true);
|
||||
}
|
||||
|
||||
// toggle state applies to all keys but we only want it for
|
||||
|
@ -1214,27 +1115,27 @@ KeyButton
|
|||
CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id,
|
||||
KeyModifierMask mask, bool isAutoRepeat) const
|
||||
{
|
||||
UINT virtualKey = 0;
|
||||
UINT extVirtualKey = 0;
|
||||
|
||||
// check for special keys
|
||||
if ((id & 0xfffff000u) == 0xe000u) {
|
||||
if ((id & 0xff00u) == 0xe000u) {
|
||||
virtualKey = s_mapE000[id & 0xffu];
|
||||
extVirtualKey = s_mapE000[id & 0xffu];
|
||||
}
|
||||
else if ((id & 0xff00) == 0xee00) {
|
||||
virtualKey = s_mapEE00[id & 0xffu];
|
||||
extVirtualKey = s_mapEE00[id & 0xffu];
|
||||
}
|
||||
else if ((id & 0xff00) == 0xef00) {
|
||||
virtualKey = s_mapEF00[id & 0xffu];
|
||||
extVirtualKey = s_mapEF00[id & 0xffu];
|
||||
}
|
||||
if (virtualKey == 0) {
|
||||
if (extVirtualKey == 0) {
|
||||
LOG((CLOG_DEBUG2 "unknown special key"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// special handling of VK_SNAPSHOT
|
||||
if (virtualKey == VK_SNAPSHOT) {
|
||||
if (extVirtualKey == VK_SNAPSHOT) {
|
||||
// ignore key repeats on print screen
|
||||
if (!isAutoRepeat) {
|
||||
// active window (with alt) or fullscreen (without alt)?
|
||||
|
@ -1251,7 +1152,7 @@ CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id,
|
|||
}
|
||||
|
||||
// handle other special keys
|
||||
if (virtualKey != 0) {
|
||||
if (extVirtualKey != 0) {
|
||||
// compute required modifiers
|
||||
KeyModifierMask requiredMask = 0;
|
||||
KeyModifierMask outMask = 0;
|
||||
|
@ -1262,6 +1163,7 @@ CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id,
|
|||
// (e.g. add, multiply) and their main keyboard counterparts.
|
||||
// therefore, we can ignore the num-lock state for movement virtual
|
||||
// keys but not for numeric keys.
|
||||
UINT virtualKey = (extVirtualKey & 0xffu);
|
||||
if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) {
|
||||
requiredMask |= KeyModifierNumLock;
|
||||
if ((getActiveModifiers() & KeyModifierNumLock) != 0) {
|
||||
|
@ -1277,9 +1179,12 @@ CMSWindowsKeyState::mapKey(Keystrokes& keys, KeyID id,
|
|||
}
|
||||
|
||||
// now generate the keystrokes and return the resulting modifier mask
|
||||
KeyButton scanCode = m_virtKeyToScanCode[virtualKey];
|
||||
LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%04x mask 0x%04x", id, virtualKey, scanCode, outMask));
|
||||
return mapToKeystrokes(keys, scanCode,
|
||||
KeyButton button = m_virtKeyToScanCode[virtualKey];
|
||||
if ((extVirtualKey & 0x100u) != 0) {
|
||||
button |= 0x100u;
|
||||
}
|
||||
LOG((CLOG_DEBUG2 "KeyID 0x%08x to virtual key %d scan code 0x%03x mask 0x%04x", id, virtualKey, button, outMask));
|
||||
return mapToKeystrokes(keys, button,
|
||||
outMask, requiredMask, isAutoRepeat);
|
||||
}
|
||||
|
||||
|
@ -1407,6 +1312,196 @@ CMSWindowsKeyState::getCodePageFromLangID(LANGID langid) const
|
|||
return codePage;
|
||||
}
|
||||
|
||||
KeyButton
|
||||
CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey,
|
||||
KeyButton& extended) const
|
||||
{
|
||||
// this method does what MapVirtualKey(virtualKey, 0) should do.
|
||||
// we have to explicitly set the extended key flag for some
|
||||
// modifiers because the win32 API is inadequate. we also find
|
||||
// the unextended and the extended scancodes for those virtual
|
||||
// keys that have both except for VK_SHIFT, VK_CONTROL, and VK_MENU.
|
||||
//
|
||||
// the windows 95 family doesn't map the side distinguishing virtual
|
||||
// keys. but we know that VK_CONTROL maps to VK_LCONTROL and
|
||||
// that VK_RCONTROL is the same scan code | 0x100. similarly for
|
||||
// VK_MENU. but VK_RSHIFT cannot be determined that way so we
|
||||
// search for it.
|
||||
extended = 0;
|
||||
KeyButton button;
|
||||
if (m_is95Family) {
|
||||
UINT scancode;
|
||||
switch (virtualKey) {
|
||||
case VK_LSHIFT:
|
||||
button = (KeyButton)MapVirtualKey(VK_SHIFT, 0);
|
||||
break;
|
||||
|
||||
case VK_RSHIFT:
|
||||
// we have to search
|
||||
scancode = MapVirtualKey(VK_SHIFT, 0);
|
||||
for (UINT i = 1; i < 256; ++i) {
|
||||
if (i != scancode && MapVirtualKey(i, 1) == VK_SHIFT) {
|
||||
return (KeyButton)(i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
button = (KeyButton)MapVirtualKey(VK_CONTROL, 0);
|
||||
break;
|
||||
|
||||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
button = (KeyButton)MapVirtualKey(VK_MENU, 0);
|
||||
break;
|
||||
|
||||
case VK_PAUSE:
|
||||
// mapped to 0. i hope this works on all keyboards.
|
||||
button = (KeyButton)0x45u;
|
||||
break;
|
||||
|
||||
case VK_DIVIDE:
|
||||
// mapped to 0. i hope this works on all keyboards.
|
||||
button = (KeyButton)0x35u;
|
||||
break;
|
||||
|
||||
default:
|
||||
button = (KeyButton)MapVirtualKey(virtualKey, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (virtualKey) {
|
||||
case VK_PAUSE:
|
||||
// mapped to 0. i hope this works on all keyboards.
|
||||
button = (KeyButton)0x45u;
|
||||
break;
|
||||
|
||||
default:
|
||||
button = (KeyButton)MapVirtualKey(virtualKey, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// map extended keys
|
||||
switch (virtualKey) {
|
||||
case VK_RETURN: // Return/numpad Enter
|
||||
case VK_PRIOR: // numpad PageUp/PageUp
|
||||
case VK_NEXT: // numpad PageDown/PageDown
|
||||
case VK_END: // numpad End/End
|
||||
case VK_HOME: // numpad Home/Home
|
||||
case VK_LEFT: // numpad Left/Left
|
||||
case VK_UP: // numpad Up/Up
|
||||
case VK_RIGHT: // numpad Right/Right
|
||||
case VK_DOWN: // numpad Down/Down
|
||||
case VK_INSERT: // numpad Insert/Insert
|
||||
case VK_DELETE: // numpad Delete/Delete
|
||||
// case VK_SELECT:
|
||||
// case VK_EXECUTE:
|
||||
// case VK_HELP:
|
||||
extended = (KeyButton)(button | 0x100u);
|
||||
break;
|
||||
}
|
||||
|
||||
// see if the win32 API can help us determine an extended key.
|
||||
// if the remapped virtual key doesn't match the starting
|
||||
// point then there's a really good chance that that virtual
|
||||
// key is mapped to an extended key. however, this is not
|
||||
// the case for modifiers that don't distinguish between left
|
||||
// and right.
|
||||
UINT virtualKey2 = MapVirtualKey(button, 3);
|
||||
if (virtualKey2 != 0 && virtualKey2 != virtualKey) {
|
||||
switch (virtualKey) {
|
||||
case VK_SHIFT:
|
||||
case VK_CONTROL:
|
||||
case VK_MENU:
|
||||
break;
|
||||
|
||||
case VK_NUMPAD0:
|
||||
case VK_NUMPAD1:
|
||||
case VK_NUMPAD2:
|
||||
case VK_NUMPAD3:
|
||||
case VK_NUMPAD4:
|
||||
case VK_NUMPAD5:
|
||||
case VK_NUMPAD6:
|
||||
case VK_NUMPAD7:
|
||||
case VK_NUMPAD8:
|
||||
case VK_NUMPAD9:
|
||||
case VK_MULTIPLY:
|
||||
case VK_ADD:
|
||||
case VK_SEPARATOR:
|
||||
case VK_SUBTRACT:
|
||||
case VK_DECIMAL:
|
||||
break;
|
||||
|
||||
default:
|
||||
button |= 0x100u;
|
||||
extended = 0;
|
||||
break;
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
// note other extended keys that the win32 API won't help us with.
|
||||
// on the windows 95 family this is the only way to find extended
|
||||
// keys since MapVirtualKey(N, 3) is unimplemented.
|
||||
switch (virtualKey) {
|
||||
case VK_CANCEL:
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
case VK_APPS:
|
||||
// case VK_SEPARATOR:
|
||||
case VK_DIVIDE:
|
||||
case VK_F13:
|
||||
case VK_F14:
|
||||
case VK_F15:
|
||||
case VK_F16:
|
||||
case VK_F17:
|
||||
case VK_F18:
|
||||
case VK_F19:
|
||||
case VK_F20:
|
||||
case VK_F21:
|
||||
case VK_F22:
|
||||
case VK_F23:
|
||||
case VK_F24:
|
||||
case VK_NUMLOCK:
|
||||
case VK_RSHIFT:
|
||||
case VK_RCONTROL:
|
||||
case VK_RMENU:
|
||||
case VK_BROWSER_BACK:
|
||||
case VK_BROWSER_FORWARD:
|
||||
case VK_BROWSER_REFRESH:
|
||||
case VK_BROWSER_STOP:
|
||||
case VK_BROWSER_SEARCH:
|
||||
case VK_BROWSER_FAVORITES:
|
||||
case VK_BROWSER_HOME:
|
||||
case VK_VOLUME_MUTE:
|
||||
case VK_VOLUME_DOWN:
|
||||
case VK_VOLUME_UP:
|
||||
case VK_MEDIA_NEXT_TRACK:
|
||||
case VK_MEDIA_PREV_TRACK:
|
||||
case VK_MEDIA_STOP:
|
||||
case VK_MEDIA_PLAY_PAUSE:
|
||||
case VK_LAUNCH_MAIL:
|
||||
case VK_LAUNCH_MEDIA_SELECT:
|
||||
case VK_LAUNCH_APP1:
|
||||
case VK_LAUNCH_APP2:
|
||||
button |= 0x100u;
|
||||
extended = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
KeyButton
|
||||
CMSWindowsKeyState::mapVirtKeyToButton(UINT virtualKey) const
|
||||
{
|
||||
KeyButton dummy;
|
||||
return mapVirtKeyToButton(virtualKey, dummy);
|
||||
}
|
||||
|
||||
KeyButton
|
||||
CMSWindowsKeyState::mapCharacter(Keystrokes& keys,
|
||||
char c, HKL hkl, bool isAutoRepeat) const
|
||||
|
|
|
@ -64,7 +64,6 @@ public:
|
|||
//@}
|
||||
|
||||
// IKeyState overrides
|
||||
virtual void setKeyDown(KeyButton button, bool down);
|
||||
virtual void sendKeyEvent(void* target,
|
||||
bool press, bool isAutoRepeat,
|
||||
KeyID key, KeyModifierMask mask,
|
||||
|
@ -88,6 +87,14 @@ private:
|
|||
// convert a language ID to a code page
|
||||
UINT getCodePageFromLangID(LANGID langid) const;
|
||||
|
||||
// map a virtual key to a button. this tries to deal with the
|
||||
// broken win32 API as best it can.
|
||||
KeyButton mapVirtKeyToButton(UINT virtualKey,
|
||||
KeyButton& extended) const;
|
||||
|
||||
// same as above and discard extended
|
||||
KeyButton mapVirtKeyToButton(UINT virtualKey) const;
|
||||
|
||||
// map character \c c given keyboard layout \c hkl to the keystrokes
|
||||
// to generate it.
|
||||
KeyButton mapCharacter(Keystrokes& keys,
|
||||
|
|
|
@ -83,6 +83,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary,
|
|||
m_mark(0),
|
||||
m_markReceived(0),
|
||||
m_keyLayout(NULL),
|
||||
m_fixTimer(NULL),
|
||||
m_screensaver(NULL),
|
||||
m_screensaverNotify(false),
|
||||
m_nextClipboardWindow(NULL),
|
||||
|
@ -218,6 +219,13 @@ CMSWindowsScreen::disable()
|
|||
CArchMiscWindows::kDISPLAY);
|
||||
}
|
||||
|
||||
// uninstall fix key timer
|
||||
if (m_fixTimer != NULL) {
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
|
||||
EVENTQUEUE->deleteTimer(m_fixTimer);
|
||||
m_fixTimer = NULL;
|
||||
}
|
||||
|
||||
// stop snooping the clipboard
|
||||
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
||||
m_nextClipboardWindow = NULL;
|
||||
|
@ -680,6 +688,10 @@ CMSWindowsScreen::onPreDispatch(HWND hwnd,
|
|||
switch (message) {
|
||||
case SYNERGY_MSG_SCREEN_SAVER:
|
||||
return onScreensaver(wParam != 0);
|
||||
|
||||
case SYNERGY_MSG_DEBUG:
|
||||
LOG((CLOG_INFO "hook: 0x%08x 0x%08x", wParam, lParam));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_isPrimary) {
|
||||
|
@ -693,27 +705,6 @@ bool
|
|||
CMSWindowsScreen::onPreDispatchPrimary(HWND,
|
||||
UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
// fake a key release for the windows keys if we think they're
|
||||
// down but they're really up. we have to do this because if the
|
||||
// user presses and releases a windows key without pressing any
|
||||
// other key while it's down then the system will eat the key
|
||||
// release. if we don't detect that and synthesize the release
|
||||
// then the client won't take the usual windows key release action
|
||||
// (which on windows is to show the start menu).
|
||||
//
|
||||
// since the key could go up at any time we'll check the state on
|
||||
// every event. only check on the windows 95 family since the NT
|
||||
// family reports the key release as usual. obviously we skip
|
||||
// this if the event is for a windows key itself.
|
||||
if (m_is95Family && message != SYNERGY_MSG_KEY) {
|
||||
if (wParam != VK_LWIN) {
|
||||
m_keyState->fixKey(getEventTarget(), VK_LWIN);
|
||||
}
|
||||
if (wParam != VK_RWIN) {
|
||||
m_keyState->fixKey(getEventTarget(), VK_RWIN);
|
||||
}
|
||||
}
|
||||
|
||||
// handle event
|
||||
switch (message) {
|
||||
case SYNERGY_MSG_MARK:
|
||||
|
@ -840,22 +831,67 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
|
|||
{
|
||||
LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, lParam));
|
||||
|
||||
// update key state. ignore key repeats.
|
||||
// fix up key state
|
||||
fixKeys();
|
||||
|
||||
// get key info
|
||||
KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16);
|
||||
if ((lParam & 0xc0000000u) == 0x00000000) {
|
||||
bool down = ((lParam & 0xc0000000u) == 0x00000000u);
|
||||
bool up = ((lParam & 0x80000000u) == 0x80000000u);
|
||||
bool wasDown = isKeyDown(button);
|
||||
|
||||
// the windows keys are a royal pain on the windows 95 family.
|
||||
// the system eats the key up events if and only if the windows
|
||||
// key wasn't combined with another key, i.e. it was tapped.
|
||||
// fixKeys() and scheduleFixKeys() are all about synthesizing
|
||||
// the missing key up. but even windows itself gets a little
|
||||
// confused and sets bit 30 in lParam if you tap the windows
|
||||
// key twice. that bit means the key was previously down and
|
||||
// that makes some sense since the up event was missing.
|
||||
// anyway, on the windows 95 family we forget about windows
|
||||
// key repeats and treat anything that's not a key down as a
|
||||
// key up.
|
||||
if (m_is95Family &&
|
||||
((wParam & 0xffu) == VK_LWIN || (wParam & 0xffu) == VK_RWIN)) {
|
||||
down = !up;
|
||||
}
|
||||
|
||||
// update key state. ignore key repeats.
|
||||
if (down) {
|
||||
m_keyState->setKeyDown(button, true);
|
||||
}
|
||||
else if ((lParam & 0x80000000u) == 0x80000000) {
|
||||
else if (up) {
|
||||
m_keyState->setKeyDown(button, false);
|
||||
}
|
||||
|
||||
// schedule a timer if we need to fix keys later
|
||||
scheduleFixKeys();
|
||||
|
||||
// special case: we detect ctrl+alt+del being pressed on some
|
||||
// systems but we don't detect the release of those keys. so
|
||||
// if ctrl, alt, and del are down then mark them up.
|
||||
KeyModifierMask mask = getActiveModifiers();
|
||||
bool ctrlAlt = ((mask & (KeyModifierControl | KeyModifierAlt)) ==
|
||||
(KeyModifierControl | KeyModifierAlt));
|
||||
if (down && ctrlAlt &&
|
||||
isKeyDown(m_keyState->virtualKeyToButton(VK_DELETE))) {
|
||||
m_keyState->setKeyDown(
|
||||
m_keyState->virtualKeyToButton(VK_LCONTROL), false);
|
||||
m_keyState->setKeyDown(
|
||||
m_keyState->virtualKeyToButton(VK_RCONTROL), false);
|
||||
m_keyState->setKeyDown(
|
||||
m_keyState->virtualKeyToButton(VK_LMENU), false);
|
||||
m_keyState->setKeyDown(
|
||||
m_keyState->virtualKeyToButton(VK_RMENU), false);
|
||||
m_keyState->setKeyDown(
|
||||
m_keyState->virtualKeyToButton(VK_DELETE), false);
|
||||
}
|
||||
|
||||
// ignore message if posted prior to last mark change
|
||||
if (!ignore()) {
|
||||
// check for ctrl+alt+del emulation
|
||||
UINT virtKey = (wParam & 0xffu);
|
||||
if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) &&
|
||||
(m_keyState->getActiveModifiers() &
|
||||
(KeyModifierControl | KeyModifierAlt)) != 0) {
|
||||
if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) && ctrlAlt) {
|
||||
LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
|
||||
// switch wParam and lParam to be as if VK_DELETE was
|
||||
// pressed or released
|
||||
|
@ -870,9 +906,41 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
|
|||
KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask);
|
||||
button = static_cast<KeyButton>((lParam & 0x01ff0000u) >> 16);
|
||||
if (key != kKeyNone) {
|
||||
// fix up key. if the key isn't down according to
|
||||
// our table then we never got the key press event
|
||||
// for it. if it's not a modifier key then we'll
|
||||
// synthesize the press first. only do this on
|
||||
// the windows 95 family, which eats certain special
|
||||
// keys like alt+tab, ctrl+esc, etc.
|
||||
if (m_is95Family && !wasDown && up) {
|
||||
switch (virtKey) {
|
||||
case VK_LSHIFT:
|
||||
case VK_RSHIFT:
|
||||
case VK_SHIFT:
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
case VK_CONTROL:
|
||||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
case VK_MENU:
|
||||
case VK_CAPITAL:
|
||||
case VK_NUMLOCK:
|
||||
case VK_SCROLL:
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
break;
|
||||
|
||||
default:
|
||||
m_keyState->sendKeyEvent(getEventTarget(),
|
||||
((lParam & 0x80000000) == 0),
|
||||
((lParam & 0x40000000) == 1),
|
||||
true, false, key, mask, 1, button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// do it
|
||||
m_keyState->sendKeyEvent(getEventTarget(),
|
||||
((lParam & 0x80000000u) == 0),
|
||||
((lParam & 0x40000000u) == 1),
|
||||
key, mask, (SInt32)(lParam & 0xffff), button);
|
||||
}
|
||||
else {
|
||||
|
@ -1225,6 +1293,58 @@ CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::fixKeys()
|
||||
{
|
||||
// fake key releases for the windows keys if we think they're
|
||||
// down but they're really up. we have to do this because if the
|
||||
// user presses and releases a windows key without pressing any
|
||||
// other key while it's down then the system will eat the key
|
||||
// release. if we don't detect that and synthesize the release
|
||||
// then the client won't take the usual windows key release action
|
||||
// (which on windows is to show the start menu).
|
||||
//
|
||||
// only check on the windows 95 family since the NT family reports
|
||||
// the key releases as usual.
|
||||
if (m_is95Family) {
|
||||
m_keyState->fixKey(getEventTarget(), VK_LWIN);
|
||||
m_keyState->fixKey(getEventTarget(), VK_RWIN);
|
||||
|
||||
// check if we need the fix timer anymore
|
||||
scheduleFixKeys();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::scheduleFixKeys()
|
||||
{
|
||||
if (m_is95Family) {
|
||||
// see if any keys that need fixing are down
|
||||
bool fix =
|
||||
(m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_LWIN)) ||
|
||||
m_keyState->isKeyDown(m_keyState->virtualKeyToButton(VK_RWIN)));
|
||||
|
||||
// start or stop fix timer
|
||||
if (fix && m_fixTimer == NULL) {
|
||||
m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL);
|
||||
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer,
|
||||
new TMethodEventJob<CMSWindowsScreen>(
|
||||
this, &CMSWindowsScreen::handleFixKeys));
|
||||
}
|
||||
else if (!fix && m_fixTimer != NULL) {
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
|
||||
EVENTQUEUE->deleteTimer(m_fixTimer);
|
||||
m_fixTimer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::handleFixKeys(const CEvent&, void*)
|
||||
{
|
||||
fixKeys();
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::updateKeysCB(void*)
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class CEventQueueTimer;
|
||||
class CMSWindowsDesks;
|
||||
class CMSWindowsKeyState;
|
||||
class CMSWindowsScreenSaver;
|
||||
|
@ -156,6 +157,16 @@ private:
|
|||
// map a button event to a button ID
|
||||
ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const;
|
||||
|
||||
// fix the key state, synthesizing fake key releases for keys
|
||||
// that aren't down anymore.
|
||||
void fixKeys();
|
||||
|
||||
// (un)schedule a later call to fixKeys
|
||||
void scheduleFixKeys();
|
||||
|
||||
// event handler to fix the key state
|
||||
void handleFixKeys(const CEvent&, void*);
|
||||
|
||||
// job to update the key state
|
||||
void updateKeysCB(void*);
|
||||
|
||||
|
@ -200,6 +211,9 @@ private:
|
|||
// the keyboard layout to use when off primary screen
|
||||
HKL m_keyLayout;
|
||||
|
||||
// the timer used to check for fixing key state
|
||||
CEventQueueTimer* m_fixTimer;
|
||||
|
||||
// screen saver stuff
|
||||
CMSWindowsScreenSaver* m_screensaver;
|
||||
bool m_screensaverNotify;
|
||||
|
|
|
@ -94,6 +94,7 @@ static SInt32 g_wScreen = 0;
|
|||
static SInt32 g_hScreen = 0;
|
||||
static WPARAM g_deadVirtKey = 0;
|
||||
static LPARAM g_deadLParam = 0;
|
||||
static WPARAM g_oldDeadVirtKey = 0;
|
||||
static BYTE g_deadKeyState[256] = { 0 };
|
||||
static DWORD g_hookThread = 0;
|
||||
static DWORD g_attachedThread = 0;
|
||||
|
@ -185,21 +186,44 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
// check for dead keys. we don't forward those to our window.
|
||||
// instead we'll leave the key in the keyboard layout (a buffer
|
||||
// internal to the system) for translation when the next key is
|
||||
// pressed.
|
||||
// pressed. note that some systems set bit 31 to indicate a
|
||||
// dead key and others bit 15. nice.
|
||||
UINT c = MapVirtualKey(wParam, 2);
|
||||
if ((c & 0x80000000u) != 0) {
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x00000000, c);
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | (c << 8) | 0x01000000, lParam);
|
||||
if ((c & 0x80008000u) != 0) {
|
||||
if ((lParam & 0x80000000u) == 0) {
|
||||
if (g_deadVirtKey == 0) {
|
||||
// dead key press, no dead key in the buffer
|
||||
g_deadVirtKey = wParam;
|
||||
g_deadLParam = lParam;
|
||||
keyboardGetState(g_deadKeyState);
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x02000000, lParam);
|
||||
return false;
|
||||
}
|
||||
// second dead key press in a row so let it pass
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x03000000, lParam);
|
||||
}
|
||||
else if (wParam == g_oldDeadVirtKey) {
|
||||
// dead key release for second dead key in a row. discard
|
||||
// because we've already handled it. also take it out of
|
||||
// the keyboard buffer.
|
||||
g_oldDeadVirtKey = 0;
|
||||
WORD c;
|
||||
UINT scanCode = ((lParam & 0x00ff0000u) >> 16);
|
||||
ToAscii(wParam, scanCode, g_deadKeyState, &c, 0);
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x09000000, lParam);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// dead key release
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x04000000, lParam);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -256,6 +280,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
// alt are being used as individual modifiers rather than AltGr.
|
||||
// we have to put the dead key back first, if there was one.
|
||||
if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) {
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x05000000, lParam);
|
||||
if (g_deadVirtKey != 0) {
|
||||
ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16,
|
||||
g_deadKeyState, &c, flags);
|
||||
|
@ -269,6 +295,9 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
n = ToAscii(wParam, scanCode, keys, &c, flags);
|
||||
}
|
||||
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | (c << 8) | ((n & 0xff) << 16) | 0x06000000,
|
||||
lParam);
|
||||
switch (n) {
|
||||
default:
|
||||
// key is a dead key; we're not expecting this since we
|
||||
|
@ -307,6 +336,9 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
if (g_deadVirtKey != 0) {
|
||||
ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16,
|
||||
g_deadKeyState, &c, flags);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
g_deadKeyState[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// clear out old dead key state
|
||||
|
@ -318,12 +350,17 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
// forwarding events to clients because this'll keep our thread's
|
||||
// key state table up to date. that's important for querying
|
||||
// the scroll lock toggle state.
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
charAndVirtKey | 0x07000000, lParam);
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam);
|
||||
|
||||
// send fake key release if the user just pressed two dead keys
|
||||
// in a row, otherwise we'll lose the release because we always
|
||||
// return from the top of this function for all dead key releases.
|
||||
if ((c & 0x80000000u) != 0) {
|
||||
if ((c & 0x80008000u) != 0) {
|
||||
g_oldDeadVirtKey = wParam;
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
|
||||
wParam | 0x08000000, lParam);
|
||||
PostThreadMessage(g_threadID, SYNERGY_MSG_KEY,
|
||||
charAndVirtKey, lParam | 0x80000000u);
|
||||
}
|
||||
|
@ -342,6 +379,14 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
case VK_SHIFT:
|
||||
case VK_LSHIFT:
|
||||
case VK_RSHIFT:
|
||||
// pass the shift modifiers. if we don't do this
|
||||
// we may not get the right dead key when caps lock
|
||||
// is on. for example, on the french layout (with
|
||||
// english keycaps) on caps lock then press shift + [
|
||||
// and q. instead of an A with ^ above it you get an
|
||||
// A with dots above it.
|
||||
break;
|
||||
|
||||
case VK_CONTROL:
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
|
@ -349,8 +394,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
case VK_HANGUL:
|
||||
// always pass the shift modifiers
|
||||
break;
|
||||
// discard the control and alt modifiers
|
||||
return true;
|
||||
|
||||
default:
|
||||
// discard
|
||||
|
@ -787,12 +832,14 @@ install()
|
|||
g_hinstance,
|
||||
0);
|
||||
}
|
||||
#if !NO_GRAB_KEYBOARD
|
||||
if (g_keyboardLL == NULL) {
|
||||
g_keyboard = SetWindowsHookEx(WH_KEYBOARD,
|
||||
&keyboardHook,
|
||||
g_hinstance,
|
||||
0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// check that we got all the hooks we wanted
|
||||
if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) ||
|
||||
|
|
|
@ -38,9 +38,10 @@
|
|||
#define SYNERGY_MSG_POST_WARP WM_APP + 0x0016 // <unused>; <unused>
|
||||
#define SYNERGY_MSG_PRE_WARP WM_APP + 0x0017 // x; y
|
||||
#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; <unused>
|
||||
#define SYNERGY_MSG_DEBUG WM_APP + 0x0019 // data, data
|
||||
#define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY
|
||||
#define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP
|
||||
#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_SCREEN_SAVER
|
||||
#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_DEBUG
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
|
|
@ -33,10 +33,9 @@ public:
|
|||
|
||||
//! Mark key as being down
|
||||
/*!
|
||||
Sets the state of \p button to down or up. If this is overridden
|
||||
it must forward to the superclass.
|
||||
Sets the state of \p button to down or up.
|
||||
*/
|
||||
virtual void setKeyDown(KeyButton button, bool down);
|
||||
void setKeyDown(KeyButton button, bool down);
|
||||
|
||||
//! Mark modifier as being toggled on
|
||||
/*!
|
||||
|
|
Loading…
Reference in New Issue