Checkpoint. Conversion to event driven system complete for Unix.
Still need to convert win32 platform specific files.
This commit is contained in:
parent
901a76df0d
commit
48908242d2
|
@ -15,7 +15,7 @@
|
|||
#include "CClientTaskBarReceiver.h"
|
||||
#include "CClient.h"
|
||||
#include "CLock.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "CArch.h"
|
||||
|
||||
//
|
||||
|
@ -23,71 +23,73 @@
|
|||
//
|
||||
|
||||
CClientTaskBarReceiver::CClientTaskBarReceiver() :
|
||||
m_quit(NULL),
|
||||
m_state(kNotRunning),
|
||||
m_client(NULL)
|
||||
m_state(kNotRunning)
|
||||
{
|
||||
// create a job for getting notification when the client's
|
||||
// status changes.
|
||||
m_job = new TMethodJob<CClientTaskBarReceiver>(this,
|
||||
&CClientTaskBarReceiver::statusChanged, NULL);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CClientTaskBarReceiver::~CClientTaskBarReceiver()
|
||||
{
|
||||
if (m_client != NULL) {
|
||||
m_client->removeStatusJob(m_job);
|
||||
}
|
||||
delete m_job;
|
||||
delete m_quit;
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::setClient(CClient* client)
|
||||
CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg)
|
||||
{
|
||||
{
|
||||
// update our status
|
||||
CLock lock(&m_mutex);
|
||||
if (m_client != client) {
|
||||
if (m_client != NULL) {
|
||||
m_client->removeStatusJob(m_job);
|
||||
m_errorMessage = errorMsg;
|
||||
if (client == NULL) {
|
||||
if (m_errorMessage.empty()) {
|
||||
m_state = kNotRunning;
|
||||
}
|
||||
m_client = client;
|
||||
if (m_client != NULL) {
|
||||
m_client->addStatusJob(m_job);
|
||||
else {
|
||||
m_state = kNotWorking;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (client->isConnected()) {
|
||||
m_state = kConnected;
|
||||
}
|
||||
else if (client->isConnecting()) {
|
||||
m_state = kConnecting;
|
||||
}
|
||||
else {
|
||||
m_state = kNotConnected;
|
||||
}
|
||||
}
|
||||
}
|
||||
ARCH->updateReceiver(this);
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::setState(EState state)
|
||||
{
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
m_state = state;
|
||||
// let subclasses have a go
|
||||
onStatusChanged(client);
|
||||
}
|
||||
ARCH->updateReceiver(this);
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::setQuitJob(IJob* job)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
delete m_quit;
|
||||
m_quit = job;
|
||||
// tell task bar
|
||||
ARCH->updateReceiver(this);
|
||||
}
|
||||
|
||||
CClientTaskBarReceiver::EState
|
||||
CClientTaskBarReceiver::getState() const
|
||||
CClientTaskBarReceiver::getStatus() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
CClient*
|
||||
CClientTaskBarReceiver::getClient() const
|
||||
const CString&
|
||||
CClientTaskBarReceiver::getErrorMessage() const
|
||||
{
|
||||
return m_client;
|
||||
return m_errorMessage;
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::quit()
|
||||
{
|
||||
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::onStatusChanged(CClient*)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -113,7 +115,10 @@ CClientTaskBarReceiver::getToolTip() const
|
|||
return CString("Synergy: ") + m_errorMessage;
|
||||
|
||||
case kNotConnected:
|
||||
return "Synergy: Waiting for clients";
|
||||
return CString("Synergy: Not connected: ") + m_errorMessage;
|
||||
|
||||
case kConnecting:
|
||||
return "Synergy: Connecting...";
|
||||
|
||||
case kConnected:
|
||||
return "Synergy: Connected";
|
||||
|
@ -122,42 +127,3 @@ CClientTaskBarReceiver::getToolTip() const
|
|||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::quit()
|
||||
{
|
||||
if (m_quit != NULL) {
|
||||
m_quit->run();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::onStatusChanged()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CClientTaskBarReceiver::statusChanged(void*)
|
||||
{
|
||||
// update our status
|
||||
switch (m_client->getStatus(&m_errorMessage)) {
|
||||
case CClient::kNotRunning:
|
||||
setState(kNotRunning);
|
||||
break;
|
||||
|
||||
case CClient::kRunning:
|
||||
setState(kConnected);
|
||||
break;
|
||||
|
||||
case CClient::kError:
|
||||
setState(kNotWorking);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// let subclasses have a go
|
||||
onStatusChanged();
|
||||
}
|
||||
|
|
|
@ -20,59 +20,21 @@
|
|||
#include "IArchTaskBarReceiver.h"
|
||||
|
||||
class CClient;
|
||||
class IJob;
|
||||
|
||||
//! Implementation of IArchTaskBarReceiver for the synergy server
|
||||
class CClientTaskBarReceiver : public IArchTaskBarReceiver {
|
||||
public:
|
||||
enum EState {
|
||||
kNotRunning,
|
||||
kNotWorking,
|
||||
kNotConnected,
|
||||
kConnected,
|
||||
kMaxState
|
||||
};
|
||||
|
||||
CClientTaskBarReceiver();
|
||||
virtual ~CClientTaskBarReceiver();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Set server
|
||||
//! Update status
|
||||
/*!
|
||||
Sets the server. The receiver will query state from this server.
|
||||
Determine the status and query required information from the client.
|
||||
*/
|
||||
void setClient(CClient*);
|
||||
|
||||
//! Set state
|
||||
/*!
|
||||
Sets the current server state.
|
||||
*/
|
||||
void setState(EState);
|
||||
|
||||
//! Set the quit job that causes the server to quit
|
||||
/*!
|
||||
Set the job that causes the server to quit.
|
||||
*/
|
||||
void setQuitJob(IJob* adopted);
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get state
|
||||
/*!
|
||||
Returns the current server state. The receiver is not locked
|
||||
by this call; the caller must do the locking.
|
||||
*/
|
||||
EState getState() const;
|
||||
|
||||
//! Get server
|
||||
/*!
|
||||
Returns the server set by \c setClient().
|
||||
*/
|
||||
CClient* getClient() const;
|
||||
void updateStatus(CClient*, const CString& errorMsg);
|
||||
|
||||
//@}
|
||||
|
||||
|
@ -86,24 +48,36 @@ public:
|
|||
virtual std::string getToolTip() const;
|
||||
|
||||
protected:
|
||||
enum EState {
|
||||
kNotRunning,
|
||||
kNotWorking,
|
||||
kNotConnected,
|
||||
kConnecting,
|
||||
kConnected,
|
||||
kMaxState
|
||||
};
|
||||
|
||||
//! Get status
|
||||
EState getStatus() const;
|
||||
|
||||
//! Get error message
|
||||
const CString& getErrorMessage() const;
|
||||
|
||||
//! Quit app
|
||||
/*!
|
||||
Causes the application to quit gracefully
|
||||
*/
|
||||
void quit();
|
||||
|
||||
//! Status change notification
|
||||
/*!
|
||||
Called when status changes. The default implementation does
|
||||
nothing.
|
||||
Called when status changes. The default implementation does nothing.
|
||||
*/
|
||||
virtual void onStatusChanged();
|
||||
|
||||
private:
|
||||
void statusChanged(void*);
|
||||
virtual void onStatusChanged(CClient* client);
|
||||
|
||||
private:
|
||||
CMutex m_mutex;
|
||||
IJob* m_quit;
|
||||
EState m_state;
|
||||
CClient* m_client;
|
||||
IJob* m_job;
|
||||
CString m_errorMessage;
|
||||
};
|
||||
|
||||
|
|
|
@ -53,9 +53,3 @@ CXWindowsClientTaskBarReceiver::getIcon() const
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsClientTaskBarReceiver::onStatusChanged()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
@ -28,10 +28,6 @@ public:
|
|||
virtual void runMenu(int x, int y);
|
||||
virtual void primaryAction();
|
||||
virtual const Icon getIcon() const;
|
||||
|
||||
protected:
|
||||
// CClientTaskBarReceiver overrides
|
||||
virtual void onStatusChanged();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,23 +13,22 @@
|
|||
*/
|
||||
|
||||
#include "CClient.h"
|
||||
#include "IScreenFactory.h"
|
||||
#include "CScreen.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "Version.h"
|
||||
#include "XScreen.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "CSocketMultiplexer.h"
|
||||
#include "CTCPSocketFactory.h"
|
||||
#include "XSocket.h"
|
||||
#include "CCondVar.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CThread.h"
|
||||
#include "XThread.h"
|
||||
#include "CFunctionJob.h"
|
||||
#include "CEventQueue.h"
|
||||
#include "CFunctionEventJob.h"
|
||||
#include "CLog.h"
|
||||
#include "LogOutputters.h"
|
||||
#include "CString.h"
|
||||
#include "LogOutputters.h"
|
||||
#include "CArch.h"
|
||||
#include "XArch.h"
|
||||
#include <cstring>
|
||||
|
||||
#define DAEMON_RUNNING(running_)
|
||||
|
@ -87,160 +86,290 @@ CArgs* CArgs::s_instance = NULL;
|
|||
// platform dependent factories
|
||||
//
|
||||
|
||||
//! Factory for creating screens
|
||||
/*!
|
||||
Objects of this type create screens appropriate for the platform.
|
||||
*/
|
||||
class CScreenFactory : public IScreenFactory {
|
||||
public:
|
||||
CScreenFactory() { }
|
||||
virtual ~CScreenFactory() { }
|
||||
|
||||
// IScreenFactory overrides
|
||||
virtual IPlatformScreen*
|
||||
create(IScreenReceiver*, IPrimaryScreenReceiver*);
|
||||
};
|
||||
|
||||
IPlatformScreen*
|
||||
CScreenFactory::create(IScreenReceiver* receiver,
|
||||
IPrimaryScreenReceiver* primaryReceiver)
|
||||
static
|
||||
CScreen*
|
||||
createScreen()
|
||||
{
|
||||
#if WINDOWS_LIKE
|
||||
return new CMSWindowsScreen(receiver, primaryReceiver);
|
||||
return new CScreen(new CMSWindowsScreen(false));
|
||||
#elif UNIX_LIKE
|
||||
return new CXWindowsScreen(receiver, primaryReceiver);
|
||||
return new CScreen(new CXWindowsScreen(false));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//! CQuitJob
|
||||
/*!
|
||||
A job that cancels a given thread.
|
||||
*/
|
||||
class CQuitJob : public IJob {
|
||||
public:
|
||||
CQuitJob(const CThread& thread);
|
||||
~CQuitJob();
|
||||
|
||||
// IJob overrides
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
CThread m_thread;
|
||||
};
|
||||
|
||||
CQuitJob::CQuitJob(const CThread& thread) :
|
||||
m_thread(thread)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CQuitJob::~CQuitJob()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CQuitJob::run()
|
||||
{
|
||||
m_thread.cancel();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// platform independent main
|
||||
//
|
||||
|
||||
static CClient* s_client = NULL;
|
||||
static CClient* s_client = NULL;
|
||||
static CScreen* s_clientScreen = NULL;
|
||||
static CClientTaskBarReceiver* s_taskBarReceiver = NULL;
|
||||
static double s_retryTime = 0.0;
|
||||
|
||||
static
|
||||
void
|
||||
updateStatus()
|
||||
{
|
||||
s_taskBarReceiver->updateStatus(s_client, "");
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
updateStatus(const CString& msg)
|
||||
{
|
||||
s_taskBarReceiver->updateStatus(s_client, msg);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
resetRestartTimeout()
|
||||
{
|
||||
s_retryTime = 0.0;
|
||||
}
|
||||
|
||||
static
|
||||
double
|
||||
nextRestartTimeout()
|
||||
{
|
||||
// choose next restart timeout. we start with rapid retries
|
||||
// then slow down.
|
||||
if (s_retryTime < 1.0) {
|
||||
s_retryTime = 1.0;
|
||||
}
|
||||
else if (s_retryTime < 3.0) {
|
||||
s_retryTime = 3.0;
|
||||
}
|
||||
else if (s_retryTime < 5.0) {
|
||||
s_retryTime = 5.0;
|
||||
}
|
||||
else if (s_retryTime < 15.0) {
|
||||
s_retryTime = 15.0;
|
||||
}
|
||||
else if (s_retryTime < 30.0) {
|
||||
s_retryTime = 30.0;
|
||||
}
|
||||
else {
|
||||
s_retryTime = 60.0;
|
||||
}
|
||||
return s_retryTime;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleScreenError(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_CRIT "error on screen"));
|
||||
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
|
||||
}
|
||||
|
||||
static
|
||||
CScreen*
|
||||
openClientScreen()
|
||||
{
|
||||
CScreen* screen = createScreen();
|
||||
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
|
||||
screen->getEventTarget(),
|
||||
new CFunctionEventJob(
|
||||
&handleScreenError));
|
||||
return screen;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
closeClientScreen(CScreen* screen)
|
||||
{
|
||||
if (screen != NULL) {
|
||||
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
|
||||
screen->getEventTarget());
|
||||
delete screen;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleClientRestart(const CEvent&, void* vtimer)
|
||||
{
|
||||
// discard old timer
|
||||
CEventQueueTimer* timer = reinterpret_cast<CEventQueueTimer*>(vtimer);
|
||||
EVENTQUEUE->deleteTimer(timer);
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, NULL);
|
||||
|
||||
// reconnect
|
||||
s_client->connect();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
scheduleClientRestart(double retryTime)
|
||||
{
|
||||
// install a timer and handler to retry later
|
||||
LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
|
||||
CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
|
||||
EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
|
||||
new CFunctionEventJob(&handleClientRestart, timer));
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleClientConnected(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_DEBUG "connected to server"));
|
||||
resetRestartTimeout();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleClientFailed(const CEvent& e, void*)
|
||||
{
|
||||
CClient::CFailInfo* info =
|
||||
reinterpret_cast<CClient::CFailInfo*>(e.getData());
|
||||
|
||||
updateStatus(CString("Failed to connect to server: ") + info->m_what);
|
||||
if (!ARG->m_restartable || !info->m_retry) {
|
||||
LOG((CLOG_ERR "failed to connect to server: %s", info->m_what));
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_WARN "failed to connect to server: %s", info->m_what));
|
||||
scheduleClientRestart(nextRestartTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleClientDisconnected(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_NOTE "disconnected from server"));
|
||||
if (!ARG->m_restartable) {
|
||||
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
|
||||
}
|
||||
else {
|
||||
s_client->connect();
|
||||
}
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
static
|
||||
CClient*
|
||||
openClient(const CString& name, const CNetworkAddress& address, CScreen* screen)
|
||||
{
|
||||
CClient* client = new CClient(name, address,
|
||||
new CTCPSocketFactory, NULL, screen);
|
||||
EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(),
|
||||
client->getEventTarget(),
|
||||
new CFunctionEventJob(handleClientConnected));
|
||||
EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(),
|
||||
client->getEventTarget(),
|
||||
new CFunctionEventJob(handleClientFailed));
|
||||
EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(),
|
||||
client->getEventTarget(),
|
||||
new CFunctionEventJob(handleClientDisconnected));
|
||||
return client;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
closeClient(CClient* client)
|
||||
{
|
||||
if (client == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client);
|
||||
EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client);
|
||||
EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client);
|
||||
delete client;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
startClient()
|
||||
{
|
||||
double retryTime;
|
||||
CScreen* clientScreen = NULL;
|
||||
try {
|
||||
clientScreen = openClientScreen();
|
||||
s_client = openClient(ARG->m_name,
|
||||
ARG->m_serverAddress, clientScreen);
|
||||
s_clientScreen = clientScreen;
|
||||
LOG((CLOG_NOTE "started client"));
|
||||
s_client->connect();
|
||||
updateStatus();
|
||||
return true;
|
||||
}
|
||||
catch (XScreenUnavailable& e) {
|
||||
LOG((CLOG_WARN "cannot open secondary screen: %s", e.what()));
|
||||
closeClientScreen(clientScreen);
|
||||
updateStatus(CString("Cannot open secondary screen: ") + e.what());
|
||||
retryTime = e.getRetryTime();
|
||||
}
|
||||
catch (XScreenOpenFailure& e) {
|
||||
LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what()));
|
||||
closeClientScreen(clientScreen);
|
||||
return false;
|
||||
}
|
||||
catch (XBase& e) {
|
||||
LOG((CLOG_CRIT "failed to start client: %s", e.what()));
|
||||
closeClientScreen(clientScreen);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ARG->m_restartable) {
|
||||
scheduleClientRestart(retryTime);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// don't try again
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
stopClient()
|
||||
{
|
||||
closeClient(s_client);
|
||||
closeClientScreen(s_clientScreen);
|
||||
s_client = NULL;
|
||||
s_clientScreen = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
realMain(void)
|
||||
realMain()
|
||||
{
|
||||
int result = kExitSuccess;
|
||||
do {
|
||||
bool opened = false;
|
||||
bool locked = true;
|
||||
try {
|
||||
// create client
|
||||
s_client = new CClient(ARG->m_name);
|
||||
s_client->setAddress(ARG->m_serverAddress);
|
||||
s_client->setScreenFactory(new CScreenFactory);
|
||||
s_client->setSocketFactory(new CTCPSocketFactory);
|
||||
s_client->setStreamFilterFactory(NULL);
|
||||
// start the client. if this return false then we've failed and
|
||||
// we shouldn't retry.
|
||||
LOG((CLOG_DEBUG1 "starting client"));
|
||||
if (!startClient()) {
|
||||
return kExitFailed;
|
||||
}
|
||||
|
||||
// open client
|
||||
try {
|
||||
s_taskBarReceiver->setClient(s_client);
|
||||
s_client->open();
|
||||
opened = true;
|
||||
// run event loop. if startClient() failed we're supposed to retry
|
||||
// later. the timer installed by startClient() will take care of
|
||||
// that.
|
||||
DAEMON_RUNNING(true);
|
||||
CEvent event;
|
||||
EVENTQUEUE->getEvent(event);
|
||||
while (event.getType() != CEvent::kQuit) {
|
||||
EVENTQUEUE->dispatchEvent(event);
|
||||
CEvent::deleteData(event);
|
||||
EVENTQUEUE->getEvent(event);
|
||||
}
|
||||
DAEMON_RUNNING(false);
|
||||
|
||||
// run client
|
||||
DAEMON_RUNNING(true);
|
||||
locked = false;
|
||||
s_client->mainLoop();
|
||||
locked = true;
|
||||
DAEMON_RUNNING(false);
|
||||
// close down
|
||||
LOG((CLOG_DEBUG1 "stopping client"));
|
||||
stopClient();
|
||||
updateStatus();
|
||||
LOG((CLOG_NOTE "stopped client"));
|
||||
|
||||
// get client status
|
||||
if (s_client->wasRejected()) {
|
||||
// try again later. we don't want to bother
|
||||
// the server very often if it doesn't want us.
|
||||
throw XScreenUnavailable(60.0);
|
||||
}
|
||||
|
||||
// clean up
|
||||
#define FINALLY do { \
|
||||
if (!locked) { \
|
||||
DAEMON_RUNNING(false); \
|
||||
locked = true; \
|
||||
} \
|
||||
if (opened) { \
|
||||
s_client->close(); \
|
||||
} \
|
||||
s_taskBarReceiver->setClient(NULL); \
|
||||
delete s_client; \
|
||||
s_client = NULL; \
|
||||
} while (false)
|
||||
FINALLY;
|
||||
}
|
||||
catch (XScreenUnavailable& e) {
|
||||
// wait before retrying if we're going to retry
|
||||
if (ARG->m_restartable) {
|
||||
LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime()));
|
||||
ARCH->sleep(e.getRetryTime());
|
||||
}
|
||||
else {
|
||||
result = kExitFailed;
|
||||
}
|
||||
FINALLY;
|
||||
}
|
||||
catch (XThread&) {
|
||||
FINALLY;
|
||||
throw;
|
||||
}
|
||||
catch (...) {
|
||||
// don't try to restart and fail
|
||||
ARG->m_restartable = false;
|
||||
result = kExitFailed;
|
||||
FINALLY;
|
||||
}
|
||||
#undef FINALLY
|
||||
}
|
||||
catch (XBase& e) {
|
||||
LOG((CLOG_CRIT "failed: %s", e.what()));
|
||||
}
|
||||
catch (XThread&) {
|
||||
// terminated
|
||||
ARG->m_restartable = false;
|
||||
result = kExitTerminated;
|
||||
}
|
||||
} while (ARG->m_restartable);
|
||||
|
||||
return result;
|
||||
return kExitSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
static
|
||||
void
|
||||
realMainEntry(void* vresult)
|
||||
|
@ -278,6 +407,7 @@ runMainInThread(void)
|
|||
throw;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
|
@ -292,14 +422,12 @@ static
|
|||
void
|
||||
version()
|
||||
{
|
||||
LOG((CLOG_PRINT
|
||||
"%s %s, protocol version %d.%d\n"
|
||||
"%s",
|
||||
ARG->m_pname,
|
||||
kVersion,
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion,
|
||||
kCopyright));
|
||||
LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s",
|
||||
ARG->m_pname,
|
||||
kVersion,
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion,
|
||||
kCopyright));
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -697,15 +825,20 @@ main(int argc, char** argv)
|
|||
{
|
||||
CArch arch;
|
||||
CLOG;
|
||||
CArgs args;
|
||||
|
||||
// go really fast
|
||||
CThread::getCurrentThread().setPriority(-14);
|
||||
|
||||
CSocketMultiplexer multiplexer;
|
||||
CEventQueue eventQueue;
|
||||
|
||||
// get program name
|
||||
CArgs args;
|
||||
ARG->m_pname = ARCH->getBasename(argv[0]);
|
||||
|
||||
// make the task bar receiver. the user can control this app
|
||||
// through the task bar.
|
||||
s_taskBarReceiver = new CXWindowsClientTaskBarReceiver;
|
||||
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
|
||||
|
||||
// parse command line
|
||||
parse(argc, argv);
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
#include "CServerTaskBarReceiver.h"
|
||||
#include "CServer.h"
|
||||
#include "CEventQueue.h"
|
||||
#include "CLock.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "CArch.h"
|
||||
|
||||
//
|
||||
|
|
|
@ -171,25 +171,35 @@ closeClientListener(CClientListener* listen)
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleScreenError(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_CRIT "error on screen"));
|
||||
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
|
||||
}
|
||||
|
||||
static
|
||||
CScreen*
|
||||
openServerScreen()
|
||||
{
|
||||
return createScreen();
|
||||
CScreen* screen = createScreen();
|
||||
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
|
||||
screen->getEventTarget(),
|
||||
new CFunctionEventJob(
|
||||
&handleScreenError));
|
||||
return screen;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
closeServerScreen(CScreen* screen)
|
||||
{
|
||||
delete screen;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
handleScreenError(const CEvent&, void*)
|
||||
{
|
||||
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
|
||||
if (screen != NULL) {
|
||||
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
|
||||
screen->getEventTarget());
|
||||
delete screen;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -197,23 +207,14 @@ CPrimaryClient*
|
|||
openPrimaryClient(const CString& name, CScreen* screen)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "creating primary screen"));
|
||||
CPrimaryClient* primaryClient = new CPrimaryClient(name, screen);
|
||||
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
|
||||
primaryClient->getEventTarget(),
|
||||
new CFunctionEventJob(
|
||||
&handleScreenError));
|
||||
return primaryClient;
|
||||
return new CPrimaryClient(name, screen);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
closePrimaryClient(CPrimaryClient* primaryClient)
|
||||
{
|
||||
if (primaryClient != NULL) {
|
||||
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
|
||||
primaryClient->getEventTarget());
|
||||
delete primaryClient;
|
||||
}
|
||||
delete primaryClient;
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -711,7 +712,7 @@ parse(int argc, const char* const* argv)
|
|||
|
||||
static
|
||||
bool
|
||||
loadConfig(const char* pathname, bool require)
|
||||
loadConfig(const char* pathname)
|
||||
{
|
||||
assert(pathname != NULL);
|
||||
|
||||
|
@ -727,15 +728,8 @@ loadConfig(const char* pathname, bool require)
|
|||
return true;
|
||||
}
|
||||
catch (XConfigRead& e) {
|
||||
if (require) {
|
||||
LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s",
|
||||
ARG->m_pname, pathname, e.what()));
|
||||
bye(kExitConfig);
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s",
|
||||
LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s",
|
||||
pathname, e.what()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -744,34 +738,38 @@ static
|
|||
void
|
||||
loadConfig()
|
||||
{
|
||||
bool loaded = false;
|
||||
|
||||
// load the config file, if specified
|
||||
if (ARG->m_configFile != NULL) {
|
||||
// require the user specified file to load correctly
|
||||
loadConfig(ARG->m_configFile, true);
|
||||
loaded = loadConfig(ARG->m_configFile);
|
||||
}
|
||||
|
||||
// load the default configuration if no explicit file given
|
||||
else {
|
||||
// get the user's home directory. use the effective user id
|
||||
// so a user can't get a setuid root program to load his file.
|
||||
bool loaded = false;
|
||||
// get the user's home directory
|
||||
CString path = ARCH->getUserDirectory();
|
||||
if (!path.empty()) {
|
||||
// complete path
|
||||
path = ARCH->concatPath(path, USR_CONFIG_NAME);
|
||||
|
||||
// now try loading the user's configuration
|
||||
loaded = loadConfig(path.c_str(), false);
|
||||
loaded = loadConfig(path.c_str());
|
||||
}
|
||||
if (!loaded) {
|
||||
// try the system-wide config file
|
||||
path = ARCH->getSystemDirectory();
|
||||
if (!path.empty()) {
|
||||
path = ARCH->concatPath(path, SYS_CONFIG_NAME);
|
||||
loadConfig(path.c_str(), false);
|
||||
loaded = loadConfig(path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname));
|
||||
bye(kExitConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -620,6 +620,13 @@ CArchNetworkBSD::nameToAddr(const std::string& name)
|
|||
sizeof(inaddr.sin_addr));
|
||||
memcpy(&addr->m_addr, &inaddr, addr->m_len);
|
||||
}
|
||||
else {
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
delete addr;
|
||||
throw XArchNetworkNameUnsupported(
|
||||
"The requested name is valid but "
|
||||
"does not have a supported address family");
|
||||
}
|
||||
|
||||
// done with static buffer
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
|
|
|
@ -64,7 +64,8 @@ class XArch {
|
|||
public:
|
||||
XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { }
|
||||
XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { }
|
||||
XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { }
|
||||
XArch(const XArch& e) : m_eval(e.m_eval != NULL ? e.m_eval->clone() : NULL),
|
||||
m_what(e.m_what) { }
|
||||
~XArch() { delete m_eval; }
|
||||
|
||||
std::string what() const throw();
|
||||
|
@ -137,7 +138,7 @@ XARCH_SUBCLASS(XArchNetworkName, XArchNetwork);
|
|||
//! The named host is unknown
|
||||
XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName);
|
||||
|
||||
//! The named host is known but has to address
|
||||
//! The named host is known but has no address
|
||||
XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName);
|
||||
|
||||
//! Non-recoverable name server error
|
||||
|
@ -146,6 +147,9 @@ XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName);
|
|||
//! Temporary name server error
|
||||
XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName);
|
||||
|
||||
//! The named host is known but no supported address
|
||||
XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName);
|
||||
|
||||
//! Generic daemon exception
|
||||
/*!
|
||||
Exceptions derived from this class are used by the daemon
|
||||
|
|
|
@ -82,8 +82,7 @@ CEvent::deleteData(const CEvent& event)
|
|||
break;
|
||||
|
||||
default:
|
||||
// yes, really delete void*
|
||||
delete event.getData();
|
||||
free(event.getData());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,9 +38,10 @@ public:
|
|||
//! Create \c CEvent with data
|
||||
/*!
|
||||
The \p type must have been registered using \c registerType().
|
||||
The \p data must be POD (plain old data) which means it cannot
|
||||
have a destructor or be composed of any types that do. \p target
|
||||
is the intended recipient of the event.
|
||||
The \p data must be POD (plain old data) allocated by malloc(),
|
||||
which means it cannot have a constructor, destructor or be
|
||||
composed of any types that do. \p target is the intended
|
||||
recipient of the event.
|
||||
*/
|
||||
CEvent(Type type, void* target = NULL, void* data = NULL);
|
||||
|
||||
|
@ -70,7 +71,7 @@ public:
|
|||
|
||||
//! Release event data
|
||||
/*!
|
||||
Deletes event data for the given event.
|
||||
Deletes event data for the given event (using free()).
|
||||
*/
|
||||
static void deleteData(const CEvent&);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,133 +15,113 @@
|
|||
#ifndef CCLIENT_H
|
||||
#define CCLIENT_H
|
||||
|
||||
#include "IScreenReceiver.h"
|
||||
#include "IClient.h"
|
||||
#include "IClipboard.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "CMutex.h"
|
||||
#include "CJobList.h"
|
||||
|
||||
class CEventQueueTimer;
|
||||
class CScreen;
|
||||
class CServerProxy;
|
||||
class CThread;
|
||||
class IDataSocket;
|
||||
class IScreenReceiver;
|
||||
class IScreenFactory;
|
||||
class ISocketFactory;
|
||||
class IStream;
|
||||
class IStreamFilterFactory;
|
||||
|
||||
//! Synergy client
|
||||
/*!
|
||||
This class implements the top-level client algorithms for synergy.
|
||||
*/
|
||||
class CClient : public IScreenReceiver, public IClient {
|
||||
class CClient : public IClient {
|
||||
public:
|
||||
enum EStatus {
|
||||
kNotRunning,
|
||||
kRunning,
|
||||
kError,
|
||||
kMaxStatus
|
||||
class CFailInfo {
|
||||
public:
|
||||
bool m_retry;
|
||||
char m_what[1];
|
||||
};
|
||||
|
||||
/*!
|
||||
This client will attempt to connect the server using \c clientName
|
||||
as its name.
|
||||
This client will attempt to connect to the server using \p name
|
||||
as its name and \p address as the server's address and \p factory
|
||||
to create the socket. \p screen is the local screen.
|
||||
*/
|
||||
CClient(const CString& clientName);
|
||||
CClient(const CString& name, const CNetworkAddress& address,
|
||||
ISocketFactory* socketFactory,
|
||||
IStreamFilterFactory* streamFilterFactory,
|
||||
CScreen* screen);
|
||||
~CClient();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Set server address
|
||||
//! Connect to server
|
||||
/*!
|
||||
Sets the server's address that the client should connect to.
|
||||
Starts an attempt to connect to the server. This is ignored if
|
||||
the client is trying to connect or is already connected.
|
||||
*/
|
||||
void setAddress(const CNetworkAddress& serverAddress);
|
||||
void connect();
|
||||
|
||||
//! Set screen factory
|
||||
//! Disconnect
|
||||
/*!
|
||||
Sets the factory for creating screens. This must be set before
|
||||
calling open(). This object takes ownership of the factory.
|
||||
Disconnects from the server with an optional error message.
|
||||
*/
|
||||
void setScreenFactory(IScreenFactory*);
|
||||
|
||||
//! Set socket factory
|
||||
/*!
|
||||
Sets the factory used to create a socket to connect to the server.
|
||||
This must be set before calling mainLoop(). This object takes
|
||||
ownership of the factory.
|
||||
*/
|
||||
void setSocketFactory(ISocketFactory*);
|
||||
|
||||
//! Set stream filter factory
|
||||
/*!
|
||||
Sets the factory used to filter the socket streams used to
|
||||
communicate with the server. This object takes ownership
|
||||
of the factory.
|
||||
*/
|
||||
void setStreamFilterFactory(IStreamFilterFactory*);
|
||||
|
||||
//! Exit event loop
|
||||
/*!
|
||||
Force mainLoop() to return. This call can return before
|
||||
mainLoop() does (i.e. asynchronously). This may only be
|
||||
called between a successful open() and close().
|
||||
*/
|
||||
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*);
|
||||
void disconnect(const char* msg);
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//!
|
||||
//! Test if connected
|
||||
/*!
|
||||
Returns true if the server rejected our connection.
|
||||
Returns true iff the client is successfully connected to the server.
|
||||
*/
|
||||
bool wasRejected() const;
|
||||
bool isConnected() const;
|
||||
|
||||
//! Get the status
|
||||
//! Test if connecting
|
||||
/*!
|
||||
Returns the current status and status message.
|
||||
Returns true iff the client is currently attempting to connect to
|
||||
the server.
|
||||
*/
|
||||
EStatus getStatus(CString* = NULL) const;
|
||||
bool isConnecting() const;
|
||||
|
||||
//! Get connected event type
|
||||
/*!
|
||||
Returns the connected event type. This is sent when the client has
|
||||
successfully connected to the server.
|
||||
*/
|
||||
static CEvent::Type getConnectedEvent();
|
||||
|
||||
//! Get connection failed event type
|
||||
/*!
|
||||
Returns the connection failed event type. This is sent when the
|
||||
server fails for some reason. The event data is a CFailInfo*.
|
||||
*/
|
||||
static CEvent::Type getConnectionFailedEvent();
|
||||
|
||||
//! Get disconnected event type
|
||||
/*!
|
||||
Returns the disconnected event type. This is sent when the client
|
||||
has disconnected from the server (and only after having successfully
|
||||
connected).
|
||||
*/
|
||||
static CEvent::Type getDisconnectedEvent();
|
||||
|
||||
//@}
|
||||
|
||||
// IScreenReceiver overrides
|
||||
virtual void onError();
|
||||
virtual void onInfoChanged(const CClientInfo&);
|
||||
virtual bool onGrabClipboard(ClipboardID);
|
||||
virtual void onClipboardChanged(ClipboardID, const CString&);
|
||||
// IScreen overrides
|
||||
virtual void* getEventTarget() const;
|
||||
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
|
||||
virtual void getShape(SInt32& x, SInt32& y,
|
||||
SInt32& width, SInt32& height) const;
|
||||
virtual void getCursorPos(SInt32& x, SInt32& y) const;
|
||||
|
||||
// IClient overrides
|
||||
virtual void open();
|
||||
virtual void mainLoop();
|
||||
virtual void close();
|
||||
virtual void enter(SInt32 xAbs, SInt32 yAbs,
|
||||
UInt32 seqNum, KeyModifierMask mask,
|
||||
bool forScreensaver);
|
||||
virtual bool leave();
|
||||
virtual void setClipboard(ClipboardID, const CString&);
|
||||
virtual void setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void grabClipboard(ClipboardID);
|
||||
virtual void setClipboardDirty(ClipboardID, bool dirty);
|
||||
virtual void setClipboardDirty(ClipboardID, bool);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
|
||||
virtual void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton);
|
||||
|
@ -154,52 +134,48 @@ public:
|
|||
virtual void resetOptions();
|
||||
virtual void setOptions(const COptionsList& options);
|
||||
virtual CString getName() const;
|
||||
virtual SInt32 getJumpZoneSize() const;
|
||||
virtual void getShape(SInt32& x, SInt32& y,
|
||||
SInt32& width, SInt32& height) const;
|
||||
virtual void getCursorPos(SInt32& x, SInt32& y) const;
|
||||
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();
|
||||
|
||||
// send the clipboard to the server
|
||||
void sendClipboard(ClipboardID);
|
||||
|
||||
// handle server messaging
|
||||
void runSession(void*);
|
||||
void deleteSession(double timeout = -1.0);
|
||||
void runServer();
|
||||
CServerProxy* handshakeServer(IDataSocket*);
|
||||
void sendEvent(CEvent::Type, void*);
|
||||
void sendConnectionFailedEvent(const char* msg);
|
||||
void setupConnecting();
|
||||
void setupConnection();
|
||||
void setupScreen();
|
||||
void setupTimer();
|
||||
void cleanupConnecting();
|
||||
void cleanupConnection();
|
||||
void cleanupScreen();
|
||||
void cleanupTimer();
|
||||
void handleConnected(const CEvent&, void*);
|
||||
void handleConnectionFailed(const CEvent&, void*);
|
||||
void handleConnectTimeout(const CEvent&, void*);
|
||||
void handleOutputError(const CEvent&, void*);
|
||||
void handleDisconnected(const CEvent&, void*);
|
||||
void handleHandshakeComplete(const CEvent&, void*);
|
||||
void handleShapeChanged(const CEvent&, void*);
|
||||
void handleClipboardGrabbed(const CEvent&, void*);
|
||||
void handleClipboardChanged(const CEvent&, void*);
|
||||
void handleHello(const CEvent&, void*);
|
||||
|
||||
private:
|
||||
CMutex m_mutex;
|
||||
CString m_name;
|
||||
CScreen* m_screen;
|
||||
IScreenReceiver* m_server;
|
||||
CNetworkAddress m_serverAddress;
|
||||
IScreenFactory* m_screenFactory;
|
||||
ISocketFactory* m_socketFactory;
|
||||
CString m_name;
|
||||
CNetworkAddress m_serverAddress;
|
||||
ISocketFactory* m_socketFactory;
|
||||
IStreamFilterFactory* m_streamFilterFactory;
|
||||
CThread* m_session;
|
||||
bool m_active;
|
||||
bool m_rejected;
|
||||
CScreen* m_screen;
|
||||
IStream* m_stream;
|
||||
CEventQueueTimer* m_timer;
|
||||
CServerProxy* m_server;
|
||||
bool m_ready;
|
||||
bool m_active;
|
||||
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;
|
||||
static CEvent::Type s_connectedEvent;
|
||||
static CEvent::Type s_connectionFailedEvent;
|
||||
static CEvent::Type s_disconnectedEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,15 +13,15 @@
|
|||
*/
|
||||
|
||||
#include "CServerProxy.h"
|
||||
#include "CClient.h"
|
||||
#include "CClipboard.h"
|
||||
#include "CProtocolUtil.h"
|
||||
#include "IClient.h"
|
||||
#include "OptionTypes.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "IInputStream.h"
|
||||
#include "IOutputStream.h"
|
||||
#include "CLock.h"
|
||||
#include "IStream.h"
|
||||
#include "CLog.h"
|
||||
#include "CStopwatch.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "TMethodEventJob.h"
|
||||
#include "XBase.h"
|
||||
#include <memory>
|
||||
|
||||
|
@ -29,293 +29,243 @@
|
|||
// CServerProxy
|
||||
//
|
||||
|
||||
CServerProxy::CServerProxy(IClient* client,
|
||||
IInputStream* adoptedInput, IOutputStream* adoptedOutput) :
|
||||
CEvent::Type CServerProxy::s_handshakeCompleteEvent =
|
||||
CEvent::kUnknown;
|
||||
|
||||
CServerProxy::CServerProxy(CClient* client, IStream* stream) :
|
||||
m_client(client),
|
||||
m_input(adoptedInput),
|
||||
m_output(adoptedOutput),
|
||||
m_stream(stream),
|
||||
m_timer(NULL),
|
||||
m_seqNum(0),
|
||||
m_heartRate(kHeartRate)
|
||||
m_compressMouse(false),
|
||||
m_ignoreMouse(false),
|
||||
m_heartRate(0.0)
|
||||
{
|
||||
assert(m_client != NULL);
|
||||
assert(m_input != NULL);
|
||||
assert(m_output != NULL);
|
||||
assert(m_stream != NULL);
|
||||
|
||||
// initialize modifier translation table
|
||||
for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id)
|
||||
m_modifierTranslationTable[id] = id;
|
||||
|
||||
// handle data on stream
|
||||
EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CServerProxy>(this,
|
||||
&CServerProxy::handleMessage));
|
||||
|
||||
// send heartbeat
|
||||
installHeartBeat(kHeartRate);
|
||||
}
|
||||
|
||||
CServerProxy::~CServerProxy()
|
||||
{
|
||||
delete m_input;
|
||||
delete m_output;
|
||||
installHeartBeat(-1.0);
|
||||
}
|
||||
|
||||
bool
|
||||
CServerProxy::mainLoop()
|
||||
CEvent::Type
|
||||
CServerProxy::getHandshakeCompleteEvent()
|
||||
{
|
||||
bool failedToConnect = false;
|
||||
try {
|
||||
// no compressed mouse motion yet
|
||||
m_compressMouse = false;
|
||||
return CEvent::registerTypeOnce(s_handshakeCompleteEvent,
|
||||
"CServerProxy::handshakeComplete");
|
||||
}
|
||||
|
||||
// not ignoring mouse motions
|
||||
m_ignoreMouse = false;
|
||||
void
|
||||
CServerProxy::installHeartBeat(double heartRate)
|
||||
{
|
||||
if (m_timer != NULL) {
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer);
|
||||
EVENTQUEUE->deleteTimer(m_timer);
|
||||
}
|
||||
m_heartRate = heartRate;
|
||||
if (m_heartRate > 0.0) {
|
||||
m_timer = EVENTQUEUE->newTimer(m_heartRate, NULL);
|
||||
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer,
|
||||
new TMethodEventJob<CServerProxy>(this,
|
||||
&CServerProxy::handleHeartBeat));
|
||||
}
|
||||
}
|
||||
|
||||
// reset sequence number
|
||||
m_seqNum = 0;
|
||||
void
|
||||
CServerProxy::handleMessage(const CEvent&, void*)
|
||||
{
|
||||
while (m_stream->isReady()) {
|
||||
// read next code
|
||||
UInt8 code[4];
|
||||
UInt32 n = m_stream->read(code, sizeof(code));
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
if (n != 4) {
|
||||
// client sent an incomplete message
|
||||
LOG((CLOG_ERR "incomplete message from server"));
|
||||
m_client->disconnect("incomplete message from server");
|
||||
return;
|
||||
}
|
||||
|
||||
// handle messages from server
|
||||
CStopwatch heartbeat;
|
||||
for (;;) {
|
||||
// if no input is pending then flush compressed mouse motion
|
||||
if (getInputStream()->getSize() == 0) {
|
||||
flushCompressedMouse();
|
||||
}
|
||||
// parse message
|
||||
LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
|
||||
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
|
||||
mouseMove();
|
||||
}
|
||||
|
||||
// wait for a message
|
||||
LOG((CLOG_DEBUG2 "waiting for message"));
|
||||
UInt8 code[4];
|
||||
UInt32 n = getInputStream()->read(code, 4, m_heartRate);
|
||||
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
|
||||
mouseWheel();
|
||||
}
|
||||
|
||||
// check if server hungup
|
||||
if (n == 0) {
|
||||
LOG((CLOG_NOTE "server disconnected"));
|
||||
break;
|
||||
}
|
||||
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
||||
keyDown();
|
||||
}
|
||||
|
||||
// check for time out
|
||||
if (n == (UInt32)-1 ||
|
||||
(m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) {
|
||||
// send heartbeat
|
||||
CLock lock(&m_mutex);
|
||||
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
|
||||
heartbeat.reset();
|
||||
if (n == (UInt32)-1) {
|
||||
// no message to process
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
|
||||
keyUp();
|
||||
}
|
||||
|
||||
// verify we got an entire code
|
||||
if (n != 4) {
|
||||
// client sent an incomplete message
|
||||
LOG((CLOG_ERR "incomplete message from server"));
|
||||
break;
|
||||
}
|
||||
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
|
||||
mouseDown();
|
||||
}
|
||||
|
||||
// parse message
|
||||
LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
|
||||
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
|
||||
mouseMove();
|
||||
}
|
||||
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
|
||||
mouseUp();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
|
||||
mouseWheel();
|
||||
}
|
||||
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
||||
keyRepeat();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
||||
keyDown();
|
||||
}
|
||||
else if (memcmp(code, kMsgCNoop, 4) == 0) {
|
||||
// accept and discard no-op
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
|
||||
keyUp();
|
||||
}
|
||||
else if (memcmp(code, kMsgCEnter, 4) == 0) {
|
||||
enter();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
|
||||
mouseDown();
|
||||
}
|
||||
else if (memcmp(code, kMsgCLeave, 4) == 0) {
|
||||
leave();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
|
||||
mouseUp();
|
||||
}
|
||||
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
|
||||
grabClipboard();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
||||
keyRepeat();
|
||||
}
|
||||
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
|
||||
screensaver();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCNoop, 4) == 0) {
|
||||
// accept and discard no-op
|
||||
}
|
||||
else if (memcmp(code, kMsgQInfo, 4) == 0) {
|
||||
queryInfo();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCEnter, 4) == 0) {
|
||||
enter();
|
||||
}
|
||||
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
|
||||
infoAcknowledgment();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCLeave, 4) == 0) {
|
||||
leave();
|
||||
}
|
||||
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
|
||||
setClipboard();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
|
||||
grabClipboard();
|
||||
}
|
||||
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
|
||||
resetOptions();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
|
||||
screensaver();
|
||||
}
|
||||
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
|
||||
setOptions();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgQInfo, 4) == 0) {
|
||||
queryInfo();
|
||||
}
|
||||
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
||||
// server wants us to hangup
|
||||
LOG((CLOG_DEBUG1 "recv close"));
|
||||
m_client->disconnect(NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
|
||||
infoAcknowledgment();
|
||||
}
|
||||
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
|
||||
SInt32 major, minor;
|
||||
CProtocolUtil::readf(m_stream,
|
||||
kMsgEIncompatible + 4, &major, &minor);
|
||||
LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
|
||||
m_client->disconnect("server has incompatible version");
|
||||
return;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
|
||||
setClipboard();
|
||||
}
|
||||
else if (memcmp(code, kMsgEBusy, 4) == 0) {
|
||||
LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str()));
|
||||
m_client->disconnect("server already has a connected client with our name");
|
||||
return;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
|
||||
resetOptions();
|
||||
}
|
||||
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
|
||||
LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str()));
|
||||
m_client->disconnect("server refused client with our name");
|
||||
return;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
|
||||
setOptions();
|
||||
}
|
||||
else if (memcmp(code, kMsgEBad, 4) == 0) {
|
||||
LOG((CLOG_ERR "server disconnected due to a protocol error"));
|
||||
m_client->disconnect("server reported a protocol error");
|
||||
return;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
||||
// server wants us to hangup
|
||||
LOG((CLOG_DEBUG1 "recv close"));
|
||||
break;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
|
||||
SInt32 major, minor;
|
||||
CProtocolUtil::readf(getInputStream(),
|
||||
kMsgEIncompatible + 4, &major, &minor);
|
||||
LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
|
||||
failedToConnect = true;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgEBusy, 4) == 0) {
|
||||
LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str()));
|
||||
failedToConnect = true;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
|
||||
LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str()));
|
||||
failedToConnect = true;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgEBad, 4) == 0) {
|
||||
LOG((CLOG_ERR "server disconnected due to a protocol error"));
|
||||
failedToConnect = true;
|
||||
break;
|
||||
}
|
||||
|
||||
else {
|
||||
// unknown message
|
||||
LOG((CLOG_ERR "unknown message from server"));
|
||||
LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3]));
|
||||
failedToConnect = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// unknown message
|
||||
LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3]));
|
||||
m_client->disconnect("unknown message from server");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (XBase& e) {
|
||||
LOG((CLOG_ERR "error: %s", e.what()));
|
||||
}
|
||||
catch (...) {
|
||||
throw;
|
||||
}
|
||||
|
||||
return !failedToConnect;
|
||||
}
|
||||
|
||||
IClient*
|
||||
CServerProxy::getClient() const
|
||||
{
|
||||
return m_client;
|
||||
}
|
||||
|
||||
CString
|
||||
CServerProxy::getName() const
|
||||
{
|
||||
return m_client->getName();
|
||||
}
|
||||
|
||||
IInputStream*
|
||||
CServerProxy::getInputStream() const
|
||||
{
|
||||
return m_input;
|
||||
}
|
||||
|
||||
IOutputStream*
|
||||
CServerProxy::getOutputStream() const
|
||||
{
|
||||
return m_output;
|
||||
flushCompressedMouse();
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::onError()
|
||||
CServerProxy::handleHeartBeat(const CEvent&, void*)
|
||||
{
|
||||
// ignore
|
||||
CProtocolUtil::writef(m_stream, kMsgCNoop);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::onInfoChanged(const CClientInfo& info)
|
||||
CServerProxy::onInfoChanged()
|
||||
{
|
||||
// ignore mouse motion until we receive acknowledgment of our info
|
||||
// change message.
|
||||
CLock lock(&m_mutex);
|
||||
m_ignoreMouse = true;
|
||||
|
||||
// send info update
|
||||
sendInfo(info);
|
||||
queryInfo();
|
||||
}
|
||||
|
||||
bool
|
||||
CServerProxy::onGrabClipboard(ClipboardID id)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "sending clipboard %d changed", id));
|
||||
CLock lock(&m_mutex);
|
||||
CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum);
|
||||
CProtocolUtil::writef(m_stream, kMsgCClipboard, id, m_seqNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::onClipboardChanged(ClipboardID id, const CString& data)
|
||||
CServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
CString data = IClipboard::marshall(clipboard);
|
||||
LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
||||
CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data);
|
||||
CProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::flushCompressedMouse()
|
||||
{
|
||||
bool send = false;
|
||||
SInt32 x = 0, y = 0;
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
if (m_compressMouse) {
|
||||
m_compressMouse = false;
|
||||
x = m_xMouse;
|
||||
y = m_yMouse;
|
||||
send = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (send) {
|
||||
getClient()->mouseMove(x, y);
|
||||
if (m_compressMouse) {
|
||||
m_compressMouse = false;
|
||||
m_client->mouseMove(m_xMouse, m_yMouse);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::sendInfo(const CClientInfo& info)
|
||||
{
|
||||
// note -- m_mutex should be locked on entry
|
||||
LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my));
|
||||
CProtocolUtil::writef(getOutputStream(), kMsgDInfo,
|
||||
LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d", info.m_x, info.m_y, info.m_w, info.m_h));
|
||||
CProtocolUtil::writef(m_stream, kMsgDInfo,
|
||||
info.m_x, info.m_y,
|
||||
info.m_w, info.m_h,
|
||||
info.m_zoneSize,
|
||||
info.m_mx, info.m_my);
|
||||
info.m_w, info.m_h, 0, 0, 0);
|
||||
}
|
||||
|
||||
KeyID
|
||||
|
@ -434,19 +384,15 @@ CServerProxy::enter()
|
|||
SInt16 x, y;
|
||||
UInt16 mask;
|
||||
UInt32 seqNum;
|
||||
CProtocolUtil::readf(getInputStream(),
|
||||
kMsgCEnter + 4, &x, &y, &seqNum, &mask);
|
||||
CProtocolUtil::readf(m_stream, kMsgCEnter + 4, &x, &y, &seqNum, &mask);
|
||||
LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask));
|
||||
|
||||
// discard old compressed mouse motion, if any
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
m_compressMouse = false;
|
||||
m_seqNum = seqNum;
|
||||
}
|
||||
m_compressMouse = false;
|
||||
m_seqNum = seqNum;
|
||||
|
||||
// forward
|
||||
getClient()->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
|
||||
m_client->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -459,7 +405,7 @@ CServerProxy::leave()
|
|||
flushCompressedMouse();
|
||||
|
||||
// forward
|
||||
getClient()->leave();
|
||||
m_client->leave();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -469,8 +415,7 @@ CServerProxy::setClipboard()
|
|||
ClipboardID id;
|
||||
UInt32 seqNum;
|
||||
CString data;
|
||||
CProtocolUtil::readf(getInputStream(),
|
||||
kMsgDClipboard + 4, &id, &seqNum, &data);
|
||||
CProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data);
|
||||
LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
|
||||
|
||||
// validate
|
||||
|
@ -479,7 +424,9 @@ CServerProxy::setClipboard()
|
|||
}
|
||||
|
||||
// forward
|
||||
getClient()->setClipboard(id, data);
|
||||
CClipboard clipboard;
|
||||
clipboard.unmarshall(data, 0);
|
||||
m_client->setClipboard(id, &clipboard);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -488,7 +435,7 @@ CServerProxy::grabClipboard()
|
|||
// parse
|
||||
ClipboardID id;
|
||||
UInt32 seqNum;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum);
|
||||
CProtocolUtil::readf(m_stream, kMsgCClipboard + 4, &id, &seqNum);
|
||||
LOG((CLOG_DEBUG "recv grab clipboard %d", id));
|
||||
|
||||
// validate
|
||||
|
@ -497,7 +444,7 @@ CServerProxy::grabClipboard()
|
|||
}
|
||||
|
||||
// forward
|
||||
getClient()->grabClipboard(id);
|
||||
m_client->grabClipboard(id);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -508,8 +455,7 @@ CServerProxy::keyDown()
|
|||
|
||||
// parse
|
||||
UInt16 id, mask, button;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4,
|
||||
&id, &mask, &button);
|
||||
CProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button));
|
||||
|
||||
// translate
|
||||
|
@ -521,7 +467,7 @@ CServerProxy::keyDown()
|
|||
LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
getClient()->keyDown(id2, mask2, button);
|
||||
m_client->keyDown(id2, mask2, button);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -532,7 +478,7 @@ CServerProxy::keyRepeat()
|
|||
|
||||
// parse
|
||||
UInt16 id, mask, count, button;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4,
|
||||
CProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4,
|
||||
&id, &mask, &count, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button));
|
||||
|
||||
|
@ -545,7 +491,7 @@ CServerProxy::keyRepeat()
|
|||
LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
getClient()->keyRepeat(id2, mask2, count, button);
|
||||
m_client->keyRepeat(id2, mask2, count, button);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -556,7 +502,7 @@ CServerProxy::keyUp()
|
|||
|
||||
// parse
|
||||
UInt16 id, mask, button;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button);
|
||||
CProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button));
|
||||
|
||||
// translate
|
||||
|
@ -568,7 +514,7 @@ CServerProxy::keyUp()
|
|||
LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
getClient()->keyUp(id2, mask2, button);
|
||||
m_client->keyUp(id2, mask2, button);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -579,11 +525,11 @@ CServerProxy::mouseDown()
|
|||
|
||||
// parse
|
||||
SInt8 id;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id);
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseDown + 4, &id);
|
||||
LOG((CLOG_DEBUG1 "recv mouse down id=%d", id));
|
||||
|
||||
// forward
|
||||
getClient()->mouseDown(static_cast<ButtonID>(id));
|
||||
m_client->mouseDown(static_cast<ButtonID>(id));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -594,11 +540,11 @@ CServerProxy::mouseUp()
|
|||
|
||||
// parse
|
||||
SInt8 id;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id);
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseUp + 4, &id);
|
||||
LOG((CLOG_DEBUG1 "recv mouse up id=%d", id));
|
||||
|
||||
// forward
|
||||
getClient()->mouseUp(static_cast<ButtonID>(id));
|
||||
m_client->mouseUp(static_cast<ButtonID>(id));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -607,30 +553,27 @@ CServerProxy::mouseMove()
|
|||
// parse
|
||||
bool ignore;
|
||||
SInt16 x, y;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y);
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseMove + 4, &x, &y);
|
||||
|
||||
{
|
||||
// note if we should ignore the move
|
||||
CLock lock(&m_mutex);
|
||||
ignore = m_ignoreMouse;
|
||||
// note if we should ignore the move
|
||||
ignore = m_ignoreMouse;
|
||||
|
||||
// compress mouse motion events if more input follows
|
||||
if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) {
|
||||
m_compressMouse = true;
|
||||
}
|
||||
// compress mouse motion events if more input follows
|
||||
if (!ignore && !m_compressMouse && m_stream->isReady()) {
|
||||
m_compressMouse = true;
|
||||
}
|
||||
|
||||
// if compressing then ignore the motion but record it
|
||||
if (m_compressMouse) {
|
||||
ignore = true;
|
||||
m_xMouse = x;
|
||||
m_yMouse = y;
|
||||
}
|
||||
// if compressing then ignore the motion but record it
|
||||
if (m_compressMouse) {
|
||||
ignore = true;
|
||||
m_xMouse = x;
|
||||
m_yMouse = y;
|
||||
}
|
||||
LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y));
|
||||
|
||||
// forward
|
||||
if (!ignore) {
|
||||
getClient()->mouseMove(x, y);
|
||||
m_client->mouseMove(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,11 +585,11 @@ CServerProxy::mouseWheel()
|
|||
|
||||
// parse
|
||||
SInt16 delta;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta);
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &delta);
|
||||
LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta));
|
||||
|
||||
// forward
|
||||
getClient()->mouseWheel(delta);
|
||||
m_client->mouseWheel(delta);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -654,11 +597,11 @@ CServerProxy::screensaver()
|
|||
{
|
||||
// parse
|
||||
SInt8 on;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on);
|
||||
CProtocolUtil::readf(m_stream, kMsgCScreenSaver + 4, &on);
|
||||
LOG((CLOG_DEBUG1 "recv screen saver on=%d", on));
|
||||
|
||||
// forward
|
||||
getClient()->screensaver(on != 0);
|
||||
m_client->screensaver(on != 0);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -668,22 +611,18 @@ CServerProxy::resetOptions()
|
|||
LOG((CLOG_DEBUG1 "recv reset options"));
|
||||
|
||||
// forward
|
||||
getClient()->resetOptions();
|
||||
m_client->resetOptions();
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
// reset heart rate
|
||||
m_heartRate = kHeartRate;
|
||||
// reset heart rate and send heartbeat if necessary
|
||||
installHeartBeat(kHeartRate);
|
||||
if (m_heartRate >= 0.0) {
|
||||
CProtocolUtil::writef(m_stream, kMsgCNoop);
|
||||
}
|
||||
|
||||
// reset modifier translation table
|
||||
for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) {
|
||||
m_modifierTranslationTable[id] = id;
|
||||
}
|
||||
|
||||
// send heartbeat if necessary
|
||||
if (m_heartRate >= 0.0) {
|
||||
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -691,13 +630,11 @@ CServerProxy::setOptions()
|
|||
{
|
||||
// parse
|
||||
COptionsList options;
|
||||
CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options);
|
||||
CProtocolUtil::readf(m_stream, kMsgDSetOptions + 4, &options);
|
||||
LOG((CLOG_DEBUG1 "recv set options size=%d", options.size()));
|
||||
|
||||
// forward
|
||||
getClient()->setOptions(options);
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
m_client->setOptions(options);
|
||||
|
||||
// update modifier table
|
||||
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
|
||||
|
@ -718,12 +655,10 @@ CServerProxy::setOptions()
|
|||
id = kKeyModifierIDSuper;
|
||||
}
|
||||
else if (options[i] == kOptionHeartbeat) {
|
||||
// update heart rate
|
||||
m_heartRate = 1.0e-3 * static_cast<double>(options[i + 1]);
|
||||
|
||||
// send heartbeat if necessary
|
||||
// update heart rate and send heartbeat if necessary
|
||||
installHeartBeat(1.0e-3 * static_cast<double>(options[i + 1]));
|
||||
if (m_heartRate >= 0.0) {
|
||||
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
|
||||
CProtocolUtil::writef(m_stream, kMsgCNoop);
|
||||
}
|
||||
}
|
||||
if (id != kKeyModifierIDNull) {
|
||||
|
@ -737,24 +672,14 @@ CServerProxy::setOptions()
|
|||
void
|
||||
CServerProxy::queryInfo()
|
||||
{
|
||||
// get current info
|
||||
CClientInfo info;
|
||||
getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||
getClient()->getCursorPos(info.m_mx, info.m_my);
|
||||
info.m_zoneSize = getClient()->getJumpZoneSize();
|
||||
|
||||
// send it
|
||||
CLock lock(&m_mutex);
|
||||
m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||
sendInfo(info);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::infoAcknowledgment()
|
||||
{
|
||||
// parse
|
||||
LOG((CLOG_DEBUG1 "recv info acknowledgment"));
|
||||
|
||||
// now allow mouse motion
|
||||
CLock lock(&m_mutex);
|
||||
m_ignoreMouse = false;
|
||||
}
|
||||
|
|
|
@ -15,88 +15,66 @@
|
|||
#ifndef CSERVERPROXY_H
|
||||
#define CSERVERPROXY_H
|
||||
|
||||
#include "IScreenReceiver.h"
|
||||
#include "ClipboardTypes.h"
|
||||
#include "KeyTypes.h"
|
||||
#include "CMutex.h"
|
||||
#include "CEvent.h"
|
||||
|
||||
class IClient;
|
||||
class IInputStream;
|
||||
class IOutputStream;
|
||||
class CClient;
|
||||
class CClientInfo;
|
||||
class CEventQueueTimer;
|
||||
class IClipboard;
|
||||
class IStream;
|
||||
|
||||
//! Proxy for server
|
||||
/*!
|
||||
This class acts a proxy for the server, converting calls into messages
|
||||
to the server and messages from the server to calls on the client.
|
||||
*/
|
||||
class CServerProxy : public IScreenReceiver {
|
||||
class CServerProxy {
|
||||
public:
|
||||
/*! \c adoptedInput is the stream from the server and
|
||||
\c adoptedOutput is the stream to the server. This object
|
||||
takes ownership of both and destroys them in the d'tor.
|
||||
Messages from the server are converted to calls on \c client.
|
||||
/*!
|
||||
Process messages from the server on \p stream and forward to
|
||||
\p client.
|
||||
*/
|
||||
CServerProxy(IClient* client,
|
||||
IInputStream* adoptedInput,
|
||||
IOutputStream* adoptedOutput);
|
||||
CServerProxy(CClient* client, IStream* stream);
|
||||
~CServerProxy();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Run event loop
|
||||
/*!
|
||||
Run the event loop and return when the server disconnects or
|
||||
requests the client to disconnect. Return true iff the server
|
||||
didn't reject our connection.
|
||||
|
||||
(cancellation point)
|
||||
*/
|
||||
bool mainLoop();
|
||||
virtual void onInfoChanged();
|
||||
virtual bool onGrabClipboard(ClipboardID);
|
||||
virtual void onClipboardChanged(ClipboardID, const IClipboard*);
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get client
|
||||
//! Get handshake complete event type
|
||||
/*!
|
||||
Returns the client passed to the c'tor.
|
||||
Returns the handshake complete event type. This is sent when the
|
||||
client has completed the handshake with the server.
|
||||
*/
|
||||
IClient* getClient() const;
|
||||
|
||||
//! Get input stream
|
||||
/*!
|
||||
Return the input stream passed to the c'tor.
|
||||
*/
|
||||
IInputStream* getInputStream() const;
|
||||
|
||||
//! Get output stream
|
||||
/*!
|
||||
Return the output stream passed to the c'tor.
|
||||
*/
|
||||
IOutputStream* getOutputStream() const;
|
||||
static CEvent::Type getHandshakeCompleteEvent();
|
||||
|
||||
//@}
|
||||
|
||||
// IScreenReceiver overrides
|
||||
virtual void onError();
|
||||
virtual void onInfoChanged(const CClientInfo&);
|
||||
virtual bool onGrabClipboard(ClipboardID);
|
||||
virtual void onClipboardChanged(ClipboardID, const CString& data);
|
||||
|
||||
private:
|
||||
|
||||
// get the client name (from the client)
|
||||
CString getName() const;
|
||||
|
||||
// if compressing mouse motion then send the last motion now
|
||||
void flushCompressedMouse();
|
||||
|
||||
void sendInfo(const CClientInfo&);
|
||||
|
||||
void installHeartBeat(double);
|
||||
|
||||
// modifier key translation
|
||||
KeyID translateKey(KeyID) const;
|
||||
KeyModifierMask translateModifierMask(KeyModifierMask) const;
|
||||
|
||||
// event handlers
|
||||
void handleMessage(const CEvent&, void*);
|
||||
void handleHeartBeat(const CEvent&, void*);
|
||||
|
||||
// message handlers
|
||||
void enter();
|
||||
void leave();
|
||||
|
@ -116,11 +94,9 @@ private:
|
|||
void infoAcknowledgment();
|
||||
|
||||
private:
|
||||
CMutex m_mutex;
|
||||
|
||||
IClient* m_client;
|
||||
IInputStream* m_input;
|
||||
IOutputStream* m_output;
|
||||
CClient* m_client;
|
||||
IStream* m_stream;
|
||||
CEventQueueTimer* m_timer;
|
||||
|
||||
UInt32 m_seqNum;
|
||||
|
||||
|
@ -131,6 +107,8 @@ private:
|
|||
|
||||
KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast];
|
||||
double m_heartRate;
|
||||
|
||||
static CEvent::Type s_handshakeCompleteEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -77,7 +77,7 @@ CStreamFilter::setEventFilter(IEventJob* filter)
|
|||
void*
|
||||
CStreamFilter::getEventTarget() const
|
||||
{
|
||||
return const_cast<void*>(reinterpret_cast<const void*>(this));
|
||||
return getStream()->getEventTarget();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -113,6 +113,9 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) :
|
|||
catch (XArchNetworkNameNoAddress&) {
|
||||
throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port);
|
||||
}
|
||||
catch (XArchNetworkNameUnsupported&) {
|
||||
throw XSocketAddress(XSocketAddress::kUnsupported, hostname, port);
|
||||
}
|
||||
catch (XArchNetworkName&) {
|
||||
throw XSocketAddress(XSocketAddress::kUnknown, hostname, port);
|
||||
}
|
||||
|
|
|
@ -19,10 +19,9 @@
|
|||
#include "TSocketMultiplexerMethodJob.h"
|
||||
#include "XSocket.h"
|
||||
#include "XIO.h"
|
||||
#include "CEvent.h"
|
||||
#include "CEventQueue.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "CArch.h"
|
||||
#include "XArch.h"
|
||||
|
||||
|
@ -125,8 +124,7 @@ CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
|
|||
return NULL;
|
||||
}
|
||||
if (read) {
|
||||
CEventQueue::getInstance()->addEvent(
|
||||
CEvent(getConnectingEvent(), this, NULL));
|
||||
EVENTQUEUE->addEvent(CEvent(getConnectingEvent(), this, NULL));
|
||||
// stop polling on this socket until the client accepts
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
#include "TSocketMultiplexerMethodJob.h"
|
||||
#include "XSocket.h"
|
||||
#include "CLock.h"
|
||||
#include "CEventQueue.h"
|
||||
#include "CLog.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "IEventJob.h"
|
||||
#include "CArch.h"
|
||||
#include "XArch.h"
|
||||
#include <string.h>
|
||||
|
||||
//
|
||||
// CTCPSocket
|
||||
|
@ -84,6 +85,9 @@ CTCPSocket::bind(const CNetworkAddress& addr)
|
|||
void
|
||||
CTCPSocket::close()
|
||||
{
|
||||
// remove ourself from the multiplexer
|
||||
setJob(NULL);
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
// clear buffers and enter disconnected state
|
||||
|
@ -92,9 +96,6 @@ CTCPSocket::close()
|
|||
}
|
||||
onDisconnected();
|
||||
|
||||
// remove ourself from the multiplexer
|
||||
setJob(NULL);
|
||||
|
||||
// close the socket
|
||||
if (m_socket != NULL) {
|
||||
CArchSocket socket = m_socket;
|
||||
|
@ -141,26 +142,29 @@ CTCPSocket::read(void* buffer, UInt32 n)
|
|||
void
|
||||
CTCPSocket::write(const void* buffer, UInt32 n)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
bool wasEmpty;
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
// must not have shutdown output
|
||||
if (!m_writable) {
|
||||
sendStreamEvent(getOutputErrorEvent());
|
||||
return;
|
||||
// must not have shutdown output
|
||||
if (!m_writable) {
|
||||
sendStreamEvent(getOutputErrorEvent());
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore empty writes
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy data to the output buffer
|
||||
wasEmpty = (m_outputBuffer.getSize() == 0);
|
||||
m_outputBuffer.write(buffer, n);
|
||||
|
||||
// there's data to write
|
||||
m_flushed = false;
|
||||
}
|
||||
|
||||
// ignore empty writes
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy data to the output buffer
|
||||
bool wasEmpty = (m_outputBuffer.getSize() == 0);
|
||||
m_outputBuffer.write(buffer, n);
|
||||
|
||||
// there's data to write
|
||||
m_flushed = false;
|
||||
|
||||
// make sure we're waiting to write
|
||||
if (wasEmpty) {
|
||||
setJob(newJob());
|
||||
|
@ -179,20 +183,26 @@ CTCPSocket::flush()
|
|||
void
|
||||
CTCPSocket::shutdownInput()
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
bool useNewJob = false;
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
// shutdown socket for reading
|
||||
try {
|
||||
ARCH->closeSocketForRead(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
// shutdown socket for reading
|
||||
try {
|
||||
ARCH->closeSocketForRead(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// shutdown buffer for reading
|
||||
if (m_readable) {
|
||||
sendStreamEvent(getInputShutdownEvent());
|
||||
onInputShutdown();
|
||||
// shutdown buffer for reading
|
||||
if (m_readable) {
|
||||
sendStreamEvent(getInputShutdownEvent());
|
||||
onInputShutdown();
|
||||
useNewJob = true;
|
||||
}
|
||||
}
|
||||
if (useNewJob) {
|
||||
setJob(newJob());
|
||||
}
|
||||
}
|
||||
|
@ -200,20 +210,26 @@ CTCPSocket::shutdownInput()
|
|||
void
|
||||
CTCPSocket::shutdownOutput()
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
bool useNewJob = false;
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
// shutdown socket for writing
|
||||
try {
|
||||
ARCH->closeSocketForWrite(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
// shutdown socket for writing
|
||||
try {
|
||||
ARCH->closeSocketForWrite(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// shutdown buffer for writing
|
||||
if (m_writable) {
|
||||
sendStreamEvent(getOutputShutdownEvent());
|
||||
onOutputShutdown();
|
||||
// shutdown buffer for writing
|
||||
if (m_writable) {
|
||||
sendStreamEvent(getOutputShutdownEvent());
|
||||
onOutputShutdown();
|
||||
useNewJob = true;
|
||||
}
|
||||
}
|
||||
if (useNewJob) {
|
||||
setJob(newJob());
|
||||
}
|
||||
}
|
||||
|
@ -249,28 +265,29 @@ CTCPSocket::getEventFilter() const
|
|||
void
|
||||
CTCPSocket::connect(const CNetworkAddress& addr)
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
// fail on attempts to reconnect
|
||||
if (m_socket == NULL || m_connected) {
|
||||
sendSocketEvent(getConnectionFailedEvent());
|
||||
return;
|
||||
}
|
||||
// fail on attempts to reconnect
|
||||
if (m_socket == NULL || m_connected) {
|
||||
sendConnectionFailedEvent("busy");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ARCH->connectSocket(m_socket, addr.getAddress());
|
||||
sendSocketEvent(getConnectedEvent());
|
||||
onConnected();
|
||||
setJob(newJob());
|
||||
}
|
||||
catch (XArchNetworkConnecting&) {
|
||||
// connection is in progress
|
||||
m_writable = true;
|
||||
setJob(newJob());
|
||||
}
|
||||
catch (XArchNetwork& e) {
|
||||
throw XSocketConnect(e.what());
|
||||
try {
|
||||
ARCH->connectSocket(m_socket, addr.getAddress());
|
||||
sendSocketEvent(getConnectedEvent());
|
||||
onConnected();
|
||||
}
|
||||
catch (XArchNetworkConnecting&) {
|
||||
// connection is in progress
|
||||
m_writable = true;
|
||||
}
|
||||
catch (XArchNetwork& e) {
|
||||
throw XSocketConnect(e.what());
|
||||
}
|
||||
}
|
||||
setJob(newJob());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -339,17 +356,27 @@ CTCPSocket::newJob()
|
|||
void
|
||||
CTCPSocket::sendSocketEvent(CEvent::Type type)
|
||||
{
|
||||
EVENTQUEUE->addEvent(CEvent(type, this, NULL));
|
||||
EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL));
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::sendConnectionFailedEvent(const char* msg)
|
||||
{
|
||||
CConnectionFailedInfo* info = (CConnectionFailedInfo*)malloc(
|
||||
sizeof(CConnectionFailedInfo) + strlen(msg));
|
||||
strcpy(info->m_what, msg);
|
||||
EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(),
|
||||
getEventTarget(), info));
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::sendStreamEvent(CEvent::Type type)
|
||||
{
|
||||
if (m_eventFilter != NULL) {
|
||||
m_eventFilter->run(CEvent(type, this, NULL));
|
||||
m_eventFilter->run(CEvent(type, getEventTarget(), NULL));
|
||||
}
|
||||
else {
|
||||
EVENTQUEUE->addEvent(CEvent(type, this, NULL));
|
||||
EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,22 +421,18 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job,
|
|||
{
|
||||
CLock lock(&m_mutex);
|
||||
|
||||
if (write && !error) {
|
||||
if (error) {
|
||||
try {
|
||||
// connection may have failed or succeeded
|
||||
ARCH->throwErrorOnSocket(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
error = true;
|
||||
catch (XArchNetwork& e) {
|
||||
sendConnectionFailedEvent(e.what().c_str());
|
||||
onDisconnected();
|
||||
return newJob();
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
sendSocketEvent(getConnectionFailedEvent());
|
||||
onDisconnected();
|
||||
return newJob();
|
||||
}
|
||||
|
||||
if (write) {
|
||||
sendSocketEvent(getConnectedEvent());
|
||||
onConnected();
|
||||
|
|
|
@ -60,6 +60,7 @@ private:
|
|||
void setJob(ISocketMultiplexerJob*);
|
||||
ISocketMultiplexerJob* newJob();
|
||||
void sendSocketEvent(CEvent::Type);
|
||||
void sendConnectionFailedEvent(const char*);
|
||||
void sendStreamEvent(CEvent::Type);
|
||||
|
||||
void onConnected();
|
||||
|
|
|
@ -25,6 +25,12 @@ represent a full-duplex data stream.
|
|||
*/
|
||||
class IDataSocket : public ISocket, public IStream {
|
||||
public:
|
||||
class CConnectionFailedInfo {
|
||||
public:
|
||||
// pointer to a string describing the failure
|
||||
char m_what[1];
|
||||
};
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
|
@ -52,6 +58,7 @@ public:
|
|||
/*!
|
||||
Returns the socket connection failed event type. A socket sends
|
||||
this event when an attempt to connect to a remote port has failed.
|
||||
The data is a pointer to a CConnectionFailedInfo.
|
||||
*/
|
||||
static CEvent::Type getConnectionFailedEvent();
|
||||
|
||||
|
|
|
@ -53,12 +53,14 @@ XSocketAddress::getWhat() const throw()
|
|||
"XSocketAddressUnknown",
|
||||
"XSocketAddressNotFound",
|
||||
"XSocketAddressNoAddress",
|
||||
"XSocketAddressUnsupported",
|
||||
"XSocketAddressBadPort"
|
||||
};
|
||||
static const char* s_errorMsg[] = {
|
||||
"unknown error for: %{1}:%{2}",
|
||||
"address not found for: %{1}",
|
||||
"no address for: %{1}",
|
||||
"unsupported address for: %{1}",
|
||||
"invalid port" // m_port may not be set to the bad port
|
||||
};
|
||||
return format(s_errorID[m_error], s_errorMsg[m_error],
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
kUnknown, //!< Unknown error
|
||||
kNotFound, //!< The hostname is unknown
|
||||
kNoAddress, //!< The hostname is valid but has no IP address
|
||||
kUnsupported, //!< The hostname is valid but has no supported address
|
||||
kBadPort //!< The port is invalid
|
||||
};
|
||||
|
||||
|
|
|
@ -835,7 +835,7 @@ CXWindowsScreen::sendEvent(CEvent::Type type, void* data)
|
|||
void
|
||||
CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id)
|
||||
{
|
||||
CClipboardInfo* info = new CClipboardInfo;
|
||||
CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo));
|
||||
info->m_id = id;
|
||||
info->m_sequenceNumber = m_sequenceNumber;
|
||||
sendEvent(type, info);
|
||||
|
@ -1028,11 +1028,11 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
|
|||
}
|
||||
|
||||
// handle key
|
||||
sendEvent(getKeyDownEvent(), new CKeyInfo(key, mask, keycode, 1));
|
||||
sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, keycode, 1));
|
||||
KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode);
|
||||
if (m_keyState->isHalfDuplex(keyMask)) {
|
||||
sendEvent(getKeyUpEvent(),
|
||||
new CKeyInfo(key, mask | keyMask, keycode, 1));
|
||||
CKeyInfo::alloc(key, mask | keyMask, keycode, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1040,7 +1040,7 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
|
|||
Bool
|
||||
CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
|
||||
{
|
||||
CKeyEventInfo* filter = reinterpret_cast<CKeyEventInfo*>(arg);
|
||||
CKeyEventFilter* filter = reinterpret_cast<CKeyEventFilter*>(arg);
|
||||
return (xevent->type == filter->m_event &&
|
||||
xevent->xkey.window == filter->m_window &&
|
||||
xevent->xkey.time == filter->m_time &&
|
||||
|
@ -1057,7 +1057,7 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey)
|
|||
// KeyPress event that has the same key and time as
|
||||
// this release event, if any. first prepare the
|
||||
// filter info.
|
||||
CKeyEventInfo filter;
|
||||
CKeyEventFilter filter;
|
||||
filter.m_event = KeyPress;
|
||||
filter.m_window = xkey.window;
|
||||
filter.m_time = xkey.time;
|
||||
|
@ -1089,9 +1089,9 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey)
|
|||
KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode);
|
||||
if (m_keyState->isHalfDuplex(keyMask)) {
|
||||
sendEvent(getKeyDownEvent(),
|
||||
new CKeyInfo(key, mask, keycode, 1));
|
||||
CKeyInfo::alloc(key, mask, keycode, 1));
|
||||
}
|
||||
sendEvent(getKeyUpEvent(), new CKeyInfo(key, mask, keycode, 1));
|
||||
sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, keycode, 1));
|
||||
}
|
||||
else {
|
||||
// found a press event following so it's a repeat.
|
||||
|
@ -1099,7 +1099,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey)
|
|||
// repeats but we'll just send a repeat of 1.
|
||||
// note that we discard the press event.
|
||||
LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
|
||||
sendEvent(getKeyRepeatEvent(), new CKeyInfo(key, mask, keycode, 1));
|
||||
sendEvent(getKeyRepeatEvent(),
|
||||
CKeyInfo::alloc(key, mask, keycode, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1110,7 +1111,7 @@ CXWindowsScreen::onMousePress(const XButtonEvent& xbutton)
|
|||
LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button));
|
||||
const ButtonID button = mapButtonFromX(&xbutton);
|
||||
if (button != kButtonNone) {
|
||||
sendEvent(getButtonDownEvent(), new CButtonInfo(button));
|
||||
sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1120,15 +1121,15 @@ CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton)
|
|||
LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button));
|
||||
const ButtonID button = mapButtonFromX(&xbutton);
|
||||
if (button != kButtonNone) {
|
||||
sendEvent(getButtonUpEvent(), new CButtonInfo(button));
|
||||
sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button));
|
||||
}
|
||||
else if (xbutton.button == 4) {
|
||||
// wheel forward (away from user)
|
||||
sendEvent(getWheelEvent(), new CWheelInfo(120));
|
||||
sendEvent(getWheelEvent(), CWheelInfo::alloc(120));
|
||||
}
|
||||
else if (xbutton.button == 5) {
|
||||
// wheel backward (toward user)
|
||||
sendEvent(getWheelEvent(), new CWheelInfo(-120));
|
||||
sendEvent(getWheelEvent(), CWheelInfo::alloc(-120));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1160,7 +1161,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
|
|||
else if (m_isOnScreen) {
|
||||
// motion on primary screen
|
||||
sendEvent(getMotionOnPrimaryEvent(),
|
||||
new CMotionInfo(m_xCursor, m_yCursor));
|
||||
CMotionInfo::alloc(m_xCursor, m_yCursor));
|
||||
}
|
||||
else {
|
||||
// motion on secondary screen. warp mouse back to
|
||||
|
@ -1190,7 +1191,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
|
|||
// warping to the primary screen's enter position,
|
||||
// effectively overriding it.
|
||||
if (x != 0 || y != 0) {
|
||||
sendEvent(getMotionOnSecondaryEvent(), new CMotionInfo(x, y));
|
||||
sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ private:
|
|||
static int ioErrorHandler(Display*);
|
||||
|
||||
private:
|
||||
class CKeyEventInfo {
|
||||
class CKeyEventFilter {
|
||||
public:
|
||||
int m_event;
|
||||
Window m_window;
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
UInt32 seqNum, KeyModifierMask mask,
|
||||
bool forScreensaver) = 0;
|
||||
virtual bool leave() = 0;
|
||||
virtual void setClipboard(ClipboardID, const CString&) = 0;
|
||||
virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
|
||||
virtual void grabClipboard(ClipboardID) = 0;
|
||||
virtual void setClipboardDirty(ClipboardID, bool) = 0;
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0;
|
||||
|
|
|
@ -218,11 +218,9 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
|
|||
}
|
||||
|
||||
void
|
||||
CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const
|
||||
CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const
|
||||
{
|
||||
assert(0 && "shouldn't be called");
|
||||
x = m_info.m_mx;
|
||||
y = m_info.m_my;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -245,13 +243,15 @@ CClientProxy1_0::leave()
|
|||
}
|
||||
|
||||
void
|
||||
CClientProxy1_0::setClipboard(ClipboardID id, const CString& data)
|
||||
CClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
|
||||
{
|
||||
// ignore if this clipboard is already clean
|
||||
if (m_clipboard[id].m_dirty) {
|
||||
// this clipboard is now clean
|
||||
m_clipboard[id].m_dirty = false;
|
||||
CClipboard::copy(&m_clipboard[id].m_clipboard, clipboard);
|
||||
|
||||
CString data = m_clipboard[id].m_clipboard.marshall();
|
||||
LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size()));
|
||||
CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data);
|
||||
}
|
||||
|
@ -366,29 +366,23 @@ bool
|
|||
CClientProxy1_0::recvInfo()
|
||||
{
|
||||
// parse the message
|
||||
SInt16 x, y, w, h, zoneSize, mx, my;
|
||||
SInt16 x, y, w, h, dummy1, dummy2, dummy3;
|
||||
if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4,
|
||||
&x, &y, &w, &h, &zoneSize, &mx, &my)) {
|
||||
&x, &y, &w, &h, &dummy1, &dummy2, &dummy3)) {
|
||||
return false;
|
||||
}
|
||||
LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my));
|
||||
LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d", getName().c_str(), x, y, w, h));
|
||||
|
||||
// validate
|
||||
if (w <= 0 || h <= 0 || zoneSize < 0) {
|
||||
return false;
|
||||
}
|
||||
if (mx < x || my < y || mx >= x + w || my >= y + h) {
|
||||
if (w <= 0 || h <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// save
|
||||
m_info.m_x = x;
|
||||
m_info.m_y = y;
|
||||
m_info.m_w = w;
|
||||
m_info.m_h = h;
|
||||
m_info.m_zoneSize = zoneSize;
|
||||
m_info.m_mx = mx;
|
||||
m_info.m_my = my;
|
||||
m_info.m_x = x;
|
||||
m_info.m_y = y;
|
||||
m_info.m_w = w;
|
||||
m_info.m_h = h;
|
||||
|
||||
// acknowledge receipt
|
||||
LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str()));
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
UInt32 seqNum, KeyModifierMask mask,
|
||||
bool forScreensaver);
|
||||
virtual bool leave();
|
||||
virtual void setClipboard(ClipboardID, const CString&);
|
||||
virtual void setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void grabClipboard(ClipboardID);
|
||||
virtual void setClipboardDirty(ClipboardID, bool);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
|
||||
|
|
|
@ -761,8 +761,9 @@ CConfig::readSectionOptions(std::istream& s)
|
|||
try {
|
||||
m_synergyAddress = CNetworkAddress(value, kDefaultPort);
|
||||
}
|
||||
catch (XSocketAddress&) {
|
||||
throw XConfigRead("invalid address argument");
|
||||
catch (XSocketAddress& e) {
|
||||
throw XConfigRead(CString("invalid address argument: ") +
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
else if (name == "heartbeat") {
|
||||
|
|
|
@ -42,6 +42,12 @@ CPrimaryClient::reconfigure(UInt32 activeSides)
|
|||
m_screen->reconfigure(activeSides);
|
||||
}
|
||||
|
||||
SInt32
|
||||
CPrimaryClient::getJumpZoneSize() const
|
||||
{
|
||||
return m_screen->getJumpZoneSize();
|
||||
}
|
||||
|
||||
void
|
||||
CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const
|
||||
{
|
||||
|
@ -72,12 +78,6 @@ CPrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
|
|||
return m_screen->getClipboard(id, clipboard);
|
||||
}
|
||||
|
||||
SInt32
|
||||
CPrimaryClient::getJumpZoneSize() const
|
||||
{
|
||||
return m_screen->getJumpZoneSize();
|
||||
}
|
||||
|
||||
void
|
||||
CPrimaryClient::getShape(SInt32& x, SInt32& y,
|
||||
SInt32& width, SInt32& height) const
|
||||
|
@ -121,19 +121,15 @@ CPrimaryClient::leave()
|
|||
}
|
||||
|
||||
void
|
||||
CPrimaryClient::setClipboard(ClipboardID id, const CString& data)
|
||||
CPrimaryClient::setClipboard(ClipboardID id, const IClipboard* clipboard)
|
||||
{
|
||||
// ignore if this clipboard is already clean
|
||||
if (m_clipboardDirty[id]) {
|
||||
// this clipboard is now clean
|
||||
m_clipboardDirty[id] = false;
|
||||
|
||||
// unmarshall data
|
||||
CClipboard clipboard;
|
||||
clipboard.unmarshall(data, 0);
|
||||
|
||||
// set clipboard
|
||||
m_screen->setClipboard(id, &clipboard);
|
||||
m_screen->setClipboard(id, clipboard);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,13 @@ public:
|
|||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get jump zone size
|
||||
/*!
|
||||
Return the jump zone size, the size of the regions on the edges of
|
||||
the screen that cause the cursor to jump to another screen.
|
||||
*/
|
||||
SInt32 getJumpZoneSize() const;
|
||||
|
||||
//! Get cursor center position
|
||||
/*!
|
||||
Return the cursor center position which is where we park the
|
||||
|
@ -76,7 +83,6 @@ public:
|
|||
// IScreen overrides
|
||||
virtual void* getEventTarget() const;
|
||||
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
|
||||
virtual SInt32 getJumpZoneSize() const;
|
||||
virtual void getShape(SInt32& x, SInt32& y,
|
||||
SInt32& width, SInt32& height) const;
|
||||
virtual void getCursorPos(SInt32& x, SInt32& y) const;
|
||||
|
@ -86,7 +92,7 @@ public:
|
|||
UInt32 seqNum, KeyModifierMask mask,
|
||||
bool forScreensaver);
|
||||
virtual bool leave();
|
||||
virtual void setClipboard(ClipboardID, const CString&);
|
||||
virtual void setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void grabClipboard(ClipboardID);
|
||||
virtual void setClipboardDirty(ClipboardID, bool);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "CClientProxy.h"
|
||||
#include "CClientProxyUnknown.h"
|
||||
#include "CPrimaryClient.h"
|
||||
#include "CPacketStreamFilter.h"
|
||||
#include "IPlatformScreen.h"
|
||||
#include "OptionTypes.h"
|
||||
#include "ProtocolTypes.h"
|
||||
|
@ -205,6 +204,11 @@ CServer::adoptClient(IClient* client)
|
|||
{
|
||||
assert(client != NULL);
|
||||
|
||||
// watch for client disconnection
|
||||
EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
|
||||
new TMethodEventJob<CServer>(this,
|
||||
&CServer::handleClientDisconnected, client));
|
||||
|
||||
// name must be in our configuration
|
||||
if (!m_config.isScreen(client->getName())) {
|
||||
LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str()));
|
||||
|
@ -221,11 +225,6 @@ CServer::adoptClient(IClient* client)
|
|||
}
|
||||
LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str()));
|
||||
|
||||
// watch for client disconnection
|
||||
EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
|
||||
new TMethodEventJob<CServer>(this,
|
||||
&CServer::handleClientDisconnected, client));
|
||||
|
||||
// send configuration options to client
|
||||
sendOptions(client);
|
||||
|
||||
|
@ -287,7 +286,11 @@ CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/)
|
|||
CString
|
||||
CServer::getName(const IClient* client) const
|
||||
{
|
||||
return m_config.getCanonicalName(client->getName());
|
||||
CString name = m_config.getCanonicalName(client->getName());
|
||||
if (name.empty()) {
|
||||
name = client->getName();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
UInt32
|
||||
|
@ -397,7 +400,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver)
|
|||
|
||||
// send the clipboard data to new active screen
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
m_active->setClipboard(id, m_clipboards[id].m_clipboardData);
|
||||
m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1078,7 +1081,7 @@ CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum)
|
|||
}
|
||||
|
||||
// send the new clipboard to the active screen
|
||||
m_active->setClipboard(id, clipboard.m_clipboardData);
|
||||
m_active->setClipboard(id, &clipboard.m_clipboard);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -101,126 +101,14 @@ CClipboard::get(EFormat format) const
|
|||
return m_data[format];
|
||||
}
|
||||
|
||||
bool
|
||||
CClipboard::copy(IClipboard* dst, const IClipboard* src)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
return copy(dst, src, src->getTime());
|
||||
}
|
||||
|
||||
bool
|
||||
CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
bool success = false;
|
||||
if (src->open(time)) {
|
||||
if (dst->open(time)) {
|
||||
if (dst->empty()) {
|
||||
for (SInt32 format = 0;
|
||||
format != IClipboard::kNumFormats; ++format) {
|
||||
IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
|
||||
if (src->has(eFormat)) {
|
||||
dst->add(eFormat, src->get(eFormat));
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
dst->close();
|
||||
}
|
||||
src->close();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void
|
||||
CClipboard::unmarshall(const CString& data, Time time)
|
||||
{
|
||||
const char* index = data.data();
|
||||
|
||||
// clear existing data
|
||||
open(time);
|
||||
empty();
|
||||
|
||||
// read the number of formats
|
||||
const UInt32 numFormats = readUInt32(index);
|
||||
index += 4;
|
||||
|
||||
// read each format
|
||||
for (UInt32 i = 0; i < numFormats; ++i) {
|
||||
// get the format id
|
||||
UInt32 format = readUInt32(index);
|
||||
index += 4;
|
||||
|
||||
// get the size of the format data
|
||||
UInt32 size = readUInt32(index);
|
||||
index += 4;
|
||||
|
||||
// save the data if it's a known format. if either the client
|
||||
// or server supports more clipboard formats than the other
|
||||
// then one of them will get a format >= kNumFormats here.
|
||||
if (format < static_cast<UInt32>(IClipboard::kNumFormats)) {
|
||||
m_added[format] = true;
|
||||
m_data[format] = CString(index, size);
|
||||
}
|
||||
index += size;
|
||||
}
|
||||
|
||||
// done
|
||||
close();
|
||||
IClipboard::unmarshall(this, data, time);
|
||||
}
|
||||
|
||||
CString
|
||||
CClipboard::marshall() const
|
||||
{
|
||||
CString data;
|
||||
|
||||
// compute size of marshalled data
|
||||
UInt32 size = 4;
|
||||
UInt32 numFormats = 0;
|
||||
UInt32 format;
|
||||
for (format = 0; format != IClipboard::kNumFormats; ++format) {
|
||||
if (m_added[format]) {
|
||||
++numFormats;
|
||||
size += 4 + 4 + m_data[format].size();
|
||||
}
|
||||
}
|
||||
|
||||
// allocate space
|
||||
data.reserve(size);
|
||||
|
||||
// marshall the data
|
||||
writeUInt32(&data, numFormats);
|
||||
for (format = 0; format != IClipboard::kNumFormats; ++format) {
|
||||
if (m_added[format]) {
|
||||
writeUInt32(&data, format);
|
||||
writeUInt32(&data, m_data[format].size());
|
||||
data += m_data[format];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
UInt32
|
||||
CClipboard::readUInt32(const char* buf) const
|
||||
{
|
||||
const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
|
||||
return (static_cast<UInt32>(ubuf[0]) << 24) |
|
||||
(static_cast<UInt32>(ubuf[1]) << 16) |
|
||||
(static_cast<UInt32>(ubuf[2]) << 8) |
|
||||
static_cast<UInt32>(ubuf[3]);
|
||||
}
|
||||
|
||||
void
|
||||
CClipboard::writeUInt32(CString* buf, UInt32 v) const
|
||||
{
|
||||
*buf += static_cast<UInt8>((v >> 24) & 0xff);
|
||||
*buf += static_cast<UInt8>((v >> 16) & 0xff);
|
||||
*buf += static_cast<UInt8>((v >> 8) & 0xff);
|
||||
*buf += static_cast<UInt8>( v & 0xff);
|
||||
return IClipboard::marshall(this);
|
||||
}
|
||||
|
|
|
@ -47,25 +47,6 @@ public:
|
|||
*/
|
||||
CString marshall() const;
|
||||
|
||||
//! Copy clipboard
|
||||
/*!
|
||||
Transfers all the data in one clipboard to another. The
|
||||
clipboards can be of any concrete clipboard type (and
|
||||
they don't have to be the same type). This also sets
|
||||
the destination clipboard's timestamp to source clipboard's
|
||||
timestamp. Returns true iff the copy succeeded.
|
||||
*/
|
||||
static bool copy(IClipboard* dst, const IClipboard* src);
|
||||
|
||||
//! Copy clipboard
|
||||
/*!
|
||||
Transfers all the data in one clipboard to another. The
|
||||
clipboards can be of any concrete clipboard type (and they
|
||||
don't have to be the same type). This also sets the
|
||||
timestamp to \c time. Returns true iff the copy succeeded.
|
||||
*/
|
||||
static bool copy(IClipboard* dst, const IClipboard* src, Time);
|
||||
|
||||
//@}
|
||||
|
||||
// IClipboard overrides
|
||||
|
@ -77,10 +58,6 @@ public:
|
|||
virtual bool has(EFormat) const;
|
||||
virtual CString get(EFormat) const;
|
||||
|
||||
private:
|
||||
UInt32 readUInt32(const char*) const;
|
||||
void writeUInt32(CString*, UInt32) const;
|
||||
|
||||
private:
|
||||
mutable bool m_open;
|
||||
mutable Time m_time;
|
||||
|
|
|
@ -34,7 +34,9 @@ CPacketStreamFilter::CPacketStreamFilter(IStream* stream, bool adoptStream) :
|
|||
|
||||
CPacketStreamFilter::~CPacketStreamFilter()
|
||||
{
|
||||
delete getStream()->getEventFilter();
|
||||
IEventJob* job = getStream()->getEventFilter();
|
||||
getStream()->setEventFilter(NULL);
|
||||
delete job;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
already known to be up to date then this may do nothing. \c data
|
||||
has marshalled clipboard data.
|
||||
*/
|
||||
virtual void setClipboard(ClipboardID, const CString& data) = 0;
|
||||
virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
|
||||
|
||||
//! Grab clipboard
|
||||
/*!
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2004 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 "IClipboard.h"
|
||||
#include "stdvector.h"
|
||||
|
||||
//
|
||||
// IClipboard
|
||||
//
|
||||
|
||||
void
|
||||
IClipboard::unmarshall(IClipboard* clipboard, const CString& data, Time time)
|
||||
{
|
||||
assert(clipboard != NULL);
|
||||
|
||||
const char* index = data.data();
|
||||
|
||||
// clear existing data
|
||||
clipboard->open(time);
|
||||
clipboard->empty();
|
||||
|
||||
// read the number of formats
|
||||
const UInt32 numFormats = readUInt32(index);
|
||||
index += 4;
|
||||
|
||||
// read each format
|
||||
for (UInt32 i = 0; i < numFormats; ++i) {
|
||||
// get the format id
|
||||
IClipboard::EFormat format =
|
||||
static_cast<IClipboard::EFormat>(readUInt32(index));
|
||||
index += 4;
|
||||
|
||||
// get the size of the format data
|
||||
UInt32 size = readUInt32(index);
|
||||
index += 4;
|
||||
|
||||
// save the data if it's a known format. if either the client
|
||||
// or server supports more clipboard formats than the other
|
||||
// then one of them will get a format >= kNumFormats here.
|
||||
if (format <IClipboard::kNumFormats) {
|
||||
clipboard->add(format, CString(index, size));
|
||||
}
|
||||
index += size;
|
||||
}
|
||||
|
||||
// done
|
||||
clipboard->close();
|
||||
}
|
||||
|
||||
CString
|
||||
IClipboard::marshall(const IClipboard* clipboard)
|
||||
{
|
||||
assert(clipboard != NULL);
|
||||
|
||||
CString data;
|
||||
|
||||
std::vector<CString> formatData;
|
||||
formatData.resize(IClipboard::kNumFormats);
|
||||
// FIXME -- use current time
|
||||
clipboard->open(0);
|
||||
|
||||
// compute size of marshalled data
|
||||
UInt32 size = 4;
|
||||
UInt32 numFormats = 0;
|
||||
for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
|
||||
if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
|
||||
++numFormats;
|
||||
formatData[format] =
|
||||
clipboard->get(static_cast<IClipboard::EFormat>(format));
|
||||
size += 4 + 4 + formatData[format].size();
|
||||
}
|
||||
}
|
||||
|
||||
// allocate space
|
||||
data.reserve(size);
|
||||
|
||||
// marshall the data
|
||||
writeUInt32(&data, numFormats);
|
||||
for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
|
||||
if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
|
||||
writeUInt32(&data, format);
|
||||
writeUInt32(&data, formatData[format].size());
|
||||
data += formatData[format];
|
||||
}
|
||||
}
|
||||
clipboard->close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
bool
|
||||
IClipboard::copy(IClipboard* dst, const IClipboard* src)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
return copy(dst, src, src->getTime());
|
||||
}
|
||||
|
||||
bool
|
||||
IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time)
|
||||
{
|
||||
assert(dst != NULL);
|
||||
assert(src != NULL);
|
||||
|
||||
bool success = false;
|
||||
if (src->open(time)) {
|
||||
if (dst->open(time)) {
|
||||
if (dst->empty()) {
|
||||
for (SInt32 format = 0;
|
||||
format != IClipboard::kNumFormats; ++format) {
|
||||
IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
|
||||
if (src->has(eFormat)) {
|
||||
dst->add(eFormat, src->get(eFormat));
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
dst->close();
|
||||
}
|
||||
src->close();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
UInt32
|
||||
IClipboard::readUInt32(const char* buf)
|
||||
{
|
||||
const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
|
||||
return (static_cast<UInt32>(ubuf[0]) << 24) |
|
||||
(static_cast<UInt32>(ubuf[1]) << 16) |
|
||||
(static_cast<UInt32>(ubuf[2]) << 8) |
|
||||
static_cast<UInt32>(ubuf[3]);
|
||||
}
|
||||
|
||||
void
|
||||
IClipboard::writeUInt32(CString* buf, UInt32 v)
|
||||
{
|
||||
*buf += static_cast<UInt8>((v >> 24) & 0xff);
|
||||
*buf += static_cast<UInt8>((v >> 16) & 0xff);
|
||||
*buf += static_cast<UInt8>((v >> 8) & 0xff);
|
||||
*buf += static_cast<UInt8>( v & 0xff);
|
||||
}
|
|
@ -111,7 +111,45 @@ public:
|
|||
*/
|
||||
virtual CString get(EFormat) const = 0;
|
||||
|
||||
//! Marshall clipboard data
|
||||
/*!
|
||||
Merge \p clipboard's data into a single buffer that can be later
|
||||
unmarshalled to restore the clipboard and return the buffer.
|
||||
*/
|
||||
static CString marshall(const IClipboard* clipboard);
|
||||
|
||||
//! Unmarshall clipboard data
|
||||
/*!
|
||||
Extract marshalled clipboard data and store it in \p clipboard.
|
||||
Sets the clipboard time to \c time.
|
||||
*/
|
||||
static void unmarshall(IClipboard* clipboard,
|
||||
const CString& data, Time time);
|
||||
|
||||
//! Copy clipboard
|
||||
/*!
|
||||
Transfers all the data in one clipboard to another. The
|
||||
clipboards can be of any concrete clipboard type (and
|
||||
they don't have to be the same type). This also sets
|
||||
the destination clipboard's timestamp to source clipboard's
|
||||
timestamp. Returns true iff the copy succeeded.
|
||||
*/
|
||||
static bool copy(IClipboard* dst, const IClipboard* src);
|
||||
|
||||
//! Copy clipboard
|
||||
/*!
|
||||
Transfers all the data in one clipboard to another. The
|
||||
clipboards can be of any concrete clipboard type (and they
|
||||
don't have to be the same type). This also sets the
|
||||
timestamp to \c time. Returns true iff the copy succeeded.
|
||||
*/
|
||||
static bool copy(IClipboard* dst, const IClipboard* src, Time);
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
static UInt32 readUInt32(const char*);
|
||||
static void writeUInt32(CString*, UInt32);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -104,14 +104,16 @@ IPlatformScreen::getScreensaverDeactivatedEvent()
|
|||
// IPlatformScreen::CKeyInfo
|
||||
//
|
||||
|
||||
IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id,
|
||||
KeyModifierMask mask, KeyButton button, SInt32 count) :
|
||||
m_key(id),
|
||||
m_mask(mask),
|
||||
m_button(button),
|
||||
m_count(count)
|
||||
IPlatformScreen::CKeyInfo*
|
||||
IPlatformScreen::CKeyInfo::alloc(KeyID id,
|
||||
KeyModifierMask mask, KeyButton button, SInt32 count)
|
||||
{
|
||||
// do nothing
|
||||
CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo));
|
||||
info->m_key = id;
|
||||
info->m_mask = mask;
|
||||
info->m_button = button;
|
||||
info->m_count = count;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
@ -119,10 +121,12 @@ IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id,
|
|||
// IPlatformScreen::CButtonInfo
|
||||
//
|
||||
|
||||
IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) :
|
||||
m_button(id)
|
||||
IPlatformScreen::CButtonInfo*
|
||||
IPlatformScreen::CButtonInfo::alloc(ButtonID id)
|
||||
{
|
||||
// do nothing
|
||||
CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo));
|
||||
info->m_button = id;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
@ -130,11 +134,13 @@ IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) :
|
|||
// IPlatformScreen::CMotionInfo
|
||||
//
|
||||
|
||||
IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) :
|
||||
m_x(x),
|
||||
m_y(y)
|
||||
IPlatformScreen::CMotionInfo*
|
||||
IPlatformScreen::CMotionInfo::alloc(SInt32 x, SInt32 y)
|
||||
{
|
||||
// do nothing
|
||||
CMotionInfo* info = (CMotionInfo*)malloc(sizeof(CMotionInfo));
|
||||
info->m_x = x;
|
||||
info->m_y = y;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
@ -142,8 +148,10 @@ IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) :
|
|||
// IPlatformScreen::CWheelInfo
|
||||
//
|
||||
|
||||
IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) :
|
||||
m_wheel(wheel)
|
||||
IPlatformScreen::CWheelInfo*
|
||||
IPlatformScreen::CWheelInfo::alloc(SInt32 wheel)
|
||||
{
|
||||
// do nothing
|
||||
CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo));
|
||||
info->m_wheel = wheel;
|
||||
return info;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
//! Key event data
|
||||
class CKeyInfo {
|
||||
public:
|
||||
CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count);
|
||||
static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
|
||||
|
||||
public:
|
||||
KeyID m_key;
|
||||
|
@ -51,7 +51,7 @@ public:
|
|||
//! Button event data
|
||||
class CButtonInfo {
|
||||
public:
|
||||
CButtonInfo(ButtonID);
|
||||
static CButtonInfo* alloc(ButtonID);
|
||||
|
||||
public:
|
||||
ButtonID m_button;
|
||||
|
@ -59,7 +59,7 @@ public:
|
|||
//! Motion event data
|
||||
class CMotionInfo {
|
||||
public:
|
||||
CMotionInfo(SInt32 x, SInt32 y);
|
||||
static CMotionInfo* alloc(SInt32 x, SInt32 y);
|
||||
|
||||
public:
|
||||
SInt32 m_x;
|
||||
|
@ -68,7 +68,7 @@ public:
|
|||
//! Wheel motion event data
|
||||
class CWheelInfo {
|
||||
public:
|
||||
CWheelInfo(SInt32);
|
||||
static CWheelInfo* alloc(SInt32);
|
||||
|
||||
public:
|
||||
SInt32 m_wheel;
|
||||
|
|
|
@ -29,6 +29,7 @@ libsynergy_a_SOURCES = \
|
|||
CPacketStreamFilter.cpp \
|
||||
CProtocolUtil.cpp \
|
||||
CScreen.cpp \
|
||||
IClipboard.cpp \
|
||||
IPlatformScreen.cpp \
|
||||
IScreen.cpp \
|
||||
XScreen.cpp \
|
||||
|
|
|
@ -267,19 +267,11 @@ public:
|
|||
*/
|
||||
SInt32 m_w, m_h;
|
||||
|
||||
//! Jump zone size
|
||||
/*!
|
||||
This is the size of the jump zone. The cursor jumps to the adjacent
|
||||
screen when it comes within this many pixels of the edge of the screen.
|
||||
*/
|
||||
SInt32 m_zoneSize;
|
||||
//! Obsolete (jump zone size)
|
||||
SInt32 obsolete1;
|
||||
|
||||
//! Mouse position
|
||||
/*!
|
||||
The position of the cursor. This is not kept up-to-date so it's
|
||||
only meaningful when receiving an update.
|
||||
*/
|
||||
SInt32 m_mx, m_my;
|
||||
//! Obsolete (mouse position)
|
||||
SInt32 obsolete2, obsolete3;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue