Win32 fixes. Fixed slightly off cursor positioning when using

absolute mouse_event().  Improved keyboard handling:  now using
keyboard layout of last foreground window when leaving server
so users can meaningfully choose the locale, moved dead key
handling into hook library so there should be no more race
conditions involving the keyboard dead key buffer, simplified
keyboard and cursor handling by using a full screen transparent
window when not using low level hooks, fixed error in restoring
buffered dead key when checking for dead keys.  This hopefully
fixes all known keyboard bugs on win32.
This commit is contained in:
crs 2004-03-08 20:53:32 +00:00
parent f068232643
commit 9e995bedbf
6 changed files with 353 additions and 277 deletions

View File

@ -706,7 +706,7 @@ const KeyButton CMSWindowsKeyMapper::s_mapEF00[] =
CMSWindowsKeyMapper::CMSWindowsKeyMapper() : m_deadKey(0)
{
// do nothing
m_keyLayout = GetKeyboardLayout(0);
}
CMSWindowsKeyMapper::~CMSWindowsKeyMapper()
@ -872,6 +872,12 @@ CMSWindowsKeyMapper::updateKey(KeyButton key, bool pressed)
}
}
void
CMSWindowsKeyMapper::setKeyLayout(HKL keyLayout)
{
m_keyLayout = keyLayout;
}
KeyButton
CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys,
const IKeyState& keyState, KeyID id,
@ -1057,8 +1063,8 @@ CMSWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys,
}
KeyID
CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info,
KeyModifierMask* maskOut, bool* altgr) const
CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM charAndVirtKey,
LPARAM info, KeyModifierMask* maskOut, bool* altgr) const
{
// note: known microsoft bugs
// Q72583 -- MapVirtualKey() maps keypad keys incorrectly
@ -1066,59 +1072,18 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info,
// 95,98,NT4: num pad scan code -> bad vk code except
// SEPARATOR, MULTIPLY, SUBTRACT, ADD
HKL hkl = GetKeyboardLayout(0);
char c = (char)((charAndVirtKey & 0xff00u) >> 8);
UINT vkCode = (charAndVirtKey & 0xffu);
// get the scan code and the extended keyboard flag
UINT scanCode = static_cast<UINT>((info & 0x00ff0000u) >> 16);
int extended = ((info & 0x01000000) == 0) ? 0 : 1;
bool press = ((info & 0x80000000) == 0);
LOG((CLOG_DEBUG1 "key vk=%d info=0x%08x ext=%d scan=%d", vkCode, info, extended, scanCode));
// handle some keys via table lookup
char c = 0;
KeyID id = s_virtualKey[vkCode][extended];
if (id == kKeyNone) {
// not in table
// save the control state then clear it. ToAscii() maps ctrl+letter
// to the corresponding control code and ctrl+backspace to delete.
// we don't want that translation so we clear the control modifier
// state. however, if we want to simulate AltGr (which is ctrl+alt)
// then we must not clear it.
BYTE keys[256];
memcpy(keys, m_keys, sizeof(keys));
BYTE control = keys[VK_CONTROL];
BYTE menu = keys[VK_MENU];
if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
keys[VK_LCONTROL] = 0;
keys[VK_RCONTROL] = 0;
keys[VK_CONTROL] = 0;
}
else {
keys[VK_LCONTROL] = 0x80;
keys[VK_CONTROL] = 0x80;
keys[VK_LMENU] = 0x80;
keys[VK_MENU] = 0x80;
}
// map to a character
bool isMenu = ((menu & 0x80) != 0);
c = mapToCharacter(vkCode, scanCode, keys, press, isMenu, hkl);
// if mapping failed and ctrl and alt are pressed then try again
// with both not pressed. this handles the case where ctrl and
// alt are being used as individual modifiers rather than AltGr.
if (c == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) {
keys[VK_LCONTROL] = 0;
keys[VK_RCONTROL] = 0;
keys[VK_CONTROL] = 0;
keys[VK_LMENU] = 0;
keys[VK_RMENU] = 0;
keys[VK_MENU] = 0;
c = mapToCharacter(vkCode, scanCode, keys, press, isMenu, hkl);
}
// map character to key id
// not in table; map character to key id
if (c != 0) {
if ((c & 0x80u) != 0) {
// character is not really ASCII. instead it's some
@ -1155,7 +1120,7 @@ CMSWindowsKeyMapper::mapKeyFromEvent(WPARAM vkCode, LPARAM info,
// required (only because it solves the problems we've seen
// so far). in the second, we'll use whatever the keyboard
// state says.
WORD virtualKeyAndModifierState = VkKeyScanEx(c, hkl);
WORD virtualKeyAndModifierState = VkKeyScanEx(c, m_keyLayout);
if (virtualKeyAndModifierState == 0xffff) {
// there is no mapping. assume AltGr.
LOG((CLOG_DEBUG1 "no VkKeyScan() mapping"));
@ -1233,8 +1198,7 @@ UINT
CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const
{
// try mapping given virtual key
HKL hkl = GetKeyboardLayout(0);
UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, hkl);
UINT code = MapVirtualKeyEx((*virtualKey) & 0xffu, 0, m_keyLayout);
if (code != 0) {
return code;
}
@ -1265,17 +1229,17 @@ CMSWindowsKeyMapper::keyToScanCode(KeyButton* virtualKey) const
case VK_LSHIFT:
case VK_RSHIFT:
*virtualKey = VK_SHIFT;
return MapVirtualKeyEx(VK_SHIFT, 0, hkl);
return MapVirtualKeyEx(VK_SHIFT, 0, m_keyLayout);
case VK_LCONTROL:
case VK_RCONTROL:
*virtualKey = VK_CONTROL;
return MapVirtualKeyEx(VK_CONTROL, 0, hkl);
return MapVirtualKeyEx(VK_CONTROL, 0, m_keyLayout);
case VK_LMENU:
case VK_RMENU:
*virtualKey = VK_MENU;
return MapVirtualKeyEx(VK_MENU, 0, hkl);
return MapVirtualKeyEx(VK_MENU, 0, m_keyLayout);
default:
return 0;
@ -1528,93 +1492,9 @@ CMSWindowsKeyMapper::isDeadChar(TCHAR c, HKL hkl, bool menu) const
toAscii(' ', hkl, 0, &dummy);
// put old dead key back if there was one
if (old == 2) {
if (old == 1 && ascii != ' ') {
toAscii(static_cast<TCHAR>(ascii & 0xffu), hkl, menu, &dummy);
}
return isDead;
}
bool
CMSWindowsKeyMapper::putBackDeadChar(TCHAR c, HKL hkl, bool menu) const
{
return (toAscii(c, hkl, menu, NULL) < 0);
}
TCHAR
CMSWindowsKeyMapper::getSavedDeadChar(HKL hkl) const
{
WORD old;
int nOld = toAscii(' ', hkl, false, &old);
if (nOld == 1 || nOld == 2) {
TCHAR c = static_cast<TCHAR>(old & 0xffu);
if (nOld == 2 || isDeadChar(c, hkl, false)) {
return c;
}
}
return 0;
}
char
CMSWindowsKeyMapper::mapToCharacter(UINT vkCode, UINT scanCode,
BYTE* keys, bool press, bool isMenu, HKL hkl) const
{
// get contents of keyboard layout buffer and clear out that
// buffer. we don't want anything placed there by some other
// app interfering and we need to put anything there back in
// place when we're done.
TCHAR oldDeadKey = getSavedDeadChar(hkl);
// put our previous dead key, if any, in the layout buffer
putBackDeadChar(m_deadKey, hkl, false);
m_deadKey = 0;
// process key
WORD ascii;
int result = ToAsciiEx(vkCode, scanCode, keys, &ascii,
isMenu ? 1 : 0, hkl);
// if result is less than zero then it was a dead key
char c = 0;
if (result < 0) {
// save dead key if a key press. we catch the dead key
// release in the result == 2 case below.
if (press) {
m_deadKey = static_cast<TCHAR>(ascii & 0xffu);
}
}
// if result is 1 then the key was succesfully converted
else if (result == 1) {
c = static_cast<char>(ascii & 0xff);
}
// if result is 2 and the two characters are the same and this
// is a key release then a dead key was released. save the
// dead key. if the two characters are the same and this is
// not a release then a dead key was pressed twice. send the
// dead key.
else if (result == 2) {
if (((ascii & 0xff00u) >> 8) == (ascii & 0x00ffu)) {
if (!press) {
m_deadKey = static_cast<TCHAR>(ascii & 0xffu);
}
else {
putBackDeadChar(oldDeadKey, hkl, false);
result = toAscii(' ', hkl, false, &ascii);
c = static_cast<char>((ascii >> 8) & 0xffu);
}
}
}
// clear keyboard layout buffer. this removes any dead key we
// may have just put there.
toAscii(' ', hkl, false, NULL);
// restore keyboard layout buffer so a dead key inserted by
// another app doesn't disappear mysteriously (from its point
// of view).
putBackDeadChar(oldDeadKey, hkl, false);
return c;
}

View File

@ -44,6 +44,13 @@ public:
*/
void updateKey(KeyButton key, bool pressed);
//! Set the active keyboard layout
/*!
Uses \p keyLayout when finding scan codes via \c keyToScanCode()
and mapping keys in \c mapKeyFromEvent().
*/
void setKeyLayout(HKL keyLayout);
//@}
//! @name accessors
//@{
@ -67,7 +74,7 @@ public:
to a modifier mask. If \c altgr is non-NULL it's set to true if
the key requires AltGr and false otherwise.
*/
KeyID mapKeyFromEvent(WPARAM vkCode, LPARAM info,
KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info,
KeyModifierMask* maskOut, bool* altgr) const;
//! Test shadow key state
@ -127,19 +134,6 @@ private:
// return true iff \c c is a dead character
bool isDeadChar(TCHAR c, HKL hkl, bool menu) const;
// put back dead key into ToAscii() internal buffer. returns true
// iff the character was a dead key.
bool putBackDeadChar(TCHAR c, HKL hkl, bool menu) const;
// get the dead key saved in the given keyboard layout, or 0 if none
TCHAR getSavedDeadChar(HKL hkl) const;
// map the given virtual key, scan code, and keyboard state to a
// character, if possible. this has the side effect of updating
// m_deadKey.
char mapToCharacter(UINT vkCode, UINT scanCode,
BYTE* keys, bool press, bool isMenu, HKL hkl) const;
private:
class CModifierKeys {
public:
@ -150,6 +144,7 @@ private:
BYTE m_keys[256];
mutable TCHAR m_deadKey;
HKL m_keyLayout;
static const CModifierKeys s_modifiers[];
static const char* s_vkToName[];

View File

@ -128,6 +128,7 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) :
m_sequenceNumber(0),
m_mark(0),
m_markReceived(0),
m_keyLayout(NULL),
m_timer(NULL),
m_screensaver(NULL),
m_screensaverNotify(false),
@ -290,7 +291,17 @@ CMSWindowsScreen::enter()
bool
CMSWindowsScreen::leave()
{
sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0);
// get keyboard layout of foreground window. we'll use this
// keyboard layout for translating keys sent to clients.
HWND window = GetForegroundWindow();
DWORD thread = GetWindowThreadProcessId(window, NULL);
m_keyLayout = GetKeyboardLayout(thread);
// tell the key mapper about the keyboard layout
m_keyMapper.setKeyLayout(m_keyLayout);
// tell desk that we're leaving and tell it the keyboard layout
sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0);
if (m_isPrimary) {
/* XXX
@ -543,7 +554,7 @@ CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const
if (!press) {
flags |= KEYEVENTF_KEYUP;
}
const UINT code = m_keyMapper.keyToScanCode(&virtualKey);
UINT code = m_keyMapper.keyToScanCode(&virtualKey);
sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags,
MAKEWORD(static_cast<BYTE>(code),
static_cast<BYTE>(virtualKey & 0xffu)));
@ -977,6 +988,9 @@ CMSWindowsScreen::onMark(UInt32 mark)
bool
CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
{
WPARAM charAndVirtKey = wParam;
wParam &= 0xffu;
// ignore message if posted prior to last mark change
if (!ignore()) {
// check for ctrl+alt+del emulation
@ -992,8 +1006,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
// process key normally
bool altgr;
KeyModifierMask mask;
const KeyID key = m_keyMapper.mapKeyFromEvent(wParam,
lParam, &mask, &altgr);
const KeyID key = m_keyMapper.mapKeyFromEvent(
charAndVirtKey, lParam, &mask, &altgr);
KeyButton button = static_cast<KeyButton>(
(lParam & 0x00ff0000u) >> 16);
if (key != kKeyNone && key != kKeyMultiKey) {
@ -1654,8 +1668,8 @@ CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const
SInt32 w = GetSystemMetrics(SM_CXSCREEN);
SInt32 h = GetSystemMetrics(SM_CYSCREEN);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65536.0 * x) / w),
(DWORD)((65536.0 * y) / h),
(DWORD)((65535.0f * x) / (w - 1) + 0.5f),
(DWORD)((65535.0f * y) / (h - 1) + 0.5f),
0, 0);
}
@ -1712,43 +1726,45 @@ CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const
}
void
CMSWindowsScreen::deskEnter(CDesk* desk, DWORD& cursorThreadID)
CMSWindowsScreen::deskEnter(CDesk* desk)
{
if (m_isPrimary) {
if (desk->m_lowLevel) {
if (cursorThreadID != 0) {
AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE);
ShowCursor(TRUE);
AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE);
cursorThreadID = 0;
}
}
}
ShowWindow(desk->m_window, SW_HIDE);
}
void
CMSWindowsScreen::deskLeave(CDesk* desk, DWORD& cursorThreadID)
CMSWindowsScreen::deskLeave(CDesk* desk, HKL keyLayout)
{
if (m_isPrimary) {
// we don't need a window to capture input but we need a window
// to hide the cursor when using low-level hooks. also take the
// activation so we use our keyboard layout, not the layout of
// whatever window was active.
// map a window to hide the cursor and to use whatever keyboard
// layout we choose rather than the keyboard layout of the last
// active window.
int x, y, w, h;
if (desk->m_lowLevel) {
SetWindowPos(desk->m_window, HWND_TOPMOST,
m_xCenter, m_yCenter, 1, 1, SWP_NOACTIVATE);
// with a low level hook the cursor will never budge so
// just a 1x1 window is sufficient.
x = m_xCenter;
y = m_yCenter;
w = 1;
h = 1;
}
else {
// with regular hooks the cursor will jitter as it's moved
// by the user then back to the center by us. to be sure
// we never lose it, cover all the monitors with the window.
x = m_x;
y = m_y;
w = m_w;
h = m_h;
}
SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE);
ShowWindow(desk->m_window, SW_SHOW);
if (cursorThreadID == 0) {
HWND hwnd = GetForegroundWindow();
cursorThreadID = GetWindowThreadProcessId(hwnd, NULL);
if (cursorThreadID != 0) {
AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE);
ShowCursor(FALSE);
AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE);
}
}
}
// switch to requested keyboard layout
ActivateKeyboardLayout(keyLayout, 0);
}
else {
// move hider window under the cursor center
@ -1767,9 +1783,6 @@ CMSWindowsScreen::deskThread(void* vdesk)
{
MSG msg;
// id of thread that had cursor when we were last told to hide it
DWORD cursorThreadID = 0;
// use given desktop for this thread
CDesk* desk = reinterpret_cast<CDesk*>(vdesk);
desk->m_threadID = GetCurrentThreadId();
@ -1833,11 +1846,11 @@ CMSWindowsScreen::deskThread(void* vdesk)
break;
case SYNERGY_MSG_ENTER:
deskEnter(desk, cursorThreadID);
deskEnter(desk);
break;
case SYNERGY_MSG_LEAVE:
deskLeave(desk, cursorThreadID);
deskLeave(desk, (HKL)msg.wParam);
break;
case SYNERGY_MSG_FAKE_KEY:
@ -1969,7 +1982,7 @@ CMSWindowsScreen::checkDesk()
// hide cursor on new desk
if (!m_isOnScreen) {
sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0);
sendDeskMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0);
}
}
}

View File

@ -186,8 +186,8 @@ private:
static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM);
void deskMouseMove(SInt32 x, SInt32 y) const;
void deskEnter(CDesk* desk, DWORD& cursorThreadID);
void deskLeave(CDesk* desk, DWORD& cursorThreadID);
void deskEnter(CDesk* desk);
void deskLeave(CDesk* desk, HKL keyLayout);
void deskThread(void* vdesk);
CDesk* addDesk(const CString& name, HDESK hdesk);
void removeDesks();
@ -234,6 +234,9 @@ private:
// the main loop's thread id
DWORD m_threadID;
// the keyboard layout to use when off primary screen
HKL m_keyLayout;
// the timer used to check for desktop switching
CEventQueueTimer* m_timer;

View File

@ -24,6 +24,12 @@
//
#define NO_GRAB_KEYBOARD 0
//
// debugging compile flag. when not zero the server will not
// install low level hooks.
//
#define NO_LOWLEVEL_HOOKS 0
//
// extra mouse wheel stuff
//
@ -86,8 +92,11 @@ static SInt32 g_xScreen = 0;
static SInt32 g_yScreen = 0;
static SInt32 g_wScreen = 0;
static SInt32 g_hScreen = 0;
static HCURSOR g_cursor = NULL;
static DWORD g_cursorThread = 0;
static WPARAM g_deadVirtKey = 0;
static LPARAM g_deadLParam = 0;
static BYTE g_deadKeyState[256] = { 0 };
static DWORD g_hookThread = 0;
static DWORD g_attachedThread = 0;
#pragma data_seg()
@ -105,49 +114,217 @@ extern "C" int _fltused=0;
static
void
hideCursor(DWORD thread)
attachThreadToForeground()
{
// we should be running the context of the window who's cursor
// we want to hide so we shouldn't have to attach thread input
// but we'll check to make sure.
g_cursorThread = thread;
if (g_cursorThread != 0) {
DWORD myThread = GetCurrentThreadId();
if (myThread != g_cursorThread)
AttachThreadInput(myThread, g_cursorThread, TRUE);
g_cursor = SetCursor(NULL);
if (myThread != g_cursorThread)
AttachThreadInput(myThread, g_cursorThread, FALSE);
// only attach threads if using low level hooks. a low level hook
// runs in the thread that installed the hook but we have to make
// changes that require being attached to the target thread (which
// should be the foreground window). a regular hook runs in the
// thread that just removed the event from its queue so we're
// already in the right thread.
if (g_hookThread != 0) {
HWND window = GetForegroundWindow();
DWORD threadID = GetWindowThreadProcessId(window, NULL);
// skip if no change
if (g_attachedThread != threadID) {
// detach from previous thread
if (g_attachedThread != 0 && g_attachedThread != g_hookThread) {
AttachThreadInput(g_hookThread, g_attachedThread, FALSE);
}
// attach to new thread
g_attachedThread = threadID;
if (g_attachedThread != 0 && g_attachedThread != g_hookThread) {
AttachThreadInput(g_hookThread, g_attachedThread, TRUE);
}
}
}
}
static
void
restoreCursor()
detachThread()
{
// restore the show cursor in the window we hid it last
if (g_cursor != NULL && g_cursorThread != 0) {
DWORD myThread = GetCurrentThreadId();
if (myThread != g_cursorThread)
AttachThreadInput(myThread, g_cursorThread, TRUE);
SetCursor(g_cursor);
if (myThread != g_cursorThread)
AttachThreadInput(myThread, g_cursorThread, FALSE);
if (g_attachedThread != 0) {
AttachThreadInput(g_hookThread, g_attachedThread, FALSE);
g_attachedThread = 0;
}
g_cursor = NULL;
g_cursorThread = 0;
}
#if !NO_GRAB_KEYBOARD
static
WPARAM
makeKeyMsg(UINT virtKey, char c)
{
return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), 0);
}
static
void
keyboardGetState(BYTE keys[256])
{
if (g_hookThread != 0) {
GetKeyboardState(keys);
}
else {
SHORT key;
for (int i = 0; i < 256; ++i) {
key = GetAsyncKeyState(i);
keys[i] = (BYTE)((key < 0) ? 0x80u : 0);
}
key = GetKeyState(VK_CAPITAL);
keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1));
}
}
static
bool
keyboardHookHandler(WPARAM wParam, LPARAM lParam)
{
attachThreadToForeground();
// 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.
UINT c = MapVirtualKey(wParam, 2);
if ((c & 0x80000000u) != 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);
return false;
}
// second dead key press in a row so let it pass
}
else {
// dead key release
return false;
}
}
// convert key to a character. this combines a saved dead key,
// if any, with this key. however, the dead key must remain in
// the keyboard layout for the application receiving this event
// so it can also convert the key to a character. we only do
// this on a key press.
WPARAM charAndVirtKey = (wParam & 0xffu);
if (c != 0) {
// we need the keyboard state for ToAscii()
BYTE keys[256];
keyboardGetState(keys);
// ToAscii() maps ctrl+letter to the corresponding control code
// and ctrl+backspace to delete. we don't want those translations
// so clear the control modifier state. however, if we want to
// simulate AltGr (which is ctrl+alt) then we must not clear it.
BYTE control = keys[VK_CONTROL];
BYTE menu = keys[VK_MENU];
if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
keys[VK_LCONTROL] = 0;
keys[VK_RCONTROL] = 0;
keys[VK_CONTROL] = 0;
}
else {
keys[VK_LCONTROL] = 0x80;
keys[VK_CONTROL] = 0x80;
keys[VK_LMENU] = 0x80;
keys[VK_MENU] = 0x80;
}
// ToAscii() needs to know if a menu is active for some reason.
// we don't know and there doesn't appear to be any way to find
// out. so we'll just assume a menu is active if the menu key
// is down.
// XXX -- figure out some way to check if a menu is active
UINT flags = 0;
if ((menu & 0x80) != 0)
flags |= 1;
// map the key event to a character. this has the side
// effect of removing the dead key from the system's keyboard
// layout buffer.
WORD c = 0;
UINT scanCode = ((lParam & 0x00ff0000u) >> 16);
int n = ToAscii(wParam, scanCode, keys, &c, flags);
// if mapping failed and ctrl and alt are pressed then try again
// with both not pressed. this handles the case where ctrl and
// 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) {
if (g_deadVirtKey != 0) {
ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16,
g_deadKeyState, &c, flags);
}
keys[VK_LCONTROL] = 0;
keys[VK_RCONTROL] = 0;
keys[VK_CONTROL] = 0;
keys[VK_LMENU] = 0;
keys[VK_RMENU] = 0;
keys[VK_MENU] = 0;
n = ToAscii(wParam, scanCode, keys, &c, flags);
}
switch (n) {
default:
// key is a dead key; we're not expecting this since we
// bailed out above for any dead key.
g_deadVirtKey = wParam;
g_deadLParam = lParam;
break;
case 0:
// key doesn't map to a character. this can happen if
// non-character keys are pressed after a dead key.
break;
case 1:
// key maps to a character composed with dead key
charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c));
break;
case 2: {
// previous dead key not composed. send a fake key press
// and release for the dead key to our window.
WPARAM deadCharAndVirtKey =
makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c));
PostThreadMessage(g_threadID, SYNERGY_MSG_KEY,
deadCharAndVirtKey, g_deadLParam & 0x7fffffffu);
PostThreadMessage(g_threadID, SYNERGY_MSG_KEY,
deadCharAndVirtKey, g_deadLParam | 0x80000000u);
// use uncomposed character
charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c));
break;
}
}
// put back the dead key, if any, for the application to use
if (g_deadVirtKey != 0) {
ToAscii(g_deadVirtKey, (g_deadLParam & 0x00ff0000u) >> 16,
g_deadKeyState, &c, flags);
}
// clear out old dead key state
g_deadVirtKey = 0;
g_deadLParam = 0;
}
// forward message to our window. do this whether or not we're
// 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_KEY, wParam, 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) {
PostThreadMessage(g_threadID, SYNERGY_MSG_KEY,
charAndVirtKey, lParam | 0x80000000u);
}
if (g_mode == kHOOK_RELAY_EVENTS) {
// let certain keys pass through
@ -160,8 +337,21 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
// lights may not stay synchronized.
break;
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
case VK_HANGUL:
// always pass the shift modifiers
break;
default:
// discard event
// discard
return true;
}
}
@ -174,6 +364,8 @@ static
bool
mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
{
attachThreadToForeground();
switch (wParam) {
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
@ -213,19 +405,6 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
case WM_NCMOUSEMOVE:
case WM_MOUSEMOVE:
if (g_mode == kHOOK_RELAY_EVENTS) {
// we want the cursor to be hidden at all times so we
// hide the cursor on whatever window has it. but then
// we have to show the cursor whenever we leave that
// window (or at some later time before we stop relaying).
// so check the window with the cursor. if it's not the
// same window that had it before then show the cursor
// in the last window and hide it in this window.
DWORD thread = GetCurrentThreadId();
if (thread != g_cursorThread) {
restoreCursor();
hideCursor(thread);
}
// relay and eat event
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return true;
@ -347,7 +526,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(g_getMessage, code, wParam, lParam);
}
#if (_WIN32_WINNT >= 0x0400)
#if (_WIN32_WINNT >= 0x0400) && !NO_LOWLEVEL_HOOKS
//
// low-level keyboard hook -- this allows us to capture and handle
@ -531,8 +710,6 @@ init(DWORD threadID)
g_yScreen = 0;
g_wScreen = 0;
g_hScreen = 0;
g_cursor = NULL;
g_cursorThread = 0;
return 1;
}
@ -562,6 +739,10 @@ install()
return kHOOK_FAILED;
}
// discard old dead keys
g_deadVirtKey = 0;
g_deadLParam = 0;
// check for mouse wheel support
g_wheelSupport = getWheelSupport();
@ -573,35 +754,43 @@ install()
0);
}
// install keyboard hook
#if !NO_GRAB_KEYBOARD
#if (_WIN32_WINNT >= 0x0400)
g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
&keyboardLLHook,
g_hinstance,
0);
#endif
if (g_keyboardLL == NULL) {
g_keyboard = SetWindowsHookEx(WH_KEYBOARD,
&keyboardHook,
g_hinstance,
0);
}
#endif
// install mouse hook
#if (_WIN32_WINNT >= 0x0400)
// install low-level hooks. we require that they both get installed.
#if (_WIN32_WINNT >= 0x0400) && !NO_LOWLEVEL_HOOKS
g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL,
&mouseLLHook,
g_hinstance,
0);
#if !NO_GRAB_KEYBOARD
g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
&keyboardLLHook,
g_hinstance,
0);
if (g_mouseLL == NULL || g_keyboardLL == NULL) {
if (g_keyboardLL != NULL) {
UnhookWindowsHookEx(g_keyboardLL);
g_keyboardLL = NULL;
}
if (g_mouseLL != NULL) {
UnhookWindowsHookEx(g_mouseLL);
g_mouseLL = NULL;
}
}
#endif
#endif
// install regular hooks
if (g_mouseLL == NULL) {
g_mouse = SetWindowsHookEx(WH_MOUSE,
&mouseHook,
g_hinstance,
0);
}
if (g_keyboardLL == NULL) {
g_keyboard = SetWindowsHookEx(WH_KEYBOARD,
&keyboardHook,
g_hinstance,
0);
}
// check that we got all the hooks we wanted
if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) ||
@ -614,6 +803,7 @@ install()
}
if (g_keyboardLL != NULL || g_mouseLL != NULL) {
g_hookThread = GetCurrentThreadId();
return kHOOK_OKAY_LL;
}
@ -625,6 +815,13 @@ uninstall(void)
{
assert(g_hinstance != NULL);
// discard old dead keys
g_deadVirtKey = 0;
g_deadLParam = 0;
// detach from thread
detachThread();
// uninstall hooks
if (g_keyboardLL != NULL) {
UnhookWindowsHookEx(g_keyboardLL);
@ -648,9 +845,6 @@ uninstall(void)
}
g_wheelSupport = kWheelNone;
// show the cursor
restoreCursor();
return 1;
}
@ -719,9 +913,6 @@ setMode(EHookMode mode)
return;
}
g_mode = mode;
if (g_mode != kHOOK_RELAY_EVENTS) {
restoreCursor();
}
}
}

View File

@ -1153,7 +1153,6 @@ void
CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button)
{
LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button));
LOG((CLOG_INFO "onKeyDown: id=%d mask=0x%04x button=0x%04x", id, mask, button));
assert(m_active != NULL);
// handle command keys
@ -1169,7 +1168,6 @@ void
CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button)
{
LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button));
LOG((CLOG_INFO "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button));
assert(m_active != NULL);
// handle command keys
@ -1186,7 +1184,6 @@ CServer::onKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
LOG((CLOG_INFO "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
assert(m_active != NULL);
// handle command keys
@ -1225,7 +1222,10 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y));
// mouse move on primary (server's) screen
assert(m_active == m_primaryClient);
if (m_active != m_primaryClient) {
// stale event -- we're actually on a secondary screen
return false;
}
// save position
m_x = x;
@ -1282,13 +1282,7 @@ CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
// mouse move on secondary (client's) screen
assert(m_active != NULL);
if (m_active == m_primaryClient) {
// we're actually on the primary screen. this can happen
// when the primary screen begins processing a mouse move
// for a secondary screen, then the active (secondary)
// screen disconnects causing us to jump to the primary
// screen, and finally the primary screen finishes
// processing the mouse move, still thinking it's for
// a secondary screen. we just ignore the motion.
// stale event -- we're actually on the primary screen
return;
}