moved client and server files into their own respective

directories.
This commit is contained in:
crs
2001-11-18 23:14:28 +00:00
parent 6a223b7da5
commit 51505783aa
20 changed files with 105 additions and 29 deletions

95
server/CScreenMap.cpp Normal file
View File

@@ -0,0 +1,95 @@
#include "CScreenMap.h"
#include <assert.h>
//
// CScreenMap
//
CScreenMap::CScreenMap()
{
// do nothing
}
CScreenMap::~CScreenMap()
{
// do nothing
}
void CScreenMap::addScreen(const CString& name)
{
if (m_map.count(name) != 0) {
assert(0 && "name already in map"); // FIXME -- throw instead
}
m_map.insert(std::make_pair(name, CCell()));
}
void CScreenMap::removeScreen(const CString& name)
{
CCellMap::iterator index = m_map.find(name);
if (index == m_map.end()) {
assert(0 && "name not in map"); // FIXME -- throw instead
}
// remove from map
m_map.erase(index);
// disconnect
for (index = m_map.begin(); index != m_map.end(); ++index) {
CCell& cell = index->second;
for (SInt32 i = 0; kLastDirection - kFirstDirection; ++i)
if (cell.m_neighbor[i] == name) {
cell.m_neighbor[i].erase();
}
}
}
void CScreenMap::removeAllScreens()
{
m_map.clear();
}
void CScreenMap::connect(const CString& srcName,
EDirection srcSide,
const CString& dstName)
{
// find source cell
CCellMap::iterator index = m_map.find(srcName);
if (index == m_map.end()) {
assert(0 && "name not in map"); // FIXME -- throw instead
}
// connect side (overriding any previous connection)
index->second.m_neighbor[srcSide - kFirstDirection] = dstName;
}
void CScreenMap::disconnect(const CString& srcName,
EDirection srcSide)
{
// find source cell
CCellMap::iterator index = m_map.find(srcName);
if (index == m_map.end()) {
assert(0 && "name not in map"); // FIXME -- throw instead
}
// disconnect side
index->second.m_neighbor[srcSide - kFirstDirection].erase();
}
CString CScreenMap::getNeighbor(const CString& srcName,
EDirection srcSide) const
{
// find source cell
CCellMap::const_iterator index = m_map.find(srcName);
if (index == m_map.end()) {
assert(0 && "name not in map"); // FIXME -- throw instead
}
// return connection
return index->second.m_neighbor[srcSide - kFirstDirection];
}
const char* CScreenMap::dirName(EDirection dir)
{
static const char* s_name[] = { "left", "right", "top", "bottom" };
return s_name[dir - kFirstDirection];
}

49
server/CScreenMap.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef CSCREENMAP_H
#define CSCREENMAP_H
#include "BasicTypes.h"
#include "CString.h"
#include <map>
class CScreenMap {
public:
enum EDirection { kLeft, kRight, kTop, kBottom,
kFirstDirection = kLeft, kLastDirection = kBottom };
CScreenMap();
virtual ~CScreenMap();
// manipulators
// add/remove screens
void addScreen(const CString& name);
void removeScreen(const CString& name);
void removeAllScreens();
// connect edges
void connect(const CString& srcName,
EDirection srcSide,
const CString& dstName);
void disconnect(const CString& srcName,
EDirection srcSide);
// accessors
// get the neighbor in the given direction. returns the empty string
// if there is no neighbor in that direction.
CString getNeighbor(const CString&, EDirection) const;
// get the name of a direction (for debugging)
static const char* dirName(EDirection);
private:
class CCell {
public:
CString m_neighbor[kLastDirection - kFirstDirection + 1];
};
typedef std::map<CString, CCell> CCellMap;
CCellMap m_map;
};
#endif

954
server/CServer.cpp Normal file
View File

@@ -0,0 +1,954 @@
#include "CServer.h"
#include "CInputPacketStream.h"
#include "COutputPacketStream.h"
#include "CServerProtocol.h"
#include "CProtocolUtil.h"
#include "IPrimaryScreen.h"
#include "ISocketFactory.h"
#include "ProtocolTypes.h"
#include "XSynergy.h"
#include "CNetworkAddress.h"
#include "ISocket.h"
#include "IListenSocket.h"
#include "XSocket.h"
#include "CLock.h"
#include "CThread.h"
#include "CTimerThread.h"
#include "CStopwatch.h"
#include "TMethodJob.h"
#include "CLog.h"
#include <assert.h>
#include <memory>
/* XXX
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
if (fork() == 0) abort();
else { wait(0); exit(1); }
*/
//
// CServer
//
CServer::CServer() : m_done(&m_mutex, false),
m_primary(NULL),
m_active(NULL),
m_primaryInfo(NULL)
{
m_socketFactory = NULL;
m_securityFactory = NULL;
m_bindTimeout = 5.0 * 60.0;
}
CServer::~CServer()
{
}
void CServer::run()
{
try {
log((CLOG_NOTE "starting server"));
// connect to primary screen
openPrimaryScreen();
// start listening for new clients
CThread(new TMethodJob<CServer>(this, &CServer::acceptClients));
// start listening for configuration connections
// FIXME
// wait until done
log((CLOG_DEBUG "waiting for quit"));
CLock lock(&m_mutex);
while (m_done == false) {
m_done.wait();
}
// clean up
log((CLOG_DEBUG "stopping server"));
closePrimaryScreen();
cleanupThreads();
}
catch (XBase& e) {
log((CLOG_ERR "server error: %s\n", e.what()));
// clean up
closePrimaryScreen();
cleanupThreads();
}
catch (...) {
log((CLOG_DEBUG "server shutdown"));
// clean up
closePrimaryScreen();
cleanupThreads();
throw;
}
}
void CServer::setScreenMap(const CScreenMap& screenMap)
{
CLock lock(&m_mutex);
// FIXME -- must disconnect screens no longer listed
// (that may include warping back to server's screen)
// FIXME -- server screen must be in new map or map is rejected
m_screenMap = screenMap;
}
void CServer::getScreenMap(CScreenMap* screenMap) const
{
assert(screenMap != NULL);
CLock lock(&m_mutex);
*screenMap = m_screenMap;
}
void CServer::setInfo(const CString& client,
SInt32 w, SInt32 h, SInt32 zoneSize)
{
CLock lock(&m_mutex);
// client must be connected
CScreenList::iterator index = m_screens.find(client);
if (index == m_screens.end()) {
throw XBadClient();
}
// update client info
CScreenInfo* info = index->second;
if (info == m_active) {
// FIXME -- ensure mouse is still on screen. warp it if necessary.
}
info->m_width = w;
info->m_height = h;
info->m_zoneSize = zoneSize;
log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize));
}
bool CServer::onCommandKey(KeyID /*id*/,
KeyModifierMask /*mask*/, bool /*down*/)
{
return false;
}
void CServer::onKeyDown(KeyID id, KeyModifierMask mask)
{
log((CLOG_DEBUG "onKeyDown id=%d mask=0x%04x", id, mask));
assert(m_active != NULL);
// handle command keys
if (onCommandKey(id, mask, true)) {
return;
}
// relay
if (m_active->m_protocol != NULL) {
m_active->m_protocol->sendKeyDown(id, mask);
}
}
void CServer::onKeyUp(KeyID id, KeyModifierMask mask)
{
log((CLOG_DEBUG "onKeyUp id=%d mask=0x%04x", id, mask));
assert(m_active != NULL);
// handle command keys
if (onCommandKey(id, mask, false)) {
return;
}
// relay
if (m_active->m_protocol != NULL) {
m_active->m_protocol->sendKeyUp(id, mask);
}
}
void CServer::onKeyRepeat(KeyID id, KeyModifierMask mask)
{
log((CLOG_DEBUG "onKeyRepeat id=%d mask=0x%04x", id, mask));
assert(m_active != NULL);
// handle command keys
if (onCommandKey(id, mask, false)) {
onCommandKey(id, mask, true);
return;
}
// relay
if (m_active->m_protocol != NULL) {
m_active->m_protocol->sendKeyRepeat(id, mask);
}
}
void CServer::onMouseDown(ButtonID id)
{
log((CLOG_DEBUG "onMouseDown id=%d", id));
assert(m_active != NULL);
// relay
if (m_active->m_protocol != NULL) {
m_active->m_protocol->sendMouseDown(id);
}
}
void CServer::onMouseUp(ButtonID id)
{
log((CLOG_DEBUG "onMouseUp id=%d", id));
assert(m_active != NULL);
// relay
if (m_active->m_protocol != NULL) {
m_active->m_protocol->sendMouseUp(id);
}
}
void CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
{
log((CLOG_DEBUG "onMouseMovePrimary %d,%d", x, y));
// mouse move on primary (server's) screen
assert(m_active != NULL);
assert(m_active->m_protocol == NULL);
// ignore if mouse is locked to screen
if (isLockedToScreen()) {
return;
}
// see if we should change screens
CScreenMap::EDirection dir;
if (x < m_active->m_zoneSize) {
x -= m_active->m_zoneSize;
dir = CScreenMap::kLeft;
log((CLOG_DEBUG "switch to left"));
}
else if (x >= m_active->m_width - m_active->m_zoneSize) {
x += m_active->m_zoneSize;
dir = CScreenMap::kRight;
log((CLOG_DEBUG "switch to right"));
}
else if (y < m_active->m_zoneSize) {
y -= m_active->m_zoneSize;
dir = CScreenMap::kTop;
log((CLOG_DEBUG "switch to top"));
}
else if (y >= m_active->m_height - m_active->m_zoneSize) {
y += m_active->m_zoneSize;
dir = CScreenMap::kBottom;
log((CLOG_DEBUG "switch to bottom"));
}
else {
// still on local screen
return;
}
// get jump destination
CScreenInfo* newScreen = getNeighbor(m_active, dir, x, y);
// if no screen in jump direction then ignore the move
if (newScreen == NULL) {
return;
}
// remap position to account for resolution differences
mapPosition(m_active, dir, newScreen, x, y);
// switch screen
switchScreen(newScreen, x, y);
}
void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
{
log((CLOG_DEBUG "onMouseMoveSecondary %+d,%+d", dx, dy));
// mouse move on secondary (client's) screen
assert(m_active != NULL);
assert(m_active->m_protocol != NULL);
// save old position
const SInt32 xOld = m_x;
const SInt32 yOld = m_y;
// accumulate motion
m_x += dx;
m_y += dy;
// switch screens if the mouse is outside the screen and not
// locked to the screen
CScreenInfo* newScreen = NULL;
if (!isLockedToScreen()) {
// find direction of neighbor
CScreenMap::EDirection dir;
if (m_x < 0)
dir = CScreenMap::kLeft;
else if (m_x > m_active->m_width - 1)
dir = CScreenMap::kRight;
else if (m_y < 0)
dir = CScreenMap::kTop;
else if (m_y > m_active->m_height - 1)
dir = CScreenMap::kBottom;
else
newScreen = m_active;
// get neighbor if we should switch
if (newScreen == NULL) {
log((CLOG_DEBUG "leave \"%s\" on %s", m_active->m_name.c_str(), CScreenMap::dirName(dir)));
SInt32 x = m_x, y = m_y;
newScreen = getNeighbor(m_active, dir, x, y);
// remap position to account for resolution differences
if (newScreen != NULL) {
mapPosition(m_active, dir, newScreen, x, y);
m_x = x;
m_y = y;
}
else {
log((CLOG_DEBUG "no neighbor; clamping"));
if (m_x < 0)
m_x = 0;
else if (m_x > m_active->m_width - 1)
m_x = m_active->m_width - 1;
if (m_y < 0)
m_y = 0;
else if (m_y > m_active->m_height - 1)
m_y = m_active->m_height - 1;
}
}
}
else {
// clamp to edge when locked
log((CLOG_DEBUG "clamp to \"%s\"", m_active->m_name.c_str()));
if (m_x < 0)
m_x = 0;
else if (m_x > m_active->m_width - 1)
m_x = m_active->m_width - 1;
if (m_y < 0)
m_y = 0;
else if (m_y > m_active->m_height - 1)
m_y = m_active->m_height - 1;
}
// warp cursor if on same screen
if (newScreen == NULL || newScreen == m_active) {
// do nothing if mouse didn't move
if (m_x != xOld || m_y != yOld) {
log((CLOG_DEBUG "move on %s to %d,%d", m_active->m_name.c_str(), m_x, m_y));
m_active->m_protocol->sendMouseMove(m_x, m_y);
}
}
// otherwise screen screens
else {
switchScreen(newScreen, m_x, m_y);
}
}
void CServer::onMouseWheel(SInt32 delta)
{
log((CLOG_DEBUG "onMouseWheel %+d", delta));
assert(m_active != NULL);
// relay
if (m_active->m_protocol != NULL) {
m_active->m_protocol->sendMouseWheel(delta);
}
}
bool CServer::isLockedToScreen() const
{
// FIXME
return false;
}
#include "CXWindowsClipboard.h" // FIXME
void CServer::switchScreen(CScreenInfo* dst,
SInt32 x, SInt32 y)
{
assert(dst != NULL);
assert(x >= 0 && y >= 0 && x < dst->m_width && y < dst->m_height);
assert(m_active != NULL);
log((CLOG_NOTE "switch from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), dst->m_name.c_str(), x, y));
// wrapping means leaving the active screen and entering it again.
// since that's a waste of time we skip that and just warp the
// mouse.
if (m_active != dst) {
// leave active screen
if (m_active->m_protocol == NULL) {
m_primary->leave();
// FIXME -- testing
CXWindowsClipboard clipboard;
m_primary->getClipboard(&clipboard);
}
else {
m_active->m_protocol->sendLeave();
}
// cut over
m_active = dst;
// enter new screen
if (m_active->m_protocol == NULL) {
m_primary->enter(x, y);
}
else {
m_active->m_protocol->sendEnter(x, y);
}
}
else {
if (m_active->m_protocol == NULL) {
m_primary->warpCursor(x, y);
}
else {
m_active->m_protocol->sendMouseMove(x, y);
}
}
// record new position
m_x = x;
m_y = y;
}
CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src,
CScreenMap::EDirection dir) const
{
assert(src != NULL);
CString srcName = src->m_name;
assert(!srcName.empty());
log((CLOG_DEBUG "find neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str()));
for (;;) {
// look up name of neighbor
const CString dstName(m_screenMap.getNeighbor(srcName, dir));
// if nothing in that direction then return NULL
if (dstName.empty()) {
log((CLOG_DEBUG "no neighbor on %s of \"%s\"", CScreenMap::dirName(dir), srcName.c_str()));
return NULL;
}
// look up neighbor cell. if the screen is connected then
// we can stop. otherwise we skip over an unconnected
// screen.
CScreenList::const_iterator index = m_screens.find(dstName);
if (index != m_screens.end()) {
log((CLOG_DEBUG "\"%s\" is on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str()));
return index->second;
}
log((CLOG_DEBUG "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CScreenMap::dirName(dir), srcName.c_str()));
srcName = dstName;
}
}
CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src,
CScreenMap::EDirection srcSide,
SInt32& x, SInt32& y) const
{
assert(src != NULL);
// get the first neighbor
CScreenInfo* lastGoodScreen = src;
CScreenInfo* dst = getNeighbor(src, srcSide);
// get the source screen's size (needed for kRight and kBottom)
SInt32 w = src->m_width, h = src->m_height;
// find destination screen, adjusting x or y (but not both)
switch (srcSide) {
case CScreenMap::kLeft:
while (dst != NULL) {
lastGoodScreen = dst;
w = lastGoodScreen->m_width;
h = lastGoodScreen->m_height;
x += w;
if (x >= 0) {
break;
}
log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str()));
dst = getNeighbor(lastGoodScreen, srcSide);
}
break;
case CScreenMap::kRight:
while (dst != NULL) {
lastGoodScreen = dst;
x -= w;
w = lastGoodScreen->m_width;
h = lastGoodScreen->m_height;
if (x < w) {
break;
}
log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str()));
dst = getNeighbor(lastGoodScreen, srcSide);
}
break;
case CScreenMap::kTop:
while (dst != NULL) {
lastGoodScreen = dst;
w = lastGoodScreen->m_width;
h = lastGoodScreen->m_height;
y += h;
if (y >= 0) {
break;
}
log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str()));
dst = getNeighbor(lastGoodScreen, srcSide);
}
break;
case CScreenMap::kBottom:
while (dst != NULL) {
lastGoodScreen = dst;
y -= h;
w = lastGoodScreen->m_width;
h = lastGoodScreen->m_height;
if (y < h) {
break;
}
log((CLOG_DEBUG "skipping over screen %s", dst->m_name.c_str()));
dst = getNeighbor(lastGoodScreen, srcSide);
}
break;
}
assert(lastGoodScreen != NULL);
// no neighbor if best neighbor is the source itself
if (lastGoodScreen == src)
return NULL;
// if entering primary screen then be sure to move in far enough
// to avoid the jump zone. if entering a side that doesn't have
// a neighbor (i.e. an asymmetrical side) then we don't need to
// move inwards because that side can't provoke a jump.
if (lastGoodScreen->m_protocol == NULL) {
const CString dstName(lastGoodScreen->m_name);
switch (srcSide) {
case CScreenMap::kLeft:
if (!m_screenMap.getNeighbor(dstName, CScreenMap::kRight).empty() &&
x > w - 1 - lastGoodScreen->m_zoneSize)
x = w - 1 - lastGoodScreen->m_zoneSize;
break;
case CScreenMap::kRight:
if (!m_screenMap.getNeighbor(dstName, CScreenMap::kLeft).empty() &&
x < lastGoodScreen->m_zoneSize)
x = lastGoodScreen->m_zoneSize;
break;
case CScreenMap::kTop:
if (!m_screenMap.getNeighbor(dstName, CScreenMap::kBottom).empty() &&
y > h - 1 - lastGoodScreen->m_zoneSize)
y = h - 1 - lastGoodScreen->m_zoneSize;
break;
case CScreenMap::kBottom:
if (!m_screenMap.getNeighbor(dstName, CScreenMap::kTop).empty() &&
y < lastGoodScreen->m_zoneSize)
y = lastGoodScreen->m_zoneSize;
break;
}
}
return lastGoodScreen;
}
void CServer::mapPosition(CScreenInfo* src,
CScreenMap::EDirection srcSide,
CScreenInfo* dst,
SInt32& x, SInt32& y) const
{
assert(src != NULL);
assert(dst != NULL);
assert(srcSide >= CScreenMap::kFirstDirection &&
srcSide <= CScreenMap::kLastDirection);
switch (srcSide) {
case CScreenMap::kLeft:
case CScreenMap::kRight:
if (y < 0)
y = 0;
else if (y >= src->m_height)
y = dst->m_height - 1;
else
y = static_cast<SInt32>(0.5 + y *
static_cast<double>(dst->m_height - 1) /
(src->m_height - 1));
break;
case CScreenMap::kTop:
case CScreenMap::kBottom:
if (x < 0)
x = 0;
else if (x >= src->m_width)
x = dst->m_width - 1;
else
x = static_cast<SInt32>(0.5 + x *
static_cast<double>(dst->m_width - 1) /
(src->m_width - 1));
break;
}
}
#include "CTCPListenSocket.h"
void CServer::acceptClients(void*)
{
log((CLOG_DEBUG "starting to wait for clients"));
// add this thread to the list of threads to cancel. remove from
// list in d'tor.
CCleanupNote cleanupNote(this);
std::auto_ptr<IListenSocket> listen;
try {
// create socket listener
// listen.reset(m_socketFactory->createListen());
listen.reset(new CTCPListenSocket); // FIXME
// bind to the desired port. keep retrying if we can't bind
// the address immediately.
CStopwatch timer;
CNetworkAddress addr(50001 /* FIXME -- m_port */);
for (;;) {
try {
log((CLOG_DEBUG "binding listen socket"));
listen->bind(addr);
break;
}
catch (XSocketAddressInUse&) {
// give up if we've waited too long
if (timer.getTime() >= m_bindTimeout) {
log((CLOG_DEBUG "waited too long to bind, giving up"));
throw;
}
// wait a bit before retrying
log((CLOG_DEBUG "bind failed; waiting to retry"));
CThread::sleep(5.0);
}
}
// accept connections and begin processing them
log((CLOG_DEBUG "waiting for client connections"));
for (;;) {
// accept connection
CThread::testCancel();
ISocket* socket = listen->accept();
log((CLOG_NOTE "accepted client connection"));
CThread::testCancel();
// start handshake thread
CThread(new TMethodJob<CServer>(
this, &CServer::handshakeClient, socket));
}
}
catch (XBase& e) {
log((CLOG_ERR "cannot listen for clients: %s", e.what()));
quit();
}
}
void CServer::handshakeClient(void* vsocket)
{
log((CLOG_DEBUG "negotiating with new client"));
// get the socket pointer from the argument
assert(vsocket != NULL);
std::auto_ptr<ISocket> socket(reinterpret_cast<ISocket*>(vsocket));
// add this thread to the list of threads to cancel. remove from
// list in d'tor.
CCleanupNote cleanupNote(this);
CString name("<unknown>");
try {
// get the input and output streams
IInputStream* srcInput = socket->getInputStream();
IOutputStream* srcOutput = socket->getOutputStream();
std::auto_ptr<IInputStream> input;
std::auto_ptr<IOutputStream> output;
// attach the encryption layer
bool own = false;
if (m_securityFactory != NULL) {
/* FIXME -- implement ISecurityFactory
input.reset(m_securityFactory->createInputFilter(srcInput, own));
output.reset(m_securityFactory->createOutputFilter(srcOutput, own));
srcInput = input.get();
srcOutput = output.get();
own = true;
*/
}
// attach the packetizing filters
input.reset(new CInputPacketStream(srcInput, own));
output.reset(new COutputPacketStream(srcOutput, own));
std::auto_ptr<IServerProtocol> protocol;
std::auto_ptr<CConnectionNote> connectedNote;
{
// give the client a limited time to complete the handshake
CTimerThread timer(30.0);
// limit the maximum length of the hello
static const UInt32 maxHelloLen = 1024;
// say hello
log((CLOG_DEBUG "saying hello"));
CProtocolUtil::writef(output.get(), "Synergy%2i%2i",
kMajorVersion, kMinorVersion);
output->flush();
// wait for the reply
log((CLOG_DEBUG "waiting for hello reply"));
UInt32 n = input->getSize();
if (n > maxHelloLen) {
throw XBadClient();
}
// get and parse the reply to hello
SInt32 major, minor;
try {
log((CLOG_DEBUG "parsing hello reply"));
CProtocolUtil::readf(input.get(), "Synergy%2i%2i%s",
&major, &minor, &name);
}
catch (XIO&) {
throw XBadClient();
}
if (major < 0 || minor < 0) {
throw XBadClient();
}
// create a protocol interpreter for the version
log((CLOG_DEBUG "creating interpreter for client %s version %d.%d", name.c_str(), major, minor));
protocol.reset(CServerProtocol::create(major, minor,
this, name, input.get(), output.get()));
// client is now pending
connectedNote.reset(new CConnectionNote(this,
name, protocol.get()));
// ask and wait for the client's info
log((CLOG_DEBUG "waiting for info for client %s", name.c_str()));
protocol->queryInfo();
}
// handle messages from client. returns when the client
// disconnects.
log((CLOG_NOTE "client %s is connected", name.c_str()));
protocol->run();
}
catch (XIncompatibleClient& e) {
// client is incompatible
log((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
// FIXME -- could print network address if socket had suitable method
}
catch (XBadClient&) {
// client not behaving
log((CLOG_WARN "protocol error from client \"%s\"", name.c_str()));
// FIXME -- could print network address if socket had suitable method
}
catch (XBase& e) {
// misc error
log((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what()));
// FIXME -- could print network address if socket had suitable method
}
}
void CServer::quit()
{
CLock lock(&m_mutex);
m_done = true;
m_done.broadcast();
}
// FIXME -- use factory to create screen
#include "CXWindowsPrimaryScreen.h"
void CServer::openPrimaryScreen()
{
assert(m_primary == NULL);
// open screen
log((CLOG_DEBUG "creating primary screen"));
m_primary = new CXWindowsPrimaryScreen;
log((CLOG_DEBUG "opening primary screen"));
m_primary->open(this);
// add connection
m_active = addConnection(CString("primary"/* FIXME */), NULL);
m_primaryInfo = m_active;
// update info
m_primary->getSize(&m_active->m_width, &m_active->m_height);
m_active->m_zoneSize = m_primary->getJumpZoneSize();
log((CLOG_NOTE "server size=%dx%d zone=%d", m_active->m_width, m_active->m_height, m_active->m_zoneSize));
// FIXME -- need way for primary screen to call us back
}
void CServer::closePrimaryScreen()
{
assert(m_primary != NULL);
// remove connection
removeConnection(CString("primary"/* FIXME */));
// close the primary screen
try {
log((CLOG_DEBUG "closing primary screen"));
m_primary->close();
}
catch (...) {
// ignore
}
// clean up
log((CLOG_DEBUG "destroying primary screen"));
delete m_primary;
m_primary = NULL;
}
void CServer::addCleanupThread(const CThread& thread)
{
CLock lock(&m_mutex);
m_cleanupList.insert(m_cleanupList.begin(), new CThread(thread));
}
void CServer::removeCleanupThread(const CThread& thread)
{
CLock lock(&m_mutex);
for (CThreadList::iterator index = m_cleanupList.begin();
index != m_cleanupList.end(); ++index) {
if (**index == thread) {
m_cleanupList.erase(index);
delete *index;
return;
}
}
}
void CServer::cleanupThreads()
{
log((CLOG_DEBUG "cleaning up threads"));
m_mutex.lock();
while (m_cleanupList.begin() != m_cleanupList.end()) {
// get the next thread and cancel it
CThread* thread = m_cleanupList.front();
thread->cancel();
// wait for thread to finish with cleanup list unlocked. the
// thread will remove itself from the cleanup list.
m_mutex.unlock();
thread->wait();
m_mutex.lock();
}
// FIXME -- delete remaining threads from list
m_mutex.unlock();
log((CLOG_DEBUG "cleaned up threads"));
}
CServer::CScreenInfo* CServer::addConnection(
const CString& name, IServerProtocol* protocol)
{
log((CLOG_DEBUG "adding connection \"%s\"", name.c_str()));
CLock lock(&m_mutex);
assert(m_screens.count(name) == 0);
CScreenInfo* newScreen = new CScreenInfo(name, protocol);
m_screens.insert(std::make_pair(name, newScreen));
return newScreen;
}
void CServer::removeConnection(const CString& name)
{
log((CLOG_DEBUG "removing connection \"%s\"", name.c_str()));
CLock lock(&m_mutex);
// find screen info
CScreenList::iterator index = m_screens.find(name);
assert(index != m_screens.end());
// if this is active screen then we have to jump off of it
if (m_active == index->second) {
// record new position (center of primary screen)
m_x = m_primaryInfo->m_width >> 1;
m_y = m_primaryInfo->m_height >> 1;
// don't notify active screen since it probably already disconnected
log((CLOG_NOTE "jump from \"%s\" to \"%s\" at %d,%d", m_active->m_name.c_str(), m_primaryInfo->m_name.c_str(), m_x, m_y));
// cut over
m_active = m_primaryInfo;
// enter new screen
m_primary->enter(m_x, m_y);
}
// done with screen info
delete index->second;
m_screens.erase(index);
}
//
// CServer::CCleanupNote
//
CServer::CCleanupNote::CCleanupNote(CServer* server) : m_server(server)
{
assert(m_server != NULL);
m_server->addCleanupThread(CThread::getCurrentThread());
}
CServer::CCleanupNote::~CCleanupNote()
{
m_server->removeCleanupThread(CThread::getCurrentThread());
}
//
// CServer::CConnectionNote
//
CServer::CConnectionNote::CConnectionNote(CServer* server,
const CString& name,
IServerProtocol* protocol) :
m_server(server),
m_name(name)
{
assert(m_server != NULL);
m_server->addConnection(m_name, protocol);
}
CServer::CConnectionNote::~CConnectionNote()
{
m_server->removeConnection(m_name);
}
//
// CServer::CScreenInfo
//
CServer::CScreenInfo::CScreenInfo(const CString& name,
IServerProtocol* protocol) :
m_name(name),
m_protocol(protocol),
m_width(0), m_height(0),
m_zoneSize(0)
{
// do nothing
}
CServer::CScreenInfo::~CScreenInfo()
{
// do nothing
}

159
server/CServer.h Normal file
View File

@@ -0,0 +1,159 @@
#ifndef CSERVER_H
#define CSERVER_H
#include "KeyTypes.h"
#include "MouseTypes.h"
#include "CScreenMap.h"
#include "CCondVar.h"
#include "CMutex.h"
#include "CString.h"
#include "XBase.h"
#include <list>
#include <map>
class CThread;
class IServerProtocol;
class ISocketFactory;
class ISecurityFactory;
class IPrimaryScreen;
class CServer {
public:
CServer();
~CServer();
// manipulators
void run();
// update screen map
void setScreenMap(const CScreenMap&);
// handle events on server's screen
void onKeyDown(KeyID, KeyModifierMask);
void onKeyUp(KeyID, KeyModifierMask);
void onKeyRepeat(KeyID, KeyModifierMask);
void onMouseDown(ButtonID);
void onMouseUp(ButtonID);
void onMouseMovePrimary(SInt32 x, SInt32 y);
void onMouseMoveSecondary(SInt32 dx, SInt32 dy);
void onMouseWheel(SInt32 delta);
// handle messages from clients
void setInfo(const CString& clientName,
SInt32 w, SInt32 h, SInt32 zoneSize);
// accessors
bool isLockedToScreen() const;
// get the current screen map
void getScreenMap(CScreenMap*) const;
protected:
bool onCommandKey(KeyID, KeyModifierMask, bool down);
void quit();
private:
class CCleanupNote {
public:
CCleanupNote(CServer*);
~CCleanupNote();
private:
CServer* m_server;
};
class CConnectionNote {
public:
CConnectionNote(CServer*, const CString&, IServerProtocol*);
~CConnectionNote();
private:
bool m_pending;
CServer* m_server;
CString m_name;
};
class CScreenInfo {
public:
CScreenInfo(const CString& name, IServerProtocol*);
~CScreenInfo();
public:
CString m_name;
IServerProtocol* m_protocol;
SInt32 m_width, m_height;
SInt32 m_zoneSize;
};
// change the active screen
void switchScreen(CScreenInfo*, SInt32 x, SInt32 y);
// lookup neighboring screen
CScreenInfo* getNeighbor(CScreenInfo*, CScreenMap::EDirection) const;
// lookup neighboring screen. given a position relative to the
// source screen, find the screen we should move onto and where.
// if the position is sufficiently far from the source then we
// cross multiple screens.
CScreenInfo* getNeighbor(CScreenInfo*,
CScreenMap::EDirection,
SInt32& x, SInt32& y) const;
// adjust coordinates to account for resolution differences. the
// position is converted to a resolution independent form then
// converted back to screen coordinates on the destination screen.
void mapPosition(CScreenInfo* src,
CScreenMap::EDirection srcSide,
CScreenInfo* dst,
SInt32& x, SInt32& y) const;
// open/close the primary screen
void openPrimaryScreen();
void closePrimaryScreen();
// cancel running threads
void cleanupThreads();
// thread method to accept incoming client connections
void acceptClients(void*);
// thread method to do startup handshake with client
void handshakeClient(void*);
// thread cleanup list maintenance
friend class CCleanupNote;
void addCleanupThread(const CThread& thread);
void removeCleanupThread(const CThread& thread);
// connection list maintenance
friend class CConnectionNote;
CScreenInfo* addConnection(const CString& name, IServerProtocol*);
void removeConnection(const CString& name);
private:
typedef std::list<CThread*> CThreadList;
typedef std::map<CString, CScreenInfo*> CScreenList;
CMutex m_mutex;
CCondVar<bool> m_done;
double m_bindTimeout;
ISocketFactory* m_socketFactory;
ISecurityFactory* m_securityFactory;
CThreadList m_cleanupList;
IPrimaryScreen* m_primary;
CScreenList m_screens;
CScreenInfo* m_active;
CScreenInfo* m_primaryInfo;
SInt32 m_x, m_y;
CScreenMap m_screenMap;
};
#endif

View File

@@ -0,0 +1,72 @@
#include "CServerProtocol.h"
#include "CServerProtocol1_0.h"
#include "ProtocolTypes.h"
#include "IOutputStream.h"
#include <stdio.h>
#include <assert.h>
//
// CServerProtocol
//
CServerProtocol::CServerProtocol(CServer* server, const CString& client,
IInputStream* input, IOutputStream* output) :
m_server(server),
m_client(client),
m_input(input),
m_output(output)
{
assert(m_server != NULL);
assert(m_input != NULL);
assert(m_output != NULL);
}
CServerProtocol::~CServerProtocol()
{
// do nothing
}
CServer* CServerProtocol::getServer() const
{
return m_server;
}
CString CServerProtocol::getClient() const
{
return m_client;
}
IInputStream* CServerProtocol::getInputStream() const
{
return m_input;
}
IOutputStream* CServerProtocol::getOutputStream() const
{
return m_output;
}
IServerProtocol* CServerProtocol::create(SInt32 major, SInt32 minor,
CServer* server, const CString& client,
IInputStream* input, IOutputStream* output)
{
// disallow connection from test versions to release versions
if (major == 0 && kMajorVersion != 0) {
output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1);
output->flush();
throw XIncompatibleClient(major, minor);
}
// hangup (with error) if version isn't supported
if (major > kMajorVersion ||
(major == kMajorVersion && minor > kMinorVersion)) {
output->write(kMsgEIncompatible, sizeof(kMsgEIncompatible) - 1);
output->flush();
throw XIncompatibleClient(major, minor);
}
// create highest version protocol object not higher than the
// given version.
return new CServerProtocol1_0(server, client, input, output);
}

58
server/CServerProtocol.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef CSERVERPROTOCOL_H
#define CSERVERPROTOCOL_H
#include "CString.h"
#include "IServerProtocol.h"
class CServer;
class IInputStream;
class IOutputStream;
class CServerProtocol : public IServerProtocol {
public:
CServerProtocol(CServer*, const CString& clientName,
IInputStream*, IOutputStream*);
~CServerProtocol();
// manipulators
// accessors
virtual CServer* getServer() const;
virtual CString getClient() const;
virtual IInputStream* getInputStream() const;
virtual IOutputStream* getOutputStream() const;
static IServerProtocol* create(SInt32 major, SInt32 minor,
CServer*, const CString& clientName,
IInputStream*, IOutputStream*);
// IServerProtocol overrides
virtual void run() = 0;
virtual void queryInfo() = 0;
virtual void sendClose() = 0;
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0;
virtual void sendLeave() = 0;
virtual void sendGrabClipboard() = 0;
virtual void sendQueryClipboard() = 0;
virtual void sendScreenSaver(bool on) = 0;
virtual void sendKeyDown(KeyID, KeyModifierMask) = 0;
virtual void sendKeyRepeat(KeyID, KeyModifierMask) = 0;
virtual void sendKeyUp(KeyID, KeyModifierMask) = 0;
virtual void sendMouseDown(ButtonID) = 0;
virtual void sendMouseUp(ButtonID) = 0;
virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs) = 0;
virtual void sendMouseWheel(SInt32 delta) = 0;
protected:
//IServerProtocol overrides
virtual void recvInfo() = 0;
private:
CServer* m_server;
CString m_client;
IInputStream* m_input;
IOutputStream* m_output;
};
#endif

View File

@@ -0,0 +1,181 @@
#include "CServerProtocol1_0.h"
#include "CServer.h"
#include "CProtocolUtil.h"
#include "ProtocolTypes.h"
#include "IInputStream.h"
#include "CLog.h"
#include <string.h>
//
// CServerProtocol1_0
//
CServerProtocol1_0::CServerProtocol1_0(CServer* server, const CString& client,
IInputStream* input, IOutputStream* output) :
CServerProtocol(server, client, input, output)
{
// do nothing
}
CServerProtocol1_0::~CServerProtocol1_0()
{
// do nothing
}
void CServerProtocol1_0::run()
{
// handle messages until the client hangs up
for (;;) {
// wait for a message
UInt8 code[4];
UInt32 n = getInputStream()->read(code, 4);
// verify we got an entire code
if (n == 0) {
log((CLOG_NOTE "client \"%s\" disconnected", getClient().c_str()));
// client hungup
return;
}
if (n != 4) {
log((CLOG_ERR "incomplete message from \"%s\": %d bytes", getClient().c_str(), n));
// client sent an incomplete message
throw XBadClient();
}
// parse message
log((CLOG_DEBUG "msg from \"%s\": %c%c%c%c", getClient().c_str(), code[0], code[1], code[2], code[3]));
if (memcmp(code, kMsgDInfo, 4) == 0) {
recvInfo();
}
// FIXME -- more message here
else {
log((CLOG_ERR "unknown message from client \"%s\"", getClient().c_str()));
// unknown message
throw XBadClient();
}
}
}
void CServerProtocol1_0::queryInfo()
{
log((CLOG_INFO "querying client \"%s\" info", getClient().c_str()));
// send request
CProtocolUtil::writef(getOutputStream(), kMsgQInfo);
// wait for and verify reply
UInt8 code[4];
UInt32 n = getInputStream()->read(code, 4);
if (n != 4 && memcmp(code, kMsgDInfo, 4) != 0) {
throw XBadClient();
}
// handle reply
recvInfo();
}
void CServerProtocol1_0::sendClose()
{
log((CLOG_INFO "send close to \"%s\"", getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCClose);
}
void CServerProtocol1_0::sendEnter(
SInt32 xAbs, SInt32 yAbs)
{
log((CLOG_INFO "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs));
CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs);
}
void CServerProtocol1_0::sendLeave()
{
log((CLOG_INFO "send leave to \"%s\"", getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCLeave);
}
void CServerProtocol1_0::sendGrabClipboard()
{
log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCClipboard);
}
void CServerProtocol1_0::sendQueryClipboard()
{
log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgQClipboard);
}
void CServerProtocol1_0::sendScreenSaver(bool on)
{
log((CLOG_INFO "send screen saver to \"%s\"", getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0);
}
void CServerProtocol1_0::sendKeyDown(
KeyID key, KeyModifierMask mask)
{
log((CLOG_INFO "send key down to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyDown, key, mask);
}
void CServerProtocol1_0::sendKeyRepeat(
KeyID key, KeyModifierMask mask)
{
log((CLOG_INFO "send key repeat to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyRepeat, key, mask);
}
void CServerProtocol1_0::sendKeyUp(
KeyID key, KeyModifierMask mask)
{
log((CLOG_INFO "send key up to \"%s\" id=%d, mask=0x%04x", getClient().c_str(), key, mask));
CProtocolUtil::writef(getOutputStream(), kMsgDKeyUp, key, mask);
}
void CServerProtocol1_0::sendMouseDown(
ButtonID button)
{
log((CLOG_INFO "send mouse down to \"%s\" id=%d", getClient().c_str(), button));
CProtocolUtil::writef(getOutputStream(), kMsgDMouseDown, button);
}
void CServerProtocol1_0::sendMouseUp(
ButtonID button)
{
log((CLOG_INFO "send mouse up to \"%s\" id=%d", getClient().c_str(), button));
CProtocolUtil::writef(getOutputStream(), kMsgDMouseUp, button);
}
void CServerProtocol1_0::sendMouseMove(
SInt32 xAbs, SInt32 yAbs)
{
log((CLOG_INFO "send mouse move to \"%s\" %d,%d", getClient().c_str(), xAbs, yAbs));
CProtocolUtil::writef(getOutputStream(), kMsgDMouseMove, xAbs, yAbs);
}
void CServerProtocol1_0::sendMouseWheel(
SInt32 delta)
{
log((CLOG_INFO "send mouse wheel to \"%s\" %+d", getClient().c_str(), delta));
CProtocolUtil::writef(getOutputStream(), kMsgDMouseWheel, delta);
}
void CServerProtocol1_0::recvInfo()
{
// parse the message
SInt32 w, h, zoneInfo;
CProtocolUtil::readf(getInputStream(), kMsgDInfo + 4, &w, &h, &zoneInfo);
log((CLOG_INFO "received client \"%s\" info size=%dx%d, zone=%d", getClient().c_str(), w, h, zoneInfo));
// validate
if (w == 0 || h == 0) {
throw XBadClient();
}
// tell server of change
getServer()->setInfo(getClient(), w, h, zoneInfo);
}

View File

@@ -0,0 +1,37 @@
#ifndef CSERVERPROTOCOL1_0_H
#define CSERVERPROTOCOL1_0_H
#include "CServerProtocol.h"
class CServerProtocol1_0 : public CServerProtocol {
public:
CServerProtocol1_0(CServer*, const CString&, IInputStream*, IOutputStream*);
~CServerProtocol1_0();
// manipulators
// accessors
// IServerProtocol overrides
virtual void run();
virtual void queryInfo();
virtual void sendClose();
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs);
virtual void sendLeave();
virtual void sendGrabClipboard();
virtual void sendQueryClipboard();
virtual void sendScreenSaver(bool on);
virtual void sendKeyDown(KeyID, KeyModifierMask);
virtual void sendKeyRepeat(KeyID, KeyModifierMask);
virtual void sendKeyUp(KeyID, KeyModifierMask);
virtual void sendMouseDown(ButtonID);
virtual void sendMouseUp(ButtonID);
virtual void sendMouseMove(SInt32 xAbs, SInt32 yAbs);
virtual void sendMouseWheel(SInt32 delta);
protected:
// IServerProtocol overrides
virtual void recvInfo();
};
#endif

View File

@@ -0,0 +1,406 @@
#include "CXWindowsPrimaryScreen.h"
#include "CServer.h"
#include "CThread.h"
#include "CLog.h"
#include <assert.h>
#include <X11/X.h>
//
// CXWindowsPrimaryScreen
//
CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() :
m_server(NULL),
m_active(false),
m_window(None)
{
// do nothing
}
CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen()
{
assert(m_window == None);
}
void CXWindowsPrimaryScreen::open(CServer* server)
{
assert(m_server == NULL);
assert(server != NULL);
// set the server
m_server = server;
// open the display
openDisplay();
}
void CXWindowsPrimaryScreen::close()
{
assert(m_server != NULL);
// close the display
closeDisplay();
// done with server
m_server = NULL;
}
void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
{
log((CLOG_INFO "entering primary at %d,%d", x, y));
assert(m_active == true);
assert(m_window != None);
CDisplayLock display(this);
// warp to requested location
XWarpPointer(display, None, m_window, 0, 0, 0, 0, x, y);
// unmap the grab window. this also ungrabs the mouse and keyboard.
XUnmapWindow(display, m_window);
// remove all input events for grab window
XEvent event;
while (XCheckWindowEvent(display, m_window,
PointerMotionMask |
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask |
KeymapStateMask,
&event)) {
// do nothing
}
// not active anymore
m_active = false;
}
void CXWindowsPrimaryScreen::leave()
{
log((CLOG_INFO "leaving primary"));
assert(m_active == false);
assert(m_window != None);
CDisplayLock display(this);
// raise and show the input window
XMapRaised(display, m_window);
// grab the mouse and keyboard. keep trying until we get them.
// if we can't grab one after grabbing the other then ungrab
// and wait before retrying.
int result;
do {
// mouse first
do {
result = XGrabPointer(display, m_window, True, 0,
GrabModeAsync, GrabModeAsync,
m_window, None, CurrentTime);
assert(result != GrabNotViewable);
if (result != GrabSuccess) {
log((CLOG_DEBUG "waiting to grab pointer"));
CThread::sleep(0.25);
}
} while (result != GrabSuccess);
log((CLOG_DEBUG "grabbed pointer"));
// now the keyboard
result = XGrabKeyboard(display, m_window, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
assert(result != GrabNotViewable);
if (result != GrabSuccess) {
// back off to avoid grab deadlock
XUngrabPointer(display, CurrentTime);
log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard"));
CThread::sleep(0.25);
}
} while (result != GrabSuccess);
log((CLOG_DEBUG "grabbed keyboard"));
// move the mouse to the center of grab window
SInt32 w, h;
getScreenSize(&w, &h);
warpCursorNoLock(display, w >> 1, h >> 1);
// local client now active
m_active = true;
}
void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
{
CDisplayLock display(this);
warpCursorNoLock(display, x, y);
}
void CXWindowsPrimaryScreen::warpCursorNoLock(
Display* display, SInt32 x, SInt32 y)
{
assert(display != NULL);
assert(m_window != None);
// warp the mouse
XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y);
XSync(display, False);
log((CLOG_DEBUG "warped to %d,%d", x, y));
// discard mouse events since we just added one we don't want
XEvent xevent;
while (XCheckWindowEvent(display, m_window,
PointerMotionMask, &xevent)) {
// do nothing
}
}
#include <X11/Xatom.h> // FIXME
void CXWindowsPrimaryScreen::setClipboard(
const IClipboard* /*clipboard*/)
{
// FIXME -- put this in superclass?
// FIXME -- don't use CurrentTime
CDisplayLock display(this);
XSetSelectionOwner(display, XA_PRIMARY, m_window, CurrentTime);
if (XGetSelectionOwner(display, XA_PRIMARY) == m_window) {
// we got the selection
log((CLOG_DEBUG "grabbed clipboard"));
}
// FIXME -- need to copy or adopt the clipboard to serve future requests
}
void CXWindowsPrimaryScreen::getSize(
SInt32* width, SInt32* height) const
{
getScreenSize(width, height);
}
SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const
{
return 1;
}
void CXWindowsPrimaryScreen::getClipboard(
IClipboard* clipboard) const
{
// FIXME -- put this in superclass?
// FIXME -- don't use CurrentTime
getDisplayClipboard(clipboard, m_window, CurrentTime);
}
void CXWindowsPrimaryScreen::onOpenDisplay()
{
assert(m_window == None);
CDisplayLock display(this);
// get size of screen
SInt32 w, h;
getScreenSize(&w, &h);
// create the grab window. this window is used to capture user
// input when the user is focussed on another client. don't let
// the window manager mess with it.
XSetWindowAttributes attr;
attr.event_mask = PointerMotionMask |// PointerMotionHintMask |
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask |
KeymapStateMask;
attr.do_not_propagate_mask = 0;
attr.override_redirect = True;
attr.cursor = createBlankCursor();
m_window = XCreateWindow(display, getRoot(), 0, 0, w, h, 0, 0,
InputOnly, CopyFromParent,
CWDontPropagate | CWEventMask |
CWOverrideRedirect | CWCursor,
&attr);
// start watching for events on other windows
selectEvents(display, getRoot());
}
void CXWindowsPrimaryScreen::onCloseDisplay()
{
assert(m_window != None);
// destroy window
CDisplayLock display(this);
XDestroyWindow(display, m_window);
m_window = None;
}
void CXWindowsPrimaryScreen::selectEvents(
Display* display, Window w) const
{
// we want to track the mouse everywhere on the display. to achieve
// that we select PointerMotionMask on every window. we also select
// SubstructureNotifyMask in order to get CreateNotify events so we
// select events on new windows too.
// we don't want to adjust our grab window
if (w == m_window)
return;
// select events of interest
XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask |
PropertyChangeMask);
// recurse on child windows
Window rw, pw, *cw;
unsigned int nc;
if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) {
for (unsigned int i = 0; i < nc; ++i)
selectEvents(display, cw[i]);
XFree(cw);
}
}
void CXWindowsPrimaryScreen::eventThread(void*)
{
for (;;) {
// wait for and get the next event
XEvent xevent;
getEvent(&xevent);
// handle event
switch (xevent.type) {
case CreateNotify: {
// select events on new window
CDisplayLock display(this);
selectEvents(display, xevent.xcreatewindow.window);
break;
}
case KeyPress: {
log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(xevent.xkey.keycode, mask);
if (key != kKeyNone) {
m_server->onKeyDown(key, mask);
}
break;
}
// FIXME -- simulate key repeat. X sends press/release for
// repeat. must detect auto repeat and use kKeyRepeat.
case KeyRelease: {
log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(xevent.xkey.keycode, mask);
if (key != kKeyNone) {
m_server->onKeyUp(key, mask);
}
break;
}
case ButtonPress: {
log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button));
const ButtonID button = mapButton(xevent.xbutton.button);
if (button != kButtonNone) {
m_server->onMouseDown(button);
}
break;
}
case ButtonRelease: {
log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button));
const ButtonID button = mapButton(xevent.xbutton.button);
if (button != kButtonNone) {
m_server->onMouseUp(button);
}
break;
}
case MotionNotify: {
log((CLOG_DEBUG "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root));
SInt32 x, y;
if (!m_active) {
x = xevent.xmotion.x_root;
y = xevent.xmotion.y_root;
m_server->onMouseMovePrimary(x, y);
}
else {
// FIXME -- slurp up all remaining motion events?
// probably not since key strokes may go to wrong place.
// get mouse deltas
{
CDisplayLock display(this);
Window root, window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int mask;
if (!XQueryPointer(display, m_window, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &mask))
break;
// compute position of center of window
SInt32 w, h;
getScreenSize(&w, &h);
x = xRoot - (w >> 1);
y = yRoot - (h >> 1);
// warp mouse back to center
warpCursorNoLock(display, w >> 1, h >> 1);
}
m_server->onMouseMoveSecondary(x, y);
}
break;
}
/*
case SelectionClear:
target->XXX(xevent.xselectionclear.);
break;
case SelectionNotify:
target->XXX(xevent.xselection.);
break;
case SelectionRequest:
target->XXX(xevent.xselectionrequest.);
break;
*/
}
}
}
KeyModifierMask CXWindowsPrimaryScreen::mapModifier(
unsigned int state) const
{
// FIXME -- should be configurable
KeyModifierMask mask = 0;
if (state & 1)
mask |= KeyModifierShift;
if (state & 2)
mask |= KeyModifierCapsLock;
if (state & 4)
mask |= KeyModifierControl;
if (state & 8)
mask |= KeyModifierAlt;
if (state & 16)
mask |= KeyModifierNumLock;
if (state & 32)
mask |= KeyModifierMeta;
if (state & 128)
mask |= KeyModifierScrollLock;
return mask;
}
KeyID CXWindowsPrimaryScreen::mapKey(
KeyCode keycode, KeyModifierMask mask) const
{
int index;
if (mask & KeyModifierShift)
index = 1;
else
index = 0;
CDisplayLock display(this);
return static_cast<KeyID>(XKeycodeToKeysym(display, keycode, index));
}
ButtonID CXWindowsPrimaryScreen::mapButton(
unsigned int button) const
{
// FIXME -- should use button mapping?
if (button >= 1 && button <= 3)
return static_cast<ButtonID>(button);
else
return kButtonNone;
}

View File

@@ -0,0 +1,46 @@
#ifndef CXWINDOWSPRIMARYSCREEN_H
#define CXWINDOWSPRIMARYSCREEN_H
#include "KeyTypes.h"
#include "MouseTypes.h"
#include "CXWindowsScreen.h"
#include "IPrimaryScreen.h"
class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen {
public:
CXWindowsPrimaryScreen();
virtual ~CXWindowsPrimaryScreen();
// IPrimaryScreen overrides
virtual void open(CServer*);
virtual void close();
virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute);
virtual void leave();
virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute);
virtual void setClipboard(const IClipboard*);
virtual void getSize(SInt32* width, SInt32* height) const;
virtual SInt32 getJumpZoneSize() const;
virtual void getClipboard(IClipboard*) const;
protected:
// CXWindowsScreen overrides
virtual void onOpenDisplay();
virtual void onCloseDisplay();
virtual void eventThread(void*);
private:
void selectEvents(Display*, Window) const;
void warpCursorNoLock(Display*,
SInt32 xAbsolute, SInt32 yAbsolute);
KeyModifierMask mapModifier(unsigned int state) const;
KeyID mapKey(KeyCode, KeyModifierMask) const;
ButtonID mapButton(unsigned int button) const;
private:
CServer* m_server;
bool m_active;
Window m_window;
};
#endif

47
server/Makefile Normal file
View File

@@ -0,0 +1,47 @@
DEPTH=..
include $(DEPTH)/Makecommon
#
# target file
#
TARGET = server
#
# source files
#
LCXXINCS = \
-I$(DEPTH)/base \
-I$(DEPTH)/mt \
-I$(DEPTH)/io \
-I$(DEPTH)/net \
-I$(DEPTH)/synergy \
$(NULL)
CXXFILES = \
CScreenMap.cpp \
CServerProtocol.cpp \
CServerProtocol1_0.cpp \
CXWindowsPrimaryScreen.cpp \
CServer.cpp \
server.cpp \
$(NULL)
#
# libraries we depend on
#
DEPLIBS = \
$(LIBDIR)/libsynergy.a \
$(LIBDIR)/libnet.a \
$(LIBDIR)/libio.a \
$(LIBDIR)/libmt.a \
$(LIBDIR)/libbase.a \
$(NULL)
LLDLIBS = \
$(DEPLIBS) \
-lpthread \
$(NULL)
targets: $(TARGET)
$(TARGET): $(OBJECTS) $(DEPLIBS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS)

32
server/server.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "CServer.h"
#include "CScreenMap.h"
#include "CThread.h"
#include <stdio.h>
int main(int argc, char** argv)
{
CThread::init();
if (argc != 1) {
fprintf(stderr, "usage: %s\n", argv[0]);
return 1;
}
CScreenMap screenMap;
screenMap.addScreen("primary");
screenMap.addScreen("ingrid");
screenMap.connect("primary", CScreenMap::kRight, "ingrid");
screenMap.connect("ingrid", CScreenMap::kLeft, "primary");
try {
CServer* server = new CServer();
server->setScreenMap(screenMap);
server->run();
}
catch (XBase& e) {
fprintf(stderr, "failed: %s\n", e.what());
return 1;
}
return 0;
}