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:
parent
d893cc16a8
commit
a5ae8011e2
|
@ -1,5 +1,4 @@
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
#include "BasicTypes.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -139,14 +138,15 @@ void CLog::output(int priority, char* msg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// print it
|
// print it
|
||||||
if (s_outputter)
|
if (s_outputter) {
|
||||||
s_outputter(msg + g_maxPriorityLength - n);
|
s_outputter(msg + g_maxPriorityLength - n);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
#if defined(CONFIG_PLATFORM_WIN32)
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
OutputDebugString(msg + g_maxPriorityLength - n);
|
openConsole();
|
||||||
#else
|
|
||||||
fprintf(stderr, "%s", msg + g_maxPriorityLength - n);
|
|
||||||
#endif
|
#endif
|
||||||
|
fprintf(stderr, "%s", msg + g_maxPriorityLength - n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,3 +174,63 @@ char* CLog::vsprint(int pad, char* buffer, int len,
|
||||||
|
|
||||||
return buffer;
|
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
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef CLOG_H
|
#ifndef CLOG_H
|
||||||
#define CLOG_H
|
#define CLOG_H
|
||||||
|
|
||||||
|
#include "BasicTypes.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
class CLog {
|
class CLog {
|
||||||
|
@ -15,6 +16,9 @@ private:
|
||||||
static void output(int priority, char* msg);
|
static void output(int priority, char* msg);
|
||||||
static char* vsprint(int pad, char*, int len, const char*, va_list);
|
static char* vsprint(int pad, char*, int len, const char*, va_list);
|
||||||
static int nprint(const char*, va_list);
|
static int nprint(const char*, va_list);
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
static void openConsole();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Outputter s_outputter;
|
static Outputter s_outputter;
|
||||||
|
|
|
@ -24,49 +24,18 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
|
||||||
assert(m_window == NULL);
|
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()
|
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"));
|
log((CLOG_INFO "entering event loop"));
|
||||||
doRun();
|
doRun();
|
||||||
log((CLOG_INFO "exiting event loop"));
|
log((CLOG_INFO "exiting event loop"));
|
||||||
CLog::setOutputter(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMSWindowsSecondaryScreen::stop()
|
void CMSWindowsSecondaryScreen::stop()
|
||||||
|
@ -117,6 +86,24 @@ void CMSWindowsSecondaryScreen::enter(
|
||||||
|
|
||||||
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
|
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
|
// warp to requested location
|
||||||
SInt32 w, h;
|
SInt32 w, h;
|
||||||
getScreenSize(&w, &h);
|
getScreenSize(&w, &h);
|
||||||
|
@ -128,21 +115,6 @@ void CMSWindowsSecondaryScreen::enter(
|
||||||
// show cursor
|
// show cursor
|
||||||
log((CLOG_INFO "show cursor"));
|
log((CLOG_INFO "show cursor"));
|
||||||
ShowWindow(m_window, SW_HIDE);
|
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()
|
void CMSWindowsSecondaryScreen::leave()
|
||||||
|
@ -363,19 +335,10 @@ void CMSWindowsSecondaryScreen::getClipboard(
|
||||||
CClipboard::copy(dst, &src);
|
CClipboard::copy(dst, &src);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "resource.h" // FIXME
|
|
||||||
|
|
||||||
void CMSWindowsSecondaryScreen::onOpenDisplay()
|
void CMSWindowsSecondaryScreen::onOpenDisplay()
|
||||||
{
|
{
|
||||||
assert(m_window == NULL);
|
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
|
// initialize clipboard owner to current owner. we don't want
|
||||||
// to take ownership of the clipboard just by starting up.
|
// to take ownership of the clipboard just by starting up.
|
||||||
m_clipboardOwner = GetClipboardOwner();
|
m_clipboardOwner = GetClipboardOwner();
|
||||||
|
@ -410,22 +373,13 @@ void CMSWindowsSecondaryScreen::onCloseDisplay()
|
||||||
// destroy window
|
// destroy window
|
||||||
DestroyWindow(m_window);
|
DestroyWindow(m_window);
|
||||||
m_window = NULL;
|
m_window = NULL;
|
||||||
|
|
||||||
CLog::setOutputter(NULL);
|
|
||||||
DestroyWindow(s_debug);
|
|
||||||
s_debug = NULL;
|
|
||||||
s_thread = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
|
bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
|
||||||
{
|
{
|
||||||
if (IsDialogMessage(s_debug, msg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LRESULT CMSWindowsSecondaryScreen::onEvent(
|
LRESULT CMSWindowsSecondaryScreen::onEvent(
|
||||||
HWND hwnd, UINT msg,
|
HWND hwnd, UINT msg,
|
||||||
WPARAM wParam, LPARAM lParam)
|
WPARAM wParam, LPARAM lParam)
|
||||||
|
@ -467,9 +421,14 @@ LRESULT CMSWindowsSecondaryScreen::onEvent(
|
||||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
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[] =
|
static const UINT g_latin1[] =
|
||||||
{
|
{
|
||||||
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
@ -859,7 +818,7 @@ static const UINT g_function[] =
|
||||||
static const UINT g_miscellany[] =
|
static const UINT g_miscellany[] =
|
||||||
{
|
{
|
||||||
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
/* 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,
|
/* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0,
|
||||||
/* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0,
|
/* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0,
|
||||||
/* 0x20 */ 0, 0, 0, 0, 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,
|
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 0x40 */ 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,
|
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 0x50 */ VK_HOME, VK_LEFT, VK_UP, VK_RIGHT,
|
/* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100,
|
||||||
/* 0x54 */ VK_DOWN, VK_PRIOR, VK_NEXT, VK_END,
|
/* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100,
|
||||||
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 0x60 */ VK_SELECT, VK_SNAPSHOT, VK_EXECUTE, VK_INSERT, 0, 0, 0, VK_APPS,
|
/* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100,
|
||||||
/* 0x68 */ 0, 0, VK_HELP, VK_CANCEL, 0, 0, 0, 0,
|
/* 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,
|
/* 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,
|
/* 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,
|
/* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP,
|
||||||
/* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT,
|
/* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT,
|
||||||
/* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE,
|
/* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE,
|
||||||
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD,
|
/* 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,
|
/* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3,
|
||||||
/* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
/* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
||||||
/* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2,
|
/* 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,
|
/* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0,
|
||||||
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL,
|
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL,
|
||||||
/* 0xe4 */ VK_RCONTROL, VK_CAPITAL, 0, VK_LWIN,
|
/* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, VK_LWIN|0x100,
|
||||||
/* 0xe8 */ VK_RWIN, VK_LMENU, VK_RMENU, 0, 0, 0, 0, 0,
|
/* 0xe8 */ VK_RWIN|0x100, VK_LMENU, VK_RMENU|0x100, 0, 0, 0, 0, 0,
|
||||||
/* 0xf0 */ 0, 0, 0, 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_katakana = NULL;
|
||||||
static const UINT* g_arabic = NULL;
|
static const UINT* g_arabic = NULL;
|
||||||
|
@ -1001,6 +961,10 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
KeyModifierMeta));
|
KeyModifierMeta));
|
||||||
log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey));
|
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 not in map then ask system to convert ascii character
|
||||||
if (virtualKey == 0) {
|
if (virtualKey == 0) {
|
||||||
if (mapID != 0) {
|
if (mapID != 0) {
|
||||||
|
@ -1010,7 +974,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate. return no keys if unknown key.
|
// translate. return no keys if unknown key.
|
||||||
SHORT vk = VkKeyScan(code);
|
SHORT vk = VkKeyScan(static_cast<TCHAR>(code));
|
||||||
if (vk == 0xffff) {
|
if (vk == 0xffff) {
|
||||||
log((CLOG_DEBUG2 "no virtual key for character %d", code));
|
log((CLOG_DEBUG2 "no virtual key for character %d", code));
|
||||||
return m_mask;
|
return m_mask;
|
||||||
|
@ -1033,24 +997,12 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
|
|
||||||
// handle combination of caps-lock and shift. if caps-lock is
|
// handle combination of caps-lock and shift. if caps-lock is
|
||||||
// off locally then use shift as necessary. if caps-lock is on
|
// off locally then use shift as necessary. if caps-lock is on
|
||||||
// locally then shift reverses its meaning (for keys that are
|
// locally then it reverses the meaning of shift for keys that
|
||||||
// subject to case conversion).
|
// are subject to case conversion.
|
||||||
if ((m_mask & KeyModifierCapsLock) != 0) {
|
if ((outMask & 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.
|
|
||||||
if (tolower(code) != toupper(code)) {
|
if (tolower(code) != toupper(code)) {
|
||||||
log((CLOG_DEBUG2 "turn off shift"));
|
log((CLOG_DEBUG2 "flip shift"));
|
||||||
outMask &= ~KeyModifierShift;
|
outMask ^= KeyModifierShift;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,11 +1049,11 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
};
|
};
|
||||||
static const CModifierInfo s_modifier[] = {
|
static const CModifierInfo s_modifier[] = {
|
||||||
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
|
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
|
||||||
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL,false },
|
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false },
|
||||||
{ KeyModifierAlt, VK_LMENU, VK_RMENU, false },
|
{ KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false },
|
||||||
{ KeyModifierMeta, VK_LWIN, VK_RWIN, false },
|
{ KeyModifierMeta, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
|
||||||
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
|
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
|
||||||
{ KeyModifierNumLock, VK_NUMLOCK, 0, true },
|
{ KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true },
|
||||||
{ KeyModifierScrollLock,VK_SCROLL, 0, true }
|
{ KeyModifierScrollLock,VK_SCROLL, 0, true }
|
||||||
};
|
};
|
||||||
static const unsigned int s_numModifiers =
|
static const unsigned int s_numModifiers =
|
||||||
|
@ -1203,7 +1155,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
UINT key = s_modifier[i].m_virtualKey;
|
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_virtualKey = key;
|
||||||
keystroke.m_press = false;
|
keystroke.m_press = false;
|
||||||
keystroke.m_repeat = false;
|
keystroke.m_repeat = false;
|
||||||
|
@ -1212,7 +1164,7 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
undo.push_back(keystroke);
|
undo.push_back(keystroke);
|
||||||
}
|
}
|
||||||
key = s_modifier[i].m_virtualKey2;
|
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_virtualKey = key;
|
||||||
keystroke.m_press = false;
|
keystroke.m_press = false;
|
||||||
keystroke.m_repeat = false;
|
keystroke.m_repeat = false;
|
||||||
|
@ -1227,23 +1179,23 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the key event
|
// add the key event
|
||||||
|
keystroke.m_virtualKey = virtualKey;
|
||||||
|
if (isExtended)
|
||||||
|
keystroke.m_virtualKey |= 0x100;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case kPress:
|
case kPress:
|
||||||
keystroke.m_virtualKey = virtualKey;
|
|
||||||
keystroke.m_press = true;
|
keystroke.m_press = true;
|
||||||
keystroke.m_repeat = false;
|
keystroke.m_repeat = false;
|
||||||
keys.push_back(keystroke);
|
keys.push_back(keystroke);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kRelease:
|
case kRelease:
|
||||||
keystroke.m_virtualKey = virtualKey;
|
|
||||||
keystroke.m_press = false;
|
keystroke.m_press = false;
|
||||||
keystroke.m_repeat = false;
|
keystroke.m_repeat = false;
|
||||||
keys.push_back(keystroke);
|
keys.push_back(keystroke);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kRepeat:
|
case kRepeat:
|
||||||
keystroke.m_virtualKey = virtualKey;
|
|
||||||
keystroke.m_press = true;
|
keystroke.m_press = true;
|
||||||
keystroke.m_repeat = true;
|
keystroke.m_repeat = true;
|
||||||
keys.push_back(keystroke);
|
keys.push_back(keystroke);
|
||||||
|
@ -1276,13 +1228,13 @@ KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
|
||||||
// can't reset bit until all keys that set it are released.
|
// can't reset bit until all keys that set it are released.
|
||||||
// scan those keys to see if any are pressed.
|
// scan those keys to see if any are pressed.
|
||||||
bool down = false;
|
bool down = false;
|
||||||
if (virtualKey != modifier.m_virtualKey &&
|
if (virtualKey != (modifier.m_virtualKey & 0xff) &&
|
||||||
(m_keys[modifier.m_virtualKey] & 0x80) != 0) {
|
(m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) {
|
||||||
down = true;
|
down = true;
|
||||||
}
|
}
|
||||||
if (modifier.m_virtualKey2 != 0 &&
|
if (modifier.m_virtualKey2 != 0 &&
|
||||||
virtualKey != modifier.m_virtualKey2 &&
|
virtualKey != (modifier.m_virtualKey2 & 0xff) &&
|
||||||
(m_keys[modifier.m_virtualKey2] & 0x80) != 0) {
|
(m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) {
|
||||||
down = true;
|
down = true;
|
||||||
}
|
}
|
||||||
if (!down)
|
if (!down)
|
||||||
|
@ -1310,9 +1262,7 @@ void CMSWindowsSecondaryScreen::doKeystrokes(
|
||||||
for (; count > 0; --count) {
|
for (; count > 0; --count) {
|
||||||
// send repeating events
|
// send repeating events
|
||||||
for (k = start; k != keys.end() && k->m_repeat; ++k) {
|
for (k = start; k != keys.end() && k->m_repeat; ++k) {
|
||||||
const UINT code = MapVirtualKey(k->m_virtualKey, 0);
|
sendKeyEvent(k->m_virtualKey, k->m_press);
|
||||||
keybd_event(k->m_virtualKey, code,
|
|
||||||
k->m_press ? 0 : KEYEVENTF_KEYUP, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1321,9 +1271,7 @@ void CMSWindowsSecondaryScreen::doKeystrokes(
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// send event
|
// send event
|
||||||
const UINT code = MapVirtualKey(k->m_virtualKey, 0);
|
sendKeyEvent(k->m_virtualKey, k->m_press);
|
||||||
keybd_event(k->m_virtualKey, code,
|
|
||||||
k->m_press ? 0 : KEYEVENTF_KEYUP, 0);
|
|
||||||
|
|
||||||
// next key
|
// next key
|
||||||
++k;
|
++k;
|
||||||
|
@ -1333,22 +1281,25 @@ void CMSWindowsSecondaryScreen::doKeystrokes(
|
||||||
|
|
||||||
void CMSWindowsSecondaryScreen::updateKeys()
|
void CMSWindowsSecondaryScreen::updateKeys()
|
||||||
{
|
{
|
||||||
// GetKeyboardKeys() doesn't seem to do the expected thing
|
// clear key state
|
||||||
memset(m_keys, 0, sizeof(m_keys));
|
memset(m_keys, 0, sizeof(m_keys));
|
||||||
m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
|
|
||||||
m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
|
// we only care about the modifier key states
|
||||||
m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT);
|
m_keys[VK_LSHIFT] = static_cast<BYTE>(GetKeyState(VK_LSHIFT));
|
||||||
m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL);
|
m_keys[VK_RSHIFT] = static_cast<BYTE>(GetKeyState(VK_RSHIFT));
|
||||||
m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL);
|
m_keys[VK_SHIFT] = static_cast<BYTE>(GetKeyState(VK_SHIFT));
|
||||||
m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL);
|
m_keys[VK_LCONTROL] = static_cast<BYTE>(GetKeyState(VK_LCONTROL));
|
||||||
m_keys[VK_LMENU] = GetKeyState(VK_LMENU);
|
m_keys[VK_RCONTROL] = static_cast<BYTE>(GetKeyState(VK_RCONTROL));
|
||||||
m_keys[VK_RMENU] = GetKeyState(VK_RMENU);
|
m_keys[VK_CONTROL] = static_cast<BYTE>(GetKeyState(VK_CONTROL));
|
||||||
m_keys[VK_MENU] = GetKeyState(VK_MENU);
|
m_keys[VK_LMENU] = static_cast<BYTE>(GetKeyState(VK_LMENU));
|
||||||
m_keys[VK_LWIN] = GetKeyState(VK_LWIN);
|
m_keys[VK_RMENU] = static_cast<BYTE>(GetKeyState(VK_RMENU));
|
||||||
m_keys[VK_RWIN] = GetKeyState(VK_RWIN);
|
m_keys[VK_MENU] = static_cast<BYTE>(GetKeyState(VK_MENU));
|
||||||
m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
|
m_keys[VK_LWIN] = static_cast<BYTE>(GetKeyState(VK_LWIN));
|
||||||
m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK);
|
m_keys[VK_RWIN] = static_cast<BYTE>(GetKeyState(VK_RWIN));
|
||||||
m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL);
|
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()
|
void CMSWindowsSecondaryScreen::updateModifiers()
|
||||||
|
@ -1376,11 +1327,97 @@ void CMSWindowsSecondaryScreen::toggleKey(
|
||||||
UINT virtualKey, KeyModifierMask mask)
|
UINT virtualKey, KeyModifierMask mask)
|
||||||
{
|
{
|
||||||
// send key events to simulate a press and release
|
// send key events to simulate a press and release
|
||||||
const UINT code = MapVirtualKey(virtualKey, 0);
|
sendKeyEvent(virtualKey, true);
|
||||||
keybd_event(virtualKey, code, 0, 0);
|
sendKeyEvent(virtualKey, false);
|
||||||
keybd_event(virtualKey, code, KEYEVENTF_KEYUP, 0);
|
|
||||||
|
|
||||||
// toggle shadow state
|
// toggle shadow state
|
||||||
m_mask ^= mask;
|
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" : "")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,9 @@ private:
|
||||||
void updateKeys();
|
void updateKeys();
|
||||||
void updateModifiers();
|
void updateModifiers();
|
||||||
void toggleKey(UINT virtualKey, KeyModifierMask mask);
|
void toggleKey(UINT virtualKey, KeyModifierMask mask);
|
||||||
|
UINT virtualKeyToScanCode(UINT& virtualKey);
|
||||||
|
bool isExtendedKey(UINT virtualKey);
|
||||||
|
void sendKeyEvent(UINT virtualKey, bool press);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CClient* m_client;
|
CClient* m_client;
|
||||||
|
@ -64,6 +67,9 @@ private:
|
||||||
HWND m_nextClipboardWindow;
|
HWND m_nextClipboardWindow;
|
||||||
HWND m_clipboardOwner;
|
HWND m_clipboardOwner;
|
||||||
|
|
||||||
|
// thread id of the event loop thread
|
||||||
|
DWORD m_threadID;
|
||||||
|
|
||||||
// virtual key states
|
// virtual key states
|
||||||
BYTE m_keys[256];
|
BYTE m_keys[256];
|
||||||
|
|
||||||
|
|
|
@ -31,47 +31,15 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
|
||||||
assert(m_hookLibrary == NULL);
|
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()
|
void CMSWindowsPrimaryScreen::run()
|
||||||
{
|
{
|
||||||
CLog::setOutputter(&debugOutput);
|
// change our priority
|
||||||
|
CThread::getCurrentThread().setPriority(-3);
|
||||||
|
|
||||||
|
// run event loop
|
||||||
|
log((CLOG_INFO "entering event loop"));
|
||||||
doRun();
|
doRun();
|
||||||
CLog::setOutputter(NULL);
|
log((CLOG_INFO "exiting event loop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::stop()
|
void CMSWindowsPrimaryScreen::stop()
|
||||||
|
@ -87,12 +55,12 @@ void CMSWindowsPrimaryScreen::open(CServer* server)
|
||||||
// set the server
|
// set the server
|
||||||
m_server = server;
|
m_server = server;
|
||||||
|
|
||||||
// get keyboard state
|
|
||||||
updateKeys();
|
|
||||||
|
|
||||||
// open the display
|
// open the display
|
||||||
openDisplay();
|
openDisplay();
|
||||||
|
|
||||||
|
// get keyboard state
|
||||||
|
updateKeys();
|
||||||
|
|
||||||
// enter the screen
|
// enter the screen
|
||||||
doEnter();
|
doEnter();
|
||||||
}
|
}
|
||||||
|
@ -113,7 +81,6 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
|
||||||
log((CLOG_INFO "entering primary at %d,%d", x, y));
|
log((CLOG_INFO "entering primary at %d,%d", x, y));
|
||||||
assert(m_active == true);
|
assert(m_active == true);
|
||||||
|
|
||||||
// do non-warp enter stuff
|
|
||||||
doEnter();
|
doEnter();
|
||||||
|
|
||||||
// warp to requested location
|
// warp to requested location
|
||||||
|
@ -122,12 +89,8 @@ void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::doEnter()
|
void CMSWindowsPrimaryScreen::doEnter()
|
||||||
{
|
{
|
||||||
// release the capture
|
// not active anymore
|
||||||
ReleaseCapture();
|
m_active = false;
|
||||||
|
|
||||||
// hide our window and restore the foreground window
|
|
||||||
SetForegroundWindow(m_lastActive);
|
|
||||||
ShowWindow(m_window, SW_HIDE);
|
|
||||||
|
|
||||||
// set the zones that should cause a jump
|
// set the zones that should cause a jump
|
||||||
SInt32 w, h;
|
SInt32 w, h;
|
||||||
|
@ -136,11 +99,23 @@ void CMSWindowsPrimaryScreen::doEnter()
|
||||||
m_hookLibrary, "setZone");
|
m_hookLibrary, "setZone");
|
||||||
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
|
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
|
// all messages prior to now are invalid
|
||||||
nextMark();
|
nextMark();
|
||||||
|
|
||||||
// not active anymore
|
|
||||||
m_active = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::leave()
|
void CMSWindowsPrimaryScreen::leave()
|
||||||
|
@ -148,30 +123,50 @@ void CMSWindowsPrimaryScreen::leave()
|
||||||
log((CLOG_INFO "leaving primary"));
|
log((CLOG_INFO "leaving primary"));
|
||||||
assert(m_active == false);
|
assert(m_active == false);
|
||||||
|
|
||||||
|
// do non-warp enter stuff
|
||||||
|
// get state of keys as we leave
|
||||||
|
updateKeys();
|
||||||
|
|
||||||
// all messages prior to now are invalid
|
// all messages prior to now are invalid
|
||||||
nextMark();
|
nextMark();
|
||||||
|
|
||||||
// remember the active window before we leave
|
// remember the active window before we leave. GetActiveWindow()
|
||||||
m_lastActive = GetForegroundWindow();
|
// 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);
|
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
|
// relay all mouse and keyboard events
|
||||||
SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress(
|
SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress(
|
||||||
m_hookLibrary, "setRelay");
|
m_hookLibrary, "setRelay");
|
||||||
setRelay();
|
setRelay();
|
||||||
|
|
||||||
// warp the mouse to the center of the screen
|
// get keyboard input and capture mouse
|
||||||
SInt32 w, h;
|
SetActiveWindow(m_window);
|
||||||
getScreenSize(&w, &h);
|
SetFocus(m_window);
|
||||||
warpCursor(w >> 1, h >> 1);
|
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();
|
nextMark();
|
||||||
|
|
||||||
// local client now active
|
// local client now active
|
||||||
|
@ -205,12 +200,8 @@ void CMSWindowsPrimaryScreen::leave()
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||||
{
|
{
|
||||||
SInt32 w, h;
|
// set the cursor position without generating an event
|
||||||
getScreenSize(&w, &h);
|
SetCursorPos(x, y);
|
||||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
|
||||||
(DWORD)((65535.99 * x) / (w - 1)),
|
|
||||||
(DWORD)((65535.99 * y) / (h - 1)),
|
|
||||||
0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::setClipboard(
|
void CMSWindowsPrimaryScreen::setClipboard(
|
||||||
|
@ -254,7 +245,7 @@ void CMSWindowsPrimaryScreen::getClipboard(
|
||||||
|
|
||||||
KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const
|
KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const
|
||||||
{
|
{
|
||||||
KeyModifierMask mask;
|
KeyModifierMask mask = 0;
|
||||||
if ((m_keys[VK_CAPITAL] & 0x01) != 0)
|
if ((m_keys[VK_CAPITAL] & 0x01) != 0)
|
||||||
mask |= KeyModifierCapsLock;
|
mask |= KeyModifierCapsLock;
|
||||||
if ((m_keys[VK_NUMLOCK] & 0x01) != 0)
|
if ((m_keys[VK_NUMLOCK] & 0x01) != 0)
|
||||||
|
@ -264,30 +255,30 @@ KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "resource.h" // FIXME
|
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::onOpenDisplay()
|
void CMSWindowsPrimaryScreen::onOpenDisplay()
|
||||||
{
|
{
|
||||||
assert(m_window == NULL);
|
assert(m_window == NULL);
|
||||||
assert(m_server != 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
|
// initialize clipboard owner to current owner. we don't want
|
||||||
// to take ownership of the clipboard just by starting up.
|
// to take ownership of the clipboard just by starting up.
|
||||||
m_clipboardOwner = GetClipboardOwner();
|
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
|
// create the window
|
||||||
m_window = CreateWindowEx(WS_EX_TOPMOST |
|
m_window = CreateWindowEx(WS_EX_TOPMOST |
|
||||||
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
|
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
|
||||||
(LPCTSTR)getClass(), "Synergy",
|
(LPCTSTR)getClass(), "Synergy",
|
||||||
WS_POPUP,
|
WS_POPUP,
|
||||||
0, 0, 1, 1, NULL, NULL,
|
0, 0, w, h, NULL, NULL,
|
||||||
getInstance(),
|
getInstance(),
|
||||||
NULL);
|
NULL);
|
||||||
assert(m_window != NULL);
|
assert(m_window != NULL);
|
||||||
|
@ -307,6 +298,7 @@ ShowWindow(s_debug, SW_SHOWNORMAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hooked) {
|
if (!hooked) {
|
||||||
|
log((CLOG_ERR "failed to install hooks"));
|
||||||
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
||||||
m_nextClipboardWindow = NULL;
|
m_nextClipboardWindow = NULL;
|
||||||
DestroyWindow(m_window);
|
DestroyWindow(m_window);
|
||||||
|
@ -342,19 +334,10 @@ void CMSWindowsPrimaryScreen::onCloseDisplay()
|
||||||
// destroy window
|
// destroy window
|
||||||
DestroyWindow(m_window);
|
DestroyWindow(m_window);
|
||||||
m_window = NULL;
|
m_window = NULL;
|
||||||
|
|
||||||
CLog::setOutputter(NULL);
|
|
||||||
DestroyWindow(s_debug);
|
|
||||||
s_debug = NULL;
|
|
||||||
s_thread = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
||||||
{
|
{
|
||||||
if (IsDialogMessage(s_debug, msg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle event
|
// handle event
|
||||||
switch (msg->message) {
|
switch (msg->message) {
|
||||||
case SYNERGY_MSG_MARK:
|
case SYNERGY_MSG_MARK:
|
||||||
|
@ -391,7 +374,9 @@ if (IsDialogMessage(s_debug, msg)) {
|
||||||
updateKey(msg->wParam, false);
|
updateKey(msg->wParam, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
log((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -431,31 +416,18 @@ if (IsDialogMessage(s_debug, msg)) {
|
||||||
m_server->onMouseMovePrimary(x, y);
|
m_server->onMouseMovePrimary(x, y);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// get mouse deltas
|
||||||
|
x -= m_xCenter;
|
||||||
|
y -= m_yCenter;
|
||||||
log((CLOG_DEBUG2 "event: active move %d,%d", x, y));
|
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
|
// warp mouse back to center
|
||||||
warpCursor(w, h);
|
warpCursor(m_xCenter, m_yCenter);
|
||||||
|
|
||||||
// send motion
|
// send motion
|
||||||
m_server->onMouseMoveSecondary(x, y);
|
m_server->onMouseMoveSecondary(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +439,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent(
|
||||||
WPARAM wParam, LPARAM lParam)
|
WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
// FIXME -- handle display changes
|
// FIXME -- handle display changes (and resize full-screen window)
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
ValidateRect(hwnd, NULL);
|
ValidateRect(hwnd, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -500,6 +472,7 @@ LRESULT CMSWindowsPrimaryScreen::onEvent(
|
||||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,6 +781,7 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
|
||||||
if ((m_keys[VK_SCROLL] & 0x01) != 0)
|
if ((m_keys[VK_SCROLL] & 0x01) != 0)
|
||||||
mask |= KeyModifierScrollLock;
|
mask |= KeyModifierScrollLock;
|
||||||
*maskOut = mask;
|
*maskOut = mask;
|
||||||
|
log((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask));
|
||||||
|
|
||||||
// get the scan code
|
// get the scan code
|
||||||
UINT scanCode = static_cast<UINT>((info & 0xff0000) >> 16);
|
UINT scanCode = static_cast<UINT>((info & 0xff0000) >> 16);
|
||||||
|
@ -827,6 +801,10 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
|
||||||
else if (vkCode >= VK_LWIN && vkCode <= VK_APPS)
|
else if (vkCode >= VK_LWIN && vkCode <= VK_APPS)
|
||||||
vkCode2 = vkCode;
|
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
|
// sadly, win32 will not distinguish between the left and right
|
||||||
// control and alt keys using the above function. however, we
|
// control and alt keys using the above function. however, we
|
||||||
// can check for those: if bit 24 of info is set then the key
|
// can check for those: if bit 24 of info is set then the key
|
||||||
|
@ -834,10 +812,12 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
|
||||||
// keys.
|
// keys.
|
||||||
if ((info & 0x1000000) != 0) {
|
if ((info & 0x1000000) != 0) {
|
||||||
switch (vkCode2) {
|
switch (vkCode2) {
|
||||||
|
case VK_CONTROL:
|
||||||
case VK_LCONTROL:
|
case VK_LCONTROL:
|
||||||
vkCode2 = VK_RCONTROL;
|
vkCode2 = VK_RCONTROL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VK_MENU:
|
||||||
case VK_LMENU:
|
case VK_LMENU:
|
||||||
vkCode2 = VK_RMENU;
|
vkCode2 = VK_RMENU;
|
||||||
break;
|
break;
|
||||||
|
@ -898,7 +878,7 @@ KeyID CMSWindowsPrimaryScreen::mapKey(
|
||||||
else if (result == 2) {
|
else if (result == 2) {
|
||||||
// get the scan code of the dead key and the shift state
|
// get the scan code of the dead key and the shift state
|
||||||
// required to generate it.
|
// required to generate it.
|
||||||
vkCode = VkKeyScan(ascii & 0x00ff);
|
vkCode = VkKeyScan(static_cast<TCHAR>(ascii & 0x00ff));
|
||||||
|
|
||||||
// set shift state required to generate key
|
// set shift state required to generate key
|
||||||
BYTE keys[256];
|
BYTE keys[256];
|
||||||
|
@ -948,25 +928,29 @@ ButtonID CMSWindowsPrimaryScreen::mapButton(
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::updateKeys()
|
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
|
// clear key state
|
||||||
memset(m_keys, 0, sizeof(m_keys));
|
memset(m_keys, 0, sizeof(m_keys));
|
||||||
|
|
||||||
// we only care about the modifier key states
|
// we only care about the modifier key states
|
||||||
m_keys[VK_LSHIFT] = GetKeyState(VK_LSHIFT);
|
m_keys[VK_LSHIFT] = static_cast<BYTE>(GetKeyState(VK_LSHIFT));
|
||||||
m_keys[VK_RSHIFT] = GetKeyState(VK_RSHIFT);
|
m_keys[VK_RSHIFT] = static_cast<BYTE>(GetKeyState(VK_RSHIFT));
|
||||||
m_keys[VK_SHIFT] = GetKeyState(VK_SHIFT);
|
m_keys[VK_SHIFT] = static_cast<BYTE>(GetKeyState(VK_SHIFT));
|
||||||
m_keys[VK_LCONTROL] = GetKeyState(VK_LCONTROL);
|
m_keys[VK_LCONTROL] = static_cast<BYTE>(GetKeyState(VK_LCONTROL));
|
||||||
m_keys[VK_RCONTROL] = GetKeyState(VK_RCONTROL);
|
m_keys[VK_RCONTROL] = static_cast<BYTE>(GetKeyState(VK_RCONTROL));
|
||||||
m_keys[VK_CONTROL] = GetKeyState(VK_CONTROL);
|
m_keys[VK_CONTROL] = static_cast<BYTE>(GetKeyState(VK_CONTROL));
|
||||||
m_keys[VK_LMENU] = GetKeyState(VK_LMENU);
|
m_keys[VK_LMENU] = static_cast<BYTE>(GetKeyState(VK_LMENU));
|
||||||
m_keys[VK_RMENU] = GetKeyState(VK_RMENU);
|
m_keys[VK_RMENU] = static_cast<BYTE>(GetKeyState(VK_RMENU));
|
||||||
m_keys[VK_MENU] = GetKeyState(VK_MENU);
|
m_keys[VK_MENU] = static_cast<BYTE>(GetKeyState(VK_MENU));
|
||||||
m_keys[VK_LWIN] = GetKeyState(VK_LWIN);
|
m_keys[VK_LWIN] = static_cast<BYTE>(GetKeyState(VK_LWIN));
|
||||||
m_keys[VK_RWIN] = GetKeyState(VK_RWIN);
|
m_keys[VK_RWIN] = static_cast<BYTE>(GetKeyState(VK_RWIN));
|
||||||
m_keys[VK_APPS] = GetKeyState(VK_APPS);
|
m_keys[VK_APPS] = static_cast<BYTE>(GetKeyState(VK_APPS));
|
||||||
m_keys[VK_CAPITAL] = GetKeyState(VK_CAPITAL);
|
m_keys[VK_CAPITAL] = static_cast<BYTE>(GetKeyState(VK_CAPITAL));
|
||||||
m_keys[VK_NUMLOCK] = GetKeyState(VK_NUMLOCK);
|
m_keys[VK_NUMLOCK] = static_cast<BYTE>(GetKeyState(VK_NUMLOCK));
|
||||||
m_keys[VK_SCROLL] = GetKeyState(VK_SCROLL);
|
m_keys[VK_SCROLL] = static_cast<BYTE>(GetKeyState(VK_SCROLL));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMSWindowsPrimaryScreen::updateKey(
|
void CMSWindowsPrimaryScreen::updateKey(
|
||||||
|
|
|
@ -52,11 +52,14 @@ private:
|
||||||
HWND m_window;
|
HWND m_window;
|
||||||
HWND m_nextClipboardWindow;
|
HWND m_nextClipboardWindow;
|
||||||
HWND m_clipboardOwner;
|
HWND m_clipboardOwner;
|
||||||
HWND m_lastActive;
|
HWND m_lastForegroundWindow;
|
||||||
|
HWND m_lastActiveWindow;
|
||||||
|
DWORD m_lastActiveThread;
|
||||||
HINSTANCE m_hookLibrary;
|
HINSTANCE m_hookLibrary;
|
||||||
UInt32 m_mark;
|
UInt32 m_mark;
|
||||||
UInt32 m_markReceived;
|
UInt32 m_markReceived;
|
||||||
BYTE m_keys[256];
|
BYTE m_keys[256];
|
||||||
|
SInt32 m_xCenter, m_yCenter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue