// This is only necessary if the Socket has been put in non-blocking | // This is only necessary if the Socket has been put in non-blocking | ||||
// mode and needs this callback to flush the buffer. | // mode and needs this callback to flush the buffer. | ||||
virtual void processSocketWriteEvent(network::Socket* sock) = 0; | virtual void processSocketWriteEvent(network::Socket* sock) = 0; | ||||
// checkTimeouts() allows the server to check socket timeouts, etc. The | |||||
// return value is the number of milliseconds to wait before | |||||
// checkTimeouts() should be called again. If this number is zero then | |||||
// there is no timeout and checkTimeouts() should be called the next time | |||||
// an event occurs. | |||||
virtual int checkTimeouts() = 0; | |||||
}; | }; | ||||
} | } |
virtual void queryConnection(network::Socket* sock, | virtual void queryConnection(network::Socket* sock, | ||||
const char* userName) = 0; | const char* userName) = 0; | ||||
// terminate() is called by the server when it wishes to terminate | |||||
// itself, e.g. because it was configured to terminate when no one is | |||||
// using it. | |||||
virtual void terminate() = 0; | |||||
// setScreenLayout() requests to reconfigure the framebuffer and/or | // setScreenLayout() requests to reconfigure the framebuffer and/or | ||||
// the layout of screens. | // the layout of screens. | ||||
virtual unsigned int setScreenLayout(int __unused_attr fb_width, | virtual unsigned int setScreenLayout(int __unused_attr fb_width, |
fenceDataLen(0), fenceData(NULL), congestionTimer(this), | fenceDataLen(0), fenceData(NULL), congestionTimer(this), | ||||
losslessTimer(this), server(server_), updates(false), | losslessTimer(this), server(server_), updates(false), | ||||
updateRenderedCursor(false), removeRenderedCursor(false), | updateRenderedCursor(false), removeRenderedCursor(false), | ||||
continuousUpdates(false), encodeManager(this), pointerEventTime(0), | |||||
clientHasCursor(false) | |||||
continuousUpdates(false), encodeManager(this), idleTimer(this), | |||||
pointerEventTime(0), clientHasCursor(false) | |||||
{ | { | ||||
setStreams(&sock->inStream(), &sock->outStream()); | setStreams(&sock->inStream(), &sock->outStream()); | ||||
peerEndpoint.buf = sock->getPeerEndpoint(); | peerEndpoint.buf = sock->getPeerEndpoint(); | ||||
// Configure the socket | // Configure the socket | ||||
setSocketTimeouts(); | setSocketTimeouts(); | ||||
lastEventTime = time(0); | |||||
// Kick off the idle timer | |||||
if (rfb::Server::idleTimeout) { | |||||
// minimum of 15 seconds while authenticating | |||||
if (rfb::Server::idleTimeout < 15) | |||||
idleTimer.start(secsToMillis(15)); | |||||
else | |||||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||||
} | |||||
} | } | ||||
} | } | ||||
int VNCSConnectionST::checkIdleTimeout() | |||||
{ | |||||
int idleTimeout = rfb::Server::idleTimeout; | |||||
if (idleTimeout == 0) return 0; | |||||
if (state() != RFBSTATE_NORMAL && idleTimeout < 15) | |||||
idleTimeout = 15; // minimum of 15 seconds while authenticating | |||||
time_t now = time(0); | |||||
if (now < lastEventTime) { | |||||
// Someone must have set the time backwards. Set lastEventTime so that the | |||||
// idleTimeout will count from now. | |||||
vlog.info("Time has gone backwards - resetting idle timeout"); | |||||
lastEventTime = now; | |||||
} | |||||
int timeLeft = lastEventTime + idleTimeout - now; | |||||
if (timeLeft < -60) { | |||||
// Our callback is over a minute late - someone must have set the time | |||||
// forwards. Set lastEventTime so that the idleTimeout will count from | |||||
// now. | |||||
vlog.info("Time has gone forwards - resetting idle timeout"); | |||||
lastEventTime = now; | |||||
return secsToMillis(idleTimeout); | |||||
} | |||||
if (timeLeft <= 0) { | |||||
close("Idle timeout"); | |||||
return 0; | |||||
} | |||||
return secsToMillis(timeLeft); | |||||
} | |||||
bool VNCSConnectionST::getComparerState() | bool VNCSConnectionST::getComparerState() | ||||
{ | { | ||||
// We interpret a low compression level as an indication that the client | // We interpret a low compression level as an indication that the client | ||||
void VNCSConnectionST::authSuccess() | void VNCSConnectionST::authSuccess() | ||||
{ | { | ||||
lastEventTime = time(0); | |||||
if (rfb::Server::idleTimeout) | |||||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||||
// - Set the connection parameters appropriately | // - Set the connection parameters appropriately | ||||
cp.width = server->getPixelBuffer()->width(); | cp.width = server->getPixelBuffer()->width(); | ||||
void VNCSConnectionST::clientInit(bool shared) | void VNCSConnectionST::clientInit(bool shared) | ||||
{ | { | ||||
lastEventTime = time(0); | |||||
if (rfb::Server::idleTimeout) | |||||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||||
if (rfb::Server::alwaysShared || reverseConnection) shared = true; | if (rfb::Server::alwaysShared || reverseConnection) shared = true; | ||||
if (!accessCheck(AccessNonShared)) shared = true; | if (!accessCheck(AccessNonShared)) shared = true; | ||||
if (rfb::Server::neverShared) shared = false; | if (rfb::Server::neverShared) shared = false; | ||||
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask) | void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask) | ||||
{ | { | ||||
pointerEventTime = lastEventTime = time(0); | |||||
if (rfb::Server::idleTimeout) | |||||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||||
pointerEventTime = time(0); | |||||
if (!accessCheck(AccessPtrEvents)) return; | if (!accessCheck(AccessPtrEvents)) return; | ||||
if (!rfb::Server::acceptPointerEvents) return; | if (!rfb::Server::acceptPointerEvents) return; | ||||
pointerEventPos = pos; | pointerEventPos = pos; | ||||
void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | ||||
rdr::U32 lookup; | rdr::U32 lookup; | ||||
lastEventTime = time(0); | |||||
if (rfb::Server::idleTimeout) | |||||
idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); | |||||
if (!accessCheck(AccessKeyEvents)) return; | if (!accessCheck(AccessKeyEvents)) return; | ||||
if (!rfb::Server::acceptKeyEvents) return; | if (!rfb::Server::acceptKeyEvents) return; | ||||
close(e.str()); | close(e.str()); | ||||
} | } | ||||
if (t == &idleTimer) | |||||
close("Idle timeout"); | |||||
return false; | return false; | ||||
} | } | ||||
void VNCSConnectionST::setSocketTimeouts() | void VNCSConnectionST::setSocketTimeouts() | ||||
{ | { | ||||
int timeoutms = rfb::Server::clientWaitTimeMillis; | int timeoutms = rfb::Server::clientWaitTimeMillis; | ||||
soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout)); | |||||
if (timeoutms == 0) | if (timeoutms == 0) | ||||
timeoutms = -1; | timeoutms = -1; | ||||
sock->inStream().setTimeout(timeoutms); | sock->inStream().setTimeout(timeoutms); |
void setLEDStateOrClose(unsigned int state); | void setLEDStateOrClose(unsigned int state); | ||||
void approveConnectionOrClose(bool accept, const char* reason); | void approveConnectionOrClose(bool accept, const char* reason); | ||||
// checkIdleTimeout() returns the number of milliseconds left until the | |||||
// idle timeout expires. If it has expired, the connection is closed and | |||||
// zero is returned. Zero is also returned if there is no idle timeout. | |||||
int checkIdleTimeout(); | |||||
// The following methods never throw exceptions | // The following methods never throw exceptions | ||||
// getComparerState() returns if this client would like the framebuffer | // getComparerState() returns if this client would like the framebuffer | ||||
std::map<rdr::U32, rdr::U32> pressedKeys; | std::map<rdr::U32, rdr::U32> pressedKeys; | ||||
time_t lastEventTime; | |||||
Timer idleTimer; | |||||
time_t pointerEventTime; | time_t pointerEventTime; | ||||
Point pointerEventPos; | Point pointerEventPos; | ||||
bool clientHasCursor; | bool clientHasCursor; |
cursor(new Cursor(0, 0, Point(), NULL)), | cursor(new Cursor(0, 0, Point(), NULL)), | ||||
renderedCursorInvalid(false), | renderedCursorInvalid(false), | ||||
keyRemapper(&KeyRemapper::defInstance), | keyRemapper(&KeyRemapper::defInstance), | ||||
lastConnectionTime(0), frameTimer(this) | |||||
idleTimer(this), disconnectTimer(this), connectTimer(this), | |||||
frameTimer(this) | |||||
{ | { | ||||
lastUserInputTime = lastDisconnectTime = time(0); | |||||
slog.debug("creating single-threaded server %s", name.buf); | slog.debug("creating single-threaded server %s", name.buf); | ||||
// FIXME: Do we really want to kick off these right away? | |||||
if (rfb::Server::maxIdleTime) | |||||
idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); | |||||
if (rfb::Server::maxDisconnectionTime) | |||||
disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime)); | |||||
} | } | ||||
VNCServerST::~VNCServerST() | VNCServerST::~VNCServerST() | ||||
name.buf = sock->getPeerEndpoint(); | name.buf = sock->getPeerEndpoint(); | ||||
connectionsLog.status("accepted: %s", name.buf); | connectionsLog.status("accepted: %s", name.buf); | ||||
if (clients.empty()) { | |||||
lastConnectionTime = time(0); | |||||
} | |||||
// Adjust the exit timers | |||||
if (rfb::Server::maxConnectionTime && clients.empty()) | |||||
connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); | |||||
disconnectTimer.stop(); | |||||
VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); | VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); | ||||
clients.push_front(client); | clients.push_front(client); | ||||
if (pointerClient == *ci) | if (pointerClient == *ci) | ||||
pointerClient = NULL; | pointerClient = NULL; | ||||
if ((*ci)->authenticated()) | |||||
lastDisconnectTime = time(0); | |||||
// Adjust the exit timers | |||||
connectTimer.stop(); | |||||
if (rfb::Server::maxDisconnectionTime && clients.empty()) | |||||
disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime)); | |||||
// - Delete the per-Socket resources | // - Delete the per-Socket resources | ||||
delete *ci; | delete *ci; | ||||
throw rdr::Exception("invalid Socket in VNCServerST"); | throw rdr::Exception("invalid Socket in VNCServerST"); | ||||
} | } | ||||
int VNCServerST::checkTimeouts() | |||||
{ | |||||
int timeout = 0; | |||||
std::list<VNCSConnectionST*>::iterator ci, ci_next; | |||||
soonestTimeout(&timeout, Timer::checkTimeouts()); | |||||
for (ci=clients.begin();ci!=clients.end();ci=ci_next) { | |||||
ci_next = ci; ci_next++; | |||||
soonestTimeout(&timeout, (*ci)->checkIdleTimeout()); | |||||
} | |||||
int timeLeft; | |||||
time_t now = time(0); | |||||
// Check MaxDisconnectionTime | |||||
if (rfb::Server::maxDisconnectionTime && clients.empty()) { | |||||
if (now < lastDisconnectTime) { | |||||
// Someone must have set the time backwards. | |||||
slog.info("Time has gone backwards - resetting lastDisconnectTime"); | |||||
lastDisconnectTime = now; | |||||
} | |||||
timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now; | |||||
if (timeLeft < -60) { | |||||
// Someone must have set the time forwards. | |||||
slog.info("Time has gone forwards - resetting lastDisconnectTime"); | |||||
lastDisconnectTime = now; | |||||
timeLeft = rfb::Server::maxDisconnectionTime; | |||||
} | |||||
if (timeLeft <= 0) { | |||||
slog.info("MaxDisconnectionTime reached, exiting"); | |||||
exit(0); | |||||
} | |||||
soonestTimeout(&timeout, timeLeft * 1000); | |||||
} | |||||
// Check MaxConnectionTime | |||||
if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) { | |||||
if (now < lastConnectionTime) { | |||||
// Someone must have set the time backwards. | |||||
slog.info("Time has gone backwards - resetting lastConnectionTime"); | |||||
lastConnectionTime = now; | |||||
} | |||||
timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now; | |||||
if (timeLeft < -60) { | |||||
// Someone must have set the time forwards. | |||||
slog.info("Time has gone forwards - resetting lastConnectionTime"); | |||||
lastConnectionTime = now; | |||||
timeLeft = rfb::Server::maxConnectionTime; | |||||
} | |||||
if (timeLeft <= 0) { | |||||
slog.info("MaxConnectionTime reached, exiting"); | |||||
exit(0); | |||||
} | |||||
soonestTimeout(&timeout, timeLeft * 1000); | |||||
} | |||||
// Check MaxIdleTime | |||||
if (rfb::Server::maxIdleTime) { | |||||
if (now < lastUserInputTime) { | |||||
// Someone must have set the time backwards. | |||||
slog.info("Time has gone backwards - resetting lastUserInputTime"); | |||||
lastUserInputTime = now; | |||||
} | |||||
timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now; | |||||
if (timeLeft < -60) { | |||||
// Someone must have set the time forwards. | |||||
slog.info("Time has gone forwards - resetting lastUserInputTime"); | |||||
lastUserInputTime = now; | |||||
timeLeft = rfb::Server::maxIdleTime; | |||||
} | |||||
if (timeLeft <= 0) { | |||||
slog.info("MaxIdleTime reached, exiting"); | |||||
exit(0); | |||||
} | |||||
soonestTimeout(&timeout, timeLeft * 1000); | |||||
} | |||||
return timeout; | |||||
} | |||||
// VNCServer methods | // VNCServer methods | ||||
void VNCServerST::blockUpdates() | void VNCServerST::blockUpdates() | ||||
void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | ||||
{ | { | ||||
lastUserInputTime = time(0); | |||||
if (rfb::Server::maxIdleTime) | |||||
idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); | |||||
// Remap the key if required | // Remap the key if required | ||||
if (keyRemapper) { | if (keyRemapper) { | ||||
void VNCServerST::pointerEvent(VNCSConnectionST* client, | void VNCServerST::pointerEvent(VNCSConnectionST* client, | ||||
const Point& pos, int buttonMask) | const Point& pos, int buttonMask) | ||||
{ | { | ||||
lastUserInputTime = time(0); | |||||
if (rfb::Server::maxIdleTime) | |||||
idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); | |||||
// Let one client own the cursor whilst buttons are pressed in order | // Let one client own the cursor whilst buttons are pressed in order | ||||
// to provide a bit more sane user experience | // to provide a bit more sane user experience | ||||
} | } | ||||
return true; | return true; | ||||
} else if (t == &idleTimer) { | |||||
slog.info("MaxIdleTime reached, exiting"); | |||||
desktop->terminate(); | |||||
} else if (t == &disconnectTimer) { | |||||
slog.info("MaxDisconnectionTime reached, exiting"); | |||||
desktop->terminate(); | |||||
} else if (t == &connectTimer) { | |||||
slog.info("MaxConnectionTime reached, exiting"); | |||||
desktop->terminate(); | |||||
} | } | ||||
return false; | return false; |
// Flush pending data from the Socket on to the network. | // Flush pending data from the Socket on to the network. | ||||
virtual void processSocketWriteEvent(network::Socket* sock); | virtual void processSocketWriteEvent(network::Socket* sock); | ||||
// checkTimeouts | |||||
// Returns the number of milliseconds left until the next idle timeout | |||||
// expires. If any have already expired, the corresponding connections | |||||
// are closed. Zero is returned if there is no idle timeout. | |||||
virtual int checkTimeouts(); | |||||
// Methods overridden from VNCServer | // Methods overridden from VNCServer | ||||
KeyRemapper* keyRemapper; | KeyRemapper* keyRemapper; | ||||
time_t lastUserInputTime; | |||||
time_t lastDisconnectTime; | |||||
time_t lastConnectionTime; | |||||
Timer idleTimer; | |||||
Timer disconnectTimer; | |||||
Timer connectTimer; | |||||
Timer frameTimer; | Timer frameTimer; | ||||
}; | }; |
*/ | */ | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <signal.h> | |||||
#include <unistd.h> | |||||
#include <rfb/LogWriter.h> | #include <rfb/LogWriter.h> | ||||
pb = 0; | pb = 0; | ||||
} | } | ||||
void XDesktop::terminate() { | |||||
kill(getpid(), SIGTERM); | |||||
} | |||||
bool XDesktop::isRunning() { | bool XDesktop::isRunning() { | ||||
return running; | return running; | ||||
} | } |
// -=- SDesktop interface | // -=- SDesktop interface | ||||
virtual void start(rfb::VNCServer* vs); | virtual void start(rfb::VNCServer* vs); | ||||
virtual void stop(); | virtual void stop(); | ||||
virtual void terminate(); | |||||
bool isRunning(); | bool isRunning(); | ||||
virtual void queryConnection(network::Socket* sock, | virtual void queryConnection(network::Socket* sock, | ||||
const char* userName); | const char* userName); |
} | } | ||||
} | } | ||||
soonestTimeout(&wait_ms, server.checkTimeouts()); | |||||
soonestTimeout(&wait_ms, Timer::checkTimeouts()); | |||||
tv.tv_sec = wait_ms / 1000; | tv.tv_sec = wait_ms / 1000; | ||||
tv.tv_usec = (wait_ms % 1000) * 1000; | tv.tv_usec = (wait_ms % 1000) * 1000; | ||||
} | } | ||||
} | } | ||||
server.checkTimeouts(); | |||||
Timer::checkTimeouts(); | |||||
// Client list could have been changed. | // Client list could have been changed. | ||||
server.getSockets(&sockets); | server.getSockets(&sockets); |
// | // | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <signal.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <strings.h> | #include <strings.h> | ||||
} | } | ||||
// Trigger timers and check when the next will expire | // Trigger timers and check when the next will expire | ||||
int nextTimeout = server->checkTimeouts(); | |||||
int nextTimeout = Timer::checkTimeouts(); | |||||
if (nextTimeout > 0 && (*timeout == -1 || nextTimeout < *timeout)) | if (nextTimeout > 0 && (*timeout == -1 || nextTimeout < *timeout)) | ||||
*timeout = nextTimeout; | *timeout = nextTimeout; | ||||
} catch (rdr::Exception& e) { | } catch (rdr::Exception& e) { | ||||
// SDesktop callbacks | // SDesktop callbacks | ||||
void XserverDesktop::terminate() | |||||
{ | |||||
kill(getpid(), SIGTERM); | |||||
} | |||||
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) | void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) | ||||
{ | { | ||||
vncPointerMove(pos.x + vncGetScreenX(screenIndex), | vncPointerMove(pos.x + vncGetScreenX(screenIndex), |
// rfb::SDesktop callbacks | // rfb::SDesktop callbacks | ||||
virtual void start(rfb::VNCServer* vs); | virtual void start(rfb::VNCServer* vs); | ||||
virtual void stop(); | virtual void stop(); | ||||
virtual void terminate(); | |||||
virtual void queryConnection(network::Socket* sock, | virtual void queryConnection(network::Socket* sock, | ||||
const char* userName); | const char* userName); | ||||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask); | virtual void pointerEvent(const rfb::Point& pos, int buttonMask); |
statusLocation(0), queryConnectionHandler(0), ledState(0) | statusLocation(0), queryConnectionHandler(0), ledState(0) | ||||
{ | { | ||||
updateEvent.h = CreateEvent(0, TRUE, FALSE, 0); | updateEvent.h = CreateEvent(0, TRUE, FALSE, 0); | ||||
terminateEvent.h = CreateEvent(0, TRUE, FALSE, 0); | |||||
} | } | ||||
SDisplay::~SDisplay() | SDisplay::~SDisplay() | ||||
if (statusLocation) *statusLocation = false; | if (statusLocation) *statusLocation = false; | ||||
} | } | ||||
void SDisplay::terminate() | |||||
{ | |||||
SetEvent(terminateEvent); | |||||
} | |||||
void SDisplay::queryConnection(network::Socket* sock, | void SDisplay::queryConnection(network::Socket* sock, | ||||
const char* userName) | const char* userName) |
virtual void start(VNCServer* vs); | virtual void start(VNCServer* vs); | ||||
virtual void stop(); | virtual void stop(); | ||||
virtual void terminate(); | |||||
virtual void queryConnection(network::Socket* sock, | virtual void queryConnection(network::Socket* sock, | ||||
const char* userName); | const char* userName); | ||||
virtual void pointerEvent(const Point& pos, int buttonmask); | virtual void pointerEvent(const Point& pos, int buttonmask); | ||||
// -=- EventHandler interface | // -=- EventHandler interface | ||||
HANDLE getUpdateEvent() {return updateEvent;} | HANDLE getUpdateEvent() {return updateEvent;} | ||||
HANDLE getTerminateEvent() {return terminateEvent;} | |||||
virtual void processEvent(HANDLE event); | virtual void processEvent(HANDLE event); | ||||
// -=- Notification of whether or not SDisplay is started | // -=- Notification of whether or not SDisplay is started | ||||
// -=- Event signalled to trigger an update to be flushed | // -=- Event signalled to trigger an update to be flushed | ||||
Handle updateEvent; | Handle updateEvent; | ||||
// -=- Event signalled to terminate the server | |||||
Handle terminateEvent; | |||||
// -=- Where to write the active/inactive indicator to | // -=- Where to write the active/inactive indicator to | ||||
bool* statusLocation; | bool* statusLocation; |
#include <winsock2.h> | #include <winsock2.h> | ||||
#include <list> | #include <list> | ||||
#include <rfb/LogWriter.h> | #include <rfb/LogWriter.h> | ||||
#include <rfb/Timer.h> | |||||
#include <rfb_win32/SocketManager.h> | #include <rfb_win32/SocketManager.h> | ||||
using namespace rfb; | using namespace rfb; | ||||
std::map<HANDLE,ListenInfo>::iterator i; | std::map<HANDLE,ListenInfo>::iterator i; | ||||
for (i=listeners.begin(); i!=listeners.end(); i++) | for (i=listeners.begin(); i!=listeners.end(); i++) | ||||
soonestTimeout(&timeout, i->second.server->checkTimeouts()); | |||||
soonestTimeout(&timeout, Timer::checkTimeouts()); | |||||
std::list<network::Socket*> shutdownSocks; | std::list<network::Socket*> shutdownSocks; | ||||
std::map<HANDLE,ConnInfo>::iterator j, j_next; | std::map<HANDLE,ConnInfo>::iterator j, j_next; |
// Register the desktop's event to be handled | // Register the desktop's event to be handled | ||||
sockMgr.addEvent(desktop.getUpdateEvent(), &desktop); | sockMgr.addEvent(desktop.getUpdateEvent(), &desktop); | ||||
sockMgr.addEvent(desktop.getTerminateEvent(), this); | |||||
// Register the queued command event to be handled | // Register the queued command event to be handled | ||||
sockMgr.addEvent(commandEvent, this); | sockMgr.addEvent(commandEvent, this); | ||||
command = NoCommand; | command = NoCommand; | ||||
commandSig->signal(); | commandSig->signal(); | ||||
} | } | ||||
} else if (event_ == sessionEvent.h) { | |||||
} else if ((event_ == sessionEvent.h) || | |||||
(event_ == desktop.getTerminateEvent())) { | |||||
stop(); | stop(); | ||||
} | } | ||||
} | } |