Added switch delay and double-tap options to win32 and added a

tray icon to the client and server that gives status feedback to
the user and allows the user to kill the app.
This commit is contained in:
crs
2003-03-12 22:34:07 +00:00
parent f411df65fb
commit 1d17f865ea
82 changed files with 4420 additions and 396 deletions

View File

@@ -23,6 +23,7 @@
#undef ARCH_NETWORK
#undef ARCH_SLEEP
#undef ARCH_STRING
#undef ARCH_TASKBAR
#undef ARCH_TIME
// include appropriate architecture implementation
@@ -35,6 +36,7 @@
# include "CArchNetworkWinsock.h"
# include "CArchSleepWindows.h"
# include "CArchStringWindows.h"
# include "CArchTaskBarWindows.h"
# include "CArchTimeWindows.h"
#elif UNIX_LIKE
# include "CArchConsoleUnix.h"
@@ -47,6 +49,7 @@
# include "CArchNetworkBSD.h"
# include "CArchSleepUnix.h"
# include "CArchStringUnix.h"
# include "CArchTaskBarXWindows.h"
# include "CArchTimeUnix.h"
#endif
@@ -82,6 +85,10 @@
# error unsupported platform for string
#endif
#if !defined(ARCH_TASKBAR)
# error unsupported platform for taskbar
#endif
#if !defined(ARCH_TIME)
# error unsupported platform for time
#endif
@@ -92,7 +99,7 @@
CArch* CArch::s_instance = NULL;
CArch::CArch(ARCH_ARGS)
CArch::CArch(ARCH_ARGS* args)
{
// only once instance of CArch
assert(s_instance == NULL);
@@ -108,11 +115,13 @@ CArch::CArch(ARCH_ARGS)
m_time = new ARCH_TIME;
m_console = new ARCH_CONSOLE;
m_daemon = new ARCH_DAEMON;
m_taskbar = new ARCH_TASKBAR(args);
}
CArch::~CArch()
{
// clean up
delete m_taskbar;
delete m_daemon;
delete m_console;
delete m_time;
@@ -337,10 +346,10 @@ CArch::wait(CArchThread thread, double timeout)
return m_mt->wait(thread, timeout);
}
bool
CArch::waitForEvent(double timeout)
IArchMultithread::EWaitResult
CArch::waitForEvent(CArchThread thread, double timeout)
{
return m_mt->waitForEvent(timeout);
return m_mt->waitForEvent(thread, timeout);
}
bool
@@ -577,6 +586,24 @@ CArch::getWideCharEncoding()
return m_string->getWideCharEncoding();
}
void
CArch::addReceiver(IArchTaskBarReceiver* receiver)
{
m_taskbar->addReceiver(receiver);
}
void
CArch::removeReceiver(IArchTaskBarReceiver* receiver)
{
m_taskbar->removeReceiver(receiver);
}
void
CArch::updateReceiver(IArchTaskBarReceiver* receiver)
{
m_taskbar->updateReceiver(receiver);
}
double
CArch::time()
{

View File

@@ -23,6 +23,7 @@
#include "IArchNetwork.h"
#include "IArchSleep.h"
#include "IArchString.h"
#include "IArchTaskBar.h"
#include "IArchTime.h"
/*!
@@ -31,7 +32,7 @@ This macro evaluates to the singleton CArch object.
*/
#define ARCH (CArch::getInstance())
#define ARCH_ARGS
#define ARCH_ARGS void
//! Delegating mplementation of architecture dependent interfaces
/*!
@@ -51,9 +52,10 @@ class CArch : public IArchConsole,
public IArchNetwork,
public IArchSleep,
public IArchString,
public IArchTaskBar,
public IArchTime {
public:
CArch(ARCH_ARGS);
CArch(ARCH_ARGS* args = NULL);
~CArch();
//
@@ -114,7 +116,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread);
@@ -164,6 +166,11 @@ public:
virtual EWideCharEncoding
getWideCharEncoding();
// IArchTaskBar
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
// IArchTime overrides
virtual double time();
@@ -178,6 +185,7 @@ private:
IArchNetwork* m_net;
IArchSleep* m_sleep;
IArchString* m_string;
IArchTaskBar* m_taskbar;
IArchTime* m_time;
};

View File

@@ -13,6 +13,7 @@
*/
#include "CArchConsoleWindows.h"
#include "IArchMultithread.h"
#include "CArch.h"
#include <cstdio>
@@ -20,12 +21,12 @@
// CArchConsoleWindows
//
DWORD CArchConsoleWindows::s_thread = 0;
CArchThread CArchConsoleWindows::s_thread = 0;
CArchConsoleWindows::CArchConsoleWindows() :
m_output(NULL)
{
s_thread = GetCurrentThreadId();
s_thread = ARCH->newCurrentThread();
m_mutex = ARCH->newMutex();
}
@@ -33,6 +34,7 @@ CArchConsoleWindows::CArchConsoleWindows() :
CArchConsoleWindows::~CArchConsoleWindows()
{
ARCH->closeMutex(m_mutex);
ARCH->closeThread(s_thread);
}
void
@@ -101,7 +103,7 @@ CArchConsoleWindows::getNewlineForConsole()
BOOL WINAPI
CArchConsoleWindows::signalHandler(DWORD)
{
// terminate cleanly and skip remaining handlers
PostThreadMessage(s_thread, WM_QUIT, 0, 0);
// terminate thread and skip remaining handlers
ARCH->cancelThread(s_thread);
return TRUE;
}

View File

@@ -39,7 +39,7 @@ private:
static BOOL WINAPI signalHandler(DWORD);
private:
static DWORD s_thread;
static CArchThread s_thread;
CArchMutex m_mutex;
HANDLE m_output;

View File

@@ -25,6 +25,7 @@
# include "CArchNetworkWinsock.cpp"
# include "CArchSleepWindows.cpp"
# include "CArchStringWindows.cpp"
# include "CArchTaskBarWindows.cpp"
# include "CArchTimeWindows.cpp"
# include "XArchWindows.cpp"
#elif UNIX_LIKE
@@ -38,6 +39,7 @@
# include "CArchNetworkBSD.cpp"
# include "CArchSleepUnix.cpp"
# include "CArchStringUnix.cpp"
# include "CArchTaskBarXWindows.cpp"
# include "CArchTimeUnix.cpp"
# include "XArchUnix.cpp"
#endif

View File

@@ -517,11 +517,11 @@ CArchMultithreadPosix::wait(CArchThread target, double timeout)
}
}
bool
CArchMultithreadPosix::waitForEvent(double /*timeout*/)
IArchMultithread::EWaitResult
CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/)
{
// not implemented
return false;
return kTimeout;
}
bool

View File

@@ -55,7 +55,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread);

View File

@@ -453,6 +453,89 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout)
}
}
IArchMultithread::EWaitResult
CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout)
{
// find current thread. ref the target so it can't go away while
// we're watching it.
lockMutex(m_threadMutex);
CArchThreadImpl* self = findNoRef(GetCurrentThreadId());
assert(self != NULL);
if (target != NULL) {
refThread(target);
}
unlockMutex(m_threadMutex);
// see if we've been cancelled before checking if any events
// are pending.
DWORD result = WaitForSingleObject(self->m_cancel, 0);
if (result == WAIT_OBJECT_0) {
if (target != NULL) {
closeThread(target);
}
testCancelThreadImpl(self);
}
// check if messages are available first. if we don't do this then
// MsgWaitForMultipleObjects() will block even if the queue isn't
// empty if the messages in the queue were there before the last
// call to GetMessage()/PeekMessage().
if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) {
return kEvent;
}
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for this thread to be cancelled or for the target thread to
// terminate.
DWORD n = (target == NULL || target == self) ? 1 : 2;
HANDLE handles[2];
handles[0] = self->m_cancel;
handles[1] = (n == 2) ? target->m_exit : NULL;
result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT);
// cancel takes priority
if (result != WAIT_OBJECT_0 + 0 &&
WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) {
result = WAIT_OBJECT_0 + 0;
}
// release target
if (target != NULL) {
closeThread(target);
}
// handle result
switch (result) {
case WAIT_OBJECT_0 + 0:
// this thread was cancelled. does not return.
testCancelThreadImpl(self);
case WAIT_OBJECT_0 + 1:
// target thread terminated
if (n == 2) {
return kExit;
}
// fall through
case WAIT_OBJECT_0 + 2:
// message is available
return kEvent;
default:
// timeout or error
return kTimeout;
}
}
/*
bool
CArchMultithreadWindows::waitForEvent(double timeout)
{
@@ -499,6 +582,7 @@ CArchMultithreadWindows::waitForEvent(double timeout)
return false;
}
}
*/
bool
CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2)

View File

@@ -69,7 +69,7 @@ public:
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool waitForEvent(double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread);

View File

@@ -0,0 +1,518 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CArchTaskBarWindows.h"
#include "IArchTaskBarReceiver.h"
#include "CArch.h"
#include "XArch.h"
#include <string.h>
#include <shellapi.h>
static const UINT kAddReceiver = WM_USER + 10;
static const UINT kRemoveReceiver = WM_USER + 11;
static const UINT kUpdateReceiver = WM_USER + 12;
static const UINT kNotifyReceiver = WM_USER + 13;
static const UINT kFirstReceiverID = WM_USER + 14;
//
// CArchTaskBarWindows
//
CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL;
HINSTANCE CArchTaskBarWindows::s_appInstance = NULL;
CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) :
m_nextID(kFirstReceiverID)
{
// save the singleton instance
s_instance = this;
// save app instance
s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
// we need a mutex
m_mutex = ARCH->newMutex();
// and a condition variable which uses the above mutex
m_ready = false;
m_condVar = ARCH->newCondVar();
// we're going to want to get a result from the thread we're
// about to create to know if it initialized successfully.
// so we lock the condition variable.
ARCH->lockMutex(m_mutex);
// open a window and run an event loop in a separate thread.
// this has to happen in a separate thread because if we
// create a window on the current desktop with the current
// thread then the current thread won't be able to switch
// desktops if it needs to.
m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this);
// wait for child thread
while (!m_ready) {
ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
}
// ready
ARCH->unlockMutex(m_mutex);
}
CArchTaskBarWindows::~CArchTaskBarWindows()
{
if (m_thread != NULL) {
ARCH->cancelThread(m_thread);
ARCH->wait(m_thread, -1.0);
ARCH->closeThread(m_thread);
}
ARCH->closeCondVar(m_condVar);
ARCH->closeMutex(m_mutex);
s_instance = NULL;
}
void
CArchTaskBarWindows::addDialog(HWND hwnd)
{
// add dialog to added dialogs list
ARCH->lockMutex(s_instance->m_mutex);
s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true));
ARCH->unlockMutex(s_instance->m_mutex);
}
void
CArchTaskBarWindows::removeDialog(HWND hwnd)
{
// mark dialog as removed
ARCH->lockMutex(s_instance->m_mutex);
CDialogs::iterator index = s_instance->m_dialogs.find(hwnd);
if (index != s_instance->m_dialogs.end()) {
index->second = false;
}
s_instance->m_addedDialogs.erase(hwnd);
ARCH->unlockMutex(s_instance->m_mutex);
}
void
CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
{
// ignore bogus receiver
if (receiver == NULL) {
return;
}
// add receiver if necessary
CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
// add it, creating a new message ID for it
CReceiverInfo info;
info.m_id = getNextID();
index = m_receivers.insert(std::make_pair(receiver, info)).first;
// add ID to receiver mapping
m_idTable.insert(std::make_pair(info.m_id, index));
}
// add receiver
PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
}
void
CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
{
// find receiver
CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
return;
}
// remove icon. wait for this to finish before returning.
SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
// recycle the ID
recycleID(index->second.m_id);
// discard
m_idTable.erase(index->second.m_id);
m_receivers.erase(index);
}
void
CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
{
// find receiver
CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
return;
}
// update icon and tool tip
PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
}
UINT
CArchTaskBarWindows::getNextID()
{
if (m_oldIDs.empty()) {
return m_nextID++;
}
UINT id = m_oldIDs.back();
m_oldIDs.pop_back();
return id;
}
void
CArchTaskBarWindows::recycleID(UINT id)
{
m_oldIDs.push_back(id);
}
void
CArchTaskBarWindows::addIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_ADD);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::removeIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
removeIconNoLock(id);
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::updateIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_MODIFY);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::addAllIcons()
{
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) {
modifyIconNoLock(index, NIM_ADD);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::removeAllIcons()
{
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) {
removeIconNoLock(index->second.m_id);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::modifyIconNoLock(
CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
{
// get receiver
UINT id = index->second.m_id;
IArchTaskBarReceiver* receiver = index->first;
// lock receiver so icon and tool tip are guaranteed to be consistent
receiver->lock();
// get icon data
HICON icon = reinterpret_cast<HICON>(
const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
// get tool tip
std::string toolTip = receiver->getToolTip();
// done querying
receiver->unlock();
// prepare to add icon
NOTIFYICONDATA data;
data.cbSize = sizeof(NOTIFYICONDATA);
data.hWnd = m_hwnd;
data.uID = id;
data.uFlags = NIF_MESSAGE;
data.uCallbackMessage = kNotifyReceiver;
data.hIcon = icon;
if (icon != NULL) {
data.uFlags |= NIF_ICON;
}
if (!toolTip.empty()) {
strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
data.szTip[sizeof(data.szTip) - 1] = '\0';
data.uFlags |= NIF_TIP;
}
else {
data.szTip[0] = '\0';
}
// add icon
if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
// failed
}
}
void
CArchTaskBarWindows::removeIconNoLock(UINT id)
{
NOTIFYICONDATA data;
data.cbSize = sizeof(NOTIFYICONDATA);
data.hWnd = m_hwnd;
data.uID = id;
if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
// failed
}
}
void
CArchTaskBarWindows::handleIconMessage(
IArchTaskBarReceiver* receiver, LPARAM lParam)
{
// process message
switch (lParam) {
case WM_LBUTTONDOWN:
receiver->showStatus();
break;
case WM_LBUTTONDBLCLK:
receiver->primaryAction();
break;
case WM_RBUTTONUP: {
POINT p;
GetCursorPos(&p);
receiver->runMenu(p.x, p.y);
break;
}
case WM_MOUSEMOVE:
// currently unused
break;
default:
// unused
break;
}
}
bool
CArchTaskBarWindows::processDialogs(MSG* msg)
{
// only one thread can be in this method on any particular object
// at any given time. that's not a problem since only our event
// loop calls this method and there's just one of those.
ARCH->lockMutex(m_mutex);
// remove removed dialogs
m_dialogs.erase(false);
// merge added dialogs into the dialog list
for (CDialogs::const_iterator index = m_addedDialogs.begin();
index != m_addedDialogs.end(); ++index) {
m_dialogs.insert(std::make_pair(index->first, index->second));
}
m_addedDialogs.clear();
ARCH->unlockMutex(m_mutex);
// check message against all dialogs until one handles it.
// note that we don't hold a lock while checking because
// the message is processed and may make calls to this
// object. that's okay because addDialog() and
// removeDialog() don't change the map itself (just the
// values of some elements).
ARCH->lockMutex(m_mutex);
for (CDialogs::const_iterator index = m_dialogs.begin();
index != m_dialogs.end(); ++index) {
if (index->second) {
ARCH->unlockMutex(m_mutex);
if (IsDialogMessage(index->first, msg)) {
return true;
}
ARCH->lockMutex(m_mutex);
}
}
ARCH->unlockMutex(m_mutex);
return false;
}
LRESULT
CArchTaskBarWindows::wndProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case kNotifyReceiver: {
// lookup receiver
CIDToReceiverMap::const_iterator index = m_idTable.find(wParam);
if (index != m_idTable.end()) {
IArchTaskBarReceiver* receiver = index->second->first;
handleIconMessage(receiver, lParam);
return 0;
}
break;
}
case kAddReceiver:
addIcon(wParam);
break;
case kRemoveReceiver:
removeIcon(wParam);
break;
case kUpdateReceiver:
updateIcon(wParam);
break;
default:
if (msg == m_taskBarRestart) {
// task bar was recreated so re-add our icons
addAllIcons();
}
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK
CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
// if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put
// it in the extra window data then forward the call.
CArchTaskBarWindows* self = NULL;
if (msg == WM_NCCREATE) {
CREATESTRUCT* createInfo;
createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
self = reinterpret_cast<CArchTaskBarWindows*>(
createInfo->lpCreateParams);
SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(self));
}
else {
// get the extra window data and forward the call
LONG data = GetWindowLong(hwnd, 0);
if (data != 0) {
self = reinterpret_cast<CArchTaskBarWindows*>(
reinterpret_cast<void*>(data));
}
}
// forward the message
if (self != NULL) {
return self->wndProc(hwnd, msg, wParam, lParam);
}
else {
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
void
CArchTaskBarWindows::threadMainLoop()
{
// register the task bar restart message
m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
// register a window class
WNDCLASSEX classInfo;
classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_NOCLOSE;
classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
classInfo.hInstance = s_appInstance;
classInfo.hIcon = NULL;
classInfo.hCursor = NULL;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = TEXT("SynergyTaskBar");
classInfo.hIconSm = NULL;
ATOM windowClass = RegisterClassEx(&classInfo);
// create window
m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
reinterpret_cast<LPCTSTR>(windowClass),
TEXT("Synergy Task Bar"),
WS_POPUP,
0, 0, 1, 1,
NULL,
NULL,
s_appInstance,
reinterpret_cast<void*>(this));
// signal ready
ARCH->lockMutex(m_mutex);
m_ready = true;
ARCH->broadcastCondVar(m_condVar);
ARCH->unlockMutex(m_mutex);
// handle failure
if (m_hwnd == NULL) {
UnregisterClass((LPCTSTR)windowClass, s_appInstance);
return;
}
try {
// main loop
MSG msg;
for (;;) {
// wait for message
if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) {
continue;
}
// peek for message and remove it. we don't GetMessage()
// because we should never block here, only in waitForEvent().
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
continue;
}
// check message against dialogs
if (!processDialogs(&msg)) {
// process message
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
catch (XThread&) {
// clean up
removeAllIcons();
DestroyWindow(m_hwnd);
UnregisterClass((LPCTSTR)windowClass, s_appInstance);
throw;
}
}
void*
CArchTaskBarWindows::threadEntry(void* self)
{
reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
return NULL;
}

View File

@@ -0,0 +1,110 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CARCHTASKBARWINDOWS_H
#define CARCHTASKBARWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "IArchTaskBar.h"
#include "IArchMultithread.h"
#include "stdmap.h"
#include "stdvector.h"
#include <windows.h>
#define ARCH_TASKBAR CArchTaskBarWindows
//! Win32 implementation of IArchTaskBar
class CArchTaskBarWindows : public IArchTaskBar {
public:
CArchTaskBarWindows(void*);
virtual ~CArchTaskBarWindows();
//! Add a dialog window
/*!
Tell the task bar event loop about a dialog. Win32 annoyingly
requires messages destined for modeless dialog boxes to be
dispatched differently than other messages.
*/
static void addDialog(HWND);
//! Remove a dialog window
/*!
Remove a dialog window added via \c addDialog().
*/
static void removeDialog(HWND);
// IArchTaskBar overrides
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
private:
class CReceiverInfo {
public:
UINT m_id;
};
typedef std::map<IArchTaskBarReceiver*, CReceiverInfo> CReceiverToInfoMap;
typedef std::map<UINT, CReceiverToInfoMap::iterator> CIDToReceiverMap;
typedef std::vector<UINT> CIDStack;
typedef std::map<HWND, bool> CDialogs;
UINT getNextID();
void recycleID(UINT);
void addIcon(UINT);
void removeIcon(UINT);
void updateIcon(UINT);
void addAllIcons();
void removeAllIcons();
void modifyIconNoLock(CReceiverToInfoMap::const_iterator,
DWORD taskBarMessage);
void removeIconNoLock(UINT id);
void handleIconMessage(IArchTaskBarReceiver*, LPARAM);
bool processDialogs(MSG*);
LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK
staticWndProc(HWND, UINT, WPARAM, LPARAM);
void threadMainLoop();
static void* threadEntry(void*);
private:
static CArchTaskBarWindows* s_instance;
static HINSTANCE s_appInstance;
// multithread data
CArchMutex m_mutex;
CArchCond m_condVar;
bool m_ready;
int m_result;
CArchThread m_thread;
// child thread data
HWND m_hwnd;
UINT m_taskBarRestart;
// shared data
CReceiverToInfoMap m_receivers;
CIDToReceiverMap m_idTable;
CIDStack m_oldIDs;
UINT m_nextID;
// dialogs
CDialogs m_dialogs;
CDialogs m_addedDialogs;
};
#endif

View File

@@ -0,0 +1,47 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CArchTaskBarXWindows.h"
//
// CArchTaskBarXWindows
//
CArchTaskBarXWindows::CArchTaskBarXWindows(void*)
{
// do nothing
}
CArchTaskBarXWindows::~CArchTaskBarXWindows()
{
// do nothing
}
void
CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}
void
CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}
void
CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}

View File

@@ -0,0 +1,34 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CARCHTASKBARXWINDOWS_H
#define CARCHTASKBARXWINDOWS_H
#include "IArchTaskBar.h"
#define ARCH_TASKBAR CArchTaskBarXWindows
//! X11 implementation of IArchTaskBar
class CArchTaskBarXWindows : public IArchTaskBar {
public:
CArchTaskBarXWindows(void*);
virtual ~CArchTaskBarXWindows();
// IArchTaskBar overrides
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
};
#endif

View File

@@ -67,6 +67,13 @@ synergy. Each architecture must implement this interface.
*/
class IArchMultithread : public IInterface {
public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Type of thread entry point
typedef void* (*ThreadFunc)(void*);
//! Type of thread identifier
@@ -102,10 +109,12 @@ public:
//! Wait on a condition variable
/*!
Waiting on a conditation variable for up to \c timeout seconds.
Wait on a conditation variable for up to \c timeout seconds.
If \c timeout is < 0 then there is no timeout. The mutex must
be locked when this method is called. The mutex is unlocked
during the wait and locked again before returning.
during the wait and locked again before returning. Returns
true if the condition variable was signalled and false on
timeout.
(Cancellation point)
*/
@@ -206,14 +215,18 @@ public:
//! Wait for a user event
/*!
Waits for up to \c timeout seconds for a pending user event.
Returns true if an event occurred, false otherwise.
Waits for up to \c timeout seconds for a pending user event or
\c thread to exit (normally or by cancellation). Waits forever
if \c timeout < 0. Returns kEvent if an event occurred, kExit
if \c thread exited, or kTimeout if the timeout expired. If
\c thread is NULL then it doesn't wait for any thread to exit
and it will not return kExit.
This method is not required by all platforms.
(Cancellation point)
*/
virtual bool waitForEvent(double timeout) = 0;
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
//! Compare threads
/*!

63
lib/arch/IArchTaskBar.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IARCHTASKBAR_H
#define IARCHTASKBAR_H
#include "IInterface.h"
class IArchTaskBarReceiver;
//! Interface for architecture dependent task bar control
/*!
This interface defines the task bar icon operations required
by synergy. Each architecture must implement this interface
though each operation can be a no-op.
*/
class IArchTaskBar : public IInterface {
public:
// Event data is architecture dependent
typedef void* Event;
//! @name manipulators
//@{
//! Add a receiver
/*!
Add a receiver object to be notified of user and application
events. This should be called before other methods. When
the receiver is added to the task bar, its icon appears on
the task bar.
*/
virtual void addReceiver(IArchTaskBarReceiver*) = 0;
//! Remove a receiver
/*!
Remove a receiver object from the task bar. This removes the
icon from the task bar.
*/
virtual void removeReceiver(IArchTaskBarReceiver*) = 0;
//! Update a receiver
/*!
Updates the display of the receiver on the task bar. This
should be called when the receiver appearance may have changed
(e.g. it's icon or tool tip has changed).
*/
virtual void updateReceiver(IArchTaskBarReceiver*) = 0;
//@}
};
#endif

View File

@@ -0,0 +1,90 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IARCHTASKBARRECEIVER_H
#define IARCHTASKBARRECEIVER_H
#include "IInterface.h"
#include "stdstring.h"
//! Interface for architecture dependent task bar event handling
/*!
This interface defines the task bar icon event handlers required
by synergy. Each architecture must implement this interface
though each operation can be a no-op.
*/
class IArchTaskBarReceiver : public IInterface {
public:
// Icon data is architecture dependent
typedef void* Icon;
//! @name manipulators
//@{
//! Show status window
/*!
Open a window displaying current status. This should return
immediately without waiting for the window to be closed.
*/
virtual void showStatus() = 0;
//! Popup menu
/*!
Popup a menu of operations at or around \c x,y and perform the
chosen operation.
*/
virtual void runMenu(int x, int y) = 0;
//! Perform primary action
/*!
Perform the primary (default) action.
*/
virtual void primaryAction() = 0;
//@}
//! @name accessors
//@{
//! Lock receiver
/*!
Locks the receiver from changing state. The receiver should be
locked when querying it's state to ensure consistent results.
Each call to \c lock() must have a matching \c unlock() and
locks cannot be nested.
*/
virtual void lock() const = 0;
//! Unlock receiver
virtual void unlock() const = 0;
//! Get icon
/*!
Returns the icon to display in the task bar. The interface
to set the icon is left to subclasses. Getting and setting
the icon must be thread safe.
*/
virtual const Icon getIcon() const = 0;
//! Get tooltip
/*!
Returns the tool tip to display in the task bar. The interface
to set the tooltip is left to sublclasses. Getting and setting
the icon must be thread safe.
*/
virtual std::string getToolTip() const = 0;
//@}
};
#endif

View File

@@ -26,6 +26,7 @@ EXTRA_DIST = \
CArchNetworkWinsock.cpp \
CArchSleepWindows.cpp \
CArchStringWindows.cpp \
CArchTaskBarWindows.cpp \
CArchTimeWindows.cpp \
XArchWindows.cpp \
CArchConsoleWindows.h \
@@ -37,6 +38,7 @@ EXTRA_DIST = \
CArchNetworkWinsock.h \
CArchSleepWindows.h \
CArchStringWindows.h \
CArchTaskBarWindows.h \
CArchTimeWindows.h \
XArchWindows.h \
$(NULL)
@@ -59,6 +61,8 @@ libarch_a_SOURCES = \
IArchNetwork.h \
IArchSleep.h \
IArchString.h \
IArchTaskBar.h \
IArchTaskBarReceiver.h \
IArchTime.h \
XArch.h \
$(NULL)
@@ -72,6 +76,7 @@ EXTRA_libarch_a_SOURCES = \
CArchNetworkBSD.cpp \
CArchSleepUnix.cpp \
CArchStringUnix.cpp \
CArchTaskBarXWindows.cpp \
CArchTimeUnix.cpp \
CMultibyte.cpp \
CMultibyteOS.cpp \
@@ -87,6 +92,7 @@ EXTRA_libarch_a_SOURCES = \
CArchNetworkBSD.h \
CArchSleepUnix.h \
CArchStringUnix.h \
CArchTaskBarXWindows.h \
CArchTimeUnix.h \
XArchUnix.h \
$(NULL)

View File

@@ -115,6 +115,10 @@ SOURCE=.\CArchDaemonWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchFileWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchImpl.h
# End Source File
# Begin Source File
@@ -143,6 +147,10 @@ SOURCE=.\CArchStringWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchTaskBarWindows.h
# End Source File
# Begin Source File
SOURCE=.\CArchTimeWindows.h
# End Source File
# Begin Source File
@@ -151,6 +159,14 @@ SOURCE=.\IArchConsole.h
# End Source File
# Begin Source File
SOURCE=.\IArchDaemon.h
# End Source File
# Begin Source File
SOURCE=.\IArchFile.h
# End Source File
# Begin Source File
SOURCE=.\IArchLog.h
# End Source File
# Begin Source File
@@ -171,6 +187,14 @@ SOURCE=.\IArchString.h
# End Source File
# Begin Source File
SOURCE=.\IArchTaskBar.h
# End Source File
# Begin Source File
SOURCE=.\IArchTaskBarReceiver.h
# End Source File
# Begin Source File
SOURCE=.\IArchTime.h
# End Source File
# Begin Source File
@@ -232,6 +256,11 @@ SOURCE=.\CArchStringWindows.cpp
# End Source File
# Begin Source File
SOURCE=.\CArchTaskBarWindows.cpp
# PROP Exclude_From_Build 1
# End Source File
# Begin Source File
SOURCE=.\CArchTimeWindows.cpp
# PROP Exclude_From_Build 1
# End Source File

113
lib/base/CJobList.cpp Normal file
View File

@@ -0,0 +1,113 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CJobList.h"
#include "IJob.h"
#include "CArch.h"
//
// CJobList
//
CJobList::CJobList()
{
m_mutex = ARCH->newMutex();
}
CJobList::~CJobList()
{
ARCH->closeMutex(m_mutex);
}
void
CJobList::addJob(IJob* job)
{
// ignore bogus job
if (job == NULL) {
return;
}
// make a temporary list with the job. later we'll splice this
// list into our jobs list. since splice() never throws we
// don't have to try/catch to unlock the mutex.
CJobs tmpList;
tmpList.push_front(job);
// add job to list
ARCH->lockMutex(m_mutex);
m_jobs.splice(m_jobs.begin(), tmpList);
ARCH->unlockMutex(m_mutex);
}
void
CJobList::removeJob(IJob* job)
{
if (job != NULL) {
ARCH->lockMutex(m_mutex);
m_jobs.remove(job);
ARCH->unlockMutex(m_mutex);
}
}
void
CJobList::runJobs() const
{
// this is a little tricky. to allow any number of threads to
// traverse the list while jobs are added and removed while
// not holding the mutex when running a job (so other threads
// or the running job can add and remove jobs), we insert a
// new element into the list. this element has a NULL job and
// is a "safe place" while we traverse the list. the safe place
// is inserted at the start of the list (with the mutex locked)
// then, for each job, we lock the mutex and move the safe place
// past the next job, unlock the mutex, run the job and repeat
// until there are no more jobs. the safe place will not be
// removed by any other thread and is after every job that has
// been run and before every job that still needs to run. when
// all the jobs have been run we remove the safe place.
// add the safe place
CJobs tmpList;
tmpList.push_front(NULL);
ARCH->lockMutex(m_mutex);
m_jobs.splice(m_jobs.begin(), tmpList);
CJobs::iterator safePlace = m_jobs.begin();
// find the next non-NULL job (NULL jobs are safe places)
CJobs::iterator next = safePlace;
while (next != m_jobs.end() && *next == NULL)
++next;
while (next != m_jobs.end()) {
// found a job. run it without holding a lock. note the
// race condition here: we release the lock, allowing
// removeJob() to remove this job before we run the job.
// therefore the caller cannot safely destroy the job
// until all runJobs() complete.
IJob* job = *next;
++next;
m_jobs.splice(next, m_jobs, safePlace);
ARCH->unlockMutex(m_mutex);
job->run();
// next real job
ARCH->lockMutex(m_mutex);
next = safePlace;
while (next != m_jobs.end() && *next == NULL)
++next;
}
// remove the safe place
m_jobs.erase(safePlace);
ARCH->unlockMutex(m_mutex);
}

72
lib/base/CJobList.h Normal file
View File

@@ -0,0 +1,72 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CJOBLIST_H
#define CJOBLIST_H
#include "IArchMultithread.h"
#include "stdlist.h"
class IJob;
class CJobList {
public:
CJobList();
~CJobList();
//! @name manipulators
//@{
//! Add a job
/*!
Add a job to the list. The client keeps ownership of the job.
Jobs can be safely added while \c runJobs() is executing.
*/
void addJob(IJob*);
//! Remove a job
/*!
Remove a job from the list. The client keeps ownership of the job.
Jobs can be safely removed while \c runJobs() is executing.
*/
void removeJob(IJob*);
//@}
//! @name accessors
//@{
//! Run all jobs
/*!
Run all jobs in the list. Any number of threads can call
\c runJobs() at once. Jobs can be added and removed while
\c runJobs() is executing in the same or another thread.
Any job added after \c runJobs() starts will not be run
by that call to runJobs(). Destroying a removed job
while \c runJobs() is executing is not safe unless the
removed completed before \c runJobs() started.
*/
void runJobs() const;
//@}
private:
typedef std::list<IJob*> CJobs;
typedef CJobs::iterator iterator;
CArchMutex m_mutex;
mutable CJobs m_jobs;
};
#endif

View File

@@ -156,3 +156,26 @@ CSystemLogOutputter::getNewline() const
{
return "";
}
//
// CSystemLogger
//
CSystemLogger::CSystemLogger(const char* title)
{
// redirect log messages
m_syslog = new CSystemLogOutputter;
m_stop = new CStopLogOutputter;
m_syslog->open(title);
CLOG->insert(m_stop);
CLOG->insert(m_syslog);
}
CSystemLogger::~CSystemLogger()
{
CLOG->remove(m_syslog);
CLOG->remove(m_stop);
delete m_stop;
delete m_syslog;
}

View File

@@ -68,4 +68,22 @@ public:
virtual const char* getNewline() const;
};
//! Write log to system log only
/*!
Creating an object of this type inserts a CStopLogOutputter followed
by a CSystemLogOutputter into CLog. The destructor removes those
outputters. Add one of these to any scope that needs to write to
the system log (only) and restore the old outputters when exiting
the scope.
*/
class CSystemLogger {
public:
CSystemLogger(const char* title);
~CSystemLogger();
private:
ILogOutputter* m_syslog;
ILogOutputter* m_stop;
};
#endif

View File

@@ -26,6 +26,7 @@ MAINTAINERCLEANFILES = \
noinst_LIBRARIES = libbase.a
libbase_a_SOURCES = \
CFunctionJob.cpp \
CJobList.cpp \
CLog.cpp \
CStopwatch.cpp \
CStringUtil.cpp \
@@ -33,6 +34,7 @@ libbase_a_SOURCES = \
LogOutputters.cpp \
XBase.cpp \
CFunctionJob.h \
CJobList.h \
CLog.h \
CStopwatch.h \
CString.h \

View File

@@ -91,6 +91,10 @@ SOURCE=.\CFunctionJob.cpp
# End Source File
# Begin Source File
SOURCE=.\CJobList.cpp
# End Source File
# Begin Source File
SOURCE=.\CLog.cpp
# End Source File
# Begin Source File
@@ -123,6 +127,10 @@ SOURCE=.\CFunctionJob.h
# End Source File
# Begin Source File
SOURCE=.\CJobList.h
# End Source File
# Begin Source File
SOURCE=.\CLog.h
# End Source File
# Begin Source File

View File

@@ -52,7 +52,8 @@ CClient::CClient(const CString& clientName) :
m_streamFilterFactory(NULL),
m_session(NULL),
m_active(false),
m_rejected(true)
m_rejected(true),
m_status(kNotRunning)
{
// do nothing
}
@@ -108,15 +109,61 @@ CClient::exitMainLoop()
m_screen->exitMainLoop();
}
void
CClient::addStatusJob(IJob* job)
{
m_statusJobs.addJob(job);
}
void
CClient::removeStatusJob(IJob* job)
{
m_statusJobs.removeJob(job);
}
bool
CClient::wasRejected() const
{
return m_rejected;
}
CClient::EStatus
CClient::getStatus(CString* msg) const
{
CLock lock(&m_mutex);
if (msg != NULL) {
*msg = m_statusMessage;
}
return m_status;
}
void
CClient::runStatusJobs() const
{
m_statusJobs.runJobs();
}
void
CClient::setStatus(EStatus status, const char* msg)
{
{
CLock lock(&m_mutex);
m_status = status;
if (m_status == kError) {
m_statusMessage = (msg == NULL) ? "Error" : msg;
}
else {
m_statusMessage = (msg == NULL) ? "" : msg;
}
}
runStatusJobs();
}
void
CClient::onError()
{
setStatus(kError);
// close down session but don't wait too long
deleteSession(3.0);
}
@@ -172,9 +219,11 @@ CClient::open()
try {
LOG((CLOG_INFO "opening screen"));
openSecondaryScreen();
setStatus(kNotRunning);
}
catch (XScreenOpenFailure&) {
catch (XScreenOpenFailure& e) {
// can't open screen
setStatus(kError, e.what());
LOG((CLOG_INFO "failed to open screen"));
throw;
}
@@ -195,6 +244,7 @@ CClient::mainLoop()
}
try {
setStatus(kNotRunning);
LOG((CLOG_NOTE "starting client \"%s\"", m_name.c_str()));
// start server interactions
@@ -213,6 +263,7 @@ CClient::mainLoop()
}
catch (XMT& e) {
LOG((CLOG_ERR "client error: %s", e.what()));
setStatus(kError, e.what());
// clean up
deleteSession();
@@ -221,6 +272,7 @@ CClient::mainLoop()
}
catch (XBase& e) {
LOG((CLOG_ERR "client error: %s", e.what()));
setStatus(kError, e.what());
// clean up
deleteSession();
@@ -229,6 +281,8 @@ CClient::mainLoop()
m_rejected = false;
}
catch (XThread&) {
setStatus(kNotRunning);
// clean up
deleteSession();
LOG((CLOG_NOTE "stopping client \"%s\"", m_name.c_str()));
@@ -236,6 +290,7 @@ CClient::mainLoop()
}
catch (...) {
LOG((CLOG_DEBUG "unknown client error"));
setStatus(kError);
// clean up
deleteSession();
@@ -529,11 +584,12 @@ CClient::runServer()
{
IDataSocket* socket = NULL;
CServerProxy* proxy = NULL;
bool timedOut;
try {
for (;;) {
try {
// allow connect this much time to succeed
CTimerThread timer(15.0);
CTimerThread timer(15.0, &timedOut);
// create socket and attempt to connect to server
LOG((CLOG_DEBUG1 "connecting to server"));
@@ -546,6 +602,7 @@ CClient::runServer()
break;
}
catch (XSocketConnect& e) {
setStatus(kError, e.what());
LOG((CLOG_DEBUG "failed to connect to server: %s", e.what()));
// failed to connect. if not camping then rethrow.
@@ -565,31 +622,47 @@ CClient::runServer()
m_server = proxy;
}
catch (XThread&) {
LOG((CLOG_ERR "connection timed out"));
if (timedOut) {
LOG((CLOG_ERR "connection timed out"));
setStatus(kError, "connection timed out");
}
else {
// cancelled by some thread other than the timer
}
delete socket;
throw;
}
catch (XBase& e) {
LOG((CLOG_ERR "connection failed: %s", e.what()));
setStatus(kError, e.what());
LOG((CLOG_DEBUG "disconnecting from server"));
delete socket;
return;
}
catch (...) {
LOG((CLOG_ERR "connection failed: <unknown error>"));
setStatus(kError);
LOG((CLOG_DEBUG "disconnecting from server"));
delete socket;
return;
}
try {
// prepare for remote control
m_screen->remoteControl();
// process messages
bool rejected = true;
if (proxy != NULL) {
LOG((CLOG_DEBUG1 "communicating with server"));
setStatus(kRunning);
rejected = !proxy->mainLoop();
setStatus(kNotRunning);
}
// prepare for local control
m_screen->localControl();
// clean up
CLock lock(&m_mutex);
m_rejected = rejected;
@@ -600,6 +673,8 @@ CClient::runServer()
delete socket;
}
catch (...) {
setStatus(kNotRunning);
m_screen->localControl();
CLock lock(&m_mutex);
m_rejected = false;
m_server = NULL;
@@ -664,9 +739,11 @@ CClient::handshakeServer(IDataSocket* socket)
}
catch (XIncompatibleClient& e) {
LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor()));
setStatus(kError, e.what());
}
catch (XBase& e) {
LOG((CLOG_WARN "error communicating with server: %s", e.what()));
setStatus(kError, e.what());
}
catch (...) {
// probably timed out

View File

@@ -20,6 +20,7 @@
#include "IClipboard.h"
#include "CNetworkAddress.h"
#include "CMutex.h"
#include "CJobList.h"
class CSecondaryScreen;
class CServerProxy;
@@ -36,6 +37,13 @@ This class implements the top-level client algorithms for synergy.
*/
class CClient : public IScreenReceiver, public IClient {
public:
enum EStatus {
kNotRunning,
kRunning,
kError,
kMaxStatus
};
/*!
This client will attempt to connect the server using \c clientName
as its name.
@@ -93,6 +101,22 @@ public:
*/
void exitMainLoop();
//! Add a job to notify of status changes
/*!
The added job is run whenever the server's status changes in
certain externally visible ways. The client keeps ownership
of the job.
*/
void addStatusJob(IJob*);
//! Remove a job to notify of status changes
/*!
Removes a previously added status notification job. A job can
remove itself when called but must not remove any other jobs.
The client keeps ownership of the job.
*/
void removeStatusJob(IJob*);
//@}
//! @name accessors
//@{
@@ -103,6 +127,12 @@ public:
*/
bool wasRejected() const;
//! Get the status
/*!
Returns the current status and status message.
*/
EStatus getStatus(CString* = NULL) const;
//@}
// IScreenReceiver overrides
@@ -140,6 +170,12 @@ public:
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
private:
// notify status jobs of a change
void runStatusJobs() const;
// set new status
void setStatus(EStatus, const char* msg = NULL);
// open/close the secondary screen
void openSecondaryScreen();
void closeSecondaryScreen();
@@ -169,6 +205,11 @@ private:
bool m_ownClipboard[kClipboardEnd];
IClipboard::Time m_timeClipboard[kClipboardEnd];
CString m_dataClipboard[kClipboardEnd];
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
};
#endif

View File

@@ -420,6 +420,7 @@ CServerProxy::translateModifierMask(KeyModifierMask mask) const
if ((mask & KeyModifierSuper) != 0) {
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]];
}
return newMask;
}
void

View File

@@ -97,10 +97,16 @@ CThread::wait(double timeout) const
return ARCH->wait(m_thread, timeout);
}
bool
CThread::waitForEvent(double timeout)
CThread::EWaitResult
CThread::waitForEvent(double timeout) const
{
return ARCH->waitForEvent(timeout);
// IArchMultithread EWaitResults map directly to our EWaitResults
static const EWaitResult s_map[] = {
kEvent,
kExit,
kTimeout
};
return s_map[ARCH->waitForEvent(m_thread, timeout)];
}
void*

View File

@@ -39,6 +39,13 @@ documentation.
// note -- do not derive from this class
class CThread {
public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Run \c adoptedJob in a new thread
/*!
Create and start a new thread executing the \c adoptedJob. The
@@ -159,18 +166,19 @@ public:
//! Wait for an event (win32)
/*!
Wait for the message queue to contain a message for up to \c timeout
seconds. This returns immediately if any message is available
(including messages that were already in the queue during the last
call to \c GetMessage() or \c PeekMessage() or waitForEvent().
Returns true iff a message is available. This will wait forever
if \c timeout < 0.0.
Wait for the message queue to contain a message or for the thread
to exit for up to \c timeout seconds. This returns immediately if
any message is available (including messages that were already in
the queue during the last call to \c GetMessage() or
\c PeekMessage() or waitForEvent(). Returns kEvent if a message
is available, kExit if the thread exited, and kTimeout otherwise.
This will wait forever if \c timeout < 0.0.
This method is available under win32 only.
(cancellation point)
*/
static bool waitForEvent(double timeout = -1.0);
EWaitResult waitForEvent(double timeout = -1.0) const;
//! Get the exit result
/*!

View File

@@ -22,8 +22,13 @@
// CTimerThread
//
CTimerThread::CTimerThread(double timeout) : m_timeout(timeout)
CTimerThread::CTimerThread(double timeout, bool* timedOut) :
m_timeout(timeout),
m_timedOut(timedOut)
{
if (m_timedOut != NULL) {
*m_timedOut = false;
}
if (m_timeout >= 0.0) {
m_callingThread = new CThread(CThread::getCurrentThread());
m_timingThread = new CThread(new TMethodJob<CTimerThread>(
@@ -53,5 +58,8 @@ CTimerThread::timer(void*)
LOG((CLOG_DEBUG1 "timeout in %f seconds", m_timeout));
ARCH->sleep(m_timeout);
LOG((CLOG_DEBUG1 "timeout"));
if (m_timedOut != NULL) {
*m_timedOut = true;
}
m_callingThread->cancel();
}

View File

@@ -30,9 +30,11 @@ public:
/*!
Cancels the calling thread after \c timeout seconds unless destroyed
before then. If \c timeout is less than zero then it never times
out and this is a no-op.
out and this is a no-op. If \c timedOutFlag is not NULL then it's
set to false in the c'tor and to true if the timeout exipires before
it's cancelled.
*/
CTimerThread(double timeout);
CTimerThread(double timeout, bool* timedOutFlag = NULL);
//! Cancel the timer thread
~CTimerThread();
@@ -45,6 +47,7 @@ private:
private:
double m_timeout;
bool* m_timedOut;
CThread* m_callingThread;
CThread* m_timingThread;
};

View File

@@ -112,6 +112,12 @@ CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/)
// no options
}
UInt32
CMSWindowsPrimaryScreen::addOneShotTimer(double timeout)
{
return m_screen->addOneShotTimer(timeout);
}
KeyModifierMask
CMSWindowsPrimaryScreen::getToggleMask() const
{
@@ -376,6 +382,12 @@ CMSWindowsPrimaryScreen::onEvent(CEvent* event)
return false;
}
void
CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id)
{
m_receiver->onOneShotTimerExpired(id);
}
SInt32
CMSWindowsPrimaryScreen::getJumpZoneSize() const
{

View File

@@ -39,6 +39,7 @@ public:
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual UInt32 addOneShotTimer(double timeout);
virtual KeyModifierMask getToggleMask() const;
virtual bool isLockedToScreen() const;
virtual IScreen* getScreen() const;
@@ -47,6 +48,7 @@ public:
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual void onOneShotTimerExpired(UInt32 id);
virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND);

View File

@@ -122,6 +122,11 @@ CMSWindowsScreen::closeDesktop()
// remove timer
if (m_timer != 0) {
KillTimer(NULL, m_timer);
m_timer = 0;
}
if (m_oneShotTimer != 0) {
KillTimer(NULL, m_oneShotTimer);
m_oneShotTimer = 0;
}
// disconnect from desktop
@@ -134,6 +139,18 @@ CMSWindowsScreen::closeDesktop()
assert(m_desk == NULL);
}
UInt32
CMSWindowsScreen::addOneShotTimer(double timeout)
{
// FIXME -- support multiple one-shot timers
if (m_oneShotTimer != 0) {
KillTimer(NULL, m_oneShotTimer);
}
m_oneShotTimer = SetTimer(NULL, 0,
static_cast<UINT>(1000.0 * timeout), NULL);
return 0;
}
bool
CMSWindowsScreen::isMultimon() const
{
@@ -205,8 +222,12 @@ CMSWindowsScreen::mainLoop()
event.m_result = 0;
for (;;) {
// wait for an event in a cancellable way
CThread::waitForEvent();
GetMessage(&event.m_msg, NULL, 0, 0);
if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) {
continue;
}
if (!PeekMessage(&event.m_msg, NULL, 0, 0, PM_REMOVE)) {
continue;
}
// handle quit message
if (event.m_msg.message == WM_QUIT) {
@@ -467,27 +488,35 @@ CMSWindowsScreen::onPreDispatch(const CEvent* event)
}
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 if (!m_screensaver->isActive()) {
// don't switch desktops when the screensaver is
// active. we'd most likely switch to the
// screensaver desktop which would have the side
// effect of forcing the screensaver to stop.
switchDesktop(desk);
}
else {
CloseDesktop(desk);
if (msg->wParam == m_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 if (!m_screensaver->isActive()) {
// don't switch desktops when the screensaver is
// active. we'd most likely switch to the
// screensaver desktop which would have the side
// effect of forcing the screensaver to stop.
switchDesktop(desk);
}
else {
CloseDesktop(desk);
}
}
}
}
else if (msg->wParam == m_oneShotTimer) {
// one shot timer expired
KillTimer(NULL, m_oneShotTimer);
m_oneShotTimer = 0;
m_eventHandler->onOneShotTimerExpired(0);
}
return true;
}

View File

@@ -64,6 +64,14 @@ public:
*/
void closeDesktop();
//! Install a one-shot timer
/*!
Installs a one-shot timer for \c timeout seconds and returns the
id of the timer (which will be passed to the receiver's
\c onTimerExpired()).
*/
UInt32 addOneShotTimer(double timeout);
//@}
//! @name accessors
//@{
@@ -169,6 +177,9 @@ private:
// the timer used to check for desktop switching
UINT m_timer;
// the one shot timer
UINT m_oneShotTimer;
// the current desk and it's name
HDESK m_desk;
CString m_deskName;

View File

@@ -262,6 +262,12 @@ CMSWindowsSecondaryScreen::onEvent(CEvent* event)
return false;
}
void
CMSWindowsSecondaryScreen::onOneShotTimerExpired(UInt32)
{
// ignore
}
SInt32
CMSWindowsSecondaryScreen::getJumpZoneSize() const
{
@@ -272,6 +278,11 @@ void
CMSWindowsSecondaryScreen::postCreateWindow(HWND window)
{
m_window = window;
// update key state
updateKeys();
// hide cursor if this screen isn't active
if (!isActive()) {
showWindow();
}

View File

@@ -53,6 +53,7 @@ public:
virtual void onScreensaver(bool activated);
virtual bool onPreDispatch(const CEvent* event);
virtual bool onEvent(CEvent* event);
virtual void onOneShotTimerExpired(UInt32 id);
virtual SInt32 getJumpZoneSize() const;
virtual void postCreateWindow(HWND);
virtual void preDestroyWindow(HWND);
@@ -69,6 +70,7 @@ protected:
virtual void hideWindow();
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void updateKeys();
virtual void releaseKeys();
virtual void setToggleState(KeyModifierMask);
virtual KeyModifierMask getToggleState() const;
@@ -98,7 +100,6 @@ private:
KeyModifierMask, EKeyAction) const;
void doKeystrokes(const Keystrokes&, SInt32 count);
void releaseKeys();
void toggleKey(UINT virtualKey, KeyModifierMask mask);
UINT virtualKeyToScanCode(UINT& virtualKey) const;
bool isExtendedKey(UINT virtualKey) const;

View File

@@ -260,6 +260,12 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam)
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return 1;
}
else {
x += g_xScreen;
y += g_yScreen;
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return 0;
}
}
}

View File

@@ -356,7 +356,7 @@ CXWindowsSecondaryScreen::destroyWindow()
CDisplayLock display(m_screen);
if (display != NULL) {
// release keys that are still pressed
releaseKeys(display);
doReleaseKeys(display);
// no longer impervious to server grabs
XTestGrabControl(display, False);
@@ -901,7 +901,7 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const
}
void
CXWindowsSecondaryScreen::releaseKeys(Display* display)
CXWindowsSecondaryScreen::doReleaseKeys(Display* display)
{
assert(display != NULL);
@@ -969,6 +969,15 @@ CXWindowsSecondaryScreen::updateKeys()
updateModifiers(display);
}
void
CXWindowsSecondaryScreen::releaseKeys()
{
CDisplayLock display(m_screen);
if (display != NULL) {
doReleaseKeys(display);
}
}
void
CXWindowsSecondaryScreen::updateModifiers(Display* display)
{

View File

@@ -67,6 +67,7 @@ protected:
virtual void hideWindow();
virtual void warpCursor(SInt32 x, SInt32 y);
virtual void updateKeys();
virtual void releaseKeys();
virtual void setToggleState(KeyModifierMask);
virtual KeyModifierMask getToggleState() const;
@@ -97,14 +98,10 @@ private:
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
KeyModifierMask, EKeyAction) const;
/*
bool findKeyCode(KeyCode&, unsigned int&,
KeyID id, unsigned int) const;
*/
void doKeystrokes(const Keystrokes&, SInt32 count);
unsigned int maskToX(KeyModifierMask) const;
void releaseKeys(Display*);
void doReleaseKeys(Display*);
void updateKeycodeMap(Display* display);
void updateModifiers(Display* display);
void updateModifierMap(Display* display);

View File

@@ -66,7 +66,8 @@ CServer::CServer(const CString& serverName) :
m_switchWaitEngaged(false),
m_switchTwoTapDelay(0.0),
m_switchTwoTapEngaged(false),
m_switchTwoTapArmed(false)
m_switchTwoTapArmed(false),
m_status(kNotRunning)
{
// do nothing
}
@@ -85,14 +86,17 @@ CServer::open()
try {
LOG((CLOG_INFO "opening screen"));
openPrimaryScreen();
setStatus(kNotRunning);
}
catch (XScreen&) {
catch (XScreen& e) {
// can't open screen
setStatus(kError, e.what());
LOG((CLOG_INFO "failed to open screen"));
throw;
}
catch (XUnknownClient& e) {
// can't open screen
setStatus(kServerNameUnknown);
LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str()));
throw;
}
@@ -108,6 +112,7 @@ CServer::mainLoop()
}
try {
setStatus(kNotRunning);
LOG((CLOG_NOTE "starting server"));
// start listening for new clients
@@ -138,11 +143,13 @@ CServer::mainLoop()
stopThreads(); \
delete m_httpServer; \
m_httpServer = NULL; \
runStatusJobs(); \
} while (false)
FINALLY;
}
catch (XMT& e) {
LOG((CLOG_ERR "server error: %s", e.what()));
setStatus(kError, e.what());
// clean up
LOG((CLOG_NOTE "stopping server"));
@@ -151,12 +158,15 @@ CServer::mainLoop()
}
catch (XBase& e) {
LOG((CLOG_ERR "server error: %s", e.what()));
setStatus(kError, e.what());
// clean up
LOG((CLOG_NOTE "stopping server"));
FINALLY;
}
catch (XThread&) {
setStatus(kNotRunning);
// clean up
LOG((CLOG_NOTE "stopping server"));
FINALLY;
@@ -164,6 +174,7 @@ CServer::mainLoop()
}
catch (...) {
LOG((CLOG_DEBUG "unknown server error"));
setStatus(kError);
// clean up
LOG((CLOG_NOTE "stopping server"));
@@ -260,6 +271,9 @@ CServer::setConfig(const CConfig& config)
sendOptions(client);
}
// notify of status
runStatusJobs();
return true;
}
@@ -287,12 +301,52 @@ CServer::setStreamFilterFactory(IStreamFilterFactory* adopted)
m_streamFilterFactory = adopted;
}
void
CServer::addStatusJob(IJob* job)
{
m_statusJobs.addJob(job);
}
void
CServer::removeStatusJob(IJob* job)
{
m_statusJobs.removeJob(job);
}
CString
CServer::getPrimaryScreenName() const
{
return m_name;
}
UInt32
CServer::getNumClients() const
{
CLock lock(&m_mutex);
return m_clients.size();
}
void
CServer::getClients(std::vector<CString>& list) const
{
CLock lock(&m_mutex);
list.clear();
for (CClientList::const_iterator index = m_clients.begin();
index != m_clients.end(); ++index) {
list.push_back(index->first);
}
}
CServer::EStatus
CServer::getStatus(CString* msg) const
{
CLock lock(&m_mutex);
if (msg != NULL) {
*msg = m_statusMessage;
}
return m_status;
}
void
CServer::getConfig(CConfig* config) const
{
@@ -302,6 +356,28 @@ CServer::getConfig(CConfig* config) const
*config = m_config;
}
void
CServer::runStatusJobs() const
{
m_statusJobs.runJobs();
}
void
CServer::setStatus(EStatus status, const char* msg)
{
{
CLock lock(&m_mutex);
m_status = status;
if (m_status == kError) {
m_statusMessage = (msg == NULL) ? "Error" : msg;
}
else {
m_statusMessage = (msg == NULL) ? "" : msg;
}
}
runStatusJobs();
}
UInt32
CServer::getActivePrimarySides() const
{
@@ -325,6 +401,8 @@ CServer::getActivePrimarySides() const
void
CServer::onError()
{
setStatus(kError);
// stop all running threads but don't wait too long since some
// threads may be unable to proceed until this thread returns.
stopThreads(3.0);
@@ -743,6 +821,10 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
case kBottom:
clearWait = (m_y <= ay + ah - 1 + zoneSize);
break;
default:
clearWait = false;
break;
}
if (clearWait) {
onNoSwitch();
@@ -1174,7 +1256,7 @@ CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y)
// if waiting before a switch then prepare to switch later
if (!allowSwitch && m_switchWaitDelay > 0.0) {
if (isNewDirection) {
if (isNewDirection || !m_switchWaitEngaged) {
m_switchWaitEngaged = true;
m_switchWaitX = x;
m_switchWaitY = y;
@@ -1413,6 +1495,7 @@ CServer::acceptClients(void*)
break;
}
catch (XSocketAddressInUse& e) {
setStatus(kError, e.what());
LOG((CLOG_WARN "bind failed: %s", e.what()));
// give up if we've waited too long
@@ -1427,6 +1510,7 @@ CServer::acceptClients(void*)
}
// accept connections and begin processing them
setStatus(kRunning);
LOG((CLOG_DEBUG1 "waiting for client connections"));
for (;;) {
// accept connection
@@ -1439,16 +1523,15 @@ CServer::acceptClients(void*)
startThread(new TMethodJob<CServer>(
this, &CServer::runClient, socket));
}
// clean up
delete listen;
}
catch (XBase& e) {
setStatus(kError, e.what());
LOG((CLOG_ERR "cannot listen for clients: %s", e.what()));
delete listen;
exitMainLoopWithError();
}
catch (...) {
setStatus(kNotRunning);
delete listen;
throw;
}
@@ -1923,71 +2006,84 @@ CServer::addConnection(IClient* client)
LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str()));
CLock lock(&m_mutex);
{
CLock lock(&m_mutex);
// name must be in our configuration
if (!m_config.isScreen(client->getName())) {
throw XUnknownClient(client->getName());
// name must be in our configuration
if (!m_config.isScreen(client->getName())) {
throw XUnknownClient(client->getName());
}
// can only have one screen with a given name at any given time
if (m_clients.count(client->getName()) != 0) {
throw XDuplicateClient(client->getName());
}
// save screen info
m_clients.insert(std::make_pair(client->getName(), client));
LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str()));
}
// can only have one screen with a given name at any given time
if (m_clients.count(client->getName()) != 0) {
throw XDuplicateClient(client->getName());
}
// save screen info
m_clients.insert(std::make_pair(client->getName(), client));
LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str()));
runStatusJobs();
}
void
CServer::removeConnection(const CString& name)
{
LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str()));
CLock lock(&m_mutex);
bool updateStatus;
{
CLock lock(&m_mutex);
// find client
CClientList::iterator index = m_clients.find(name);
assert(index != m_clients.end());
// find client
CClientList::iterator index = m_clients.find(name);
assert(index != m_clients.end());
// if this is active screen then we have to jump off of it
IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active;
if (active == index->second && active != m_primaryClient) {
// record new position (center of primary screen)
m_primaryClient->getCursorCenter(m_x, m_y);
// if this is active screen then we have to jump off of it
IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active;
if (active == index->second && active != m_primaryClient) {
// record new position (center of primary screen)
m_primaryClient->getCursorCenter(m_x, m_y);
// stop waiting to switch if we were
if (active == m_switchScreen) {
clearSwitchState();
// stop waiting to switch if we were
if (active == m_switchScreen) {
clearSwitchState();
}
// don't notify active screen since it probably already
// disconnected.
LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y));
// cut over
m_active = m_primaryClient;
// enter new screen (unless we already have because of the
// screen saver)
if (m_activeSaver == NULL) {
m_primaryClient->enter(m_x, m_y, m_seqNum,
m_primaryClient->getToggleMask(), false);
}
}
// don't notify active screen since it probably already disconnected
LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y));
// cut over
m_active = m_primaryClient;
// enter new screen (unless we already have because of the
// screen saver)
if (m_activeSaver == NULL) {
m_primaryClient->enter(m_x, m_y, m_seqNum,
m_primaryClient->getToggleMask(), false);
// if this screen had the cursor when the screen saver activated
// then we can't switch back to it when the screen saver
// deactivates.
if (m_activeSaver == index->second) {
m_activeSaver = NULL;
}
// done with client
delete index->second;
m_clients.erase(index);
// remove any thread for this client
m_clientThreads.erase(name);
updateStatus = (m_clients.size() <= 1);
}
// if this screen had the cursor when the screen saver activated
// then we can't switch back to it when the screen saver
// deactivates.
if (m_activeSaver == index->second) {
m_activeSaver = NULL;
if (updateStatus) {
runStatusJobs();
}
// done with client
delete index->second;
m_clients.erase(index);
// remove any thread for this client
m_clientThreads.erase(name);
}

View File

@@ -22,9 +22,11 @@
#include "CCondVar.h"
#include "CMutex.h"
#include "CThread.h"
#include "CJobList.h"
#include "CStopwatch.h"
#include "stdlist.h"
#include "stdmap.h"
#include "stdvector.h"
class CClientProxy;
class CHTTPServer;
@@ -42,6 +44,14 @@ This class implements the top-level server algorithms for synergy.
*/
class CServer : public IServer, public IPrimaryScreenReceiver {
public:
enum EStatus {
kNotRunning,
kRunning,
kServerNameUnknown,
kError,
kMaxStatus
};
/*!
The server will look itself up in the configuration using \c serverName
as its name.
@@ -116,6 +126,22 @@ public:
*/
void setStreamFilterFactory(IStreamFilterFactory*);
//! Add a job to notify of status changes
/*!
The added job is run whenever the server's status changes in
certain externally visible ways. The client keeps ownership
of the job.
*/
void addStatusJob(IJob*);
//! Remove a job to notify of status changes
/*!
Removes a previously added status notification job. A job can
remove itself when called but must not remove any other jobs.
The client keeps ownership of the job.
*/
void removeStatusJob(IJob*);
//@}
//! @name accessors
//@{
@@ -132,6 +158,24 @@ public:
*/
CString getPrimaryScreenName() const;
//! Get number of connected clients
/*!
Returns the number of connected clients, including the server itself.
*/
UInt32 getNumClients() const;
//! Get the list of connected clients
/*!
Set the \c list to the names of the currently connected clients.
*/
void getClients(std::vector<CString>& list) const;
//! Get the status
/*!
Returns the current status and status message.
*/
EStatus getStatus(CString* = NULL) const;
//@}
// IServer overrides
@@ -170,6 +214,12 @@ protected:
private:
typedef std::list<CThread> CThreadList;
// notify status jobs of a change
void runStatusJobs() const;
// set new status
void setStatus(EStatus, const char* msg = NULL);
// get the sides of the primary screen that have neighbors
UInt32 getActivePrimarySides() const;
@@ -352,6 +402,11 @@ private:
CStopwatch m_switchTwoTapTimer;
bool m_switchTwoTapEngaged;
bool m_switchTwoTapArmed;
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
};
#endif

View File

@@ -74,17 +74,6 @@ CSecondaryScreen::open()
// create and prepare our window
createWindow();
// assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
grabClipboard(id);
}
// update keyboard state
updateKeys();
// disable the screen saver
getScreen()->openScreensaver(false);
// subclass hook
onPostOpen();
@@ -95,6 +84,30 @@ CSecondaryScreen::open()
close();
throw;
}
}
void
CSecondaryScreen::close()
{
onPreClose();
destroyWindow();
getScreen()->close();
onPostClose();
}
void
CSecondaryScreen::remoteControl()
{
// assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
grabClipboard(id);
}
// update keyboard state
updateKeys();
// disable the screen saver
getScreen()->openScreensaver(false);
// hide the cursor
{
@@ -105,13 +118,9 @@ CSecondaryScreen::open()
}
void
CSecondaryScreen::close()
CSecondaryScreen::localControl()
{
onPreClose();
getScreen()->closeScreensaver();
destroyWindow();
getScreen()->close();
onPostClose();
}
void

View File

@@ -41,11 +41,10 @@ public:
//! Open screen
/*!
Opens the screen. This includes initializing the screen,
hiding the cursor, and disabling the screen saver. It also causes
events to the reported to an IScreenReceiver (which is set through
some other interface). Calls close() before returning (rethrowing)
if it fails for any reason.
Opens the screen. It also causes events to the reported to an
IScreenReceiver (which is set through some other interface).
Calls close() before returning (rethrowing) if it fails for any
reason.
*/
void open();
@@ -64,12 +63,26 @@ public:
*/
void exitMainLoop();
//! Prepare for remote control
/*!
Prepares the screen for remote control by the server. In
particular, it disables the screen saver.
*/
void remoteControl();
//! Release from remote control
/*!
Cleans up the screen from remote control by the server. In
particular, it enables the screen saver. It also synthesizes
key up events for any keys that are logically down; without
this the client will leave its keyboard in the wrong logical
state.
*/
void localControl();
//! Close screen
/*!
Closes the screen. This restores the screen saver, shows the cursor
and closes the screen. It also synthesizes key up events for any
keys that are logically down; without this the client will leave
its keyboard in the wrong logical state.
Closes the screen.
*/
void close();
@@ -332,6 +345,13 @@ protected:
*/
virtual void updateKeys() = 0;
//! Release keys
/*!
Synthesizes key release event for any key that our key state
says is down.
*/
virtual void releaseKeys() = 0;
//! Synchronize toggle key state
/*!
Toggle modifiers that don't match the given state so that they do.