added mutex to all public methods that didn't already have it.

fixed two blown assertions.  first, if user tried to switch to
a client that had connected but hadn't yet sent the first info
message it would assert on the zero size screen.  second, if
the primary screen was handling a mouse motion on behalf of a
secondary screen when that secondary screen disconnected then
an assert would blow because the primary screen would call
onMouseMoveSecondary() but m_protocol on the active screen is
NULL because disconnecting the active secondary screen caused
the mouse to jump to the primary screen.
This commit is contained in:
crs 2002-06-01 10:52:02 +00:00
parent 3a80df28dd
commit 1ac62a9533
3 changed files with 116 additions and 28 deletions

View File

@ -125,15 +125,24 @@ bool CServer::setConfig(const CConfig& config)
// get the set of screens that are connected but are being // get the set of screens that are connected but are being
// dropped from the configuration. don't add the primary // dropped from the configuration. don't add the primary
// screen. // screen. also tell the secondary screen to disconnect.
for (CScreenList::const_iterator index = m_screens.begin(); for (CScreenList::const_iterator index = m_screens.begin();
index != m_screens.end(); ++index) { index != m_screens.end(); ++index) {
if (!config.isScreen(index->first) && if (!config.isScreen(index->first) &&
index->second != m_primaryInfo) { index->second != m_primaryInfo) {
assert(index->second->m_protocol != NULL);
index->second->m_protocol->sendClose();
threads.push_back(index->second->m_thread); threads.push_back(index->second->m_thread);
} }
} }
// wait a moment to allow each secondary screen to close
// its connection before we close it (to avoid having our
// socket enter TIME_WAIT).
if (threads.size() > 0) {
CThread::sleep(1.0);
}
// cancel the old secondary screen threads // cancel the old secondary screen threads
for (CThreads::iterator index = threads.begin(); for (CThreads::iterator index = threads.begin();
index != threads.end(); ++index) { index != threads.end(); ++index) {
@ -196,28 +205,39 @@ void CServer::setInfo(
SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 w, SInt32 h, SInt32 zoneSize,
SInt32 x, SInt32 y) SInt32 x, SInt32 y)
{ {
setInfo(m_primaryInfo->m_name, w, h, zoneSize, x, y); CLock lock(&m_mutex);
assert(m_primaryInfo != NULL);
setInfoNoLock(m_primaryInfo->m_name, w, h, zoneSize, x, y);
} }
void CServer::setInfo(const CString& client, void CServer::setInfo(const CString& client,
SInt32 w, SInt32 h, SInt32 zoneSize, SInt32 w, SInt32 h, SInt32 zoneSize,
SInt32 x, SInt32 y) SInt32 x, SInt32 y)
{ {
assert(!client.empty()); CLock lock(&m_mutex);
setInfoNoLock(client, w, h, zoneSize, x, y);
}
void CServer::setInfoNoLock(const CString& screen,
SInt32 w, SInt32 h, SInt32 zoneSize,
SInt32 x, SInt32 y)
{
assert(!screen.empty());
assert(w > 0); assert(w > 0);
assert(h > 0); assert(h > 0);
assert(zoneSize >= 0); assert(zoneSize >= 0);
CLock lock(&m_mutex); // screen must be connected
CScreenList::iterator index = m_screens.find(screen);
// client must be connected
CScreenList::iterator index = m_screens.find(client);
if (index == m_screens.end()) { if (index == m_screens.end()) {
throw XBadClient(); throw XBadClient();
} }
// update client info // screen is now ready (i.e. available to user)
CScreenInfo* info = index->second; CScreenInfo* info = index->second;
info->m_ready = true;
// update screen info
if (info == m_active) { if (info == m_active) {
// update the remote mouse coordinates // update the remote mouse coordinates
m_x = x; m_x = x;
@ -226,9 +246,9 @@ void CServer::setInfo(const CString& client,
info->m_width = w; info->m_width = w;
info->m_height = h; info->m_height = h;
info->m_zoneSize = zoneSize; info->m_zoneSize = zoneSize;
log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d pos=%d,%d", client.c_str(), w, h, zoneSize, x, y)); log((CLOG_NOTE "screen \"%s\" size=%dx%d zone=%d pos=%d,%d", screen.c_str(), w, h, zoneSize, x, y));
// send acknowledgement (if client isn't the primary) // send acknowledgement (if screen isn't the primary)
if (info->m_protocol != NULL) { if (info->m_protocol != NULL) {
info->m_protocol->sendInfoAcknowledgment(); info->m_protocol->sendInfoAcknowledgment();
} }
@ -236,17 +256,19 @@ void CServer::setInfo(const CString& client,
// handle resolution change to primary screen // handle resolution change to primary screen
else { else {
if (info == m_active) { if (info == m_active) {
onMouseMovePrimary(x, y); onMouseMovePrimaryNoLock(x, y);
} }
else { else {
onMouseMoveSecondary(0, 0); onMouseMoveSecondaryNoLock(0, 0);
} }
} }
} }
void CServer::grabClipboard(ClipboardID id) void CServer::grabClipboard(ClipboardID id)
{ {
grabClipboard(id, 0, m_primaryInfo->m_name); CLock lock(&m_mutex);
assert(m_primaryInfo != NULL);
grabClipboardNoLock(id, 0, m_primaryInfo->m_name);
} }
void CServer::grabClipboard( void CServer::grabClipboard(
@ -254,25 +276,33 @@ void CServer::grabClipboard(
const CString& client) const CString& client)
{ {
CLock lock(&m_mutex); CLock lock(&m_mutex);
grabClipboardNoLock(id, seqNum, client);
}
void CServer::grabClipboardNoLock(
ClipboardID id, UInt32 seqNum,
const CString& screen)
{
// note -- must be locked on entry
CClipboardInfo& clipboard = m_clipboards[id]; CClipboardInfo& clipboard = m_clipboards[id];
// screen must be connected // screen must be connected
CScreenList::iterator index = m_screens.find(client); CScreenList::iterator index = m_screens.find(screen);
if (index == m_screens.end()) { if (index == m_screens.end()) {
throw XBadClient(); throw XBadClient();
} }
// ignore grab if sequence number is old. always allow primary // ignore grab if sequence number is old. always allow primary
// screen to grab. // screen to grab.
if (client != m_primaryInfo->m_name && if (screen != m_primaryInfo->m_name &&
seqNum < clipboard.m_clipboardSeqNum) { seqNum < clipboard.m_clipboardSeqNum) {
log((CLOG_INFO "ignored client \"%s\" grab of clipboard %d", client.c_str(), id)); log((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", screen.c_str(), id));
return; return;
} }
// mark screen as owning clipboard // mark screen as owning clipboard
log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str())); log((CLOG_NOTE "screen \"%s\" grabbed clipboard %d from \"%s\"", screen.c_str(), id, clipboard.m_clipboardOwner.c_str()));
clipboard.m_clipboardOwner = client; clipboard.m_clipboardOwner = screen;
clipboard.m_clipboardSeqNum = seqNum; clipboard.m_clipboardSeqNum = seqNum;
// no screens have the new clipboard except the sender // no screens have the new clipboard except the sender
@ -281,7 +311,7 @@ void CServer::grabClipboard(
// tell all other screens to take ownership of clipboard // tell all other screens to take ownership of clipboard
for (index = m_screens.begin(); index != m_screens.end(); ++index) { for (index = m_screens.begin(); index != m_screens.end(); ++index) {
if (index->first != client) { if (index->first != screen) {
CScreenInfo* info = index->second; CScreenInfo* info = index->second;
if (info->m_protocol == NULL) { if (info->m_protocol == NULL) {
m_primary->grabClipboard(id); m_primary->grabClipboard(id);
@ -317,12 +347,12 @@ void CServer::setClipboard(ClipboardID id,
// ignore update if sequence number is old // ignore update if sequence number is old
if (seqNum < clipboard.m_clipboardSeqNum) { if (seqNum < clipboard.m_clipboardSeqNum) {
log((CLOG_INFO "ignored client \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); log((CLOG_INFO "ignored screen \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
return; return;
} }
// unmarshall into our clipboard buffer // unmarshall into our clipboard buffer
log((CLOG_NOTE "client \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); log((CLOG_NOTE "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
clipboard.m_clipboardReady = true; clipboard.m_clipboardReady = true;
clipboard.m_clipboardData = data; clipboard.m_clipboardData = data;
clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0);
@ -349,6 +379,7 @@ bool CServer::onCommandKey(KeyID /*id*/,
void CServer::onKeyDown(KeyID id, KeyModifierMask mask) void CServer::onKeyDown(KeyID id, KeyModifierMask mask)
{ {
log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask)); log((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x", id, mask));
CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
// handle command keys // handle command keys
@ -365,6 +396,7 @@ void CServer::onKeyDown(KeyID id, KeyModifierMask mask)
void CServer::onKeyUp(KeyID id, KeyModifierMask mask) void CServer::onKeyUp(KeyID id, KeyModifierMask mask)
{ {
log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask)); log((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x", id, mask));
CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
// handle command keys // handle command keys
@ -382,6 +414,7 @@ void CServer::onKeyRepeat(
KeyID id, KeyModifierMask mask, SInt32 count) KeyID id, KeyModifierMask mask, SInt32 count)
{ {
log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count)); log((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d", id, mask, count));
CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
// handle command keys // handle command keys
@ -399,6 +432,7 @@ void CServer::onKeyRepeat(
void CServer::onMouseDown(ButtonID id) void CServer::onMouseDown(ButtonID id)
{ {
log((CLOG_DEBUG1 "onMouseDown id=%d", id)); log((CLOG_DEBUG1 "onMouseDown id=%d", id));
CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
// relay // relay
@ -410,6 +444,7 @@ void CServer::onMouseDown(ButtonID id)
void CServer::onMouseUp(ButtonID id) void CServer::onMouseUp(ButtonID id)
{ {
log((CLOG_DEBUG1 "onMouseUp id=%d", id)); log((CLOG_DEBUG1 "onMouseUp id=%d", id));
CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
// relay // relay
@ -421,13 +456,18 @@ void CServer::onMouseUp(ButtonID id)
bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
{ {
log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); log((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y));
CLock lock(&m_mutex);
return onMouseMovePrimaryNoLock(x, y);
}
bool CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y)
{
// mouse move on primary (server's) screen // mouse move on primary (server's) screen
assert(m_active != NULL); assert(m_active != NULL);
assert(m_active->m_protocol == NULL); assert(m_active->m_protocol == NULL);
// ignore if mouse is locked to screen // ignore if mouse is locked to screen
if (isLockedToScreen()) { if (isLockedToScreenNoLock()) {
return false; return false;
} }
@ -477,10 +517,25 @@ bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
{ {
log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); log((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy));
CLock lock(&m_mutex);
onMouseMoveSecondaryNoLock(dx, dy);
}
void CServer::onMouseMoveSecondaryNoLock(
SInt32 dx, SInt32 dy)
{
// mouse move on secondary (client's) screen // mouse move on secondary (client's) screen
assert(m_active != NULL); assert(m_active != NULL);
assert(m_active->m_protocol != NULL); if (m_active->m_protocol == NULL) {
// we're actually on the primary screen. this can happen
// when the primary screen begins processing a mouse move
// for a secondary screen, then the active (secondary)
// screen disconnects causing us to jump to the primary
// screen, and finally the primary screen finishes
// processing the mouse move, still thinking it's for
// a secondary screen. we just ignore the motion.
return;
}
// save old position // save old position
const SInt32 xOld = m_x; const SInt32 xOld = m_x;
@ -493,7 +548,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
// switch screens if the mouse is outside the screen and not // switch screens if the mouse is outside the screen and not
// locked to the screen // locked to the screen
CScreenInfo* newScreen = NULL; CScreenInfo* newScreen = NULL;
if (!isLockedToScreen()) { if (!isLockedToScreenNoLock()) {
// find direction of neighbor // find direction of neighbor
CConfig::EDirection dir; CConfig::EDirection dir;
if (m_x < 0) if (m_x < 0)
@ -564,6 +619,7 @@ void CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
void CServer::onMouseWheel(SInt32 delta) void CServer::onMouseWheel(SInt32 delta)
{ {
log((CLOG_DEBUG1 "onMouseWheel %+d", delta)); log((CLOG_DEBUG1 "onMouseWheel %+d", delta));
CLock lock(&m_mutex);
assert(m_active != NULL); assert(m_active != NULL);
// relay // relay
@ -573,6 +629,12 @@ void CServer::onMouseWheel(SInt32 delta)
} }
bool CServer::isLockedToScreen() const bool CServer::isLockedToScreen() const
{
CLock lock(&m_mutex);
return isLockedToScreenNoLock();
}
bool CServer::isLockedToScreenNoLock() const
{ {
// locked if primary says we're locked // locked if primary says we're locked
if (m_primary->isLockedToScreen()) { if (m_primary->isLockedToScreen()) {
@ -673,11 +735,11 @@ CServer::CScreenInfo* CServer::getNeighbor(CScreenInfo* src,
return NULL; return NULL;
} }
// look up neighbor cell. if the screen is connected then // look up neighbor cell. if the screen is connected and
// we can stop. otherwise we skip over an unconnected // ready then we can stop. otherwise we skip over an
// screen. // unconnected screen.
CScreenList::const_iterator index = m_screens.find(dstName); CScreenList::const_iterator index = m_screens.find(dstName);
if (index != m_screens.end()) { if (index != m_screens.end() && index->second->m_ready) {
log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); log((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str()));
return index->second; return index->second;
} }
@ -1371,6 +1433,7 @@ CServer::CScreenInfo::CScreenInfo(const CString& name,
m_thread(CThread::getCurrentThread()), m_thread(CThread::getCurrentThread()),
m_name(name), m_name(name),
m_protocol(protocol), m_protocol(protocol),
m_ready(false),
m_width(0), m_height(0), m_width(0), m_height(0),
m_zoneSize(0) m_zoneSize(0)
{ {

View File

@ -66,6 +66,7 @@ public:
// accessors // accessors
// returns true if the mouse should be locked to the current screen
bool isLockedToScreen() const; bool isLockedToScreen() const;
// get the current screen map // get the current screen map
@ -107,14 +108,34 @@ private:
~CScreenInfo(); ~CScreenInfo();
public: public:
// the thread handling this screen's connection. used when
// forcing a screen to disconnect.
CThread m_thread; CThread m_thread;
CString m_name; CString m_name;
IServerProtocol* m_protocol; IServerProtocol* m_protocol;
bool m_ready;
SInt32 m_width, m_height; SInt32 m_width, m_height;
SInt32 m_zoneSize; SInt32 m_zoneSize;
bool m_gotClipboard[kClipboardEnd]; bool m_gotClipboard[kClipboardEnd];
}; };
// handle mouse motion
bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y);
void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy);
// update screen info
void setInfoNoLock(const CString& screenName,
SInt32 wScreen, SInt32 hScreen,
SInt32 zoneSize,
SInt32 xMouse, SInt32 yMouse);
// grab the clipboard
void grabClipboardNoLock(ClipboardID,
UInt32 seqNum, const CString& clientName);
// returns true iff mouse should be locked to the current screen
bool isLockedToScreenNoLock() const;
// change the active screen // change the active screen
void switchScreen(CScreenInfo*, SInt32 x, SInt32 y); void switchScreen(CScreenInfo*, SInt32 x, SInt32 y);

View File

@ -4,6 +4,7 @@
#include "CProtocolUtil.h" #include "CProtocolUtil.h"
#include "ProtocolTypes.h" #include "ProtocolTypes.h"
#include "IInputStream.h" #include "IInputStream.h"
#include "IOutputStream.h"
#include "CLog.h" #include "CLog.h"
#include "CThread.h" #include "CThread.h"
#include <string.h> #include <string.h>
@ -92,6 +93,9 @@ void CServerProtocol1_0::sendClose()
{ {
log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str())); log((CLOG_DEBUG1 "send close to \"%s\"", getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCClose); CProtocolUtil::writef(getOutputStream(), kMsgCClose);
// force the close to be sent before we return
getOutputStream()->flush();
} }
void CServerProtocol1_0::sendEnter( void CServerProtocol1_0::sendEnter(