win32 changes. replaced log dialog hack with a windows console

window.  now attaching thread input queues as necessary. shifted
code around so toggling toggle keys is immediately reflected by
secondary screen's keyboard.  now setting extended key flag for
keys that need it.  fixed handling of shift + caps-lock.  added
handling of keys that should distinguish between left and right
but don't.  fixed get/set of active window on leave/enter of
primary screen.  replaced 1x1 primary window with a full screen
window to work around a problem with losing key events.  changed
calculation of mouse move deltas.
This commit is contained in:
crs 2002-05-22 17:01:17 +00:00
parent d893cc16a8
commit a5ae8011e2
6 changed files with 371 additions and 277 deletions

View File

@ -1,5 +1,4 @@
#include "CLog.h"
#include "BasicTypes.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -139,14 +138,15 @@ void CLog::output(int priority, char* msg)
#endif
// print it
if (s_outputter)
if (s_outputter) {
s_outputter(msg + g_maxPriorityLength - n);
else
}
else {
#if defined(CONFIG_PLATFORM_WIN32)
OutputDebugString(msg + g_maxPriorityLength - n);
#else
fprintf(stderr, "%s", msg + g_maxPriorityLength - n);
openConsole();
#endif
fprintf(stderr, "%s", msg + g_maxPriorityLength - n);
}
}
}
@ -174,3 +174,63 @@ char* CLog::vsprint(int pad, char* buffer, int len,
return buffer;
}
#if defined(CONFIG_PLATFORM_WIN32)
static DWORD s_thread = 0;
static BOOL WINAPI CLogSignalHandler(DWORD)
{
// terminate cleanly and skip remaining handlers
PostThreadMessage(s_thread, WM_QUIT, 0, 0);
return TRUE;
}
void CLog::openConsole()
{
static bool s_hasConsole = false;
// ignore if already created
if (s_hasConsole)
return;
// remember the current thread. when we get a ctrl+break or the
// console is closed we'll post WM_QUIT to this thread to shutdown
// cleanly.
// note -- win95/98/me are broken and will not receive a signal
// when the console is closed nor during logoff or shutdown,
// see microsoft articles Q130717 and Q134284. we could work
// around this in a painful way using hooks and hidden windows
// (as apache does) but it's not worth it. the app will still
// quit, just not cleanly. users in-the-know can use ctrl+c.
s_thread = GetCurrentThreadId();
// open a console
if (!AllocConsole())
return;
// get the handle for error output
HANDLE herr = GetStdHandle(STD_ERROR_HANDLE);
// prep console. windows 95 and its ilk have braindead
// consoles that can't even resize independently of the
// buffer size. use a 25 line buffer for those systems.
OSVERSIONINFO osInfo;
COORD size = { 80, 1000 };
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
if (GetVersionEx(&osInfo) &&
osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
size.Y = 25;
SetConsoleScreenBufferSize(herr, size);
SetConsoleTextAttribute(herr,
FOREGROUND_RED |
FOREGROUND_GREEN |
FOREGROUND_BLUE);
SetConsoleCtrlHandler(CLogSignalHandler, TRUE);
// reopen stderr to point at console
freopen("con", "w", stderr);
s_hasConsole = true;
}
#endif

View File

@ -1,6 +1,7 @@
#ifndef CLOG_H
#define CLOG_H
#include "BasicTypes.h"
#include <stdarg.h>
class CLog {
@ -15,6 +16,9 @@ private:
static void output(int priority, char* msg);
static char* vsprint(int pad, char*, int len, const char*, va_list);
static int nprint(const char*, va_list);
#if defined(CONFIG_PLATFORM_WIN32)
static void openConsole();
#endif
private:
static Outputter s_outputter;

View File

@ -24,49 +24,18 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
assert(m_window == NULL);
}
static CString s_log;
static CString s_logMore;
static HWND s_debug = NULL;
static HWND s_debugLog = NULL;
static DWORD s_thread = 0;
static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
return TRUE;
case WM_CLOSE:
PostQuitMessage(0);
return TRUE;
case WM_APP:
if (!s_logMore.empty()) {
if (s_log.size() > 40000)
s_log = s_logMore;
else
s_log += s_logMore;
s_logMore = "";
SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str());
SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size());
SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0);
}
return TRUE;
}
return FALSE;
}
static void debugOutput(const char* msg)
{
s_logMore += msg;
PostMessage(s_debug, WM_APP, 0, 0);
}
void CMSWindowsSecondaryScreen::run()
{
CLog::setOutputter(&debugOutput);
// change our priority
CThread::getCurrentThread().setPriority(-7);
// save thread id
m_threadID = GetCurrentThreadId();
// run event loop
log((CLOG_INFO "entering event loop"));
doRun();
log((CLOG_INFO "exiting event loop"));
CLog::setOutputter(NULL);
}
void CMSWindowsSecondaryScreen::stop()
@ -117,6 +86,24 @@ void CMSWindowsSecondaryScreen::enter(
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
// attach thread input queues
AttachThreadInput(GetCurrentThreadId(), m_threadID, TRUE);
// update our keyboard state to reflect the local state
updateKeys();
updateModifiers();
// toggle modifiers that don't match the desired state
if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) {
toggleKey(VK_CAPITAL, KeyModifierCapsLock);
}
if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) {
toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock);
}
if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) {
toggleKey(VK_SCROLL, KeyModifierScrollLock);
}
// warp to requested location
SInt32 w, h;
getScreenSize(&w, &h);
@ -128,21 +115,6 @@ void CMSWindowsSecondaryScreen::enter(
// show cursor
log((CLOG_INFO "show cursor"));
ShowWindow(m_window, SW_HIDE);
// update our keyboard state to reflect the local state
updateKeys();
updateModifiers();
// toggle modifiers that don't match the desired state
if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) {
toggleKey(VK_CAPITAL, KeyModifierCapsLock);
}
if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) {
toggleKey(VK_NUMLOCK, KeyModifierNumLock);
}
if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) {
toggleKey(VK_SCROLL, KeyModifierScrollLock);
}
}
void CMSWindowsSecondaryScreen::leave()
@ -363,19 +335,10 @@ void CMSWindowsSecondaryScreen::getClipboard(
CClipboard::copy(dst, &src);
}
#include "resource.h" // FIXME
void CMSWindowsSecondaryScreen::onOpenDisplay()
{
assert(m_window == NULL);
// create debug dialog
s_thread = GetCurrentThreadId();;
s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc);
s_debugLog = ::GetDlgItem(s_debug, IDC_LOG);
CLog::setOutputter(&debugOutput);
ShowWindow(s_debug, SW_SHOWNORMAL);
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
@ -410,22 +373,13 @@ void CMSWindowsSecondaryScreen::onCloseDisplay()
// destroy window
DestroyWindow(m_window);
m_window = NULL;
CLog::setOutputter(NULL);
DestroyWindow(s_debug);
s_debug = NULL;
s_thread = 0;
}
bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
{
if (IsDialogMessage(s_debug, msg)) {
return true;
}
return false;
}
LRESULT CMSWindowsSecondaryScreen::onEvent(
HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
@ -467,9 +421,14 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// these tables map KeyID (a X windows KeySym) to virtual key codes.
// if the key is an extended key then the entry is the virtual key
// code | 0x100. keys that map to normal characters have a 0 entry
// and the conversion is done elsewhere.
static const UINT g_latin1[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
@ -859,7 +818,7 @@ static const UINT g_function[] =
static const UINT g_miscellany[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ VK_BACK, VK_TAB, /*0x100 +*/ VK_RETURN, VK_CLEAR, 0, VK_RETURN, 0, 0,
/* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0,
/* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0,
/* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
@ -868,21 +827,22 @@ static const UINT g_miscellany[] =
/* 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|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100,
/* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, 0, 0, 0, VK_APPS,
/* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0,
/* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100,
/* 0x64 */ 0, 0, 0, VK_APPS|0x100,
/* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE, VK_NUMLOCK,
/* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE|0x100, VK_NUMLOCK|0x100,
/* 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|0x100, 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_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE,
/* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100,
/* 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,
@ -891,10 +851,10 @@ static const UINT g_miscellany[] =
/* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 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, VK_LWIN,
/* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0,
/* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, VK_LWIN|0x100,
/* 0xe8 */ VK_RWIN|0x100, VK_LMENU, VK_RMENU|0x100, 0, 0, 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|0x100
};
static const UINT* g_katakana = NULL;
static const UINT* g_arabic = NULL;
@ -1001,6 +961,10 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
KeyModifierMeta));
log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey));
// extract extended key flag
const bool isExtended = ((virtualKey & 0x100) != 0);
virtualKey &= ~0x100;
// if not in map then ask system to convert ascii character
if (virtualKey == 0) {
if (mapID != 0) {
@ -1010,7 +974,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
}
// translate. return no keys if unknown key.
SHORT vk = VkKeyScan(code);
SHORT vk = VkKeyScan(static_cast<TCHAR>(code));
if (vk == 0xffff) {
log((CLOG_DEBUG2 "no virtual key for character %d", code));
return m_mask;
@ -1033,24 +997,12 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
// handle combination of caps-lock and shift. if caps-lock is
// off locally then use shift as necessary. if caps-lock is on
// locally then shift reverses its meaning (for keys that are
// subject to case conversion).
if ((m_mask & KeyModifierCapsLock) != 0) {
// caps-lock is on. note if shift is required.
log((CLOG_DEBUG2 "caps-lock is on"));
const bool needShift = ((outMask & KeyModifierShift) != 0);
// if needShift is true then see if the key is subject to
// case conversion. if it is then caps-lock and shift
// cancel out. if not then caps-lock is ignored and shift
// is required.
if (needShift) {
// FIXME -- there should be some system call to test
// if a key is subject to case conversion.
// locally then it reverses the meaning of shift for keys that
// are subject to case conversion.
if ((outMask & KeyModifierCapsLock) != 0) {
if (tolower(code) != toupper(code)) {
log((CLOG_DEBUG2 "turn off shift"));
outMask &= ~KeyModifierShift;
}
log((CLOG_DEBUG2 "flip shift"));
outMask ^= KeyModifierShift;
}
}
@ -1097,12 +1049,12 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
};
static const CModifierInfo s_modifier[] = {
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL,false },
{ KeyModifierAlt, VK_LMENU, VK_RMENU, false },
{ KeyModifierMeta, VK_LWIN, VK_RWIN, false },
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false },
{ KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false },
{ KeyModifierMeta, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
{ KeyModifierNumLock, VK_NUMLOCK, 0, true },
{ KeyModifierScrollLock, VK_SCROLL, 0, true }
{ KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true },
{ KeyModifierScrollLock,VK_SCROLL, 0, true }
};
static const unsigned int s_numModifiers =
sizeof(s_modifier) / sizeof(s_modifier[0]);
@ -1203,7 +1155,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
}
else {
UINT key = s_modifier[i].m_virtualKey;
if ((m_keys[key] & 0x80) != 0) {
if ((m_keys[key & 0xff] & 0x80) != 0) {
keystroke.m_virtualKey = key;
keystroke.m_press = false;
keystroke.m_repeat = false;
@ -1212,7 +1164,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
undo.push_back(keystroke);
}
key = s_modifier[i].m_virtualKey2;
if (key != 0 && (m_keys[key] & 0x80) != 0) {
if (key != 0 && (m_keys[key & 0xff] & 0x80) != 0) {
keystroke.m_virtualKey = key;
keystroke.m_press = false;
keystroke.m_repeat = false;
@ -1227,23 +1179,23 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
}
// add the key event
keystroke.m_virtualKey = virtualKey;
if (isExtended)
keystroke.m_virtualKey |= 0x100;
switch (action) {
case kPress:
keystroke.m_virtualKey = virtualKey;
keystroke.m_press = true;
keystroke.m_repeat = false;
keys.push_back(keystroke);
break;
case kRelease:
keystroke.m_virtualKey = virtualKey;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
break;
case kRepeat:
keystroke.m_virtualKey = virtualKey;
keystroke.m_press = true;
keystroke.m_repeat = true;
keys.push_back(keystroke);
@ -1276,13 +1228,13 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
// can't reset bit until all keys that set it are released.
// scan those keys to see if any are pressed.
bool down = false;
if (virtualKey != modifier.m_virtualKey &&
(m_keys[modifier.m_virtualKey] & 0x80) != 0) {
if (virtualKey != (modifier.m_virtualKey & 0xff) &&
(m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) {
down = true;
}
if (modifier.m_virtualKey2 != 0 &&
virtualKey != modifier.m_virtualKey2 &&
(m_keys[modifier.m_virtualKey2] & 0x80) != 0) {
virtualKey != (modifier.m_virtualKey2 & 0xff) &&
(m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) {
down = true;
}
if (!down)
@ -1310,9 +1262,7 @@ void CMSWindowsSecondaryScreen::doKeystrokes(
for (; count > 0; --count) {
// send repeating events
for (k = start; k != keys.end() && k->m_repeat; ++k) {
const UINT code = MapVirtualKey(k->m_virtualKey, 0);
keybd_event(k->m_virtualKey, code,
k->m_press ? 0 : KEYEVENTF_KEYUP, 0);
sendKeyEvent(k->m_virtualKey, k->m_press);
}
}
@ -1321,9 +1271,7 @@ void CMSWindowsSecondaryScreen::doKeystrokes(
}
else {
// send event
const UINT code = MapVirtualKey(k->m_virtualKey, 0);
keybd_event(k->m_virtualKey, code,
k->m_press ? 0 : KEYEVENTF_KEYUP, 0);
sendKeyEvent(k->m_virtualKey, k->m_press);
// next key
++k;
@ -1333,22 +1281,25 @@ void CMSWindowsSecondaryScreen::doKeystrokes(
void CMSWindowsSecondaryScreen::updateKeys()
{
// GetKeyboardKeys() doesn't seem to do the expected thing
// clear key state
memset(m_keys, 0, sizeof(m_keys));
m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT);
m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL);
m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL);
m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL);
m_keys[VK_LMENU] = GetKeyState(VK_LMENU);
m_keys[VK_RMENU] = GetKeyState(VK_RMENU);
m_keys[VK_MENU] = GetKeyState(VK_MENU);
m_keys[VK_LWIN] = GetKeyState(VK_LWIN);
m_keys[VK_RWIN] = GetKeyState(VK_RWIN);
m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK);
m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL);
// we only care about the modifier key states
m_keys[VK_LSHIFT] = static_cast<BYTE>(GetKeyState(VK_LSHIFT));
m_keys[VK_RSHIFT] = static_cast<BYTE>(GetKeyState(VK_RSHIFT));
m_keys[VK_SHIFT] = static_cast<BYTE>(GetKeyState(VK_SHIFT));
m_keys[VK_LCONTROL] = static_cast<BYTE>(GetKeyState(VK_LCONTROL));
m_keys[VK_RCONTROL] = static_cast<BYTE>(GetKeyState(VK_RCONTROL));
m_keys[VK_CONTROL] = static_cast<BYTE>(GetKeyState(VK_CONTROL));
m_keys[VK_LMENU] = static_cast<BYTE>(GetKeyState(VK_LMENU));
m_keys[VK_RMENU] = static_cast<BYTE>(GetKeyState(VK_RMENU));
m_keys[VK_MENU] = static_cast<BYTE>(GetKeyState(VK_MENU));
m_keys[VK_LWIN] = static_cast<BYTE>(GetKeyState(VK_LWIN));
m_keys[VK_RWIN] = static_cast<BYTE>(GetKeyState(VK_RWIN));
m_keys[VK_APPS] = static_cast<BYTE>(GetKeyState(VK_APPS));
m_keys[VK_CAPITAL] = static_cast<BYTE>(GetKeyState(VK_CAPITAL));
m_keys[VK_NUMLOCK] = static_cast<BYTE>(GetKeyState(VK_NUMLOCK));
m_keys[VK_SCROLL] = static_cast<BYTE>(GetKeyState(VK_SCROLL));
}
void CMSWindowsSecondaryScreen::updateModifiers()
@ -1376,11 +1327,97 @@ void CMSWindowsSecondaryScreen::toggleKey(
UINT virtualKey, KeyModifierMask mask)
{
// send key events to simulate a press and release
const UINT code = MapVirtualKey(virtualKey, 0);
keybd_event(virtualKey, code, 0, 0);
keybd_event(virtualKey, code, KEYEVENTF_KEYUP, 0);
sendKeyEvent(virtualKey, true);
sendKeyEvent(virtualKey, false);
// toggle shadow state
m_mask ^= mask;
m_keys[virtualKey] ^= 0x01;
m_keys[virtualKey & 0xff] ^= 0x01;
}
UINT CMSWindowsSecondaryScreen::virtualKeyToScanCode(
UINT& virtualKey)
{
// try mapping given virtual key
UINT code = MapVirtualKey(virtualKey & 0xff, 0);
if (code != 0)
return code;
// no dice. if the virtual key distinguishes between left/right
// then try the one that doesn't distinguish sides. windows (or
// keyboard drivers) are inconsistent in their treatment of these
// virtual keys. the following behaviors have been observed:
//
// win2k (gateway desktop):
// MapVirtualKey(vk, 0):
// VK_SHIFT == VK_LSHIFT != VK_RSHIFT
// VK_CONTROL == VK_LCONTROL == VK_RCONTROL
// VK_MENU == VK_LMENU == VK_RMENU
// MapVirtualKey(sc, 3):
// VK_LSHIFT and VK_RSHIFT mapped independently
// VK_LCONTROL is mapped but not VK_RCONTROL
// VK_LMENU is mapped but not VK_RMENU
//
// 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)
switch (virtualKey & 0xff) {
case VK_LSHIFT:
case VK_RSHIFT:
virtualKey = VK_SHIFT;
return MapVirtualKey(VK_SHIFT, 0);
case VK_LCONTROL:
case VK_RCONTROL:
virtualKey = VK_CONTROL;
return MapVirtualKey(VK_CONTROL, 0);
case VK_LMENU:
case VK_RMENU:
virtualKey = VK_MENU;
return MapVirtualKey(VK_MENU, 0);
default:
return 0;
}
}
bool CMSWindowsSecondaryScreen::isExtendedKey(
UINT virtualKey)
{
// see if we've already encoded the extended flag
if ((virtualKey & 0x100) != 0)
return true;
// check known virtual keys
switch (virtualKey & 0xff) {
case VK_NUMLOCK:
case VK_RCONTROL:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
case VK_APPS:
return true;
default:
return false;
}
}
void CMSWindowsSecondaryScreen::sendKeyEvent(
UINT virtualKey, bool press)
{
DWORD flags = 0;
if (isExtendedKey(virtualKey))
flags |= KEYEVENTF_EXTENDEDKEY;
if (!press)
flags |= KEYEVENTF_KEYUP;
const UINT code = virtualKeyToScanCode(virtualKey);
keybd_event(static_cast<BYTE>(virtualKey & 0xff),
static_cast<BYTE>(code), flags, 0);
log((CLOG_DEBUG2 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : "")));
}

View File

@ -57,6 +57,9 @@ private:
void updateKeys();
void updateModifiers();
void toggleKey(UINT virtualKey, KeyModifierMask mask);
UINT virtualKeyToScanCode(UINT& virtualKey);
bool isExtendedKey(UINT virtualKey);
void sendKeyEvent(UINT virtualKey, bool press);
private:
CClient* m_client;
@ -64,6 +67,9 @@ private:
HWND m_nextClipboardWindow;
HWND m_clipboardOwner;
// thread id of the event loop thread
DWORD m_threadID;
// virtual key states
BYTE m_keys[256];

View File

@ -31,47 +31,15 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
assert(m_hookLibrary == NULL);
}
static CString s_log;
static CString s_logMore;
static HWND s_debug = NULL;
static HWND s_debugLog = NULL;
static DWORD s_thread = 0;
static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
return TRUE;
case WM_CLOSE:
PostQuitMessage(0);
return TRUE;
case WM_APP:
if (!s_logMore.empty()) {
if (s_log.size() > 20000)
s_log = s_logMore;
else
s_log += s_logMore;
s_logMore = "";
SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)s_log.c_str());
SendMessage(s_debugLog, EM_SETSEL, s_log.size(), s_log.size());
SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0);
}
return TRUE;
}
return FALSE;
}
static void debugOutput(const char* msg)
{
s_logMore += msg;
PostMessage(s_debug, WM_APP, 0, 0);
}
void CMSWindowsPrimaryScreen::run()
{
CLog::setOutputter(&debugOutput);
// change our priority
CThread::getCurrentThread().setPriority(-3);
// run event loop
log((CLOG_INFO "entering event loop"));
doRun();
CLog::setOutputter(NULL);
log((CLOG_INFO "exiting event loop"));
}
void CMSWindowsPrimaryScreen::stop()
@ -87,12 +55,12 @@ void CMSWindowsPrimaryScreen::open(CServer* server)
// set the server
m_server = server;
// get keyboard state
updateKeys();
// open the display
openDisplay();
// get keyboard state
updateKeys();
// enter the screen
doEnter();
}
@ -113,7 +81,6 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
log((CLOG_INFO "entering primary at %d,%d", x, y));
assert(m_active == true);
// do non-warp enter stuff
doEnter();
// warp to requested location
@ -122,12 +89,8 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
void CMSWindowsPrimaryScreen::doEnter()
{
// release the capture
ReleaseCapture();
// hide our window and restore the foreground window
SetForegroundWindow(m_lastActive);
ShowWindow(m_window, SW_HIDE);
// not active anymore
m_active = false;
// set the zones that should cause a jump
SInt32 w, h;
@ -136,11 +99,23 @@ void CMSWindowsPrimaryScreen::doEnter()
m_hookLibrary, "setZone");
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
// restore the active window and hide our window. we can only set
// the active window for another thread if we first attach our input
// to that thread.
ReleaseCapture();
if (m_lastActiveWindow != NULL) {
DWORD myThread = GetCurrentThreadId();
if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) {
// FIXME -- shouldn't raise window if X-Mouse is enabled
// but i have no idea how to do that or check if enabled.
SetActiveWindow(m_lastActiveWindow);
AttachThreadInput(myThread, m_lastActiveThread, FALSE);
}
}
ShowWindow(m_window, SW_HIDE);
// all messages prior to now are invalid
nextMark();
// not active anymore
m_active = false;
}
void CMSWindowsPrimaryScreen::leave()
@ -148,30 +123,50 @@ void CMSWindowsPrimaryScreen::leave()
log((CLOG_INFO "leaving primary"));
assert(m_active == false);
// do non-warp enter stuff
// get state of keys as we leave
updateKeys();
// all messages prior to now are invalid
nextMark();
// remember the active window before we leave
m_lastActive = GetForegroundWindow();
// remember the active window before we leave. GetActiveWindow()
// will only return the active window for the thread's queue (i.e.
// our app) but we need the globally active window. get that by
// attaching input to the foreground window's thread then calling
// GetActiveWindow() and then detaching our input.
m_lastActiveWindow = NULL;
m_lastForegroundWindow = GetForegroundWindow();
m_lastActiveThread = GetWindowThreadProcessId(
m_lastForegroundWindow, NULL);
if (m_lastActiveThread != 0) {
DWORD myThread = GetCurrentThreadId();
if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) {
m_lastActiveWindow = GetActiveWindow();
AttachThreadInput(myThread, m_lastActiveThread, FALSE);
}
}
// show our window and put it in the foreground
// show our window
ShowWindow(m_window, SW_SHOW);
SetForegroundWindow(m_window);
// capture the cursor so we don't lose keyboard input
SetCapture(m_window);
// relay all mouse and keyboard events
SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress(
m_hookLibrary, "setRelay");
setRelay();
// warp the mouse to the center of the screen
SInt32 w, h;
getScreenSize(&w, &h);
warpCursor(w >> 1, h >> 1);
// get keyboard input and capture mouse
SetActiveWindow(m_window);
SetFocus(m_window);
SetCapture(m_window);
// warp is also invalid
// warp the mouse to the center of the screen
getScreenSize(&m_xCenter, &m_yCenter);
m_xCenter >>= 1;
m_yCenter >>= 1;
warpCursor(m_xCenter, m_yCenter);
// discard messages until after the warp
nextMark();
// local client now active
@ -205,12 +200,8 @@ void CMSWindowsPrimaryScreen::leave()
void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
{
SInt32 w, h;
getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65535.99 * x) / (w - 1)),
(DWORD)((65535.99 * y) / (h - 1)),
0, 0);
// set the cursor position without generating an event
SetCursorPos(x, y);
}
void CMSWindowsPrimaryScreen::setClipboard(
@ -254,7 +245,7 @@ void CMSWindowsPrimaryScreen::getClipboard(
KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const
{
KeyModifierMask mask;
KeyModifierMask mask = 0;
if ((m_keys[VK_CAPITAL] & 0x01) != 0)
mask |= KeyModifierCapsLock;
if ((m_keys[VK_NUMLOCK] & 0x01) != 0)
@ -264,30 +255,30 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const
return mask;
}
#include "resource.h" // FIXME
void CMSWindowsPrimaryScreen::onOpenDisplay()
{
assert(m_window == NULL);
assert(m_server != NULL);
// create debug dialog
s_thread = GetCurrentThreadId();;
s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc);
s_debugLog = ::GetDlgItem(s_debug, IDC_LOG);
CLog::setOutputter(&debugOutput);
ShowWindow(s_debug, SW_SHOWNORMAL);
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
// get screen size
// note -- we use a fullscreen window to grab input. it should
// be possible to use a 1x1 window but i've run into problems
// with losing keyboard input (focus?) in that case.
// unfortunately, hiding the full screen window causes all other
// windows to redraw.
SInt32 w, h;
getScreenSize(&w, &h);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
0, 0, w, h, NULL, NULL,
getInstance(),
NULL);
assert(m_window != NULL);
@ -307,6 +298,7 @@ ShowWindow(s_debug, SW_SHOWNORMAL);
}
}
if (!hooked) {
log((CLOG_ERR "failed to install hooks"));
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
DestroyWindow(m_window);
@ -342,19 +334,10 @@ void CMSWindowsPrimaryScreen::onCloseDisplay()
// destroy window
DestroyWindow(m_window);
m_window = NULL;
CLog::setOutputter(NULL);
DestroyWindow(s_debug);
s_debug = NULL;
s_thread = 0;
}
bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
{
if (IsDialogMessage(s_debug, msg)) {
return true;
}
// handle event
switch (msg->message) {
case SYNERGY_MSG_MARK:
@ -391,7 +374,9 @@ if (IsDialogMessage(s_debug, msg)) {
updateKey(msg->wParam, false);
}
}
else {
log((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam));
}
}
return true;
@ -431,31 +416,18 @@ if (IsDialogMessage(s_debug, msg)) {
m_server->onMouseMovePrimary(x, y);
}
else {
// get mouse deltas
x -= m_xCenter;
y -= m_yCenter;
log((CLOG_DEBUG2 "event: active move %d,%d", x, y));
// get screen size
SInt32 w, h;
getScreenSize(&w, &h);
// get center pixel
w >>= 1;
h >>= 1;
// ignore and discard message if motion is to center of
// screen. those are caused by us warping the mouse.
if (x != w || y != h) {
// get mouse deltas
x -= w;
y -= h;
// warp mouse back to center
warpCursor(w, h);
warpCursor(m_xCenter, m_yCenter);
// send motion
m_server->onMouseMoveSecondary(x, y);
}
}
}
return true;
}
@ -467,7 +439,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent(
WPARAM wParam, LPARAM lParam)
{
switch (msg) {
// FIXME -- handle display changes
// FIXME -- handle display changes (and resize full-screen window)
case WM_PAINT:
ValidateRect(hwnd, NULL);
return 0;
@ -500,6 +472,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent(
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
@ -808,6 +781,7 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
if ((m_keys[VK_SCROLL] & 0x01) != 0)
mask |= KeyModifierScrollLock;
*maskOut = mask;
log((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask));
// get the scan code
UINT scanCode = static_cast<UINT>((info & 0xff0000) >> 16);
@ -827,6 +801,10 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
else if (vkCode >= VK_LWIN && vkCode <= VK_APPS)
vkCode2 = vkCode;
// if MapVirtualKey failed then use original virtual key
else if (vkCode2 == 0)
vkCode2 = vkCode;
// sadly, win32 will not distinguish between the left and right
// control and alt keys using the above function. however, we
// can check for those: if bit 24 of info is set then the key
@ -834,10 +812,12 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
// keys.
if ((info & 0x1000000) != 0) {
switch (vkCode2) {
case VK_CONTROL:
case VK_LCONTROL:
vkCode2 = VK_RCONTROL;
break;
case VK_MENU:
case VK_LMENU:
vkCode2 = VK_RMENU;
break;
@ -898,7 +878,7 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
else if (result == 2) {
// get the scan code of the dead key and the shift state
// required to generate it.
vkCode = VkKeyScan(ascii & 0x00ff);
vkCode = VkKeyScan(static_cast<TCHAR>(ascii & 0x00ff));
// set shift state required to generate key
BYTE keys[256];
@ -948,25 +928,29 @@ ButtonID CMSWindowsPrimaryScreen::mapButton(
void CMSWindowsPrimaryScreen::updateKeys()
{
// not using GetKeyboardState() because that doesn't seem to give
// up-to-date results. i don't know why that is or why GetKeyState()
// should give different results.
// clear key state
memset(m_keys, 0, sizeof(m_keys));
// we only care about the modifier key states
m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT);
m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL);
m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL);
m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL);
m_keys[VK_LMENU] = GetKeyState(VK_LMENU);
m_keys[VK_RMENU] = GetKeyState(VK_RMENU);
m_keys[VK_MENU] = GetKeyState(VK_MENU);
m_keys[VK_LWIN] = GetKeyState(VK_LWIN);
m_keys[VK_RWIN] = GetKeyState(VK_RWIN);
m_keys[VK_APPS] = GetKeyState(VK_APPS);
m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK);
m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL);
m_keys[VK_LSHIFT] = static_cast<BYTE>(GetKeyState(VK_LSHIFT));
m_keys[VK_RSHIFT] = static_cast<BYTE>(GetKeyState(VK_RSHIFT));
m_keys[VK_SHIFT] = static_cast<BYTE>(GetKeyState(VK_SHIFT));
m_keys[VK_LCONTROL] = static_cast<BYTE>(GetKeyState(VK_LCONTROL));
m_keys[VK_RCONTROL] = static_cast<BYTE>(GetKeyState(VK_RCONTROL));
m_keys[VK_CONTROL] = static_cast<BYTE>(GetKeyState(VK_CONTROL));
m_keys[VK_LMENU] = static_cast<BYTE>(GetKeyState(VK_LMENU));
m_keys[VK_RMENU] = static_cast<BYTE>(GetKeyState(VK_RMENU));
m_keys[VK_MENU] = static_cast<BYTE>(GetKeyState(VK_MENU));
m_keys[VK_LWIN] = static_cast<BYTE>(GetKeyState(VK_LWIN));
m_keys[VK_RWIN] = static_cast<BYTE>(GetKeyState(VK_RWIN));
m_keys[VK_APPS] = static_cast<BYTE>(GetKeyState(VK_APPS));
m_keys[VK_CAPITAL] = static_cast<BYTE>(GetKeyState(VK_CAPITAL));
m_keys[VK_NUMLOCK] = static_cast<BYTE>(GetKeyState(VK_NUMLOCK));
m_keys[VK_SCROLL] = static_cast<BYTE>(GetKeyState(VK_SCROLL));
}
void CMSWindowsPrimaryScreen::updateKey(

View File

@ -52,11 +52,14 @@ private:
HWND m_window;
HWND m_nextClipboardWindow;
HWND m_clipboardOwner;
HWND m_lastActive;
HWND m_lastForegroundWindow;
HWND m_lastActiveWindow;
DWORD m_lastActiveThread;
HINSTANCE m_hookLibrary;
UInt32 m_mark;
UInt32 m_markReceived;
BYTE m_keys[256];
SInt32 m_xCenter, m_yCenter;
};
#endif