Added support for heartbeat global option.

This commit is contained in:
crs 2003-02-22 16:20:23 +00:00
parent 8685afd9f6
commit 366537dc22
11 changed files with 196 additions and 57 deletions

View File

@ -34,7 +34,8 @@ CServerProxy::CServerProxy(IClient* client,
m_client(client), m_client(client),
m_input(adoptedInput), m_input(adoptedInput),
m_output(adoptedOutput), m_output(adoptedOutput),
m_seqNum(0) m_seqNum(0),
m_heartRate(kHeartRate)
{ {
assert(m_client != NULL); assert(m_client != NULL);
assert(m_input != NULL); assert(m_input != NULL);
@ -76,7 +77,7 @@ CServerProxy::mainLoop()
// wait for a message // wait for a message
LOG((CLOG_DEBUG2 "waiting for message")); LOG((CLOG_DEBUG2 "waiting for message"));
UInt8 code[4]; UInt8 code[4];
UInt32 n = getInputStream()->read(code, 4, kHeartRate); UInt32 n = getInputStream()->read(code, 4, m_heartRate);
// check if server hungup // check if server hungup
if (n == 0) { if (n == 0) {
@ -86,7 +87,7 @@ CServerProxy::mainLoop()
// check for time out // check for time out
if (n == (UInt32)-1 || if (n == (UInt32)-1 ||
(kHeartRate >= 0.0 && heartbeat.getTime() > kHeartRate)) { (m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) {
// send heartbeat // send heartbeat
CLock lock(&m_mutex); CLock lock(&m_mutex);
CProtocolUtil::writef(getOutputStream(), kMsgCNoop); CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
@ -663,9 +664,20 @@ CServerProxy::resetOptions()
// forward // forward
getClient()->resetOptions(); getClient()->resetOptions();
CLock lock(&m_mutex);
// reset heart rate
m_heartRate = kHeartRate;
// reset modifier translation table // reset modifier translation table
for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) {
m_modifierTranslationTable[id] = id; m_modifierTranslationTable[id] = id;
}
// send heartbeat if necessary
if (m_heartRate >= 0.0) {
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
}
} }
void void
@ -679,6 +691,8 @@ CServerProxy::setOptions()
// forward // forward
getClient()->setOptions(options); getClient()->setOptions(options);
CLock lock(&m_mutex);
// update modifier table // update modifier table
for (UInt32 i = 0, n = options.size(); i < n; i += 2) { for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
KeyModifierID id = kKeyModifierIDNull; KeyModifierID id = kKeyModifierIDNull;
@ -697,6 +711,15 @@ CServerProxy::setOptions()
else if (options[i] == kOptionModifierMapForSuper) { else if (options[i] == kOptionModifierMapForSuper) {
id = kKeyModifierIDSuper; id = kKeyModifierIDSuper;
} }
else if (options[i] == kOptionHeartbeat) {
// update heart rate
m_heartRate = 1.0e-3 * static_cast<double>(options[i + 1]);
// send heartbeat if necessary
if (m_heartRate >= 0.0) {
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
}
}
if (id != kKeyModifierIDNull) { if (id != kKeyModifierIDNull) {
m_modifierTranslationTable[id] = m_modifierTranslationTable[id] =
static_cast<KeyModifierID>(options[i + 1]); static_cast<KeyModifierID>(options[i + 1]);

View File

@ -130,6 +130,7 @@ private:
bool m_ignoreMouse; bool m_ignoreMouse;
KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast];
double m_heartRate;
}; };
#endif #endif

View File

@ -59,3 +59,9 @@ CClientProxy::getName() const
{ {
return m_name; return m_name;
} }
const CMutex*
CClientProxy::getMutex() const
{
return &m_mutex;
}

View File

@ -16,6 +16,7 @@
#define CCLIENTPROXY_H #define CCLIENTPROXY_H
#include "IClient.h" #include "IClient.h"
#include "CMutex.h"
#include "CString.h" #include "CString.h"
class IInputStream; class IInputStream;
@ -84,7 +85,16 @@ public:
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
protected:
//! Get mutex
/*!
Returns the mutex for this object. Subclasses should use this
mutex to protect their data.
*/
const CMutex* getMutex() const;
private: private:
CMutex m_mutex;
IServer* m_server; IServer* m_server;
CString m_name; CString m_name;
IInputStream* m_input; IInputStream* m_input;

View File

@ -31,7 +31,9 @@
CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name, CClientProxy1_0::CClientProxy1_0(IServer* server, const CString& name,
IInputStream* input, IOutputStream* output) : IInputStream* input, IOutputStream* output) :
CClientProxy(server, name, input, output) CClientProxy(server, name, input, output),
m_heartRate(kHeartRate),
m_heartDeath(kHeartRate * kHeartBeatsUntilDeath)
{ {
for (UInt32 i = 0; i < kClipboardEnd; ++i) { for (UInt32 i = 0; i < kClipboardEnd; ++i) {
m_clipboardDirty[i] = true; m_clipboardDirty[i] = true;
@ -81,7 +83,7 @@ CClientProxy1_0::mainLoop()
// wait for a message // wait for a message
UInt8 code[4]; UInt8 code[4];
UInt32 n = getInputStream()->read(code, 4, kHeartRate); UInt32 n = getInputStream()->read(code, 4, m_heartRate);
CThread::testCancel(); CThread::testCancel();
// check if client hungup // check if client hungup
@ -92,7 +94,7 @@ CClientProxy1_0::mainLoop()
// check if client has stopped sending heartbeats // check if client has stopped sending heartbeats
if (n == (UInt32)-1) { if (n == (UInt32)-1) {
if (kHeartDeath >= 0.0 && heartTimer.getTime() > kHeartDeath) { if (m_heartDeath >= 0.0 && heartTimer.getTime() > m_heartDeath) {
LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str()));
return; return;
} }
@ -117,6 +119,7 @@ CClientProxy1_0::mainLoop()
} }
else if (memcmp(code, kMsgCNoop, 4) == 0) { else if (memcmp(code, kMsgCNoop, 4) == 0) {
// discard no-ops // discard no-ops
LOG((CLOG_DEBUG2 "no-op from", getName().c_str()));
continue; continue;
} }
else if (memcmp(code, kMsgCClipboard, 4) == 0) { else if (memcmp(code, kMsgCClipboard, 4) == 0) {
@ -168,7 +171,7 @@ void
CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) CClientProxy1_0::setClipboard(ClipboardID id, const CString& data)
{ {
// ignore if this clipboard is already clean // ignore if this clipboard is already clean
CLock lock(&m_mutex); CLock lock(getMutex());
if (m_clipboardDirty[id]) { if (m_clipboardDirty[id]) {
// this clipboard is now clean // this clipboard is now clean
m_clipboardDirty[id] = false; m_clipboardDirty[id] = false;
@ -185,14 +188,14 @@ CClientProxy1_0::grabClipboard(ClipboardID id)
CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0);
// this clipboard is now dirty // this clipboard is now dirty
CLock lock(&m_mutex); CLock lock(getMutex());
m_clipboardDirty[id] = true; m_clipboardDirty[id] = true;
} }
void void
CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty) CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
{ {
CLock lock(&m_mutex); CLock lock(getMutex());
m_clipboardDirty[id] = dirty; m_clipboardDirty[id] = dirty;
} }
@ -257,6 +260,11 @@ CClientProxy1_0::resetOptions()
{ {
LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str())); LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions); CProtocolUtil::writef(getOutputStream(), kMsgCResetOptions);
// reset heart rate and death
CLock lock(getMutex());
m_heartRate = kHeartRate;
m_heartDeath = kHeartRate * kHeartBeatsUntilDeath;
} }
void void
@ -264,19 +272,31 @@ CClientProxy1_0::setOptions(const COptionsList& options)
{ {
LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size())); LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size()));
CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options); CProtocolUtil::writef(getOutputStream(), kMsgDSetOptions, &options);
// check options
CLock lock(getMutex());
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
if (options[i] == kOptionHeartbeat) {
m_heartRate = 1.0e-3 * static_cast<double>(options[i + 1]);
if (m_heartRate <= 0.0) {
m_heartRate = -1.0;
}
m_heartDeath = m_heartRate * kHeartBeatsUntilDeath;
}
}
} }
SInt32 SInt32
CClientProxy1_0::getJumpZoneSize() const CClientProxy1_0::getJumpZoneSize() const
{ {
CLock lock(&m_mutex); CLock lock(getMutex());
return m_info.m_zoneSize; return m_info.m_zoneSize;
} }
void void
CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
{ {
CLock lock(&m_mutex); CLock lock(getMutex());
x = m_info.m_x; x = m_info.m_x;
y = m_info.m_y; y = m_info.m_y;
w = m_info.m_w; w = m_info.m_w;
@ -292,7 +312,7 @@ CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const
void void
CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const
{ {
CLock lock(&m_mutex); CLock lock(getMutex());
x = m_info.m_mx; x = m_info.m_mx;
y = m_info.m_my; y = m_info.m_my;
} }
@ -301,7 +321,7 @@ void
CClientProxy1_0::recvInfo(bool notify) CClientProxy1_0::recvInfo(bool notify)
{ {
{ {
CLock lock(&m_mutex); CLock lock(getMutex());
// parse the message // parse the message
SInt16 x, y, w, h, zoneSize, mx, my; SInt16 x, y, w, h, zoneSize, mx, my;

View File

@ -17,7 +17,6 @@
#include "CClientProxy.h" #include "CClientProxy.h"
#include "ProtocolTypes.h" #include "ProtocolTypes.h"
#include "CMutex.h"
//! Proxy for client implementing protocol version 1.0 //! Proxy for client implementing protocol version 1.0
class CClientProxy1_0 : public CClientProxy { class CClientProxy1_0 : public CClientProxy {
@ -60,9 +59,10 @@ private:
void recvGrabClipboard(); void recvGrabClipboard();
private: private:
CMutex m_mutex;
CClientInfo m_info; CClientInfo m_info;
bool m_clipboardDirty[kClipboardEnd]; bool m_clipboardDirty[kClipboardEnd];
double m_heartRate;
double m_heartDeath;
}; };
#endif #endif

View File

@ -17,6 +17,7 @@
#include "XSocket.h" #include "XSocket.h"
#include "stdistream.h" #include "stdistream.h"
#include "stdostream.h" #include "stdostream.h"
#include <stdlib.h>
// //
// CConfig // CConfig
@ -504,31 +505,57 @@ CConfig::readLine(std::istream& s, CString& line)
return false; return false;
} }
bool OptionValue
CConfig::parseBoolean(const CString& arg) CConfig::parseBoolean(const CString& arg)
{ {
if (CStringUtil::CaselessCmp::equal(arg, "true")) if (CStringUtil::CaselessCmp::equal(arg, "true")) {
return true; return static_cast<OptionValue>(true);
if (CStringUtil::CaselessCmp::equal(arg, "false")) }
return false; if (CStringUtil::CaselessCmp::equal(arg, "false")) {
return static_cast<OptionValue>(false);
}
throw XConfigRead("invalid argument"); throw XConfigRead("invalid argument");
} }
OptionValue
CConfig::parseInt(const CString& arg)
{
const char* s = arg.c_str();
char* end;
long tmp = strtol(s, &end, 10);
if (*end != '\0') {
// invalid characters
throw XConfigRead("invalid argument");
}
OptionValue value = static_cast<OptionValue>(tmp);
if (value != tmp) {
// out of range
throw XConfigRead("argument out of range");
}
return value;
}
OptionValue OptionValue
CConfig::parseModifierKey(const CString& arg) CConfig::parseModifierKey(const CString& arg)
{ {
if (CStringUtil::CaselessCmp::equal(arg, "shift")) if (CStringUtil::CaselessCmp::equal(arg, "shift")) {
return static_cast<OptionValue>(kKeyModifierIDShift); return static_cast<OptionValue>(kKeyModifierIDShift);
if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) }
if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) {
return static_cast<OptionValue>(kKeyModifierIDControl); return static_cast<OptionValue>(kKeyModifierIDControl);
if (CStringUtil::CaselessCmp::equal(arg, "alt")) }
if (CStringUtil::CaselessCmp::equal(arg, "alt")) {
return static_cast<OptionValue>(kKeyModifierIDAlt); return static_cast<OptionValue>(kKeyModifierIDAlt);
if (CStringUtil::CaselessCmp::equal(arg, "meta")) }
if (CStringUtil::CaselessCmp::equal(arg, "meta")) {
return static_cast<OptionValue>(kKeyModifierIDMeta); return static_cast<OptionValue>(kKeyModifierIDMeta);
if (CStringUtil::CaselessCmp::equal(arg, "super")) }
if (CStringUtil::CaselessCmp::equal(arg, "super")) {
return static_cast<OptionValue>(kKeyModifierIDSuper); return static_cast<OptionValue>(kKeyModifierIDSuper);
if (CStringUtil::CaselessCmp::equal(arg, "none")) }
if (CStringUtil::CaselessCmp::equal(arg, "none")) {
return static_cast<OptionValue>(kKeyModifierIDNull); return static_cast<OptionValue>(kKeyModifierIDNull);
}
throw XConfigRead("invalid argument"); throw XConfigRead("invalid argument");
} }
@ -556,10 +583,13 @@ CConfig::getOptionName(OptionID id)
if (id == kOptionModifierMapForSuper) { if (id == kOptionModifierMapForSuper) {
return "super"; return "super";
} }
if (id == kOptionHeartbeat) {
return "heartbeat";
}
return NULL; return NULL;
} }
const char* CString
CConfig::getOptionValue(OptionID id, OptionValue value) CConfig::getOptionValue(OptionID id, OptionValue value)
{ {
if (id == kOptionHalfDuplexCapsLock || if (id == kOptionHalfDuplexCapsLock ||
@ -591,6 +621,9 @@ CConfig::getOptionValue(OptionID id, OptionValue value)
return "none"; return "none";
} }
} }
if (id == kOptionHeartbeat) {
return CStringUtil::print("%d", value);
}
return ""; return "";
} }
@ -599,7 +632,7 @@ void
CConfig::readSection(std::istream& s) CConfig::readSection(std::istream& s)
{ {
static const char s_section[] = "section:"; static const char s_section[] = "section:";
static const char s_network[] = "network"; static const char s_options[] = "options";
static const char s_screens[] = "screens"; static const char s_screens[] = "screens";
static const char s_links[] = "links"; static const char s_links[] = "links";
static const char s_aliases[] = "aliases"; static const char s_aliases[] = "aliases";
@ -627,8 +660,8 @@ CConfig::readSection(std::istream& s)
} }
// read section // read section
if (name == s_network) { if (name == s_options) {
readSectionNetwork(s); readSectionOptions(s);
} }
else if (name == s_screens) { else if (name == s_screens) {
readSectionScreens(s); readSectionScreens(s);
@ -645,7 +678,7 @@ CConfig::readSection(std::istream& s)
} }
void void
CConfig::readSectionNetwork(std::istream& s) CConfig::readSectionOptions(std::istream& s)
{ {
CString line; CString line;
CString name; CString name;
@ -693,6 +726,9 @@ CConfig::readSectionNetwork(std::istream& s)
throw XConfigRead("invalid http argument"); throw XConfigRead("invalid http argument");
} }
} }
else if (name == "heartbeat") {
addOption("", kOptionHeartbeat, parseInt(value));
}
else { else {
throw XConfigRead("unknown argument"); throw XConfigRead("unknown argument");
} }
@ -931,15 +967,28 @@ operator>>(std::istream& s, CConfig& config)
std::ostream& std::ostream&
operator<<(std::ostream& s, const CConfig& config) operator<<(std::ostream& s, const CConfig& config)
{ {
// network section // options section
s << "section: network" << std::endl; s << "section: options" << std::endl;
const CConfig::CScreenOptions* options = config.getOptions("");
if (options != NULL && options->size() > 0) {
for (CConfig::CScreenOptions::const_iterator
option = options->begin();
option != options->end(); ++option) {
const char* name = CConfig::getOptionName(option->first);
CString value = CConfig::getOptionValue(option->first,
option->second);
if (name != NULL && !value.empty()) {
s << "\t" << name << " = " << value << std::endl;
}
}
}
if (config.m_synergyAddress.isValid()) { if (config.m_synergyAddress.isValid()) {
s << "\taddress=" << config.m_synergyAddress.getHostname().c_str() << s << "\taddress = " <<
std::endl; config.m_synergyAddress.getHostname().c_str() << std::endl;
} }
if (config.m_httpAddress.isValid()) { if (config.m_httpAddress.isValid()) {
s << "\thttp=" << config.m_httpAddress.getHostname().c_str() << s << "\thttp = " <<
std::endl; config.m_httpAddress.getHostname().c_str() << std::endl;
} }
s << "end" << std::endl; s << "end" << std::endl;
@ -953,10 +1002,10 @@ operator<<(std::ostream& s, const CConfig& config)
for (CConfig::CScreenOptions::const_iterator for (CConfig::CScreenOptions::const_iterator
option = options->begin(); option = options->begin();
option != options->end(); ++option) { option != options->end(); ++option) {
const char* name = CConfig::getOptionName(option->first); const char* name = CConfig::getOptionName(option->first);
const char* value = CConfig::getOptionValue(option->first, CString value = CConfig::getOptionValue(option->first,
option->second); option->second);
if (name != NULL && value != NULL) { if (name != NULL && !value.empty()) {
s << "\t\t" << name << " = " << value << std::endl; s << "\t\t" << name << " = " << value << std::endl;
} }
} }

View File

@ -292,12 +292,13 @@ public:
private: private:
static bool readLine(std::istream&, CString&); static bool readLine(std::istream&, CString&);
static bool parseBoolean(const CString&); static OptionValue parseBoolean(const CString&);
static OptionValue parseInt(const CString&);
static OptionValue parseModifierKey(const CString&); static OptionValue parseModifierKey(const CString&);
static const char* getOptionName(OptionID); static const char* getOptionName(OptionID);
static const char* getOptionValue(OptionID, OptionValue); static CString getOptionValue(OptionID, OptionValue);
void readSection(std::istream&); void readSection(std::istream&);
void readSectionNetwork(std::istream&); void readSectionOptions(std::istream&);
void readSectionScreens(std::istream&); void readSectionScreens(std::istream&);
void readSectionLinks(std::istream&); void readSectionLinks(std::istream&);
void readSectionAliases(std::istream&); void readSectionAliases(std::istream&);

View File

@ -217,12 +217,29 @@ CServer::setConfig(const CConfig& config)
CLock lock(&m_mutex); CLock lock(&m_mutex);
m_config = config; m_config = config;
// process global options
const CConfig::CScreenOptions* options = m_config.getOptions("");
if (options != NULL && options->size() > 0) {
/*
for (CConfig::CScreenOptions::const_iterator index = options->begin();
index != options->end(); ++index) {
const OptionID id = index->first;
const OptionValue value = index->second;
}
*/
}
// tell primary screen about reconfiguration // tell primary screen about reconfiguration
if (m_primaryClient != NULL) { if (m_primaryClient != NULL) {
m_primaryClient->reconfigure(getActivePrimarySides()); m_primaryClient->reconfigure(getActivePrimarySides());
} }
// FIXME -- tell all (connected) clients about current options // tell all (connected) clients about current options
for (CClientList::const_iterator index = m_clients.begin();
index != m_clients.end(); ++index) {
IClient* client = index->second;
sendOptions(client);
}
return true; return true;
} }
@ -1622,20 +1639,31 @@ CServer::sendOptions(IClient* client) const
{ {
// note -- must be locked on entry // note -- must be locked on entry
// look up options for client. we're done if there aren't any. COptionsList optionsList;
// look up options for client
const CConfig::CScreenOptions* options = const CConfig::CScreenOptions* options =
m_config.getOptions(client->getName()); m_config.getOptions(client->getName());
if (options == NULL || options->size() == 0) { if (options != NULL && options->size() > 0) {
return; // convert options to a more convenient form for sending
optionsList.reserve(2 * options->size());
for (CConfig::CScreenOptions::const_iterator index = options->begin();
index != options->end(); ++index) {
optionsList.push_back(index->first);
optionsList.push_back(static_cast<UInt32>(index->second));
}
} }
// convert options to a more convenient form for sending // look up global options
COptionsList optionsList; options = m_config.getOptions("");
optionsList.reserve(2 * options->size()); if (options != NULL && options->size() > 0) {
for (CConfig::CScreenOptions::const_iterator index = options->begin(); // convert options to a more convenient form for sending
index != options->end(); ++index) { optionsList.reserve(optionsList.size() + 2 * options->size());
optionsList.push_back(index->first); for (CConfig::CScreenOptions::const_iterator index = options->begin();
optionsList.push_back(static_cast<UInt32>(index->second)); index != options->end(); ++index) {
optionsList.push_back(index->first);
optionsList.push_back(static_cast<UInt32>(index->second));
}
} }
// send the options // send the options

View File

@ -50,6 +50,7 @@ static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC");
static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA");
static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM");
static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR");
static const OptionID kOptionHeartbeat = OPTION_CODE("HART");
//@} //@}
#undef OPTION_CODE #undef OPTION_CODE

View File

@ -31,8 +31,8 @@ static const UInt32 kMaxHelloLength = 1024;
// heartbeat. // heartbeat.
static const double kHeartRate = -1.0; static const double kHeartRate = -1.0;
// time without a heartbeat that constitutes death // number of skipped heartbeats that constitutes death
static const double kHeartDeath = 3.0 * kHeartRate; static const double kHeartBeatsUntilDeath = 3.0;
// direction constants // direction constants
enum EDirection { enum EDirection {