mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-10 08:21:40 +08:00
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:
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
518
lib/arch/CArchTaskBarWindows.cpp
Normal file
518
lib/arch/CArchTaskBarWindows.cpp
Normal 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;
|
||||
}
|
||||
110
lib/arch/CArchTaskBarWindows.h
Normal file
110
lib/arch/CArchTaskBarWindows.h
Normal 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
|
||||
47
lib/arch/CArchTaskBarXWindows.cpp
Normal file
47
lib/arch/CArchTaskBarXWindows.cpp
Normal 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
|
||||
}
|
||||
34
lib/arch/CArchTaskBarXWindows.h
Normal file
34
lib/arch/CArchTaskBarXWindows.h
Normal 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
|
||||
@@ -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
63
lib/arch/IArchTaskBar.h
Normal 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
|
||||
90
lib/arch/IArchTaskBarReceiver.h
Normal file
90
lib/arch/IArchTaskBarReceiver.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
113
lib/base/CJobList.cpp
Normal 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
72
lib/base/CJobList.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -420,6 +420,7 @@ CServerProxy::translateModifierMask(KeyModifierMask mask) const
|
||||
if ((mask & KeyModifierSuper) != 0) {
|
||||
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]];
|
||||
}
|
||||
return newMask;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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
|
||||
/*!
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user