Fixed keyboard handling on windows 95 family.

This commit is contained in:
crs 2004-03-28 14:07:58 +00:00
parent e50146119f
commit 97046541b9
8 changed files with 519 additions and 209 deletions

View File

@ -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;

View File

@ -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 {
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;
}
KeyButton button2;
KeyButton button = mapVirtKeyToButton(i, button2);
if (button == 0) {
continue;
}
// 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

View File

@ -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,

View File

@ -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(),
true, false, key, mask, 1, button);
break;
}
}
// do it
m_keyState->sendKeyEvent(getEventTarget(),
((lParam & 0x80000000) == 0),
((lParam & 0x40000000) == 1),
((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*)
{

View File

@ -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;

View File

@ -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) ||

View File

@ -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" {

View File

@ -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
/*!