mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-11 00:58:14 +08:00
Initial commit of the synergy trunk sources from sf.net
This commit is contained in:
662
lib/client/CClient.cpp
Normal file
662
lib/client/CClient.cpp
Normal file
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CClient.h"
|
||||
#include "CServerProxy.h"
|
||||
#include "CScreen.h"
|
||||
#include "CClipboard.h"
|
||||
#include "CPacketStreamFilter.h"
|
||||
#include "CProtocolUtil.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "XSynergy.h"
|
||||
#include "IDataSocket.h"
|
||||
#include "ISocketFactory.h"
|
||||
#include "IStreamFilterFactory.h"
|
||||
#include "CLog.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "TMethodEventJob.h"
|
||||
|
||||
//
|
||||
// CClient
|
||||
//
|
||||
|
||||
CEvent::Type CClient::s_connectedEvent = CEvent::kUnknown;
|
||||
CEvent::Type CClient::s_connectionFailedEvent = CEvent::kUnknown;
|
||||
CEvent::Type CClient::s_disconnectedEvent = CEvent::kUnknown;
|
||||
|
||||
CClient::CClient(const CString& name, const CNetworkAddress& address,
|
||||
ISocketFactory* socketFactory,
|
||||
IStreamFilterFactory* streamFilterFactory,
|
||||
CScreen* screen) :
|
||||
m_name(name),
|
||||
m_serverAddress(address),
|
||||
m_socketFactory(socketFactory),
|
||||
m_streamFilterFactory(streamFilterFactory),
|
||||
m_screen(screen),
|
||||
m_stream(NULL),
|
||||
m_timer(NULL),
|
||||
m_server(NULL),
|
||||
m_ready(false),
|
||||
m_active(false),
|
||||
m_suspended(false),
|
||||
m_connectOnResume(false)
|
||||
{
|
||||
assert(m_socketFactory != NULL);
|
||||
assert(m_screen != NULL);
|
||||
|
||||
// register suspend/resume event handlers
|
||||
EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(),
|
||||
getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleSuspend));
|
||||
EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(),
|
||||
getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleResume));
|
||||
}
|
||||
|
||||
CClient::~CClient()
|
||||
{
|
||||
EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(),
|
||||
getEventTarget());
|
||||
EVENTQUEUE->removeHandler(IScreen::getResumeEvent(),
|
||||
getEventTarget());
|
||||
|
||||
cleanupTimer();
|
||||
cleanupScreen();
|
||||
cleanupConnecting();
|
||||
cleanupConnection();
|
||||
delete m_socketFactory;
|
||||
delete m_streamFilterFactory;
|
||||
}
|
||||
|
||||
void
|
||||
CClient::connect()
|
||||
{
|
||||
if (m_stream != NULL) {
|
||||
return;
|
||||
}
|
||||
if (m_suspended) {
|
||||
m_connectOnResume = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// resolve the server hostname. do this every time we connect
|
||||
// in case we couldn't resolve the address earlier or the address
|
||||
// has changed (which can happen frequently if this is a laptop
|
||||
// being shuttled between various networks). patch by Brent
|
||||
// Priddy.
|
||||
m_serverAddress.resolve();
|
||||
|
||||
// create the socket
|
||||
IDataSocket* socket = m_socketFactory->create();
|
||||
|
||||
// filter socket messages, including a packetizing filter
|
||||
m_stream = socket;
|
||||
if (m_streamFilterFactory != NULL) {
|
||||
m_stream = m_streamFilterFactory->create(m_stream, true);
|
||||
}
|
||||
m_stream = new CPacketStreamFilter(m_stream, true);
|
||||
|
||||
// connect
|
||||
LOG((CLOG_DEBUG1 "connecting to server"));
|
||||
setupConnecting();
|
||||
setupTimer();
|
||||
socket->connect(m_serverAddress);
|
||||
}
|
||||
catch (XBase& e) {
|
||||
cleanupTimer();
|
||||
cleanupConnecting();
|
||||
delete m_stream;
|
||||
m_stream = NULL;
|
||||
LOG((CLOG_DEBUG1 "connection failed"));
|
||||
sendConnectionFailedEvent(e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::disconnect(const char* msg)
|
||||
{
|
||||
m_connectOnResume = false;
|
||||
cleanupTimer();
|
||||
cleanupScreen();
|
||||
cleanupConnecting();
|
||||
cleanupConnection();
|
||||
if (msg != NULL) {
|
||||
sendConnectionFailedEvent(msg);
|
||||
}
|
||||
else {
|
||||
sendEvent(getDisconnectedEvent(), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handshakeComplete()
|
||||
{
|
||||
m_ready = true;
|
||||
m_screen->enable();
|
||||
sendEvent(getConnectedEvent(), NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
CClient::isConnected() const
|
||||
{
|
||||
return (m_server != NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
CClient::isConnecting() const
|
||||
{
|
||||
return (m_timer != NULL);
|
||||
}
|
||||
|
||||
CNetworkAddress
|
||||
CClient::getServerAddress() const
|
||||
{
|
||||
return m_serverAddress;
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CClient::getConnectedEvent()
|
||||
{
|
||||
return CEvent::registerTypeOnce(s_connectedEvent,
|
||||
"CClient::connected");
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CClient::getConnectionFailedEvent()
|
||||
{
|
||||
return CEvent::registerTypeOnce(s_connectionFailedEvent,
|
||||
"CClient::failed");
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CClient::getDisconnectedEvent()
|
||||
{
|
||||
return CEvent::registerTypeOnce(s_disconnectedEvent,
|
||||
"CClient::disconnected");
|
||||
}
|
||||
|
||||
void*
|
||||
CClient::getEventTarget() const
|
||||
{
|
||||
return m_screen->getEventTarget();
|
||||
}
|
||||
|
||||
bool
|
||||
CClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
|
||||
{
|
||||
return m_screen->getClipboard(id, clipboard);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
|
||||
{
|
||||
m_screen->getShape(x, y, w, h);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::getCursorPos(SInt32& x, SInt32& y) const
|
||||
{
|
||||
m_screen->getCursorPos(x, y);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool)
|
||||
{
|
||||
m_active = true;
|
||||
m_screen->mouseMove(xAbs, yAbs);
|
||||
m_screen->enter(mask);
|
||||
}
|
||||
|
||||
bool
|
||||
CClient::leave()
|
||||
{
|
||||
m_screen->leave();
|
||||
|
||||
m_active = false;
|
||||
|
||||
// send clipboards that we own and that have changed
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
if (m_ownClipboard[id]) {
|
||||
sendClipboard(id);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setClipboard(ClipboardID id, const IClipboard* clipboard)
|
||||
{
|
||||
m_screen->setClipboard(id, clipboard);
|
||||
m_ownClipboard[id] = false;
|
||||
m_sentClipboard[id] = false;
|
||||
}
|
||||
|
||||
void
|
||||
CClient::grabClipboard(ClipboardID id)
|
||||
{
|
||||
m_screen->grabClipboard(id);
|
||||
m_ownClipboard[id] = false;
|
||||
m_sentClipboard[id] = false;
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setClipboardDirty(ClipboardID, bool)
|
||||
{
|
||||
assert(0 && "shouldn't be called");
|
||||
}
|
||||
|
||||
void
|
||||
CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
||||
{
|
||||
m_screen->keyDown(id, mask, button);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::keyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
{
|
||||
m_screen->keyRepeat(id, mask, count, button);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button)
|
||||
{
|
||||
m_screen->keyUp(id, mask, button);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::mouseDown(ButtonID id)
|
||||
{
|
||||
m_screen->mouseDown(id);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::mouseUp(ButtonID id)
|
||||
{
|
||||
m_screen->mouseUp(id);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::mouseMove(SInt32 x, SInt32 y)
|
||||
{
|
||||
m_screen->mouseMove(x, y);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::mouseRelativeMove(SInt32 dx, SInt32 dy)
|
||||
{
|
||||
m_screen->mouseRelativeMove(dx, dy);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::mouseWheel(SInt32 xDelta, SInt32 yDelta)
|
||||
{
|
||||
m_screen->mouseWheel(xDelta, yDelta);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::screensaver(bool activate)
|
||||
{
|
||||
m_screen->screensaver(activate);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::resetOptions()
|
||||
{
|
||||
m_screen->resetOptions();
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setOptions(const COptionsList& options)
|
||||
{
|
||||
m_screen->setOptions(options);
|
||||
}
|
||||
|
||||
CString
|
||||
CClient::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void
|
||||
CClient::sendClipboard(ClipboardID id)
|
||||
{
|
||||
// note -- m_mutex must be locked on entry
|
||||
assert(m_screen != NULL);
|
||||
assert(m_server != NULL);
|
||||
|
||||
// get clipboard data. set the clipboard time to the last
|
||||
// clipboard time before getting the data from the screen
|
||||
// as the screen may detect an unchanged clipboard and
|
||||
// avoid copying the data.
|
||||
CClipboard clipboard;
|
||||
if (clipboard.open(m_timeClipboard[id])) {
|
||||
clipboard.close();
|
||||
}
|
||||
m_screen->getClipboard(id, &clipboard);
|
||||
|
||||
// check time
|
||||
if (m_timeClipboard[id] == 0 ||
|
||||
clipboard.getTime() != m_timeClipboard[id]) {
|
||||
// save new time
|
||||
m_timeClipboard[id] = clipboard.getTime();
|
||||
|
||||
// marshall the data
|
||||
CString data = clipboard.marshall();
|
||||
|
||||
// save and send data if different or not yet sent
|
||||
if (!m_sentClipboard[id] || data != m_dataClipboard[id]) {
|
||||
m_sentClipboard[id] = true;
|
||||
m_dataClipboard[id] = data;
|
||||
m_server->onClipboardChanged(id, &clipboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::sendEvent(CEvent::Type type, void* data)
|
||||
{
|
||||
EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
|
||||
}
|
||||
|
||||
void
|
||||
CClient::sendConnectionFailedEvent(const char* msg)
|
||||
{
|
||||
CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg));
|
||||
info->m_retry = true;
|
||||
strcpy(info->m_what, msg);
|
||||
sendEvent(getConnectionFailedEvent(), info);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setupConnecting()
|
||||
{
|
||||
assert(m_stream != NULL);
|
||||
|
||||
EVENTQUEUE->adoptHandler(IDataSocket::getConnectedEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleConnected));
|
||||
EVENTQUEUE->adoptHandler(IDataSocket::getConnectionFailedEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleConnectionFailed));
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setupConnection()
|
||||
{
|
||||
assert(m_stream != NULL);
|
||||
|
||||
EVENTQUEUE->adoptHandler(ISocket::getDisconnectedEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleDisconnected));
|
||||
EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleHello));
|
||||
EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleOutputError));
|
||||
EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleDisconnected));
|
||||
EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleDisconnected));
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setupScreen()
|
||||
{
|
||||
assert(m_server == NULL);
|
||||
|
||||
m_ready = false;
|
||||
m_server = new CServerProxy(this, m_stream);
|
||||
EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(),
|
||||
getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleShapeChanged));
|
||||
EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(),
|
||||
getEventTarget(),
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleClipboardGrabbed));
|
||||
}
|
||||
|
||||
void
|
||||
CClient::setupTimer()
|
||||
{
|
||||
assert(m_timer == NULL);
|
||||
|
||||
m_timer = EVENTQUEUE->newOneShotTimer(15.0, NULL);
|
||||
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer,
|
||||
new TMethodEventJob<CClient>(this,
|
||||
&CClient::handleConnectTimeout));
|
||||
}
|
||||
|
||||
void
|
||||
CClient::cleanupConnecting()
|
||||
{
|
||||
if (m_stream != NULL) {
|
||||
EVENTQUEUE->removeHandler(IDataSocket::getConnectedEvent(),
|
||||
m_stream->getEventTarget());
|
||||
EVENTQUEUE->removeHandler(IDataSocket::getConnectionFailedEvent(),
|
||||
m_stream->getEventTarget());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::cleanupConnection()
|
||||
{
|
||||
if (m_stream != NULL) {
|
||||
EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
|
||||
m_stream->getEventTarget());
|
||||
EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(),
|
||||
m_stream->getEventTarget());
|
||||
EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(),
|
||||
m_stream->getEventTarget());
|
||||
EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(),
|
||||
m_stream->getEventTarget());
|
||||
EVENTQUEUE->removeHandler(ISocket::getDisconnectedEvent(),
|
||||
m_stream->getEventTarget());
|
||||
delete m_stream;
|
||||
m_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::cleanupScreen()
|
||||
{
|
||||
if (m_server != NULL) {
|
||||
if (m_ready) {
|
||||
m_screen->disable();
|
||||
m_ready = false;
|
||||
}
|
||||
EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(),
|
||||
getEventTarget());
|
||||
EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(),
|
||||
getEventTarget());
|
||||
delete m_server;
|
||||
m_server = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::cleanupTimer()
|
||||
{
|
||||
if (m_timer != NULL) {
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer);
|
||||
EVENTQUEUE->deleteTimer(m_timer);
|
||||
m_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleConnected(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "connected; wait for hello"));
|
||||
cleanupConnecting();
|
||||
setupConnection();
|
||||
|
||||
// reset clipboard state
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
m_ownClipboard[id] = false;
|
||||
m_sentClipboard[id] = false;
|
||||
m_timeClipboard[id] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleConnectionFailed(const CEvent& event, void*)
|
||||
{
|
||||
IDataSocket::CConnectionFailedInfo* info =
|
||||
reinterpret_cast<IDataSocket::CConnectionFailedInfo*>(event.getData());
|
||||
|
||||
cleanupTimer();
|
||||
cleanupConnecting();
|
||||
delete m_stream;
|
||||
m_stream = NULL;
|
||||
LOG((CLOG_DEBUG1 "connection failed"));
|
||||
sendConnectionFailedEvent(info->m_what);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleConnectTimeout(const CEvent&, void*)
|
||||
{
|
||||
cleanupTimer();
|
||||
cleanupConnecting();
|
||||
cleanupConnection();
|
||||
delete m_stream;
|
||||
m_stream = NULL;
|
||||
LOG((CLOG_DEBUG1 "connection timed out"));
|
||||
sendConnectionFailedEvent("Timed out");
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleOutputError(const CEvent&, void*)
|
||||
{
|
||||
cleanupTimer();
|
||||
cleanupScreen();
|
||||
cleanupConnection();
|
||||
LOG((CLOG_WARN "error sending to server"));
|
||||
sendEvent(getDisconnectedEvent(), NULL);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleDisconnected(const CEvent&, void*)
|
||||
{
|
||||
cleanupTimer();
|
||||
cleanupScreen();
|
||||
cleanupConnection();
|
||||
LOG((CLOG_DEBUG1 "disconnected"));
|
||||
sendEvent(getDisconnectedEvent(), NULL);
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleShapeChanged(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_DEBUG "resolution changed"));
|
||||
m_server->onInfoChanged();
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleClipboardGrabbed(const CEvent& event, void*)
|
||||
{
|
||||
const IScreen::CClipboardInfo* info =
|
||||
reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData());
|
||||
|
||||
// grab ownership
|
||||
m_server->onGrabClipboard(info->m_id);
|
||||
|
||||
// we now own the clipboard and it has not been sent to the server
|
||||
m_ownClipboard[info->m_id] = true;
|
||||
m_sentClipboard[info->m_id] = false;
|
||||
m_timeClipboard[info->m_id] = 0;
|
||||
|
||||
// if we're not the active screen then send the clipboard now,
|
||||
// otherwise we'll wait until we leave.
|
||||
if (!m_active) {
|
||||
sendClipboard(info->m_id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleHello(const CEvent&, void*)
|
||||
{
|
||||
SInt16 major, minor;
|
||||
if (!CProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) {
|
||||
sendConnectionFailedEvent("Protocol error from server");
|
||||
cleanupTimer();
|
||||
cleanupConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
// check versions
|
||||
LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor));
|
||||
if (major < kProtocolMajorVersion ||
|
||||
(major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) {
|
||||
sendConnectionFailedEvent(XIncompatibleClient(major, minor).what());
|
||||
cleanupTimer();
|
||||
cleanupConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
// say hello back
|
||||
LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
|
||||
CProtocolUtil::writef(m_stream, kMsgHelloBack,
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion, &m_name);
|
||||
|
||||
// now connected but waiting to complete handshake
|
||||
setupScreen();
|
||||
cleanupTimer();
|
||||
|
||||
// make sure we process any remaining messages later. we won't
|
||||
// receive another event for already pending messages so we fake
|
||||
// one.
|
||||
if (m_stream->isReady()) {
|
||||
EVENTQUEUE->addEvent(CEvent(IStream::getInputReadyEvent(),
|
||||
m_stream->getEventTarget()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleSuspend(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_INFO "suspend"));
|
||||
m_suspended = true;
|
||||
bool wasConnected = isConnected();
|
||||
disconnect(NULL);
|
||||
m_connectOnResume = wasConnected;
|
||||
}
|
||||
|
||||
void
|
||||
CClient::handleResume(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_INFO "resume"));
|
||||
m_suspended = false;
|
||||
if (m_connectOnResume) {
|
||||
m_connectOnResume = false;
|
||||
connect();
|
||||
}
|
||||
}
|
||||
198
lib/client/CClient.h
Normal file
198
lib/client/CClient.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CCLIENT_H
|
||||
#define CCLIENT_H
|
||||
|
||||
#include "IClient.h"
|
||||
#include "IClipboard.h"
|
||||
#include "CNetworkAddress.h"
|
||||
|
||||
class CEventQueueTimer;
|
||||
class CScreen;
|
||||
class CServerProxy;
|
||||
class IDataSocket;
|
||||
class ISocketFactory;
|
||||
class IStream;
|
||||
class IStreamFilterFactory;
|
||||
|
||||
//! Synergy client
|
||||
/*!
|
||||
This class implements the top-level client algorithms for synergy.
|
||||
*/
|
||||
class CClient : public IClient {
|
||||
public:
|
||||
class CFailInfo {
|
||||
public:
|
||||
bool m_retry;
|
||||
char m_what[1];
|
||||
};
|
||||
|
||||
/*!
|
||||
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& name, const CNetworkAddress& address,
|
||||
ISocketFactory* socketFactory,
|
||||
IStreamFilterFactory* streamFilterFactory,
|
||||
CScreen* screen);
|
||||
~CClient();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Connect to server
|
||||
/*!
|
||||
Starts an attempt to connect to the server. This is ignored if
|
||||
the client is trying to connect or is already connected.
|
||||
*/
|
||||
void connect();
|
||||
|
||||
//! Disconnect
|
||||
/*!
|
||||
Disconnects from the server with an optional error message.
|
||||
*/
|
||||
void disconnect(const char* msg);
|
||||
|
||||
//! Notify of handshake complete
|
||||
/*!
|
||||
Notifies the client that the connection handshake has completed.
|
||||
*/
|
||||
void handshakeComplete();
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Test if connected
|
||||
/*!
|
||||
Returns true iff the client is successfully connected to the server.
|
||||
*/
|
||||
bool isConnected() const;
|
||||
|
||||
//! Test if connecting
|
||||
/*!
|
||||
Returns true iff the client is currently attempting to connect to
|
||||
the server.
|
||||
*/
|
||||
bool isConnecting() const;
|
||||
|
||||
//! Get address of server
|
||||
/*!
|
||||
Returns the address of the server the client is connected (or wants
|
||||
to connect) to.
|
||||
*/
|
||||
CNetworkAddress getServerAddress() 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();
|
||||
|
||||
//@}
|
||||
|
||||
// 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 enter(SInt32 xAbs, SInt32 yAbs,
|
||||
UInt32 seqNum, KeyModifierMask mask,
|
||||
bool forScreensaver);
|
||||
virtual bool leave();
|
||||
virtual void setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void grabClipboard(ClipboardID);
|
||||
virtual void setClipboardDirty(ClipboardID, bool);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
|
||||
virtual void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton);
|
||||
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
|
||||
virtual void mouseDown(ButtonID);
|
||||
virtual void mouseUp(ButtonID);
|
||||
virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);
|
||||
virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
|
||||
virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta);
|
||||
virtual void screensaver(bool activate);
|
||||
virtual void resetOptions();
|
||||
virtual void setOptions(const COptionsList& options);
|
||||
virtual CString getName() const;
|
||||
|
||||
private:
|
||||
void sendClipboard(ClipboardID);
|
||||
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 handleShapeChanged(const CEvent&, void*);
|
||||
void handleClipboardGrabbed(const CEvent&, void*);
|
||||
void handleHello(const CEvent&, void*);
|
||||
void handleSuspend(const CEvent& event, void*);
|
||||
void handleResume(const CEvent& event, void*);
|
||||
|
||||
private:
|
||||
CString m_name;
|
||||
CNetworkAddress m_serverAddress;
|
||||
ISocketFactory* m_socketFactory;
|
||||
IStreamFilterFactory* m_streamFilterFactory;
|
||||
CScreen* m_screen;
|
||||
IStream* m_stream;
|
||||
CEventQueueTimer* m_timer;
|
||||
CServerProxy* m_server;
|
||||
bool m_ready;
|
||||
bool m_active;
|
||||
bool m_suspended;
|
||||
bool m_connectOnResume;
|
||||
bool m_ownClipboard[kClipboardEnd];
|
||||
bool m_sentClipboard[kClipboardEnd];
|
||||
IClipboard::Time m_timeClipboard[kClipboardEnd];
|
||||
CString m_dataClipboard[kClipboardEnd];
|
||||
|
||||
static CEvent::Type s_connectedEvent;
|
||||
static CEvent::Type s_connectionFailedEvent;
|
||||
static CEvent::Type s_disconnectedEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
816
lib/client/CServerProxy.cpp
Normal file
816
lib/client/CServerProxy.cpp
Normal file
@@ -0,0 +1,816 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CServerProxy.h"
|
||||
#include "CClient.h"
|
||||
#include "CClipboard.h"
|
||||
#include "CProtocolUtil.h"
|
||||
#include "OptionTypes.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "IStream.h"
|
||||
#include "CLog.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "TMethodEventJob.h"
|
||||
#include "XBase.h"
|
||||
#include <memory>
|
||||
|
||||
//
|
||||
// CServerProxy
|
||||
//
|
||||
|
||||
CServerProxy::CServerProxy(CClient* client, IStream* stream) :
|
||||
m_client(client),
|
||||
m_stream(stream),
|
||||
m_seqNum(0),
|
||||
m_compressMouse(false),
|
||||
m_compressMouseRelative(false),
|
||||
m_xMouse(0),
|
||||
m_yMouse(0),
|
||||
m_dxMouse(0),
|
||||
m_dyMouse(0),
|
||||
m_ignoreMouse(false),
|
||||
m_keepAliveAlarm(0.0),
|
||||
m_keepAliveAlarmTimer(NULL),
|
||||
m_parser(&CServerProxy::parseHandshakeMessage)
|
||||
{
|
||||
assert(m_client != 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::handleData));
|
||||
|
||||
// send heartbeat
|
||||
setKeepAliveRate(kKeepAliveRate);
|
||||
}
|
||||
|
||||
CServerProxy::~CServerProxy()
|
||||
{
|
||||
setKeepAliveRate(-1.0);
|
||||
EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
|
||||
m_stream->getEventTarget());
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::resetKeepAliveAlarm()
|
||||
{
|
||||
if (m_keepAliveAlarmTimer != NULL) {
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, m_keepAliveAlarmTimer);
|
||||
EVENTQUEUE->deleteTimer(m_keepAliveAlarmTimer);
|
||||
m_keepAliveAlarmTimer = NULL;
|
||||
}
|
||||
if (m_keepAliveAlarm > 0.0) {
|
||||
m_keepAliveAlarmTimer =
|
||||
EVENTQUEUE->newOneShotTimer(m_keepAliveAlarm, NULL);
|
||||
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_keepAliveAlarmTimer,
|
||||
new TMethodEventJob<CServerProxy>(this,
|
||||
&CServerProxy::handleKeepAliveAlarm));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::setKeepAliveRate(double rate)
|
||||
{
|
||||
m_keepAliveAlarm = rate * kKeepAlivesUntilDeath;
|
||||
resetKeepAliveAlarm();
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::handleData(const CEvent&, void*)
|
||||
{
|
||||
// handle messages until there are no more. first read message code.
|
||||
UInt8 code[4];
|
||||
UInt32 n = m_stream->read(code, 4);
|
||||
while (n != 0) {
|
||||
// verify we got an entire code
|
||||
if (n != 4) {
|
||||
LOG((CLOG_ERR "incomplete message from server: %d bytes", n));
|
||||
m_client->disconnect("incomplete message from server");
|
||||
return;
|
||||
}
|
||||
|
||||
// parse message
|
||||
LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
|
||||
switch ((this->*m_parser)(code)) {
|
||||
case kOkay:
|
||||
break;
|
||||
|
||||
case kUnknown:
|
||||
LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
|
||||
m_client->disconnect("invalid message from server");
|
||||
return;
|
||||
|
||||
case kDisconnect:
|
||||
return;
|
||||
}
|
||||
|
||||
// next message
|
||||
n = m_stream->read(code, 4);
|
||||
}
|
||||
|
||||
flushCompressedMouse();
|
||||
}
|
||||
|
||||
CServerProxy::EResult
|
||||
CServerProxy::parseHandshakeMessage(const UInt8* code)
|
||||
{
|
||||
if (memcmp(code, kMsgQInfo, 4) == 0) {
|
||||
queryInfo();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
|
||||
infoAcknowledgment();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
|
||||
setOptions();
|
||||
|
||||
// handshake is complete
|
||||
m_parser = &CServerProxy::parseMessage;
|
||||
m_client->handshakeComplete();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
|
||||
resetOptions();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
|
||||
// echo keep alives and reset alarm
|
||||
CProtocolUtil::writef(m_stream, kMsgCKeepAlive);
|
||||
resetKeepAliveAlarm();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCNoop, 4) == 0) {
|
||||
// accept and discard no-op
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
||||
// server wants us to hangup
|
||||
LOG((CLOG_DEBUG1 "recv close"));
|
||||
m_client->disconnect(NULL);
|
||||
return kDisconnect;
|
||||
}
|
||||
|
||||
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 kDisconnect;
|
||||
}
|
||||
|
||||
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 kDisconnect;
|
||||
}
|
||||
|
||||
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 kDisconnect;
|
||||
}
|
||||
|
||||
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 kDisconnect;
|
||||
}
|
||||
else {
|
||||
return kUnknown;
|
||||
}
|
||||
|
||||
return kOkay;
|
||||
}
|
||||
|
||||
CServerProxy::EResult
|
||||
CServerProxy::parseMessage(const UInt8* code)
|
||||
{
|
||||
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
|
||||
mouseMove();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseRelMove, 4) == 0) {
|
||||
mouseRelativeMove();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
|
||||
mouseWheel();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
||||
keyDown();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
|
||||
keyUp();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
|
||||
mouseDown();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
|
||||
mouseUp();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
||||
keyRepeat();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
|
||||
// echo keep alives and reset alarm
|
||||
CProtocolUtil::writef(m_stream, kMsgCKeepAlive);
|
||||
resetKeepAliveAlarm();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCNoop, 4) == 0) {
|
||||
// accept and discard no-op
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCEnter, 4) == 0) {
|
||||
enter();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCLeave, 4) == 0) {
|
||||
leave();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
|
||||
grabClipboard();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
|
||||
screensaver();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgQInfo, 4) == 0) {
|
||||
queryInfo();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
|
||||
infoAcknowledgment();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
|
||||
setClipboard();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
|
||||
resetOptions();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
|
||||
setOptions();
|
||||
}
|
||||
|
||||
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
||||
// server wants us to hangup
|
||||
LOG((CLOG_DEBUG1 "recv close"));
|
||||
m_client->disconnect(NULL);
|
||||
return kDisconnect;
|
||||
}
|
||||
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 kDisconnect;
|
||||
}
|
||||
else {
|
||||
return kUnknown;
|
||||
}
|
||||
|
||||
// send a reply. this is intended to work around a delay when
|
||||
// running a linux server and an OS X (any BSD?) client. the
|
||||
// client waits to send an ACK (if the system control flag
|
||||
// net.inet.tcp.delayed_ack is 1) in hopes of piggybacking it
|
||||
// on a data packet. we provide that packet here. i don't
|
||||
// know why a delayed ACK should cause the server to wait since
|
||||
// TCP_NODELAY is enabled.
|
||||
CProtocolUtil::writef(m_stream, kMsgCNoop);
|
||||
|
||||
return kOkay;
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::handleKeepAliveAlarm(const CEvent&, void*)
|
||||
{
|
||||
LOG((CLOG_NOTE "server is dead"));
|
||||
m_client->disconnect("server is not responding");
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::onInfoChanged()
|
||||
{
|
||||
// ignore mouse motion until we receive acknowledgment of our info
|
||||
// change message.
|
||||
m_ignoreMouse = true;
|
||||
|
||||
// send info update
|
||||
queryInfo();
|
||||
}
|
||||
|
||||
bool
|
||||
CServerProxy::onGrabClipboard(ClipboardID id)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "sending clipboard %d changed", id));
|
||||
CProtocolUtil::writef(m_stream, kMsgCClipboard, id, m_seqNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
|
||||
{
|
||||
CString data = IClipboard::marshall(clipboard);
|
||||
LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
||||
CProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::flushCompressedMouse()
|
||||
{
|
||||
if (m_compressMouse) {
|
||||
m_compressMouse = false;
|
||||
m_client->mouseMove(m_xMouse, m_yMouse);
|
||||
}
|
||||
if (m_compressMouseRelative) {
|
||||
m_compressMouseRelative = false;
|
||||
m_client->mouseRelativeMove(m_dxMouse, m_dyMouse);
|
||||
m_dxMouse = 0;
|
||||
m_dyMouse = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::sendInfo(const CClientInfo& info)
|
||||
{
|
||||
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, 0,
|
||||
info.m_mx, info.m_my);
|
||||
}
|
||||
|
||||
KeyID
|
||||
CServerProxy::translateKey(KeyID id) const
|
||||
{
|
||||
static const KeyID s_translationTable[kKeyModifierIDLast][2] = {
|
||||
{ kKeyNone, kKeyNone },
|
||||
{ kKeyShift_L, kKeyShift_R },
|
||||
{ kKeyControl_L, kKeyControl_R },
|
||||
{ kKeyAlt_L, kKeyAlt_R },
|
||||
{ kKeyMeta_L, kKeyMeta_R },
|
||||
{ kKeySuper_L, kKeySuper_R }
|
||||
};
|
||||
|
||||
KeyModifierID id2 = kKeyModifierIDNull;
|
||||
UInt32 side = 0;
|
||||
switch (id) {
|
||||
case kKeyShift_L:
|
||||
id2 = kKeyModifierIDShift;
|
||||
side = 0;
|
||||
break;
|
||||
|
||||
case kKeyShift_R:
|
||||
id2 = kKeyModifierIDShift;
|
||||
side = 1;
|
||||
break;
|
||||
|
||||
case kKeyControl_L:
|
||||
id2 = kKeyModifierIDControl;
|
||||
side = 0;
|
||||
break;
|
||||
|
||||
case kKeyControl_R:
|
||||
id2 = kKeyModifierIDControl;
|
||||
side = 1;
|
||||
break;
|
||||
|
||||
case kKeyAlt_L:
|
||||
id2 = kKeyModifierIDAlt;
|
||||
side = 0;
|
||||
break;
|
||||
|
||||
case kKeyAlt_R:
|
||||
id2 = kKeyModifierIDAlt;
|
||||
side = 1;
|
||||
break;
|
||||
|
||||
case kKeyMeta_L:
|
||||
id2 = kKeyModifierIDMeta;
|
||||
side = 0;
|
||||
break;
|
||||
|
||||
case kKeyMeta_R:
|
||||
id2 = kKeyModifierIDMeta;
|
||||
side = 1;
|
||||
break;
|
||||
|
||||
case kKeySuper_L:
|
||||
id2 = kKeyModifierIDSuper;
|
||||
side = 0;
|
||||
break;
|
||||
|
||||
case kKeySuper_R:
|
||||
id2 = kKeyModifierIDSuper;
|
||||
side = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (id2 != kKeyModifierIDNull) {
|
||||
return s_translationTable[m_modifierTranslationTable[id2]][side];
|
||||
}
|
||||
else {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
KeyModifierMask
|
||||
CServerProxy::translateModifierMask(KeyModifierMask mask) const
|
||||
{
|
||||
static const KeyModifierMask s_masks[kKeyModifierIDLast] = {
|
||||
0x0000,
|
||||
KeyModifierShift,
|
||||
KeyModifierControl,
|
||||
KeyModifierAlt,
|
||||
KeyModifierMeta,
|
||||
KeyModifierSuper
|
||||
};
|
||||
|
||||
KeyModifierMask newMask = mask & ~(KeyModifierShift |
|
||||
KeyModifierControl |
|
||||
KeyModifierAlt |
|
||||
KeyModifierMeta |
|
||||
KeyModifierSuper);
|
||||
if ((mask & KeyModifierShift) != 0) {
|
||||
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]];
|
||||
}
|
||||
if ((mask & KeyModifierControl) != 0) {
|
||||
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDControl]];
|
||||
}
|
||||
if ((mask & KeyModifierAlt) != 0) {
|
||||
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAlt]];
|
||||
}
|
||||
if ((mask & KeyModifierMeta) != 0) {
|
||||
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDMeta]];
|
||||
}
|
||||
if ((mask & KeyModifierSuper) != 0) {
|
||||
newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]];
|
||||
}
|
||||
return newMask;
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::enter()
|
||||
{
|
||||
// parse
|
||||
SInt16 x, y;
|
||||
UInt16 mask;
|
||||
UInt32 seqNum;
|
||||
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
|
||||
m_compressMouse = false;
|
||||
m_compressMouseRelative = false;
|
||||
m_dxMouse = 0;
|
||||
m_dyMouse = 0;
|
||||
m_seqNum = seqNum;
|
||||
|
||||
// forward
|
||||
m_client->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::leave()
|
||||
{
|
||||
// parse
|
||||
LOG((CLOG_DEBUG1 "recv leave"));
|
||||
|
||||
// send last mouse motion
|
||||
flushCompressedMouse();
|
||||
|
||||
// forward
|
||||
m_client->leave();
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::setClipboard()
|
||||
{
|
||||
// parse
|
||||
ClipboardID id;
|
||||
UInt32 seqNum;
|
||||
CString data;
|
||||
CProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data);
|
||||
LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
|
||||
|
||||
// validate
|
||||
if (id >= kClipboardEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
// forward
|
||||
CClipboard clipboard;
|
||||
clipboard.unmarshall(data, 0);
|
||||
m_client->setClipboard(id, &clipboard);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::grabClipboard()
|
||||
{
|
||||
// parse
|
||||
ClipboardID id;
|
||||
UInt32 seqNum;
|
||||
CProtocolUtil::readf(m_stream, kMsgCClipboard + 4, &id, &seqNum);
|
||||
LOG((CLOG_DEBUG "recv grab clipboard %d", id));
|
||||
|
||||
// validate
|
||||
if (id >= kClipboardEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
// forward
|
||||
m_client->grabClipboard(id);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::keyDown()
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
UInt16 id, mask, button;
|
||||
CProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key down id=0x%08x, mask=0x%04x, button=0x%04x", id, mask, button));
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
KeyModifierMask mask2 = translateModifierMask(
|
||||
static_cast<KeyModifierMask>(mask));
|
||||
if (id2 != static_cast<KeyID>(id) ||
|
||||
mask2 != static_cast<KeyModifierMask>(mask))
|
||||
LOG((CLOG_DEBUG1 "key down translated to id=0x%08x, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
m_client->keyDown(id2, mask2, button);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::keyRepeat()
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
UInt16 id, mask, count, button;
|
||||
CProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4,
|
||||
&id, &mask, &count, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key repeat id=0x%08x, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button));
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
KeyModifierMask mask2 = translateModifierMask(
|
||||
static_cast<KeyModifierMask>(mask));
|
||||
if (id2 != static_cast<KeyID>(id) ||
|
||||
mask2 != static_cast<KeyModifierMask>(mask))
|
||||
LOG((CLOG_DEBUG1 "key repeat translated to id=0x%08x, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
m_client->keyRepeat(id2, mask2, count, button);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::keyUp()
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
UInt16 id, mask, button;
|
||||
CProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key up id=0x%08x, mask=0x%04x, button=0x%04x", id, mask, button));
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
KeyModifierMask mask2 = translateModifierMask(
|
||||
static_cast<KeyModifierMask>(mask));
|
||||
if (id2 != static_cast<KeyID>(id) ||
|
||||
mask2 != static_cast<KeyModifierMask>(mask))
|
||||
LOG((CLOG_DEBUG1 "key up translated to id=0x%08x, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
m_client->keyUp(id2, mask2, button);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::mouseDown()
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
SInt8 id;
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseDown + 4, &id);
|
||||
LOG((CLOG_DEBUG1 "recv mouse down id=%d", id));
|
||||
|
||||
// forward
|
||||
m_client->mouseDown(static_cast<ButtonID>(id));
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::mouseUp()
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
SInt8 id;
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseUp + 4, &id);
|
||||
LOG((CLOG_DEBUG1 "recv mouse up id=%d", id));
|
||||
|
||||
// forward
|
||||
m_client->mouseUp(static_cast<ButtonID>(id));
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::mouseMove()
|
||||
{
|
||||
// parse
|
||||
bool ignore;
|
||||
SInt16 x, y;
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseMove + 4, &x, &y);
|
||||
|
||||
// note if we should ignore the move
|
||||
ignore = m_ignoreMouse;
|
||||
|
||||
// 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) {
|
||||
m_compressMouseRelative = false;
|
||||
ignore = true;
|
||||
m_xMouse = x;
|
||||
m_yMouse = y;
|
||||
m_dxMouse = 0;
|
||||
m_dyMouse = 0;
|
||||
}
|
||||
LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y));
|
||||
|
||||
// forward
|
||||
if (!ignore) {
|
||||
m_client->mouseMove(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::mouseRelativeMove()
|
||||
{
|
||||
// parse
|
||||
bool ignore;
|
||||
SInt16 dx, dy;
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseRelMove + 4, &dx, &dy);
|
||||
|
||||
// note if we should ignore the move
|
||||
ignore = m_ignoreMouse;
|
||||
|
||||
// compress mouse motion events if more input follows
|
||||
if (!ignore && !m_compressMouseRelative && m_stream->isReady()) {
|
||||
m_compressMouseRelative = true;
|
||||
}
|
||||
|
||||
// if compressing then ignore the motion but record it
|
||||
if (m_compressMouseRelative) {
|
||||
ignore = true;
|
||||
m_dxMouse += dx;
|
||||
m_dyMouse += dy;
|
||||
}
|
||||
LOG((CLOG_DEBUG2 "recv mouse relative move %d,%d", dx, dy));
|
||||
|
||||
// forward
|
||||
if (!ignore) {
|
||||
m_client->mouseRelativeMove(dx, dy);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::mouseWheel()
|
||||
{
|
||||
// get mouse up to date
|
||||
flushCompressedMouse();
|
||||
|
||||
// parse
|
||||
SInt16 xDelta, yDelta;
|
||||
CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &xDelta, &yDelta);
|
||||
LOG((CLOG_DEBUG2 "recv mouse wheel %+d,%+d", xDelta, yDelta));
|
||||
|
||||
// forward
|
||||
m_client->mouseWheel(xDelta, yDelta);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::screensaver()
|
||||
{
|
||||
// parse
|
||||
SInt8 on;
|
||||
CProtocolUtil::readf(m_stream, kMsgCScreenSaver + 4, &on);
|
||||
LOG((CLOG_DEBUG1 "recv screen saver on=%d", on));
|
||||
|
||||
// forward
|
||||
m_client->screensaver(on != 0);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::resetOptions()
|
||||
{
|
||||
// parse
|
||||
LOG((CLOG_DEBUG1 "recv reset options"));
|
||||
|
||||
// forward
|
||||
m_client->resetOptions();
|
||||
|
||||
// reset keep alive
|
||||
setKeepAliveRate(kKeepAliveRate);
|
||||
|
||||
// reset modifier translation table
|
||||
for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) {
|
||||
m_modifierTranslationTable[id] = id;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::setOptions()
|
||||
{
|
||||
// parse
|
||||
COptionsList options;
|
||||
CProtocolUtil::readf(m_stream, kMsgDSetOptions + 4, &options);
|
||||
LOG((CLOG_DEBUG1 "recv set options size=%d", options.size()));
|
||||
|
||||
// forward
|
||||
m_client->setOptions(options);
|
||||
|
||||
// update modifier table
|
||||
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
|
||||
KeyModifierID id = kKeyModifierIDNull;
|
||||
if (options[i] == kOptionModifierMapForShift) {
|
||||
id = kKeyModifierIDShift;
|
||||
}
|
||||
else if (options[i] == kOptionModifierMapForControl) {
|
||||
id = kKeyModifierIDControl;
|
||||
}
|
||||
else if (options[i] == kOptionModifierMapForAlt) {
|
||||
id = kKeyModifierIDAlt;
|
||||
}
|
||||
else if (options[i] == kOptionModifierMapForMeta) {
|
||||
id = kKeyModifierIDMeta;
|
||||
}
|
||||
else if (options[i] == kOptionModifierMapForSuper) {
|
||||
id = kKeyModifierIDSuper;
|
||||
}
|
||||
else if (options[i] == kOptionHeartbeat) {
|
||||
// update keep alive
|
||||
setKeepAliveRate(1.0e-3 * static_cast<double>(options[i + 1]));
|
||||
}
|
||||
if (id != kKeyModifierIDNull) {
|
||||
m_modifierTranslationTable[id] =
|
||||
static_cast<KeyModifierID>(options[i + 1]);
|
||||
LOG((CLOG_DEBUG1 "modifier %d mapped to %d", id, m_modifierTranslationTable[id]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::queryInfo()
|
||||
{
|
||||
CClientInfo info;
|
||||
m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||
m_client->getCursorPos(info.m_mx, info.m_my);
|
||||
sendInfo(info);
|
||||
}
|
||||
|
||||
void
|
||||
CServerProxy::infoAcknowledgment()
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "recv info acknowledgment"));
|
||||
m_ignoreMouse = false;
|
||||
}
|
||||
115
lib/client/CServerProxy.h
Normal file
115
lib/client/CServerProxy.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CSERVERPROXY_H
|
||||
#define CSERVERPROXY_H
|
||||
|
||||
#include "ClipboardTypes.h"
|
||||
#include "KeyTypes.h"
|
||||
#include "CEvent.h"
|
||||
|
||||
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:
|
||||
/*!
|
||||
Process messages from the server on \p stream and forward to
|
||||
\p client.
|
||||
*/
|
||||
CServerProxy(CClient* client, IStream* stream);
|
||||
~CServerProxy();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
void onInfoChanged();
|
||||
bool onGrabClipboard(ClipboardID);
|
||||
void onClipboardChanged(ClipboardID, const IClipboard*);
|
||||
|
||||
//@}
|
||||
|
||||
protected:
|
||||
enum EResult { kOkay, kUnknown, kDisconnect };
|
||||
EResult parseHandshakeMessage(const UInt8* code);
|
||||
EResult parseMessage(const UInt8* code);
|
||||
|
||||
private:
|
||||
// if compressing mouse motion then send the last motion now
|
||||
void flushCompressedMouse();
|
||||
|
||||
void sendInfo(const CClientInfo&);
|
||||
|
||||
void resetKeepAliveAlarm();
|
||||
void setKeepAliveRate(double);
|
||||
|
||||
// modifier key translation
|
||||
KeyID translateKey(KeyID) const;
|
||||
KeyModifierMask translateModifierMask(KeyModifierMask) const;
|
||||
|
||||
// event handlers
|
||||
void handleData(const CEvent&, void*);
|
||||
void handleKeepAliveAlarm(const CEvent&, void*);
|
||||
|
||||
// message handlers
|
||||
void enter();
|
||||
void leave();
|
||||
void setClipboard();
|
||||
void grabClipboard();
|
||||
void keyDown();
|
||||
void keyRepeat();
|
||||
void keyUp();
|
||||
void mouseDown();
|
||||
void mouseUp();
|
||||
void mouseMove();
|
||||
void mouseRelativeMove();
|
||||
void mouseWheel();
|
||||
void screensaver();
|
||||
void resetOptions();
|
||||
void setOptions();
|
||||
void queryInfo();
|
||||
void infoAcknowledgment();
|
||||
|
||||
private:
|
||||
typedef EResult (CServerProxy::*MessageParser)(const UInt8*);
|
||||
|
||||
CClient* m_client;
|
||||
IStream* m_stream;
|
||||
|
||||
UInt32 m_seqNum;
|
||||
|
||||
bool m_compressMouse;
|
||||
bool m_compressMouseRelative;
|
||||
SInt32 m_xMouse, m_yMouse;
|
||||
SInt32 m_dxMouse, m_dyMouse;
|
||||
|
||||
bool m_ignoreMouse;
|
||||
|
||||
KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast];
|
||||
|
||||
double m_keepAliveAlarm;
|
||||
CEventQueueTimer* m_keepAliveAlarmTimer;
|
||||
|
||||
MessageParser m_parser;
|
||||
};
|
||||
|
||||
#endif
|
||||
40
lib/client/Makefile.am
Normal file
40
lib/client/Makefile.am
Normal file
@@ -0,0 +1,40 @@
|
||||
# synergy -- mouse and keyboard sharing utility
|
||||
# Copyright (C) 2002 Chris Schoeneman
|
||||
#
|
||||
# This package is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# found in the file COPYING that should have accompanied this file.
|
||||
#
|
||||
# This package is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
## Process this file with automake to produce Makefile.in
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = \
|
||||
Makefile.win \
|
||||
$(NULL)
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
Makefile.in \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = libclient.a
|
||||
libclient_a_SOURCES = \
|
||||
CClient.cpp \
|
||||
CServerProxy.cpp \
|
||||
CClient.h \
|
||||
CServerProxy.h \
|
||||
$(NULL)
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/lib/common \
|
||||
-I$(top_srcdir)/lib/arch \
|
||||
-I$(top_srcdir)/lib/base \
|
||||
-I$(top_srcdir)/lib/mt \
|
||||
-I$(top_srcdir)/lib/io \
|
||||
-I$(top_srcdir)/lib/net \
|
||||
-I$(top_srcdir)/lib/synergy \
|
||||
-I$(top_srcdir)/lib/platform \
|
||||
$(NULL)
|
||||
63
lib/client/Makefile.win
Normal file
63
lib/client/Makefile.win
Normal file
@@ -0,0 +1,63 @@
|
||||
# synergy -- mouse and keyboard sharing utility
|
||||
# Copyright (C) 2007 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.
|
||||
|
||||
LIB_CLIENT_SRC = lib\client
|
||||
LIB_CLIENT_DST = $(BUILD_DST)\$(LIB_CLIENT_SRC)
|
||||
LIB_CLIENT_LIB = "$(LIB_CLIENT_DST)\client.lib"
|
||||
LIB_CLIENT_CPP = \
|
||||
"CClient.cpp" \
|
||||
"CServerProxy.cpp" \
|
||||
$(NULL)
|
||||
LIB_CLIENT_OBJ = \
|
||||
"$(LIB_CLIENT_DST)\CClient.obj" \
|
||||
"$(LIB_CLIENT_DST)\CServerProxy.obj" \
|
||||
$(NULL)
|
||||
LIB_CLIENT_INC = \
|
||||
/I"lib\common" \
|
||||
/I"lib\arch" \
|
||||
/I"lib\base" \
|
||||
/I"lib\mt" \
|
||||
/I"lib\io" \
|
||||
/I"lib\net" \
|
||||
/I"lib\synergy" \
|
||||
/I"lib\platform" \
|
||||
$(NULL)
|
||||
|
||||
CPP_FILES = $(CPP_FILES) $(LIB_CLIENT_CPP)
|
||||
OBJ_FILES = $(OBJ_FILES) $(LIB_CLIENT_OBJ)
|
||||
LIB_FILES = $(LIB_FILES) $(LIB_CLIENT_LIB)
|
||||
|
||||
# Dependency rules
|
||||
$(LIB_CLIENT_OBJ): $(AUTODEP)
|
||||
!if EXIST($(LIB_CLIENT_DST)\deps.mak)
|
||||
!include $(LIB_CLIENT_DST)\deps.mak
|
||||
!endif
|
||||
|
||||
# Build rules. Use batch-mode rules if possible.
|
||||
!if DEFINED(_NMAKE_VER)
|
||||
{$(LIB_CLIENT_SRC)\}.cpp{$(LIB_CLIENT_DST)\}.obj::
|
||||
!else
|
||||
{$(LIB_CLIENT_SRC)\}.cpp{$(LIB_CLIENT_DST)\}.obj:
|
||||
!endif
|
||||
@$(ECHO) Compile in $(LIB_CLIENT_SRC)
|
||||
-@$(MKDIR) $(LIB_CLIENT_DST) 2>NUL:
|
||||
$(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \
|
||||
$(LIB_CLIENT_INC) \
|
||||
/Fo$(LIB_CLIENT_DST)\ \
|
||||
/Fd$(LIB_CLIENT_LIB:.lib=.pdb) \
|
||||
$< | $(AUTODEP) $(LIB_CLIENT_SRC) $(LIB_CLIENT_DST)
|
||||
$(LIB_CLIENT_LIB): $(LIB_CLIENT_OBJ)
|
||||
@$(ECHO) Link $(@F)
|
||||
$(implib) $(ildebug) $(ilflags) \
|
||||
/out:$@ \
|
||||
$**
|
||||
$(AUTODEP) $(LIB_CLIENT_SRC) $(LIB_CLIENT_DST) $(**:.obj=.d)
|
||||
Reference in New Issue
Block a user