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

View File

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

View File

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

View File

@ -16,6 +16,7 @@
#define CCLIENTPROXY_H
#include "IClient.h"
#include "CMutex.h"
#include "CString.h"
class IInputStream;
@ -84,7 +85,16 @@ public:
virtual void getCursorPos(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:
CMutex m_mutex;
IServer* m_server;
CString m_name;
IInputStream* m_input;

View File

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

View File

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

View File

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

View File

@ -292,12 +292,13 @@ public:
private:
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 const char* getOptionName(OptionID);
static const char* getOptionValue(OptionID, OptionValue);
static CString getOptionValue(OptionID, OptionValue);
void readSection(std::istream&);
void readSectionNetwork(std::istream&);
void readSectionOptions(std::istream&);
void readSectionScreens(std::istream&);
void readSectionLinks(std::istream&);
void readSectionAliases(std::istream&);

View File

@ -217,12 +217,29 @@ CServer::setConfig(const CConfig& config)
CLock lock(&m_mutex);
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
if (m_primaryClient != NULL) {
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;
}
@ -1622,20 +1639,31 @@ CServer::sendOptions(IClient* client) const
{
// 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 =
m_config.getOptions(client->getName());
if (options == NULL || options->size() == 0) {
return;
if (options != NULL && options->size() > 0) {
// 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
COptionsList optionsList;
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));
// look up global options
options = m_config.getOptions("");
if (options != NULL && options->size() > 0) {
// convert options to a more convenient form for sending
optionsList.reserve(optionsList.size() + 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));
}
}
// 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 kOptionModifierMapForMeta = OPTION_CODE("MMFM");
static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR");
static const OptionID kOptionHeartbeat = OPTION_CODE("HART");
//@}
#undef OPTION_CODE

View File

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