checkpoint. refactored win32 code. had to edit and rename some

files so this is only a checkpoint.
This commit is contained in:
crs 2002-07-15 15:01:36 +00:00
parent f48a5fe387
commit 4b46862026
33 changed files with 1210 additions and 1661 deletions

View File

@ -1,12 +1,8 @@
#include "CMSWindowsSecondaryScreen.h"
#include "IScreenReceiver.h"
#include "CClipboard.h"
#include "CMSWindowsClipboard.h"
#include "CMSWindowsScreenSaver.h"
#include "CMSWindowsScreen.h"
#include "CPlatform.h"
#include "XScreen.h"
#include "CLock.h"
#include "CThread.h"
#include "CLog.h"
#include <cctype>
@ -24,155 +20,18 @@
CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen(
IScreenReceiver* receiver) :
m_receiver(receiver),
m_threadID(0),
m_lastThreadID(0),
m_desk(NULL),
m_deskName(),
m_is95Family(CPlatform::isWindows95Family()),
m_window(NULL),
m_active(false),
m_nextClipboardWindow(NULL)
m_mask(0)
{
assert(m_receiver != NULL);
m_is95Family = CPlatform::isWindows95Family();
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
m_screen = new CMSWindowsScreen(receiver, this);
}
CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
{
assert(m_window == NULL);
}
void
CMSWindowsSecondaryScreen::run()
{
assert(m_window != NULL);
// must call run() from same thread as open()
assert(m_threadID == GetCurrentThreadId());
// change our priority
CThread::getCurrentThread().setPriority(-7);
// run event loop
try {
log((CLOG_INFO "entering event loop"));
mainLoop();
log((CLOG_INFO "exiting event loop"));
}
catch (...) {
log((CLOG_INFO "exiting event loop"));
throw;
}
}
void
CMSWindowsSecondaryScreen::stop()
{
exitMainLoop();
}
void
CMSWindowsSecondaryScreen::open()
{
assert(m_window == NULL);
try {
// open the display
openDisplay();
// create and prepare our window
createWindow();
// initialize the clipboards; assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
grabClipboard(id);
}
// get keyboard state
updateKeys();
updateModifiers();
// disable the screen saver
installScreenSaver();
}
catch (...) {
close();
throw;
}
// hide the cursor
m_active = true;
leave();
}
void
CMSWindowsSecondaryScreen::close()
{
uninstallScreenSaver();
destroyWindow();
closeDisplay();
}
void
CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
{
CLock lock(&m_mutex);
assert(m_window != NULL);
assert(m_active == false);
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
syncDesktop();
// now active
m_active = 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
warpCursor(x, y);
// show mouse
hideWindow();
}
void
CMSWindowsSecondaryScreen::leave()
{
CLock lock(&m_mutex);
assert(m_window != NULL);
assert(m_active == true);
log((CLOG_INFO "leaving screen"));
syncDesktop();
// hide mouse
showWindow();
// not active anymore
m_active = false;
// make sure our idea of clipboard ownership is correct
checkClipboard();
delete m_screen;
}
void
@ -182,8 +41,7 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask)
UINT virtualKey;
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
// get the sequence of keys to simulate key press and the final
// modifier state.
@ -223,8 +81,7 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key,
UINT virtualKey;
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
// get the sequence of keys to simulate key repeat and the final
// modifier state.
@ -244,8 +101,7 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask)
UINT virtualKey;
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
// get the sequence of keys to simulate key release and the final
// modifier state.
@ -302,8 +158,7 @@ void
CMSWindowsSecondaryScreen::mouseDown(ButtonID button)
{
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
// map button id to button flag
DWORD flags = mapButton(button, true);
@ -318,8 +173,7 @@ void
CMSWindowsSecondaryScreen::mouseUp(ButtonID button)
{
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
// map button id to button flag
DWORD flags = mapButton(button, false);
@ -334,8 +188,7 @@ void
CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
{
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
warpCursor(x, y);
}
@ -343,61 +196,50 @@ void
CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta)
{
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
m_screen->syncDesktop();
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0);
}
void
CMSWindowsSecondaryScreen::setClipboard(ClipboardID /*id*/,
const IClipboard* src)
IScreen*
CMSWindowsSecondaryScreen::getScreen() const
{
CLock lock(&m_mutex);
assert(m_window != NULL);
CMSWindowsClipboard dst(m_window);
CClipboard::copy(&dst, src);
return m_screen;
}
void
CMSWindowsSecondaryScreen::grabClipboard(ClipboardID /*id*/)
CMSWindowsSecondaryScreen::onError()
{
CLock lock(&m_mutex);
assert(m_window != NULL);
CMSWindowsClipboard clipboard(m_window);
if (clipboard.open(0)) {
clipboard.close();
}
// ignore
}
void
CMSWindowsSecondaryScreen::screenSaver(bool activate)
CMSWindowsSecondaryScreen::onScreensaver(bool)
{
if (activate) {
getScreenSaver()->activate();
}
else {
getScreenSaver()->deactivate();
}
// ignore
}
void
CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const
bool
CMSWindowsSecondaryScreen::onPreDispatch(const CEvent*)
{
CLock lock(&m_mutex);
assert(m_window != NULL);
syncDesktop();
getCursorPos(x, y);
return false;
}
void
CMSWindowsSecondaryScreen::getShape(
SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
bool
CMSWindowsSecondaryScreen::onEvent(CEvent* event)
{
getScreenShape(x, y, w, h);
assert(event != NULL);
const MSG& msg = event->m_msg;
switch (msg.message) {
case WM_ACTIVATEAPP:
if (msg.wParam == FALSE) {
// some other app activated. hide the hider window.
ShowWindow(m_window, SW_HIDE);
}
break;
}
return false;
}
SInt32
@ -407,153 +249,63 @@ CMSWindowsSecondaryScreen::getJumpZoneSize() const
}
void
CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/,
IClipboard* dst) const
CMSWindowsSecondaryScreen::postCreateWindow(HWND window)
{
m_window = window;
if (!isActive()) {
showWindow();
}
}
void
CMSWindowsSecondaryScreen::preDestroyWindow(HWND)
{
// do nothing
}
void
CMSWindowsSecondaryScreen::onPreRun()
{
CLock lock(&m_mutex);
assert(m_window != NULL);
CMSWindowsClipboard src(m_window);
CClipboard::copy(dst, &src);
}
bool
CMSWindowsSecondaryScreen::onPreDispatch(const CEvent* event)
void
CMSWindowsSecondaryScreen::onPreOpen()
{
assert(event != NULL);
// forward to superclass
if (CMSWindowsScreen::onPreDispatch(event)) {
return true;
assert(m_window == NULL);
}
// handle event
const MSG* msg = &event->m_msg;
switch (msg->message) {
case WM_TIMER:
// if current desktop is not the input desktop then switch to it
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else {
switchDesktop(desk);
}
}
}
return true;
}
return false;
}
bool
CMSWindowsSecondaryScreen::onEvent(CEvent* event)
void
CMSWindowsSecondaryScreen::onPreEnter()
{
assert(event != NULL);
const MSG& msg = event->msg;
switch (msg.message) {
case WM_QUERYENDSESSION:
if (m_is95Family) {
event->m_result = TRUE;
return true;
}
break;
case WM_ENDSESSION:
if (m_is95Family) {
if (msg.wParam == TRUE && msg.lParam == 0) {
stop();
}
return true;
}
break;
case WM_PAINT:
ValidateRect(msg.hwnd, NULL);
return true;
case WM_ACTIVATEAPP:
if (msg.wParam == FALSE) {
// some other app activated. hide the hider window.
ShowWindow(m_window, SW_HIDE);
}
break;
case WM_DRAWCLIPBOARD:
log((CLOG_DEBUG "clipboard was taken"));
// first pass it on
if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow,
msg.message, msg.wParam, msg.lParam);
assert(m_window != NULL);
}
// now notify client that somebody changed the clipboard (unless
// we're now the owner, in which case it's because we took
// ownership, or now it's owned by nobody, which will happen if
// we owned it and switched desktops because we destroy our
// window to do that).
try {
m_clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_receiver->onGrabClipboard(kClipboardClipboard);
m_receiver->onGrabClipboard(kClipboardSelection);
}
}
catch (XBadClient&) {
// ignore. this can happen if we receive this event
// before we've fully started up.
}
return true;
case WM_CHANGECBCHAIN:
if (m_nextClipboardWindow == (HWND)msg.wParam) {
m_nextClipboardWindow = (HWND)msg.lParam;
}
else if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow,
msg.message, msg.wParam, msg.lParam);
}
return true;
case WM_DISPLAYCHANGE:
void
CMSWindowsSecondaryScreen::onPreLeave()
{
// screen resolution may have changed. get old shape.
SInt32 xOld, yOld, wOld, hOld;
getScreenShape(xOld, yOld, wOld, hOld);
// update shape
updateScreenShape();
m_multimon = isMultimon();
// collect new screen info
CClientInfo info;
getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h);
getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = getJumpZoneSize();
// do nothing if resolution hasn't changed
if (info.m_x != xOld || info.m_y != yOld ||
info.m_w != wOld || info.m_h != hOld) {
// send new screen info
m_receiver->onInfoChanged(info);
assert(m_window != NULL);
}
return true;
}
}
return false;
}
CString
CMSWindowsSecondaryScreen::getCurrentDesktopName() const
void
CMSWindowsSecondaryScreen::createWindow()
{
return m_deskName;
// open the desktop and the window
m_window = m_screen->openDesktop();
if (m_window == NULL) {
throw XScreenOpenFailure();
}
}
void
CMSWindowsSecondaryScreen::destroyWindow()
{
// release keys that are logically pressed
releaseKeys();
// close the desktop and the window
m_screen->closeDesktop();
m_window = NULL;
}
void
@ -580,9 +332,9 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y)
{
// move the mouse directly to target position on NT family or if
// not using multiple monitors.
if (!m_multimon || !m_is95Family) {
if (m_screen->isMultimon() || !m_is95Family) {
SInt32 x0, y0, w, h;
getScreenShape(x0, y0, w, h);
m_screen->getShape(x0, y0, w, h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65535.99 * (x - x0)) / (w - 1)),
(DWORD)((65535.99 * (y - y0)) / (h - 1)),
@ -653,259 +405,70 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y)
}
void
CMSWindowsSecondaryScreen::checkClipboard()
CMSWindowsSecondaryScreen::updateKeys()
{
// if we think we own the clipboard but we don't then somebody
// grabbed the clipboard on this screen without us knowing.
// tell the server that this screen grabbed the clipboard.
//
// this works around bugs in the clipboard viewer chain.
// sometimes NT will simply never send WM_DRAWCLIPBOARD
// messages for no apparent reason and rebooting fixes the
// problem. since we don't want a broken clipboard until the
// next reboot we do this double check. clipboard ownership
// won't be reflected on other screens until we leave but at
// least the clipboard itself will work.
HWND clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != clipboardOwner) {
try {
m_clipboardOwner = clipboardOwner;
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_receiver->onGrabClipboard(kClipboardClipboard);
m_receiver->onGrabClipboard(kClipboardSelection);
// clear key state
memset(m_keys, 0, sizeof(m_keys));
// 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));
// update active modifier mask
m_mask = 0;
if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) {
m_mask |= KeyModifierShift;
}
if ((m_keys[VK_LCONTROL] & 0x80) != 0 ||
(m_keys[VK_RCONTROL] & 0x80) != 0) {
m_mask |= KeyModifierControl;
}
catch (XBadClient&) {
// ignore
if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) {
m_mask |= KeyModifierAlt;
}
if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) {
m_mask |= KeyModifierMeta;
}
if ((m_keys[VK_CAPITAL] & 0x01) != 0) {
m_mask |= KeyModifierCapsLock;
}
if ((m_keys[VK_NUMLOCK] & 0x01) != 0) {
m_mask |= KeyModifierNumLock;
}
if ((m_keys[VK_SCROLL] & 0x01) != 0) {
m_mask |= KeyModifierScrollLock;
}
log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask));
}
void
CMSWindowsPrimaryScreen::createWindow()
CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask)
{
// save thread id
m_threadID = GetCurrentThreadId();
// note if using multiple monitors
m_multimon = isMultimon();
// get the input desktop and switch to it
if (m_is95Family) {
if (!openDesktop()) {
throw XScreenOpenFailure();
// 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);
}
else {
if (!switchDesktop(openInputDesktop())) {
throw XScreenOpenFailure();
if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) {
toggleKey(VK_SCROLL, KeyModifierScrollLock);
}
}
// poll input desktop to see if it changes (onPreDispatch()
// handles WM_TIMER)
m_timer = 0;
if (!m_is95Family) {
m_timer = SetTimer(NULL, 0, 200, NULL);
}
}
void
CMSWindowsPrimaryScreen::destroyWindow()
{
// remove timer
if (m_timer != 0) {
KillTimer(NULL, m_timer);
}
// release keys that are logically pressed
releaseKeys();
// disconnect from desktop
if (m_is95Family) {
closeDesktop();
}
else {
switchDesktop(NULL);
}
// clear thread id
m_threadID = 0;
assert(m_window == NULL);
assert(m_desk == NULL);
}
void
CMSWindowsSecondaryScreen::installScreenSaver()
{
getScreenSaver()->disable();
}
void
CMSWindowsSecondaryScreen::uninstallScreenSaver()
{
getScreenSaver()->enable();
}
bool
CMSWindowsSecondaryScreen::openDesktop()
{
CLock lock(&m_mutex);
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
// create the cursor hiding window. this window is used to hide the
// cursor when it's not on the screen. the window is hidden as soon
// as the cursor enters the screen or the display's real cursor is
// moved.
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
getInstance(),
NULL);
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
return true;
}
void
CMSWindowsSecondaryScreen::closeDesktop()
{
CLock lock(&m_mutex);
if (m_window != NULL) {
// remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
}
}
bool
CMSWindowsSecondaryScreen::switchDesktop(HDESK desk)
{
CLock lock(&m_mutex);
bool ownClipboard = false;
if (m_window != NULL) {
// note if we own the clipboard
ownClipboard = (m_clipboardOwner == m_window);
// remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
}
// done with desktop
if (m_desk != NULL) {
CloseDesktop(m_desk);
m_desk = NULL;
m_deskName = "";
}
// if no new desktop then we're done
if (desk == NULL) {
log((CLOG_INFO "disconnecting desktop"));
return true;
}
// set the desktop. can only do this when there are no windows
// and hooks on the current desktop owned by this thread.
if (SetThreadDesktop(desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
CloseDesktop(desk);
return false;
}
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
// create the cursor hiding window. this window is used to hide the
// cursor when it's not on the screen. the window is hidden as soon
// as the cursor enters the screen or the display's real cursor is
// moved.
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
CloseDesktop(desk);
return false;
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
// if we owned the desktop then set the clipboard owner
if (ownClipboard) {
m_clipboardOwner = GetClipboardOwner();
}
// save new desktop
m_desk = desk;
m_deskName = getDesktopName(m_desk);
log((CLOG_INFO "switched to desktop %s", m_deskName.c_str()));
// get desktop up to date
if (!m_active) {
showWindow();
}
return true;
}
void
CMSWindowsSecondaryScreen::syncDesktop() const
{
// note -- mutex must be locked on entry
// change calling thread's desktop
if (!m_is95Family) {
if (SetThreadDesktop(m_desk) == 0) {
log((CLOG_WARN "failed to set desktop: %d", GetLastError()));
}
}
// attach input queues if not already attached. this has a habit
// of sucking up more and more CPU each time it's called (even if
// the threads are already attached). since we only expect one
// thread to call this more than once we can save just the last
// the attached thread.
DWORD threadID = GetCurrentThreadId();
if (threadID != m_lastThreadID && threadID != m_threadID) {
m_lastThreadID = threadID;
AttachThreadInput(threadID, m_threadID, TRUE);
}
}
bool
CMSWindowsSecondaryScreen::isMultimon() const
{
SInt32 x0, y0, w, h;
getScreenShape(x0, y0, w, h);
return (w != GetSystemMetrics(SM_CXSCREEN) ||
h != GetSystemMetrics(SM_CYSCREEN));
}
// 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
@ -1768,7 +1331,7 @@ CMSWindowsSecondaryScreen::releaseKeys()
{
CLock lock(&m_mutex);
syncDesktop();
m_screen->syncDesktop();
// release left/right modifier keys first. if the platform doesn't
// support them then they won't be set and the non-side-distinuishing
@ -1814,60 +1377,6 @@ CMSWindowsSecondaryScreen::releaseKeys()
}
}
void
CMSWindowsSecondaryScreen::updateKeys()
{
// clear key state
memset(m_keys, 0, sizeof(m_keys));
// 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()
{
// update active modifier mask
m_mask = 0;
if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) {
m_mask |= KeyModifierShift;
}
if ((m_keys[VK_LCONTROL] & 0x80) != 0 ||
(m_keys[VK_RCONTROL] & 0x80) != 0) {
m_mask |= KeyModifierControl;
}
if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) {
m_mask |= KeyModifierAlt;
}
if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) {
m_mask |= KeyModifierMeta;
}
if ((m_keys[VK_CAPITAL] & 0x01) != 0) {
m_mask |= KeyModifierCapsLock;
}
if ((m_keys[VK_NUMLOCK] & 0x01) != 0) {
m_mask |= KeyModifierNumLock;
}
if ((m_keys[VK_SCROLL] & 0x01) != 0) {
m_mask |= KeyModifierScrollLock;
}
log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask));
}
void
CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask)
{

View File

@ -7,28 +7,22 @@
#define _WIN32_WINNT 0x401
#endif
#include "CMSWindowsScreen.h"
#include "ISecondaryScreen.h"
#include "CSecondaryScreen.h"
#include "IMSWindowsScreenEventHandler.h"
#include "CMutex.h"
#include "CString.h"
#include "stdvector.h"
class CMSWindowsScreen;
class IScreenReceiver;
class CMSWindowsSecondaryScreen : public CMSWindowsScreen,
public ISecondaryScreen {
class CMSWindowsSecondaryScreen :
public CSecondaryScreen, public IMSWindowsScreenEventHandler {
public:
CMSWindowsSecondaryScreen(IScreenReceiver*);
virtual ~CMSWindowsSecondaryScreen();
// ISecondaryScreen overrides
virtual void run();
virtual void stop();
virtual void open();
virtual void close();
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute,
KeyModifierMask mask);
virtual void leave();
// CSecondaryScreen overrides
virtual void keyDown(KeyID, KeyModifierMask);
virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count);
virtual void keyUp(KeyID, KeyModifierMask);
@ -36,19 +30,30 @@ public:
virtual void mouseUp(ButtonID);
virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute);
virtual void mouseWheel(SInt32 delta);
virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID);
virtual void screenSaver(bool activate);
virtual void getMousePos(SInt32& x, SInt32& y) const;
virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const;
virtual SInt32 getJumpZoneSize() const;
virtual void getClipboard(ClipboardID, IClipboard*) const;
virtual IScreen* getScreen() const;
protected:
// CMSWindowsScreen overrides
// IMSWindowsScreenEventHandler overrides
virtual void onError();
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual CString getCurrentDesktopName() const;
virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND);
protected:
// CSecondaryScreen overrides
virtual void onPreRun();
virtual void onPreOpen();
virtual void onPreEnter();
virtual void onPreLeave();
virtual void createWindow();
virtual void destroyWindow();
virtual void showWindow();
virtual void hideWindow();
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void updateKeys();
virtual void setToggleState(KeyModifierMask);
private:
enum EKeyAction { kPress, kRelease, kRepeat };
@ -60,26 +65,6 @@ private:
};
typedef std::vector<Keystroke> Keystrokes;
void showWindow();
void hideWindow();
// warp the mouse to the specified position
void warpCursor(SInt32 x, SInt32 y);
// check clipboard ownership and, if necessary, tell the receiver
// of a grab.
void checkClipboard();
// create/destroy window
// also attach to desktop; this destroys and recreates the window
// as necessary.
void createWindow();
void destroyWindow();
// start/stop watch for screen saver changes
void installScreenSaver();
void uninstallScreenSaver();
// open/close desktop (for windows 95/98/me)
bool openDesktop();
void closeDesktop();
@ -87,9 +72,6 @@ private:
// make desk the thread desktop (for windows NT/2000/XP)
bool switchDesktop(HDESK desk);
// get calling thread to use the input desktop
void syncDesktop() const;
// returns true iff there appear to be multiple monitors
bool isMultimon() const;
@ -100,8 +82,6 @@ private:
void doKeystrokes(const Keystrokes&, SInt32 count);
void releaseKeys();
void updateKeys();
void updateModifiers();
void toggleKey(UINT virtualKey, KeyModifierMask mask);
UINT virtualKeyToScanCode(UINT& virtualKey);
bool isExtendedKey(UINT virtualKey);
@ -109,37 +89,14 @@ private:
private:
CMutex m_mutex;
IScreenReceiver* m_receiver;
CMSWindowsScreen* m_screen;
// true if windows 95/98/me
bool m_is95Family;
// true if system appears to have multiple monitors
bool m_multimon;
// the main loop's thread id
DWORD m_threadID;
// the timer used to check for desktop switching
UINT m_timer;
// the thread id of the last attached thread
mutable DWORD m_lastThreadID;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;
// our window (for getting clipboard changes)
// our window
HWND m_window;
// m_active is true if this screen has been entered
bool m_active;
// clipboard stuff
HWND m_nextClipboardWindow;
HWND m_clipboardOwner;
// virtual key states
BYTE m_keys[256];

View File

@ -74,7 +74,7 @@ CSecondaryScreen::open()
updateKeys();
// disable the screen saver
getScreen()->openScreenSaver(false);
getScreen()->openScreensaver(false);
// subclass hook
onPostOpen();
@ -93,7 +93,7 @@ void
CSecondaryScreen::close()
{
onPreClose();
getScreen()->closeScreenSaver();
getScreen()->closeScreensaver();
destroyWindow();
getScreen()->close();
onPostClose();
@ -182,12 +182,6 @@ CSecondaryScreen::getClipboard(ClipboardID id,
getScreen()->getClipboard(id, clipboard);
}
SInt32
CSecondaryScreen::getJumpZoneSize() const
{
return 0;
}
void
CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
{

View File

@ -78,8 +78,8 @@ public:
void getClipboard(ClipboardID, IClipboard*) const;
// returns the size of the zone on the edges of the screen that
// causes the cursor to jump to another screen. default returns 0.
virtual SInt32 getJumpZoneSize() const;
// causes the cursor to jump to another screen.
virtual SInt32 getJumpZoneSize() const = 0;
// get the shape (position of upper-left corner and size) of the
// screen

View File

@ -145,12 +145,6 @@ CXWindowsSecondaryScreen::getScreen() const
return m_screen;
}
void
CXWindowsSecondaryScreen::onScreensaver(bool)
{
// ignore
}
void
CXWindowsSecondaryScreen::onError()
{
@ -158,6 +152,12 @@ CXWindowsSecondaryScreen::onError()
// FIXME -- forward this? to whom?
}
void
CXWindowsSecondaryScreen::onScreensaver(bool)
{
// ignore
}
bool
CXWindowsSecondaryScreen::onPreDispatch(const CEvent*)
{
@ -184,6 +184,12 @@ CXWindowsSecondaryScreen::onEvent(CEvent* event)
}
}
SInt32
CXWindowsSecondaryScreen::getJumpZoneSize() const
{
return 0;
}
void
CXWindowsSecondaryScreen::onPreRun()
{
@ -260,7 +266,7 @@ CXWindowsSecondaryScreen::createWindow()
XTestGrabControl(display, True);
}
// tell our superclass about the window
// tell generic screen about the window
m_screen->setWindow(m_window);
}
@ -275,15 +281,19 @@ CXWindowsSecondaryScreen::destroyWindow()
// no longer impervious to server grabs
XTestGrabControl(display, False);
// destroy window
if (m_window != None) {
XDestroyWindow(display, m_window);
m_window = None;
}
// update
XSync(display, False);
}
// destroy window
if (m_window != None) {
m_screen->setWindow(None);
CDisplayLock display(m_screen);
if (display != NULL) {
XDestroyWindow(display, m_window);
}
m_window = None;
}
}
void

View File

@ -35,6 +35,7 @@ public:
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual SInt32 getJumpZoneSize() const;
protected:
// CSecondaryScreen overrides

View File

@ -110,6 +110,10 @@ SOURCE=.\CMSWindowsSecondaryScreen.cpp
# End Source File
# Begin Source File
SOURCE=.\CSecondaryScreen.cpp
# End Source File
# Begin Source File
SOURCE=.\CServerProxy.cpp
# End Source File
# End Group
@ -126,11 +130,11 @@ SOURCE=.\CMSWindowsSecondaryScreen.h
# End Source File
# Begin Source File
SOURCE=.\CServerProxy.h
SOURCE=.\CSecondaryScreen.h
# End Source File
# Begin Source File
SOURCE=.\ISecondaryScreen.h
SOURCE=.\CServerProxy.h
# End Source File
# Begin Source File

View File

@ -1,5 +1,10 @@
#include "CMSWindowsScreen.h"
#include "CMSWindowsClipboard.h"
#include "CMSWindowsScreenSaver.h"
#include "CClipboard.h"
#include "IMSWindowsScreenEventHandler.h"
#include "IScreenReceiver.h"
#include "XSynergy.h"
#include "CThread.h"
#include "CLock.h"
#include "TMethodJob.h"
@ -8,7 +13,7 @@
#include <cstring>
//
// add backwards compatible multihead support (suppress bogus warning)
// add backwards compatible multihead support (and suppress bogus warning)
//
#pragma warning(push)
#pragma warning(disable: 4706) // assignment within conditional
@ -23,21 +28,46 @@
HINSTANCE CMSWindowsScreen::s_instance = NULL;
CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL;
CMSWindowsScreen::CMSWindowsScreen() :
m_class(0),
CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver,
IMSWindowsScreenEventHandler* eventHandler) :
m_receiver(receiver),
m_eventHandler(eventHandler),
m_class(NULL),
m_icon(NULL),
m_cursor(NULL),
m_window(NULL),
m_x(0), m_y(0),
m_w(0), m_h(0),
m_thread(0),
m_screenSaver(NULL)
m_multimon(false),
m_threadID(0),
m_lastThreadID(0),
m_nextClipboardWindow(NULL),
m_clipboardOwner(NULL),
m_timer(0),
m_desk(NULL),
m_deskName(),
m_hookLibrary(NULL),
m_installScreensaver(NULL),
m_uninstallScreensaver(NULL),
m_screensaver(NULL),
m_screensaverNotify(false)
{
assert(s_screen == NULL);
assert(m_receiver != NULL);
assert(m_eventHandler != NULL);
s_screen = this;
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
}
CMSWindowsScreen::~CMSWindowsScreen()
{
assert(s_screen != NULL);
assert(m_class == 0);
s_screen = NULL;
}
@ -47,11 +77,110 @@ CMSWindowsScreen::init(HINSTANCE instance)
s_instance = instance;
}
HWND
CMSWindowsScreen::openDesktop()
{
// save thread id
m_threadID = GetCurrentThreadId();
// get the input desktop and switch to it
if (!switchDesktop(openInputDesktop())) {
return NULL;
}
// poll input desktop to see if it changes (onPreDispatch()
// handles WM_TIMER)
m_timer = 0;
if (!m_is95Family) {
m_timer = SetTimer(NULL, 0, 200, NULL);
}
return m_window;
}
void
CMSWindowsScreen::closeDesktop()
{
// remove timer
if (m_timer != 0) {
KillTimer(NULL, m_timer);
}
// disconnect from desktop
switchDesktop(NULL);
// clear thread id
m_threadID = 0;
assert(m_window == NULL);
assert(m_desk == NULL);
}
bool
CMSWindowsScreen::isMultimon() const
{
return m_multimon;
}
HINSTANCE
CMSWindowsScreen::getInstance()
{
return s_instance;
}
void
CMSWindowsScreen::open()
{
assert(s_instance != NULL);
assert(m_class == 0);
log((CLOG_DEBUG "opening display"));
// create the transparent cursor
createBlankCursor();
// register a window class
WNDCLASSEX classInfo;
classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = 0;
classInfo.hInstance = s_instance;
classInfo.hIcon = NULL;
classInfo.hCursor = m_cursor;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = "Synergy";
classInfo.hIconSm = NULL;
m_class = RegisterClassEx(&classInfo);
// get screen shape
updateScreenShape();
// initialize the screen saver
m_screensaver = new CMSWindowsScreenSaver();
// load the hook library and get the screen saver functions
m_hookLibrary = LoadLibrary("synrgyhk");
if (m_hookLibrary != NULL) {
m_installScreensaver = (InstallScreenSaverFunc)GetProcAddress(
m_hookLibrary, "installScreenSaver");
m_uninstallScreensaver = (UninstallScreenSaverFunc)GetProcAddress(
m_hookLibrary, "uninstallScreenSaver");
if (m_installScreensaver == NULL || m_uninstallScreensaver == NULL) {
// disable if either install or uninstall is unavailable
m_installScreensaver = NULL;
m_uninstallScreensaver = NULL;
}
}
}
void
CMSWindowsScreen::mainLoop()
{
// save thread id for posting quit message
m_thread = GetCurrentThreadId();
// must call mainLoop() from same thread as openDesktop()
assert(m_threadID == GetCurrentThreadId());
// event loop
CEvent event;
@ -77,55 +206,25 @@ CMSWindowsScreen::mainLoop()
void
CMSWindowsScreen::exitMainLoop()
{
PostThreadMessage(m_thread, WM_QUIT, 0, 0);
}
bool
CMSWindowsScreen::onPreDispatch(const CEvent*)
{
return false;
PostThreadMessage(m_threadID, WM_QUIT, 0, 0);
}
void
CMSWindowsScreen::openDisplay()
CMSWindowsScreen::close()
{
assert(s_instance != NULL);
assert(m_class == 0);
// create the transparent cursor
createBlankCursor();
// register a window class
WNDCLASSEX classInfo;
classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = 0;
classInfo.hInstance = s_instance;
classInfo.hIcon = NULL;
classInfo.hCursor = m_cursor;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = "Synergy";
classInfo.hIconSm = NULL;
m_class = RegisterClassEx(&classInfo);
// get screen shape
updateScreenShape();
// initialize the screen saver
m_screenSaver = new CMSWindowsScreenSaver();
// done with hook library
if (m_hookLibrary != NULL) {
FreeLibrary(m_hookLibrary);
m_installScreensaver = NULL;
m_uninstallScreensaver = NULL;
m_hookLibrary = NULL;
}
void
CMSWindowsScreen::closeDisplay()
{
assert(s_instance != NULL);
// done with screen saver
delete m_screenSaver;
m_screenSaver = NULL;
delete m_screensaver;
m_screensaver = NULL;
// unregister the window class
if (m_class != 0) {
@ -142,30 +241,130 @@ CMSWindowsScreen::closeDisplay()
log((CLOG_DEBUG "closed display"));
}
HINSTANCE
CMSWindowsScreen::getInstance()
bool
CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src)
{
return s_instance;
CMSWindowsClipboard dst(m_window);
if (src != NULL) {
// save clipboard data
return CClipboard::copy(&dst, src);
}
else {
// assert clipboard ownership
if (!dst.open(0)) {
return false;
}
dst.empty();
dst.close();
return true;
}
ATOM
CMSWindowsScreen::getClass() const
{
return m_class;
}
void
CMSWindowsScreen::updateScreenShape()
CMSWindowsScreen::checkClipboards()
{
m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h));
// if we think we own the clipboard but we don't then somebody
// grabbed the clipboard on this screen without us knowing.
// tell the server that this screen grabbed the clipboard.
//
// this works around bugs in the clipboard viewer chain.
// sometimes NT will simply never send WM_DRAWCLIPBOARD
// messages for no apparent reason and rebooting fixes the
// problem. since we don't want a broken clipboard until the
// next reboot we do this double check. clipboard ownership
// won't be reflected on other screens until we leave but at
// least the clipboard itself will work.
HWND clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != clipboardOwner) {
try {
m_clipboardOwner = clipboardOwner;
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_receiver->onGrabClipboard(kClipboardClipboard);
m_receiver->onGrabClipboard(kClipboardSelection);
}
}
catch (XBadClient&) {
// ignore
}
}
}
void
CMSWindowsScreen::getScreenShape(SInt32& x, SInt32& y,
CMSWindowsScreen::openScreensaver(bool notify)
{
assert(m_screensaver != NULL);
m_screensaverNotify = notify;
if (m_screensaverNotify) {
if (m_installScreensaver != NULL) {
m_installScreensaver();
}
}
else {
m_screensaver->disable();
}
}
void
CMSWindowsScreen::closeScreensaver()
{
if (m_screensaver != NULL) {
if (m_screensaverNotify) {
if (m_uninstallScreensaver != NULL) {
m_uninstallScreensaver();
}
}
else {
m_screensaver->enable();
}
}
m_screensaverNotify = false;
}
void
CMSWindowsScreen::screensaver(bool activate)
{
assert(m_screensaver != NULL);
if (activate) {
m_screensaver->activate();
}
else {
m_screensaver->deactivate();
}
}
void
CMSWindowsScreen::syncDesktop()
{
// change calling thread's desktop
if (!m_is95Family) {
if (SetThreadDesktop(m_desk) == 0) {
log((CLOG_WARN "failed to set desktop: %d", GetLastError()));
}
}
// attach input queues if not already attached. this has a habit
// of sucking up more and more CPU each time it's called (even if
// the threads are already attached). since we only expect one
// thread to call this more than once we can save just the last
// the attached thread.
DWORD threadID = GetCurrentThreadId();
if (threadID != m_lastThreadID && threadID != m_threadID) {
m_lastThreadID = threadID;
AttachThreadInput(threadID, m_threadID, TRUE);
}
}
bool
CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const
{
CMSWindowsClipboard src(m_window);
CClipboard::copy(dst, &src);
return true;
}
void
CMSWindowsScreen::getShape(SInt32& x, SInt32& y,
SInt32& w, SInt32& h) const
{
assert(m_class != 0);
@ -196,10 +395,153 @@ CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
y = GetSystemMetrics(SM_CYSCREEN) >> 1;
}
HCURSOR
CMSWindowsScreen::getBlankCursor() const
void
CMSWindowsScreen::updateScreenShape()
{
return m_cursor;
// get shape
m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h));
// check for multiple monitors
m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) ||
m_h != GetSystemMetrics(SM_CYSCREEN));
}
bool
CMSWindowsScreen::onPreDispatch(const CEvent* event)
{
// handle event
const MSG* msg = &event->m_msg;
switch (msg->message) {
case SYNERGY_MSG_SCREEN_SAVER:
if (msg->wParam != 0) {
if (m_screensaver->checkStarted(msg->message, FALSE, 0)) {
m_eventHandler->onScreensaver(true);
}
}
else {
m_eventHandler->onScreensaver(false);
}
return true;
case WM_TIMER:
// if current desktop is not the input desktop then switch to it.
// windows 95 doesn't support multiple desktops so don't bother
// to check under it.
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else {
switchDesktop(desk);
}
}
}
return true;
}
return m_eventHandler->onPreDispatch(event);
}
bool
CMSWindowsScreen::onEvent(CEvent* event)
{
assert(event != NULL);
const MSG& msg = event->m_msg;
switch (msg.message) {
case WM_QUERYENDSESSION:
if (m_is95Family) {
event->m_result = TRUE;
return true;
}
break;
case WM_ENDSESSION:
if (m_is95Family) {
if (msg.wParam == TRUE && msg.lParam == 0) {
exitMainLoop();
}
return true;
}
break;
case WM_PAINT:
ValidateRect(msg.hwnd, NULL);
return true;
case WM_DRAWCLIPBOARD:
log((CLOG_DEBUG "clipboard was taken"));
// first pass it on
if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow,
msg.message, msg.wParam, msg.lParam);
}
// now notify client that somebody changed the clipboard (unless
// we're now the owner, in which case it's because we took
// ownership, or now it's owned by nobody, which will happen if
// we owned it and switched desktops because we destroy our
// window to do that).
try {
m_clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_receiver->onGrabClipboard(kClipboardClipboard);
m_receiver->onGrabClipboard(kClipboardSelection);
}
}
catch (XBadClient&) {
// ignore. this can happen if we receive this event
// before we've fully started up.
}
return true;
case WM_CHANGECBCHAIN:
if (m_nextClipboardWindow == (HWND)msg.wParam) {
m_nextClipboardWindow = (HWND)msg.lParam;
}
else if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow,
msg.message, msg.wParam, msg.lParam);
}
return true;
case WM_DISPLAYCHANGE:
{
// screen resolution may have changed. get old shape.
SInt32 xOld, yOld, wOld, hOld;
getShape(xOld, yOld, wOld, hOld);
// update shape
updateScreenShape();
// collect new screen info
CClientInfo info;
getShape(info.m_x, info.m_y, info.m_w, info.m_h);
getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = m_eventHandler->getJumpZoneSize();
// do nothing if resolution hasn't changed
if (info.m_x != xOld || info.m_y != yOld ||
info.m_w != wOld || info.m_h != hOld) {
// forward event
m_eventHandler->onEvent(event);
// send new screen info
m_receiver->onInfoChanged(info);
}
return true;
}
}
return m_eventHandler->onEvent(event);
}
void
@ -217,14 +559,122 @@ CMSWindowsScreen::createBlankCursor()
delete[] cursorAND;
}
bool
CMSWindowsScreen::switchDesktop(HDESK desk)
{
// did we own the clipboard?
bool ownClipboard = (m_clipboardOwner == m_window && m_window != NULL);
// destroy old window
if (m_window != NULL) {
// first remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// we no longer own the clipboard
if (ownClipboard) {
m_clipboardOwner = NULL;
}
// let client clean up before we destroy the window
m_eventHandler->preDestroyWindow(m_window);
// now destroy window
DestroyWindow(m_window);
m_window = NULL;
// done with desk
if (!m_is95Family) {
CloseDesktop(m_desk);
}
m_desk = NULL;
m_deskName = "";
}
// if no new desktop then we're done
if (desk == NULL) {
log((CLOG_INFO "disconnecting desktop"));
return true;
}
// uninstall screen saver hooks
if (m_screensaverNotify) {
if (m_uninstallScreensaver != NULL) {
m_uninstallScreensaver();
}
}
// set the desktop. can only do this when there are no windows
// and hooks on the current desktop owned by this thread.
if (SetThreadDesktop(desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
if (!m_is95Family) {
CloseDesktop(desk);
}
return false;
}
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT |
WS_EX_TOOLWINDOW,
(LPCTSTR)m_class,
"Synergy",
WS_POPUP,
0, 0, 1, 1,
NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
if (!m_is95Family) {
CloseDesktop(desk);
}
return false;
}
// reinstall screen saver hooks
if (m_screensaverNotify) {
if (m_installScreensaver != NULL) {
m_installScreensaver();
}
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
// reassert clipboard ownership
if (ownClipboard) {
// FIXME -- take clipboard ownership, but we should also set
// the clipboard data.
}
m_clipboardOwner = GetClipboardOwner();
// save new desktop
m_desk = desk;
m_deskName = getDesktopName(m_desk);
log((CLOG_INFO "switched to desktop \"%s\"", m_deskName.c_str()));
// let client prepare the window
m_eventHandler->postCreateWindow(m_window);
return true;
}
HDESK
CMSWindowsScreen::openInputDesktop() const
{
if (m_is95Family) {
// there's only one desktop on windows 95 et al.
return GetThreadDesktop(GetCurrentThreadId());
}
else {
return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE,
DESKTOP_CREATEWINDOW |
DESKTOP_HOOKCONTROL |
GENERIC_WRITE);
}
}
CString
CMSWindowsScreen::getDesktopName(HDESK desk) const
@ -232,6 +682,9 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const
if (desk == NULL) {
return CString();
}
else if (m_is95Family) {
return "desktop";
}
else {
DWORD size;
GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
@ -246,14 +699,7 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const
bool
CMSWindowsScreen::isCurrentDesktop(HDESK desk) const
{
return CStringUtil::CaselessCmp::equal(getDesktopName(desk),
getCurrentDesktopName());
}
CMSWindowsScreenSaver*
CMSWindowsScreen::getScreenSaver() const
{
return m_screenSaver;
return CStringUtil::CaselessCmp::equal(getDesktopName(desk), m_deskName);
}
LRESULT CALLBACK

View File

@ -1,7 +1,8 @@
#ifndef CMSWINDOWSSCREEN_H
#define CMSWINDOWSSCREEN_H
#include "IClipboard.h"
#include "IScreen.h"
#include "CSynergyHook.h"
#include "CMutex.h"
#include "CString.h"
#define WIN32_LEAN_AND_MEAN
@ -16,52 +17,69 @@ public:
LRESULT m_result;
};
class CMSWindowsScreen {
class IScreenReceiver;
class IMSWindowsScreenEventHandler;
class CMSWindowsScreen : public IScreen {
public:
CMSWindowsScreen();
CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*);
virtual ~CMSWindowsScreen();
// manipulators
static void init(HINSTANCE);
// open the desktop and create and return the window. returns NULL
// on failure.
HWND openDesktop();
// close the window and desktop
void closeDesktop();
// accessors
// returns true iff the system appears to have multiple monitors
bool isMultimon() const;
// get the application instance handle
static HINSTANCE getInstance();
protected:
// runs an event loop and returns when exitMainLoop() is called
// IScreen overrides
// note -- this class expects the hook DLL to have been loaded
// and initialized before open() is called.
void open();
void mainLoop();
// force mainLoop() to return
void exitMainLoop();
void close();
bool setClipboard(ClipboardID, const IClipboard*);
void checkClipboards();
void openScreensaver(bool notify);
void closeScreensaver();
void screensaver(bool activate);
void syncDesktop();
bool getClipboard(ClipboardID, IClipboard*) const;
void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const;
void getCursorPos(SInt32&, SInt32&) const;
void getCursorCenter(SInt32&, SInt32&) const;
// open the X display. calls onOpenDisplay() after opening the display,
// getting the screen, its size, and root window. then it starts the
// event thread.
void openDisplay();
// destroy the window and close the display. calls onCloseDisplay()
// after the event thread has been shut down but before the display
// is closed.
void closeDisplay();
// get the registered window class atom
ATOM getClass() const;
private:
// update screen size cache
void updateScreenShape();
// get the size of the screen
void getScreenShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
// internal pre-dispatch event processing
bool onPreDispatch(const CEvent* event);
// get the current cursor position
void getCursorPos(SInt32& x, SInt32& y) const;
// internal (post-dispatch) event processing
bool onEvent(CEvent* event);
// get the cursor center position
void getCursorCenter(SInt32& x, SInt32& y) const;
// create the transparent cursor
void createBlankCursor();
// switch to the given desktop. this destroys the window and unhooks
// all hooks, switches the desktop, then creates the window and rehooks
// all hooks (because you can't switch the thread's desktop if it has
// any windows or hooks).
bool switchDesktop(HDESK desk);
// get the input desktop. caller must CloseDesktop() the result.
// do not call under windows 95/98/me.
@ -74,38 +92,56 @@ protected:
// windows 95/98/me.
bool isCurrentDesktop(HDESK desk) const;
// get the screen saver object
CMSWindowsScreenSaver*
getScreenSaver() const;
// called for each event before event translation and dispatch. return
// true to skip translation and dispatch. subclasses should call the
// superclass's version first and return true if it returns true.
virtual bool onPreDispatch(const CEvent* event);
// called by mainLoop(). iff the event was handled return true and
// store the result, if any, in m_result, which defaults to zero.
virtual bool onEvent(CEvent* event) = 0;
// called by isCurrentDesktop() to get the current desktop name
virtual CString getCurrentDesktopName() const = 0;
private:
// create the transparent cursor
void createBlankCursor();
// our window proc
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
private:
static HINSTANCE s_instance;
IScreenReceiver* m_receiver;
IMSWindowsScreenEventHandler* m_eventHandler;
ATOM m_class;
HICON m_icon;
HCURSOR m_cursor;
// true if windows 95/98/me
bool m_is95Family;
// our window
HWND m_window;
// screen shape
SInt32 m_x, m_y;
SInt32 m_w, m_h;
DWORD m_thread;
CMSWindowsScreenSaver* m_screenSaver;
// true if system appears to have multiple monitors
bool m_multimon;
// the main loop's thread id
DWORD m_threadID;
// the thread id of the last attached thread
DWORD m_lastThreadID;
// clipboard stuff
HWND m_nextClipboardWindow;
HWND m_clipboardOwner;
// the timer used to check for desktop switching
UINT m_timer;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;
// screen saver stuff
HINSTANCE m_hookLibrary;
InstallScreenSaverFunc m_installScreensaver;
UninstallScreenSaverFunc m_uninstallScreensaver;
CMSWindowsScreenSaver* m_screensaver;
bool m_screensaverNotify;
static CMSWindowsScreen* s_screen;
};

View File

@ -10,8 +10,6 @@
#include "CThread.h"
#include "CLog.h"
#include "IJob.h"
//#include "CString.h"
//#include <cstdlib>
#include <cstring>
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
@ -310,7 +308,7 @@ CXWindowsScreen::checkClipboards()
}
void
CXWindowsScreen::openScreenSaver(bool notify)
CXWindowsScreen::openScreensaver(bool notify)
{
CLock lock(&m_mutex);
assert(m_screensaver != NULL);
@ -325,7 +323,7 @@ CXWindowsScreen::openScreenSaver(bool notify)
}
void
CXWindowsScreen::closeScreenSaver()
CXWindowsScreen::closeScreensaver()
{
CLock lock(&m_mutex);
if (m_screensaver != NULL) {

View File

@ -2,7 +2,6 @@
#define CXWINDOWSSCREEN_H
#include "IScreen.h"
#include "ClipboardTypes.h"
#include "CMutex.h"
#include "CStopwatch.h"
#include "stdvector.h"
@ -59,8 +58,8 @@ public:
void close();
bool setClipboard(ClipboardID, const IClipboard*);
void checkClipboards();
void openScreenSaver(bool notify);
void closeScreenSaver();
void openScreensaver(bool notify);
void closeScreensaver();
void screensaver(bool activate);
void syncDesktop();
bool getClipboard(ClipboardID, IClipboard*) const;

View File

@ -0,0 +1,26 @@
#ifndef IMSWINDOWSSCREENEVENTHANDLER_H
#define IMSWINDOWSSCREENEVENTHANDLER_H
#include "IScreenEventHandler.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class IMSWindowsScreenEventHandler : public IScreenEventHandler {
public:
// manipulators
// called after the window is created
virtual void postCreateWindow(HWND) = 0;
// called before the window is destroyed
virtual void preDestroyWindow(HWND) = 0;
// IScreenEventHandler overrides
virtual void onError() = 0;
virtual void onScreensaver(bool activated) = 0;
virtual bool onPreDispatch(const CEvent* event) = 0;
virtual bool onEvent(CEvent* event) = 0;
virtual SInt32 getJumpZoneSize() const = 0;
};
#endif

View File

@ -127,6 +127,10 @@ SOURCE=.\CWin32Platform.h
# End Source File
# Begin Source File
SOURCE=.\IMSWindowsScreenEventHandler.h
# End Source File
# Begin Source File
SOURCE=.\IPlatform.h
# End Source File
# End Group

View File

@ -624,22 +624,22 @@ operator<<(std::ostream& s, const CConfig& config)
screen != config.end(); ++screen) {
s << "\t" << screen->c_str() << ":" << std::endl;
neighbor = config.getNeighbor(*screen, CConfig::kLeft);
neighbor = config.getNeighbor(*screen, kLeft);
if (!neighbor.empty()) {
s << "\t\tleft=" << neighbor.c_str() << std::endl;
}
neighbor = config.getNeighbor(*screen, CConfig::kRight);
neighbor = config.getNeighbor(*screen, kRight);
if (!neighbor.empty()) {
s << "\t\tright=" << neighbor.c_str() << std::endl;
}
neighbor = config.getNeighbor(*screen, CConfig::kTop);
neighbor = config.getNeighbor(*screen, kTop);
if (!neighbor.empty()) {
s << "\t\tup=" << neighbor.c_str() << std::endl;
}
neighbor = config.getNeighbor(*screen, CConfig::kBottom);
neighbor = config.getNeighbor(*screen, kBottom);
if (!neighbor.empty()) {
s << "\t\tdown=" << neighbor.c_str() << std::endl;
}

View File

@ -1,6 +1,7 @@
#ifndef CCONFIG_H
#define CCONFIG_H
#include "ProtocolTypes.h"
#include "CNetworkAddress.h"
#include "XBase.h"
#include "stdmap.h"
@ -20,11 +21,6 @@ struct iterator_traits<CConfig> {
};
class CConfig {
public:
enum EDirection { kLeft, kRight, kTop, kBottom,
kFirstDirection = kLeft, kLastDirection = kBottom };
enum EDirectionMask { kLeftMask = 1, kRightMask = 2,
kTopMask = 4, kBottomMask = 8 };
private:
class CCell {
public:

View File

@ -649,7 +649,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config)
// insert the screen's neighbors
// FIXME -- handle edge wrapping
CString neighbor;
neighbor = config.getNeighbor(name, CConfig::kLeft);
neighbor = config.getNeighbor(name, kLeft);
if (!neighbor.empty() && doneSet.count(neighbor) == 0) {
// insert left neighbor, adding a column if necessary
if (x == 0 || get(x - 1, y) != neighbor) {
@ -659,7 +659,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config)
}
screenStack.push_back(neighbor);
}
neighbor = config.getNeighbor(name, CConfig::kRight);
neighbor = config.getNeighbor(name, kRight);
if (!neighbor.empty() && doneSet.count(neighbor) == 0) {
// insert right neighbor, adding a column if necessary
if (x == m_w - 1 || get(x + 1, y) != neighbor) {
@ -668,7 +668,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config)
}
screenStack.push_back(neighbor);
}
neighbor = config.getNeighbor(name, CConfig::kTop);
neighbor = config.getNeighbor(name, kTop);
if (!neighbor.empty() && doneSet.count(neighbor) == 0) {
// insert top neighbor, adding a row if necessary
if (y == 0 || get(x, y - 1) != neighbor) {
@ -678,7 +678,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config)
}
screenStack.push_back(neighbor);
}
neighbor = config.getNeighbor(name, CConfig::kBottom);
neighbor = config.getNeighbor(name, kBottom);
if (!neighbor.empty() && doneSet.count(neighbor) == 0) {
// insert bottom neighbor, adding a row if necessary
if (y == m_h - 1 || get(x, y + 1) != neighbor) {
@ -699,25 +699,25 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config)
}
CString neighbor;
neighbor = config.getNeighbor(name, CConfig::kLeft);
neighbor = config.getNeighbor(name, kLeft);
if ((x == 0 && !neighbor.empty()) ||
(x > 0 && get(x - 1, y) != neighbor)) {
return false;
}
neighbor = config.getNeighbor(name, CConfig::kRight);
neighbor = config.getNeighbor(name, kRight);
if ((x == m_w - 1 && !neighbor.empty()) ||
(x < m_w - 1 && get(x + 1, y) != neighbor)) {
return false;
}
neighbor = config.getNeighbor(name, CConfig::kTop);
neighbor = config.getNeighbor(name, kTop);
if ((y == 0 && !neighbor.empty()) ||
(y > 0 && get(x, y - 1) != neighbor)) {
return false;
}
neighbor = config.getNeighbor(name, CConfig::kBottom);
neighbor = config.getNeighbor(name, kBottom);
if ((y == m_h - 1 && !neighbor.empty()) ||
(y < m_h - 1 && get(x, y + 1) != neighbor)) {
return false;
@ -764,24 +764,16 @@ CHTTPServer::CScreenArray::convertTo(CConfig& config) const
continue;
}
if (x > x0 && isSet(x - 1, y)) {
config.connect(get(x, y),
CConfig::kLeft,
get(x - 1, y));
config.connect(get(x, y), kLeft, get(x - 1, y));
}
if (x < x1 && isSet(x + 1, y)) {
config.connect(get(x, y),
CConfig::kRight,
get(x + 1, y));
config.connect(get(x, y), kRight, get(x + 1, y));
}
if (y > y0 && isSet(x, y - 1)) {
config.connect(get(x, y),
CConfig::kTop,
get(x, y - 1));
config.connect(get(x, y), kTop, get(x, y - 1));
}
if (y < y1 && isSet(x, y + 1)) {
config.connect(get(x, y),
CConfig::kBottom,
get(x, y + 1));
config.connect(get(x, y), kBottom, get(x, y + 1));
}
}
}

View File

@ -1,14 +1,8 @@
#include "CMSWindowsPrimaryScreen.h"
#include "IScreenReceiver.h"
#include "CMSWindowsScreen.h"
#include "IPrimaryScreenReceiver.h"
#include "CMSWindowsClipboard.h"
#include "CMSWindowsScreenSaver.h"
#include "CPlatform.h"
#include "CClipboard.h"
#include "ProtocolTypes.h"
#include "XScreen.h"
#include "XSynergy.h"
#include "CThread.h"
#include "CLog.h"
#include <cstring>
@ -19,21 +13,16 @@
CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen(
IScreenReceiver* receiver,
IPrimaryScreenReceiver* primaryReceiver) :
m_receiver(receiver),
m_primaryReceiver(primaryReceiver),
CPrimaryScreen(receiver),
m_receiver(primaryReceiver),
m_is95Family(CPlatform::isWindows95Family()),
m_threadID(0),
m_timer(0),
m_desk(NULL),
m_deskName(),
m_window(NULL),
m_active(false),
m_mark(0),
m_markReceived(0),
m_nextClipboardWindow(NULL),
m_clipboardOwner(NULL)
m_mouseMoveIgnore(0)
{
assert(m_receiver != NULL);
assert(m_primaryReceiver != NULL);
// load the hook library
m_hookLibrary = LoadLibrary("synrgyhk");
@ -60,22 +49,8 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen(
throw XScreenOpenFailure();
}
// get the screen saver functions
m_installScreenSaver = (InstallScreenSaverFunc)GetProcAddress(
m_hookLibrary, "installScreenSaver");
m_uninstallScreenSaver = (UninstallScreenSaverFunc)GetProcAddress(
m_hookLibrary, "uninstallScreenSaver");
if (m_installScreenSaver == NULL || m_uninstallScreenSaver == NULL) {
// disable uninstall if install is unavailable
m_uninstallScreenSaver = NULL;
}
// detect operating system
m_is95Family = CPlatform::isWindows95Family();
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// create screen
m_screen = new CMSWindowsScreen(receiver, this);
}
CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
@ -83,169 +58,10 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
assert(m_hookLibrary != NULL);
assert(m_window == NULL);
// done with hook library
delete m_screen;
FreeLibrary(m_hookLibrary);
}
void
CMSWindowsPrimaryScreen::run()
{
// must call run() from same thread as open()
assert(m_threadID == GetCurrentThreadId());
// change our priority
CThread::getCurrentThread().setPriority(-3);
// run event loop
try {
log((CLOG_INFO "entering event loop"));
mainLoop();
log((CLOG_INFO "exiting event loop"));
}
catch (...) {
log((CLOG_INFO "exiting event loop"));
throw;
}
}
void
CMSWindowsPrimaryScreen::stop()
{
exitMainLoop();
}
void
CMSWindowsPrimaryScreen::open()
{
assert(m_window == NULL);
CClientInfo info;
try {
// initialize hook library
m_threadID = GetCurrentThreadId();
m_init(m_threadID);
// open the display
openDisplay();
// create and prepare our window
createWindow();
// set jump zones
m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize);
// initialize marks
m_mark = 0;
m_markReceived = 0;
nextMark();
// collect screen info
getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h);
getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = getJumpZoneSize();
// save mouse position
m_x = info.m_mx;
m_y = info.m_my;
// compute center pixel of primary screen
getCursorCenter(m_xCenter, m_yCenter);
// get keyboard state
updateKeys();
// get notified of screen saver activation/deactivation
installScreenSaver();
}
catch (...) {
close();
throw;
}
// enter the screen
enterNoWarp();
// send screen info
m_receiver->onInfoChanged(info);
}
void
CMSWindowsPrimaryScreen::close()
{
uninstallScreenSaver();
destroyWindow();
closeDisplay();
m_cleanup();
m_threadID = 0;
}
void
CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver)
{
log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : ""));
assert(m_active == true);
assert(m_window != NULL);
// enter the screen
enterNoWarp();
// warp to requested location
if (!forScreenSaver) {
warpCursor(x, y);
}
}
bool
CMSWindowsPrimaryScreen::leave()
{
log((CLOG_INFO "leaving primary"));
assert(m_active == false);
assert(m_window != NULL);
// all messages prior to now are invalid
nextMark();
// show our window
if (!showWindow()) {
return false;
}
// relay all mouse and keyboard events
m_setRelay(true);
// get state of keys as we leave
updateKeys();
// warp mouse to center of screen
warpCursorToCenter();
// ignore this many mouse motion events (not including the already
// queued events). on (at least) the win2k login desktop, one
// motion event is reported using a position from before the above
// warpCursor(). i don't know why it does that and other desktops
// don't have the same problem. anyway, simply ignoring that event
// works around it.
// FIXME -- is this true now that we're using mouse_event?
m_mouseMoveIgnore = 1;
// disable ctrl+alt+del, alt+tab, etc
if (m_is95Family) {
DWORD dummy = 0;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0);
}
// discard messages until after the warp
nextMark();
// local client now active
m_active = true;
// make sure our idea of clipboard ownership is correct
checkClipboard();
return true;
}
void
CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides)
{
@ -264,41 +80,6 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
m_y = y;
}
void
CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/,
const IClipboard* src)
{
// FIXME -- this is identical to CMSWindowsSecondaryScreen's code
assert(m_window != NULL);
CMSWindowsClipboard dst(m_window);
CClipboard::copy(&dst, src);
}
void
CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/)
{
// FIXME -- this is identical to CMSWindowsSecondaryScreen's code
assert(m_window != NULL);
CMSWindowsClipboard clipboard(m_window);
if (clipboard.open(0)) {
// FIXME -- don't we need to empty it?
clipboard.close();
}
}
void
CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/,
IClipboard* dst) const
{
// FIXME -- this is identical to CMSWindowsSecondaryScreen's code
assert(m_window != NULL);
CMSWindowsClipboard src(m_window);
CClipboard::copy(dst, &src);
}
KeyModifierMask
CMSWindowsPrimaryScreen::getToggleMask() const
{
@ -334,7 +115,7 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const
// that have been pulled off the queue. in general, we won't get
// these key messages because they're not for our window. if any
// key (or mouse button) is down then we're locked to the screen.
if (m_active) {
if (isActive()) {
// use shadow keyboard state in m_keys
for (UInt32 i = 0; i < 256; ++i) {
if ((m_keys[i] & 0x80) != 0) {
@ -358,16 +139,29 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const
return false;
}
IScreen*
CMSWindowsPrimaryScreen::getScreen() const
{
return m_screen;
}
void
CMSWindowsPrimaryScreen::onError()
{
// ignore
}
void
CMSWindowsPrimaryScreen::onScreensaver(bool activated)
{
m_receiver->onScreensaver(activated);
}
bool
CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
{
assert(event != NULL);
// forward to superclass
if (CMSWindowsScreen::onPreTranslate(event)) {
return true;
}
// handle event
const MSG* msg = &event->m_msg;
switch (msg->message) {
@ -386,11 +180,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
const SInt32 repeat = (SInt32)(msg->lParam & 0xffff);
if (repeat >= 2) {
log((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat));
m_primaryReceiver->onKeyRepeat(key, mask, repeat);
m_receiver->onKeyRepeat(key, mask, repeat);
}
else {
log((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask));
m_primaryReceiver->onKeyDown(key, mask);
m_receiver->onKeyDown(key, mask);
}
// update key state
@ -399,7 +193,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
else {
// key release
log((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask));
m_primaryReceiver->onKeyUp(key, mask);
m_receiver->onKeyUp(key, mask);
// update key state
updateKey(msg->wParam, false);
@ -428,7 +222,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
case WM_RBUTTONDOWN:
log((CLOG_DEBUG1 "event: button press button=%d", button));
if (button != kButtonNone) {
m_primaryReceiver->onMouseDown(button);
m_receiver->onMouseDown(button);
m_keys[s_vkButton[button]] |= 0x80;
}
break;
@ -438,7 +232,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
case WM_RBUTTONUP:
log((CLOG_DEBUG1 "event: button release button=%d", button));
if (button != kButtonNone) {
m_primaryReceiver->onMouseUp(button);
m_receiver->onMouseUp(button);
m_keys[s_vkButton[button]] &= ~0x80;
}
break;
@ -450,7 +244,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
// ignore message if posted prior to last mark change
if (m_markReceived == m_mark) {
log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam));
m_primaryReceiver->onMouseWheel(msg->wParam);
m_receiver->onMouseWheel(msg->wParam);
}
return true;
@ -459,8 +253,8 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
if (m_markReceived == m_mark) {
SInt32 x = static_cast<SInt32>(msg->wParam);
SInt32 y = static_cast<SInt32>(msg->lParam);
if (!m_active) {
m_primaryReceiver->onMouseMovePrimary(x, y);
if (!isActive()) {
m_receiver->onMouseMovePrimary(x, y);
}
else {
// compute motion delta. this is relative to the
@ -480,7 +274,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
warpCursorToCenter();
// send motion
m_primaryReceiver->onMouseMoveSecondary(x, y);
m_receiver->onMouseMoveSecondary(x, y);
}
}
else {
@ -495,32 +289,6 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
m_x = static_cast<SInt32>(msg->wParam);
m_y = static_cast<SInt32>(msg->lParam);
return true;
case SYNERGY_MSG_SCREEN_SAVER:
if (msg->wParam != 0) {
if (getScreenSaver()->checkStarted(msg->message, FALSE, 0)) {
m_primaryReceiver->onScreenSaver(true);
}
}
else {
m_primaryReceiver->onScreenSaver(false);
}
return true;
case WM_TIMER:
// if current desktop is not the input desktop then switch to it
if (!m_is95Family) {
HDESK desk = openInputDesktop();
if (desk != NULL) {
if (isCurrentDesktop(desk)) {
CloseDesktop(desk);
}
else {
switchDesktop(desk);
}
}
}
return true;
}
return false;
@ -531,111 +299,29 @@ CMSWindowsPrimaryScreen::onEvent(CEvent* event)
{
assert(event != NULL);
const MSG& msg = event->msg;
const MSG& msg = event->m_msg;
switch (msg.message) {
case WM_QUERYENDSESSION:
if (m_is95Family) {
event->m_result = TRUE;
return true;
}
break;
case WM_ENDSESSION:
if (m_is95Family) {
if (msg.wParam == TRUE && msg.lParam == 0) {
stop();
}
return true;
}
break;
case WM_PAINT:
ValidateRect(msg.hwnd, NULL);
return true;
case WM_DRAWCLIPBOARD:
log((CLOG_DEBUG "clipboard was taken"));
// first pass it on
if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow,
msg.message, msg.wParam, msg.lParam);
}
// now notify server that somebody changed the clipboard.
// skip that if we're the new owner.
try {
m_clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_receiver->onGrabClipboard(kClipboardClipboard);
m_receiver->onGrabClipboard(kClipboardSelection);
}
}
catch (XBadClient&) {
// ignore. this can happen if we receive this event
// before we've fully started up.
}
return true;
case WM_CHANGECBCHAIN:
if (m_nextClipboardWindow == (HWND)msg.wParam) {
m_nextClipboardWindow = (HWND)msg.lParam;
}
else if (m_nextClipboardWindow != NULL) {
SendMessage(m_nextClipboardWindow,
msg.message, msg.wParam, msg.lParam);
}
return true;
case WM_DISPLAYCHANGE:
{
// screen resolution may have changed. get old shape.
SInt32 xOld, yOld, wOld, hOld;
getScreenShape(xOld, yOld, wOld, hOld);
// update shape
updateScreenShape();
// collect new screen info
CClientInfo info;
getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h);
getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = getJumpZoneSize();
// do nothing if resolution hasn't changed
if (info.m_x != xOld || info.m_y != yOld ||
info.m_w != wOld || info.m_h != hOld) {
// recompute center pixel of primary screen
getCursorCenter(m_xCenter, m_yCenter);
m_screen->getCursorCenter(m_xCenter, m_yCenter);
// warp mouse to center if active
if (m_active) {
if (isActive()) {
warpCursorToCenter();
}
// tell hook about resize if not active
else {
m_setZone(info.m_x, info.m_y,
info.m_w, info.m_h, info.m_zoneSize);
SInt32 x, y, w, h;
m_screen->getShape(x, y, w, h);
m_setZone(x, y, w, h, getJumpZoneSize());
}
// send new screen info
m_receiver->onInfoChanged(info);
}
return true;
}
}
return false;
}
CString
CMSWindowsPrimaryScreen::getCurrentDesktopName() const
{
return m_deskName;
}
SInt32
CMSWindowsPrimaryScreen::getJumpZoneSize() const
{
@ -643,25 +329,96 @@ CMSWindowsPrimaryScreen::getJumpZoneSize() const
}
void
CMSWindowsPrimaryScreen::warpCursorToCenter()
CMSWindowsPrimaryScreen::postCreateWindow(HWND window)
{
// warp to center. the extra info tells the hook DLL to send
// SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE.
// save window
m_window = window;
// install hooks
m_install();
// resize window
// 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 (when entering
// the screen) causes all other windows to redraw.
SInt32 x, y, w, h;
getScreenShape(x, y, w, h);
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
(DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)),
(DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)),
0,
0x12345678);
// FIXME -- ignore mouse until we get warp notification?
m_screen->getShape(x, y, w, h);
MoveWindow(m_window, x, y, w, h, FALSE);
if (isActive()) {
// hide the cursor
showWindow();
}
else {
// watch jump zones
m_setRelay(false);
// all messages prior to now are invalid
nextMark();
}
}
void
CMSWindowsPrimaryScreen::enterNoWarp()
CMSWindowsPrimaryScreen::preDestroyWindow(HWND)
{
// not active anymore
m_active = false;
// hide the window if it's visible
if (isActive()) {
hideWindow();
}
// uninstall hooks
m_uninstall();
}
void
CMSWindowsPrimaryScreen::onPreRun()
{
// must call run() from same thread as open()
assert(m_threadID == GetCurrentThreadId());
assert(m_window != NULL);
}
void
CMSWindowsPrimaryScreen::onPreOpen()
{
assert(m_window == NULL);
// initialize hook library
m_threadID = GetCurrentThreadId();
m_init(m_threadID);
}
void
CMSWindowsPrimaryScreen::onPostOpen()
{
// get cursor info
m_screen->getCursorPos(m_x, m_y);
m_screen->getCursorCenter(m_xCenter, m_yCenter);
// set jump zones
SInt32 x, y, w, h;
m_screen->getShape(x, y, w, h);
m_setZone(x, y, w, h, getJumpZoneSize());
// initialize marks
m_mark = 0;
m_markReceived = 0;
nextMark();
}
void
CMSWindowsPrimaryScreen::onPostClose()
{
m_cleanup();
m_threadID = 0;
}
void
CMSWindowsPrimaryScreen::onPreEnter()
{
assert(m_window != NULL);
// reset motion ignore count
m_mouseMoveIgnore = 0;
@ -674,14 +431,78 @@ CMSWindowsPrimaryScreen::enterNoWarp()
// watch jump zones
m_setRelay(false);
}
// restore active window and hide our window
hideWindow();
void
CMSWindowsPrimaryScreen::onPostEnter()
{
// all messages prior to now are invalid
nextMark();
}
void
CMSWindowsPrimaryScreen::onPreLeave()
{
assert(m_window != NULL);
// all messages prior to now are invalid
nextMark();
}
void
CMSWindowsPrimaryScreen::onPostLeave(bool success)
{
if (success) {
// relay all mouse and keyboard events
m_setRelay(true);
// ignore this many mouse motion events (not including the already
// queued events). on (at least) the win2k login desktop, one
// motion event is reported using a position from before the above
// warpCursor(). i don't know why it does that and other desktops
// don't have the same problem. anyway, simply ignoring that event
// works around it.
// FIXME -- is this true now that we're using mouse_event?
m_mouseMoveIgnore = 1;
// disable ctrl+alt+del, alt+tab, etc
if (m_is95Family) {
DWORD dummy = 0;
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0);
}
// discard messages until after the warp
nextMark();
}
}
void
CMSWindowsPrimaryScreen::createWindow()
{
// open the desktop and the window
m_window = m_screen->openDesktop();
if (m_window == NULL) {
throw XScreenOpenFailure();
}
// 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 (when entering
// the scren causes all other windows to redraw).
SInt32 x, y, w, h;
m_screen->getShape(x, y, w, h);
MoveWindow(m_window, x, y, w, h, FALSE);
}
void
CMSWindowsPrimaryScreen::destroyWindow()
{
// close the desktop and the window
m_screen->closeDesktop();
m_window = NULL;
}
bool
CMSWindowsPrimaryScreen::showWindow()
{
@ -733,262 +554,18 @@ CMSWindowsPrimaryScreen::hideWindow()
}
void
CMSWindowsPrimaryScreen::checkClipboard()
CMSWindowsPrimaryScreen::warpCursorToCenter()
{
// if we think we own the clipboard but we don't then somebody
// grabbed the clipboard on this screen without us knowing.
// tell the server that this screen grabbed the clipboard.
//
// this works around bugs in the clipboard viewer chain.
// sometimes NT will simply never send WM_DRAWCLIPBOARD
// messages for no apparent reason and rebooting fixes the
// problem. since we don't want a broken clipboard until the
// next reboot we do this double check. clipboard ownership
// won't be reflected on other screens until we leave but at
// least the clipboard itself will work.
HWND clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != clipboardOwner) {
try {
m_clipboardOwner = clipboardOwner;
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
m_receiver->onGrabClipboard(kClipboardClipboard);
m_receiver->onGrabClipboard(kClipboardSelection);
}
}
catch (XBadClient&) {
// ignore
}
}
}
void
CMSWindowsPrimaryScreen::createWindow()
{
// get the input desktop and switch to it
if (m_is95Family) {
if (!openDesktop()) {
throw XScreenOpenFailure();
}
}
else {
if (!switchDesktop(openInputDesktop())) {
throw XScreenOpenFailure();
}
}
// poll input desktop to see if it changes (preTranslateMessage()
// handles WM_TIMER)
m_timer = 0;
if (!m_is95Family) {
m_timer = SetTimer(NULL, 0, 200, NULL);
}
}
void
CMSWindowsPrimaryScreen::destroyWindow()
{
// remove timer
if (m_timer != 0) {
KillTimer(NULL, m_timer);
}
// disconnect from desktop
if (m_is95Family) {
closeDesktop();
}
else {
switchDesktop(NULL);
}
assert(m_window == NULL);
assert(m_desk == NULL);
}
void
CMSWindowsPrimaryScreen::installScreenSaver()
{
// install the screen saver hook
if (m_installScreenSaver != NULL) {
m_installScreenSaver();
}
}
void
CMSWindowsPrimaryScreen::uninstallScreenSaver()
{
// uninstall the screen saver hook
if (m_uninstallScreenSaver != NULL) {
m_uninstallScreenSaver();
}
}
bool
CMSWindowsPrimaryScreen::openDesktop()
{
// install hooks
m_install();
// 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 (when entering
// the scren causes all other windows to redraw).
// warp to center. the extra info tells the hook DLL to send
// SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE.
SInt32 x, y, w, h;
getScreenShape(x, y, w, h);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT |
WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(),
"Synergy",
WS_POPUP,
x, y, w, h, NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
m_uninstall();
return false;
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
return true;
}
void
CMSWindowsPrimaryScreen::closeDesktop()
{
// destroy old window
if (m_window != NULL) {
// restore active window and hide ours
if (m_active) {
hideWindow();
}
// first remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// we no longer own the clipboard
if (m_clipboardOwner == m_window) {
m_clipboardOwner = NULL;
}
// now destroy window
DestroyWindow(m_window);
m_window = NULL;
}
// unhook
m_uninstall();
}
bool
CMSWindowsPrimaryScreen::switchDesktop(HDESK desk)
{
// did we own the clipboard?
bool ownClipboard = (m_clipboardOwner == m_window);
// destroy old window
if (m_window != NULL) {
// restore active window and hide ours
if (m_active) {
hideWindow();
}
// first remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// we no longer own the clipboard
if (ownClipboard) {
m_clipboardOwner = NULL;
}
// now destroy window
DestroyWindow(m_window);
m_window = NULL;
}
// unhook
if (m_desk != NULL) {
m_uninstall();
CloseDesktop(m_desk);
m_desk = NULL;
m_deskName = "";
}
// if no new desktop then we're done
if (desk == NULL) {
log((CLOG_INFO "disconnecting desktop"));
return true;
}
// set the desktop. can only do this when there are no windows
// and hooks on the current desktop owned by this thread.
if (SetThreadDesktop(desk) == 0) {
log((CLOG_ERR "failed to set desktop: %d", GetLastError()));
CloseDesktop(desk);
return false;
}
// install hooks
m_install();
// 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 (when entering
// the scren causes all other windows to redraw).
SInt32 x, y, w, h;
getScreenShape(x, y, w, h);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT |
WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(),
"Synergy",
WS_POPUP,
x, y, w, h, NULL, NULL,
getInstance(),
NULL);
if (m_window == NULL) {
log((CLOG_ERR "failed to create window: %d", GetLastError()));
m_uninstall();
CloseDesktop(desk);
return false;
}
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
// reassert clipboard ownership
if (ownClipboard) {
// FIXME
}
// save new desktop
m_desk = desk;
m_deskName = getDesktopName(m_desk);
log((CLOG_INFO "switched to desktop %s", m_deskName.c_str()));
// get active window and show ours
if (m_active) {
showWindow();
}
else {
// watch jump zones
m_setRelay(false);
// all messages prior to now are invalid
nextMark();
}
return true;
m_screen->getShape(x, y, w, h);
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
(DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)),
(DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)),
0,
0x12345678);
// FIXME -- ignore mouse until we get warp notification?
}
void

View File

@ -1,74 +1,61 @@
#ifndef CMSWINDOWSPRIMARYSCREEN_H
#define CMSWINDOWSPRIMARYSCREEN_H
#include "CMSWindowsScreen.h"
#include "IPrimaryScreen.h"
#include "CPrimaryScreen.h"
#include "IMSWindowsScreenEventHandler.h"
#include "CSynergyHook.h"
#include "MouseTypes.h"
#include "CString.h"
class CMSWindowsScreen;
class IScreenReceiver;
class IPrimaryScreenReceiver;
class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen {
class CMSWindowsPrimaryScreen :
public CPrimaryScreen, public IMSWindowsScreenEventHandler {
public:
typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM);
CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*);
virtual ~CMSWindowsPrimaryScreen();
// IPrimaryScreen overrides
virtual void run();
virtual void stop();
virtual void open();
virtual void close();
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool);
virtual bool leave();
// CPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute);
virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID);
virtual void getClipboard(ClipboardID, IClipboard*) const;
virtual void warpCursor(SInt32 x, SInt32 y);
virtual KeyModifierMask getToggleMask() const;
virtual bool isLockedToScreen() const;
virtual IScreen* getScreen() const;
// IMSWindowsScreenEventHandler overrides
virtual void onError();
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND);
protected:
// CMSWindowsScreen overrides
virtual bool onPreTranslate(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual CString getCurrentDesktopName() const;
// CPrimaryScreen overrides
virtual void onPreRun();
virtual void onPreOpen();
virtual void onPostOpen();
virtual void onPostClose();
virtual void onPreEnter();
virtual void onPostEnter();
virtual void onPreLeave();
virtual void onPostLeave(bool);
virtual void createWindow();
virtual void destroyWindow();
virtual bool showWindow();
virtual void hideWindow();
virtual void warpCursorToCenter();
virtual void updateKeys();
private:
SInt32 getJumpZoneSize() const;
// warp mouse to center of primary display (used when computing
// motion deltas while mouse is on secondary screen).
void warpCursorToCenter();
void enterNoWarp();
bool showWindow();
void hideWindow();
// check clipboard ownership and, if necessary, tell the receiver
// of a grab.
void checkClipboard();
// create/destroy window
// also attach to desktop; this destroys and recreates the window
// as necessary.
void createWindow();
void destroyWindow();
// start/stop watch for screen saver changes
void installScreenSaver();
void uninstallScreenSaver();
// open/close desktop (for windows 95/98/me)
bool openDesktop();
void closeDesktop();
// make desk the thread desktop (for windows NT/2000/XP)
bool switchDesktop(HDESK desk);
// discard posted messages
void nextMark();
@ -77,12 +64,11 @@ private:
KeyID mapKey(WPARAM keycode, LPARAM info,
KeyModifierMask* maskOut);
ButtonID mapButton(WPARAM button) const;
void updateKeys();
void updateKey(UINT vkCode, bool press);
private:
IScreenReceiver* m_receiver;
IPrimaryScreenReceiver* m_primaryReceiver;
IPrimaryScreenReceiver* m_receiver;
CMSWindowsScreen* m_screen;
// true if windows 95/98/me
bool m_is95Family;
@ -90,27 +76,13 @@ private:
// the main loop's thread id
DWORD m_threadID;
// the timer used to check for desktop switching
UINT m_timer;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;
// our window (for getting clipboard changes)
// our window
HWND m_window;
// m_active is true the hooks are relaying events
bool m_active;
// used to discard queued messages that are no longer needed
UInt32 m_mark;
UInt32 m_markReceived;
// clipboard stuff
HWND m_nextClipboardWindow;
HWND m_clipboardOwner;
// map of key state
BYTE m_keys[256];
@ -132,8 +104,6 @@ private:
SetSidesFunc m_setSides;
SetZoneFunc m_setZone;
SetRelayFunc m_setRelay;
InstallScreenSaverFunc m_installScreenSaver;
UninstallScreenSaverFunc m_uninstallScreenSaver;
// stuff for restoring active window
HWND m_lastForegroundWindow;

View File

@ -73,7 +73,7 @@ CPrimaryScreen::open()
updateKeys();
// get notified of screen saver activation/deactivation
getScreen()->openScreenSaver(true);
getScreen()->openScreensaver(true);
// subclass hook
onPostOpen();
@ -94,7 +94,7 @@ void
CPrimaryScreen::close()
{
onPreClose();
getScreen()->closeScreenSaver();
getScreen()->closeScreensaver();
destroyWindow();
getScreen()->close();
onPostClose();
@ -197,12 +197,6 @@ CPrimaryScreen::getClipboard(ClipboardID id,
getScreen()->getClipboard(id, clipboard);
}
SInt32
CPrimaryScreen::getJumpZoneSize() const
{
return 1;
}
void
CPrimaryScreen::onPreRun()
{

View File

@ -42,8 +42,8 @@ public:
bool leave();
// called when the configuration has changed. activeSides is a
// bitmask of CConfig::EDirectionMask indicating which sides of
// the primary screen are linked to clients.
// bitmask of EDirectionMask indicating which sides of the
// primary screen are linked to clients.
virtual void reconfigure(UInt32 activeSides) = 0;
// warp the cursor to the given absolute coordinates
@ -66,8 +66,8 @@ public:
void getClipboard(ClipboardID, IClipboard*) const;
// returns the size of the zone on the edges of the screen that
// causes the cursor to jump to another screen. default returns 1.
virtual SInt32 getJumpZoneSize() const;
// causes the cursor to jump to another screen.
virtual SInt32 getJumpZoneSize() const = 0;
// get the primary screen's current toggle modifier key state.
// the returned mask should have the corresponding bit set for

View File

@ -197,21 +197,17 @@ CServer::getActivePrimarySides() const
{
// note -- m_mutex must be locked on entry
UInt32 sides = 0;
if (!m_config.getNeighbor(getPrimaryScreenName(),
CConfig::kLeft).empty()) {
sides |= CConfig::kLeftMask;
if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) {
sides |= kLeftMask;
}
if (!m_config.getNeighbor(getPrimaryScreenName(),
CConfig::kRight).empty()) {
sides |= CConfig::kRightMask;
if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) {
sides |= kRightMask;
}
if (!m_config.getNeighbor(getPrimaryScreenName(),
CConfig::kTop).empty()) {
sides |= CConfig::kTopMask;
if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) {
sides |= kTopMask;
}
if (!m_config.getNeighbor(getPrimaryScreenName(),
CConfig::kBottom).empty()) {
sides |= CConfig::kBottomMask;
if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) {
sides |= kBottomMask;
}
return sides;
}
@ -505,25 +501,25 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y)
SInt32 zoneSize = m_active->getJumpZoneSize();
// see if we should change screens
CConfig::EDirection dir;
EDirection dir;
if (x < ax + zoneSize) {
x -= zoneSize;
dir = CConfig::kLeft;
dir = kLeft;
log((CLOG_DEBUG1 "switch to left"));
}
else if (x >= ax + aw - zoneSize) {
x += zoneSize;
dir = CConfig::kRight;
dir = kRight;
log((CLOG_DEBUG1 "switch to right"));
}
else if (y < ay + zoneSize) {
y -= zoneSize;
dir = CConfig::kTop;
dir = kTop;
log((CLOG_DEBUG1 "switch to top"));
}
else if (y >= ay + ah - zoneSize) {
y += zoneSize;
dir = CConfig::kBottom;
dir = kBottom;
log((CLOG_DEBUG1 "switch to bottom"));
}
else {
@ -584,24 +580,24 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
IClient* newScreen = NULL;
if (!isLockedToScreenNoLock()) {
// find direction of neighbor
CConfig::EDirection dir;
EDirection dir;
if (m_x < ax) {
dir = CConfig::kLeft;
dir = kLeft;
}
else if (m_x > ax + aw - 1) {
dir = CConfig::kRight;
dir = kRight;
}
else if (m_y < ay) {
dir = CConfig::kTop;
dir = kTop;
}
else if (m_y > ay + ah - 1) {
dir = CConfig::kBottom;
dir = kBottom;
}
else {
newScreen = m_active;
// keep compiler quiet about unset variable
dir = CConfig::kLeft;
dir = kLeft;
}
// get neighbor if we should switch
@ -760,7 +756,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver)
}
IClient*
CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const
CServer::getNeighbor(IClient* src, EDirection dir) const
{
assert(src != NULL);
@ -793,7 +789,7 @@ CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const
IClient*
CServer::getNeighbor(IClient* src,
CConfig::EDirection srcSide, SInt32& x, SInt32& y) const
EDirection srcSide, SInt32& x, SInt32& y) const
{
assert(src != NULL);
@ -816,7 +812,7 @@ CServer::getNeighbor(IClient* src,
// actual to canonical position on entry to and from canonical to
// actual on exit from the search.
switch (srcSide) {
case CConfig::kLeft:
case kLeft:
x -= dx;
while (dst != NULL && dst != lastGoodScreen) {
lastGoodScreen = dst;
@ -832,7 +828,7 @@ CServer::getNeighbor(IClient* src,
x += dx;
break;
case CConfig::kRight:
case kRight:
x -= dx;
while (dst != NULL) {
x -= dw;
@ -848,7 +844,7 @@ CServer::getNeighbor(IClient* src,
x += dx;
break;
case CConfig::kTop:
case kTop:
y -= dy;
while (dst != NULL) {
lastGoodScreen = dst;
@ -864,7 +860,7 @@ CServer::getNeighbor(IClient* src,
y += dy;
break;
case CConfig::kBottom:
case kBottom:
y -= dy;
while (dst != NULL) {
y -= dh;
@ -892,26 +888,26 @@ CServer::getNeighbor(IClient* src,
if (dst == m_primaryClient) {
const CString dstName(dst->getName());
switch (srcSide) {
case CConfig::kLeft:
if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() &&
case kLeft:
if (!m_config.getNeighbor(dstName, kRight).empty() &&
x > dx + dw - 1 - dst->getJumpZoneSize())
x = dx + dw - 1 - dst->getJumpZoneSize();
break;
case CConfig::kRight:
if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() &&
case kRight:
if (!m_config.getNeighbor(dstName, kLeft).empty() &&
x < dx + dst->getJumpZoneSize())
x = dx + dst->getJumpZoneSize();
break;
case CConfig::kTop:
if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() &&
case kTop:
if (!m_config.getNeighbor(dstName, kBottom).empty() &&
y > dy + dh - 1 - dst->getJumpZoneSize())
y = dy + dh - 1 - dst->getJumpZoneSize();
break;
case CConfig::kBottom:
if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() &&
case kBottom:
if (!m_config.getNeighbor(dstName, kTop).empty() &&
y < dy + dst->getJumpZoneSize())
y = dy + dst->getJumpZoneSize();
break;
@ -925,8 +921,8 @@ CServer::getNeighbor(IClient* src,
// should be set 120 pixels from the top (again 20% from the
// top).
switch (srcSide) {
case CConfig::kLeft:
case CConfig::kRight:
case kLeft:
case kRight:
y -= sy;
if (y < 0) {
y = 0;
@ -941,8 +937,8 @@ CServer::getNeighbor(IClient* src,
y += dy;
break;
case CConfig::kTop:
case CConfig::kBottom:
case kTop:
case kBottom:
x -= sx;
if (x < 0) {
x = 0;

View File

@ -96,15 +96,14 @@ private:
SInt32 x, SInt32 y, bool forScreenSaver);
// lookup neighboring screen
IClient* getNeighbor(IClient*, CConfig::EDirection) const;
IClient* getNeighbor(IClient*, EDirection) const;
// lookup neighboring screen. given a position relative to the
// source screen, find the screen we should move onto and where.
// if the position is sufficiently far from the source then we
// cross multiple screens. if there is no suitable screen then
// return NULL and x,y are not modified.
IClient* getNeighbor(IClient*,
CConfig::EDirection,
IClient* getNeighbor(IClient*, EDirection,
SInt32& x, SInt32& y) const;
// open/close the primary screen

View File

@ -331,6 +331,12 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event)
return false;
}
SInt32
CXWindowsPrimaryScreen::getJumpZoneSize() const
{
return 1;
}
void
CXWindowsPrimaryScreen::onPreRun()
{
@ -419,7 +425,7 @@ CXWindowsPrimaryScreen::createWindow()
selectEvents(display, m_screen->getRoot());
}
// tell our superclass about the window
// tell generic screen about the window
m_screen->setWindow(m_window);
}

View File

@ -32,6 +32,7 @@ public:
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual SInt32 getJumpZoneSize() const;
protected:
// CPrimaryScreen overrides

View File

@ -118,6 +118,10 @@ SOURCE=.\CPrimaryClient.cpp
# End Source File
# Begin Source File
SOURCE=.\CPrimaryScreen.cpp
# End Source File
# Begin Source File
SOURCE=.\CServer.cpp
# End Source File
# Begin Source File
@ -158,11 +162,11 @@ SOURCE=.\CPrimaryClient.h
# End Source File
# Begin Source File
SOURCE=.\CServer.h
SOURCE=.\CPrimaryScreen.h
# End Source File
# Begin Source File
SOURCE=.\IPrimaryScreen.h
SOURCE=.\CServer.h
# End Source File
# Begin Source File

View File

@ -37,8 +37,8 @@ public:
// will call IScreenEventHandler's onScreenSaver() when the screensaver
// activates or deactivates until close. if notify is false then
// the screen saver is disabled on open and restored on close.
virtual void openScreenSaver(bool notify) = 0;
virtual void closeScreenSaver() = 0;
virtual void openScreensaver(bool notify) = 0;
virtual void closeScreensaver() = 0;
// activate or deactivate the screen saver
virtual void screensaver(bool activate) = 0;

View File

@ -34,6 +34,11 @@ public:
// called by mainLoop(). iff the event was handled return true and
// store the result, if any, in m_result, which defaults to zero.
virtual bool onEvent(CEvent* event) = 0;
// accessors
// called to get the jump zone size
virtual SInt32 getJumpZoneSize() const = 0;
};
#endif

View File

@ -16,6 +16,23 @@ static const double kHeartRate = 2.0;
// time without a heartbeat that we call death
static const double kHeartDeath = 3.0 * kHeartRate;
// direction constants
enum EDirection {
kLeft,
kRight,
kTop,
kBottom,
kFirstDirection = kLeft,
kLastDirection = kBottom
};
enum EDirectionMask {
kLeftMask = 1 << kLeft,
kRightMask = 1 << kRight,
kTopMask = 1 << kTop,
kBottomMask = 1 << kBottom
};
//
// message codes (trailing NUL is not part of code). in comments, $n
// refers to the n'th argument (counting from one). message codes are

View File

@ -155,6 +155,14 @@ SOURCE=.\IPrimaryScreenReceiver.h
# End Source File
# Begin Source File
SOURCE=.\IScreen.h
# End Source File
# Begin Source File
SOURCE=.\IScreenEventHandler.h
# End Source File
# Begin Source File
SOURCE=.\IScreenReceiver.h
# End Source File
# Begin Source File