diff options
author | Pierre Ossman <ossman@cendio.se> | 2024-06-19 16:45:12 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2024-06-19 16:45:12 +0200 |
commit | 858a859e59dcd794f0e6d50030e183be6ded0f7c (patch) | |
tree | d5247bbb8f30932544f86ebbbab9b2161fe4c1b5 /common | |
parent | 78510b981b211e29a06a30fa091b08070429b829 (diff) | |
parent | 28c3f121613807df6d53dde9ac653916dcf8902d (diff) | |
download | tigervnc-858a859e59dcd794f0e6d50030e183be6ded0f7c.tar.gz tigervnc-858a859e59dcd794f0e6d50030e183be6ded0f7c.zip |
Merge branch 'present' of github.com:CendioOssman/tigervnc
Diffstat (limited to 'common')
-rw-r--r-- | common/rfb/EncodeManager.cxx | 6 | ||||
-rw-r--r-- | common/rfb/EncodeManager.h | 2 | ||||
-rw-r--r-- | common/rfb/SConnection.cxx | 8 | ||||
-rw-r--r-- | common/rfb/SConnection.h | 2 | ||||
-rw-r--r-- | common/rfb/SDesktop.h | 25 | ||||
-rw-r--r-- | common/rfb/Timer.cxx | 67 | ||||
-rw-r--r-- | common/rfb/Timer.h | 77 | ||||
-rw-r--r-- | common/rfb/VNCSConnectionST.cxx | 4 | ||||
-rw-r--r-- | common/rfb/VNCSConnectionST.h | 2 | ||||
-rw-r--r-- | common/rfb/VNCServer.h | 3 | ||||
-rw-r--r-- | common/rfb/VNCServerST.cxx | 80 | ||||
-rw-r--r-- | common/rfb/VNCServerST.h | 5 | ||||
-rw-r--r-- | common/rfb/util.h | 7 |
13 files changed, 173 insertions, 115 deletions
diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index bc15e74a..c2658a70 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -297,7 +297,7 @@ void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* p Region(), Point(), pb, renderedCursor); } -bool EncodeManager::handleTimeout(Timer* t) +void EncodeManager::handleTimeout(Timer* t) { if (t == &recentChangeTimer) { // Any lossy region that wasn't recently updated can @@ -307,10 +307,8 @@ bool EncodeManager::handleTimeout(Timer* t) // Will there be more to do? (i.e. do we need another round) if (!lossyRegion.subtract(pendingRefreshRegion).is_empty()) - return true; + t->repeat(); } - - return false; } void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h index f2fd4ca4..33484db8 100644 --- a/common/rfb/EncodeManager.h +++ b/common/rfb/EncodeManager.h @@ -61,7 +61,7 @@ namespace rfb { size_t maxUpdateSize); protected: - virtual bool handleTimeout(Timer* t); + virtual void handleTimeout(Timer* t); void doUpdate(bool allowLossy, const Region& changed, const Region& copied, const Point& copy_delta, diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index 7a930af5..12ba0f1a 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -275,11 +275,11 @@ bool SConnection::processInitMsg() return reader_->readClientInit(); } -bool SConnection::handleAuthFailureTimeout(Timer* /*t*/) +void SConnection::handleAuthFailureTimeout(Timer* /*t*/) { if (state_ != RFBSTATE_SECURITY_FAILURE) { close("SConnection::handleAuthFailureTimeout: invalid state"); - return false; + return; } try { @@ -292,12 +292,10 @@ bool SConnection::handleAuthFailureTimeout(Timer* /*t*/) os->flush(); } catch (rdr::Exception& e) { close(e.str()); - return false; + return; } close(authFailureMsg.c_str()); - - return false; } void SConnection::throwConnFailedException(const char* format, ...) diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index cc88cd1e..5bc61677 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -238,7 +238,7 @@ namespace rfb { bool processSecurityFailure(); bool processInitMsg(); - bool handleAuthFailureTimeout(Timer* t); + void handleAuthFailureTimeout(Timer* t); int defaultMajorVersion, defaultMinorVersion; diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index 9db08116..94e4b028 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2019 Pierre Ossman for Cendio AB + * Copyright 2009-2024 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,20 +49,21 @@ namespace rfb { class SDesktop : public InputHandler { public: + // init() is called immediately when the VNCServer gets a reference + // to the SDesktop, so that a reverse reference can be set up. + virtual void init(rfb::VNCServer* vs) = 0; + // start() is called by the server when the first client authenticates // successfully, and can be used to begin any expensive tasks which are not // needed when there are no clients. A valid PixelBuffer must have been // set via the VNCServer's setPixelBuffer() method by the time this call // returns. - - virtual void start(VNCServer* vs) = 0; + virtual void start() {} // stop() is called by the server when there are no longer any // authenticated clients, and therefore the desktop can cease any - // expensive tasks. No further calls to the VNCServer passed to start() - // can be made once stop has returned. - - virtual void stop() = 0; + // expensive tasks. + virtual void stop() {} // queryConnection() is called when a connection has been // successfully authenticated. The sock and userName arguments @@ -86,6 +87,10 @@ namespace rfb { return resultProhibited; } + // frameTick() is called whenever a frame update has been processed, + // signalling that a good time to render new data + virtual void frameTick(uint64_t msc) { (void)msc; } + // InputHandler interface // pointerEvent(), keyEvent() and clientCutText() are called in response to // the relevant RFB protocol messages from clients. @@ -136,14 +141,10 @@ namespace rfb { if (buffer) delete buffer; } - virtual void start(VNCServer* vs) { + virtual void init(VNCServer* vs) { server = vs; server->setPixelBuffer(buffer); } - virtual void stop() { - server->setPixelBuffer(0); - server = 0; - } virtual void queryConnection(network::Socket* sock, const char* /*userName*/) { server->approveConnection(sock, true, NULL); diff --git a/common/rfb/Timer.cxx b/common/rfb/Timer.cxx index 4ff15bc5..e9ae5227 100644 --- a/common/rfb/Timer.cxx +++ b/common/rfb/Timer.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2016-2018 Pierre Ossman for Cendio AB + * Copyright 2016-2024 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -61,36 +61,20 @@ int Timer::checkTimeouts() { timeval start; if (pending.empty()) - return 0; + return -1; gettimeofday(&start, 0); while (pending.front()->isBefore(start)) { Timer* timer; - timeval before; timer = pending.front(); pending.pop_front(); - gettimeofday(&before, 0); - if (timer->cb->handleTimeout(timer)) { - timeval now; + timer->lastDueTime = timer->dueTime; + timer->cb->handleTimeout(timer); - gettimeofday(&now, 0); - - timer->dueTime = addMillis(timer->dueTime, timer->timeoutMs); - if (timer->isBefore(now)) { - // Time has jumped forwards, or we're not getting enough - // CPU time for the timers - - timer->dueTime = addMillis(before, timer->timeoutMs); - if (timer->isBefore(now)) - timer->dueTime = now; - } - - insertTimer(timer); - } else if (pending.empty()) { - return 0; - } + if (pending.empty()) + return -1; } return getNextTimeout(); } @@ -98,7 +82,12 @@ int Timer::checkTimeouts() { int Timer::getNextTimeout() { timeval now; gettimeofday(&now, 0); - int toWait = __rfbmax(1, pending.front()->getRemainingMs()); + + if (pending.empty()) + return -1; + + int toWait = pending.front()->getRemainingMs(); + if (toWait > pending.front()->timeoutMs) { if (toWait - pending.front()->timeoutMs < 1000) { vlog.info("gettimeofday is broken..."); @@ -107,8 +96,9 @@ int Timer::getNextTimeout() { // Time has jumped backwards! vlog.info("time has moved backwards!"); pending.front()->dueTime = now; - toWait = 1; + toWait = 0; } + return toWait; } @@ -128,13 +118,36 @@ void Timer::start(int timeoutMs_) { gettimeofday(&now, 0); stop(); timeoutMs = timeoutMs_; - // The rest of the code assumes non-zero timeout - if (timeoutMs <= 0) - timeoutMs = 1; dueTime = addMillis(now, timeoutMs); insertTimer(this); } +void Timer::repeat(int timeoutMs_) { + timeval now; + + gettimeofday(&now, 0); + + if (isStarted()) { + vlog.error("Incorrectly repeating already running timer"); + stop(); + } + + if (msBetween(&lastDueTime, &dueTime) != 0) + vlog.error("Timer incorrectly modified whilst repeating"); + + if (timeoutMs_ != -1) + timeoutMs = timeoutMs_; + + dueTime = addMillis(lastDueTime, timeoutMs); + if (isBefore(now)) { + // Time has jumped forwards, or we're not getting enough + // CPU time for the timers + dueTime = now; + } + + insertTimer(this); +} + void Timer::stop() { pending.remove(this); } diff --git a/common/rfb/Timer.h b/common/rfb/Timer.h index ddfce1b2..36ec46c5 100644 --- a/common/rfb/Timer.h +++ b/common/rfb/Timer.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2018 Pierre Ossman for Cendio AB + * Copyright 2018-2024 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,93 +27,108 @@ namespace rfb { /* Timer - Cross-platform timeout handling. The caller creates instances of Timer and passes a - Callback implementation to each. The Callback will then be called with a pointer to - the Timer instance that timed-out when the timeout occurs. + Cross-platform timeout handling. The caller creates instances of + Timer and passes a Callback implementation to each. The Callback + will then be called with a pointer to the Timer instance that + timed-out when the timeout occurs. - The static methods of Timer are used by the main loop of the application both to - dispatch elapsed Timer callbacks and to determine how long to wait in select() for - the next timeout to occur. + The static methods of Timer are used by the main loop of the + application both to dispatch elapsed Timer callbacks and to + determine how long to wait in select() for the next timeout to + occur. - For classes that can be derived it's best to use MethodTimer which can call a specific - method on the class, thus avoiding conflicts when subclassing. + For classes that can be derived it's best to use MethodTimer which + can call a specific method on the class, thus avoiding conflicts + when subclassing. */ struct Timer { struct Callback { // handleTimeout - // Passed a pointer to the Timer that has timed out. If the handler returns true - // then the Timer is reset and left running, causing another timeout after the - // appropriate interval. + // Passed a pointer to the Timer that has timed out. If the + // handler returns true then the Timer is reset and left + // running, causing another timeout after the appropriate + // interval. // If the handler returns false then the Timer is cancelled. - virtual bool handleTimeout(Timer* t) = 0; + virtual void handleTimeout(Timer* t) = 0; virtual ~Callback() {} }; // checkTimeouts() - // Dispatches any elapsed Timers, and returns the number of milliseconds until the - // next Timer will timeout. + // Dispatches any elapsed Timers, and returns the number of + // milliseconds until the next Timer will timeout. static int checkTimeouts(); // getNextTimeout() - // Returns the number of milliseconds until the next timeout, without dispatching - // any elapsed Timers. + // Returns the number of milliseconds until the next timeout, + // without dispatching any elapsed Timers. static int getNextTimeout(); // Create a Timer with the specified callback handler Timer(Callback* cb_) {cb = cb_;} ~Timer() {stop();} - // startTimer - // Starts the timer, causing a timeout after the specified number of milliseconds. - // If the timer is already active then it will be implicitly cancelled and re-started. + // start() + // Starts the timer, causing a timeout after the specified number + // of milliseconds. If the timer is already active then it will + // be implicitly cancelled and re-started. void start(int timeoutMs_); - // stopTimer + // repeat() + // Restarts the timer in a way that repeats that last timeout. + // This allows you to have a periodic timer without the risk of + // accumulating drift caused by processing delays. + // A new interval can be specified, otherwise the previous + // interval is reused. + void repeat(int timeoutMs_=-1); + + // stop() // Cancels the timer. void stop(); - // isStarted + // isStarted() // Determines whether the timer is started. bool isStarted(); - // getTimeoutMs + // getTimeoutMs() // Determines the previously used timeout value, if any. // Usually used with isStarted() to get the _current_ timeout. int getTimeoutMs(); - // getRemainingMs + // getRemainingMs() // Determines how many milliseconds are left before the Timer // will timeout. Only valid for an active timer. int getRemainingMs(); - // isBefore - // Determine whether the Timer will timeout before the specified time. + // isBefore() + // Determine whether the Timer will timeout before the specified + // time. bool isBefore(timeval other); protected: - timeval dueTime; + timeval dueTime, lastDueTime; int timeoutMs; Callback* cb; static void insertTimer(Timer* t); - // The list of currently active Timers, ordered by time left until timeout. + // The list of currently active Timers, ordered by time left until + // timeout. static std::list<Timer*> pending; }; template<class T> class MethodTimer : public Timer, public Timer::Callback { public: - MethodTimer(T* obj_, bool (T::*cb_)(Timer*)) + MethodTimer(T* obj_, void (T::*cb_)(Timer*)) : Timer(this), obj(obj_), cb(cb_) {} - virtual bool handleTimeout(Timer* t) { return (obj->*cb)(t); } + virtual void handleTimeout(Timer* t) { (obj->*cb)(t); } private: T* obj; - bool (T::*cb)(Timer*); + void (T::*cb)(Timer*); }; }; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index f1194eb6..306bba1d 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -800,7 +800,7 @@ void VNCSConnectionST::supportsLEDState() writer()->writeLEDState(); } -bool VNCSConnectionST::handleTimeout(Timer* t) +void VNCSConnectionST::handleTimeout(Timer* t) { try { if ((t == &congestionTimer) || @@ -812,8 +812,6 @@ bool VNCSConnectionST::handleTimeout(Timer* t) if (t == &idleTimer) close("Idle timeout"); - - return false; } bool VNCSConnectionST::isShiftPressed() diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 2f117a75..3a9ec242 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -141,7 +141,7 @@ namespace rfb { virtual void supportsLEDState(); // Timer callbacks - virtual bool handleTimeout(Timer* t); + virtual void handleTimeout(Timer* t); // Internal methods diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index cf14bd86..3ac9fb94 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -73,6 +73,9 @@ namespace rfb { virtual void blockUpdates() = 0; virtual void unblockUpdates() = 0; + virtual uint64_t getMsc() = 0; + virtual void queueMsc(uint64_t target) = 0; + // setPixelBuffer() tells the server to use the given pixel buffer (and // optionally a modified screen layout). If this differs in size from // the previous pixel buffer, this may result in protocol messages being diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index fb421068..b9579f12 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2019 Pierre Ossman for Cendio AB + * Copyright 2009-2024 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -90,10 +90,12 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) renderedCursorInvalid(false), keyRemapper(&KeyRemapper::defInstance), idleTimer(this), disconnectTimer(this), connectTimer(this), - frameTimer(this) + msc(0), queuedMsc(0), frameTimer(this) { slog.debug("creating single-threaded server %s", name.c_str()); + desktop_->init(this); + // FIXME: Do we really want to kick off these right away? if (rfb::Server::maxIdleTime) idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); @@ -248,11 +250,22 @@ void VNCServerST::unblockUpdates() blockCounter--; - // Restart the frame clock if we have updates - if (blockCounter == 0) { - if (!comparer->is_empty()) - startFrameClock(); - } + // Restart the frame clock in case we have updates + if (blockCounter == 0) + startFrameClock(); +} + +uint64_t VNCServerST::getMsc() +{ + return msc; +} + +void VNCServerST::queueMsc(uint64_t target) +{ + if (target > queuedMsc) + queuedMsc = target; + + startFrameClock(); } void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout) @@ -623,22 +636,33 @@ SConnection* VNCServerST::getConnection(network::Socket* sock) { return 0; } -bool VNCServerST::handleTimeout(Timer* t) +void VNCServerST::handleTimeout(Timer* t) { if (t == &frameTimer) { - // We keep running until we go a full interval without any updates - if (comparer->is_empty()) - return false; + int timeout; - writeUpdate(); + // We keep running until we go a full interval without any updates, + // or there are no active clients anymore + if (comparer->is_empty() || !desktopStarted) { + // Unless something waits for us to advance the frame count + if (queuedMsc < msc) + return; + } // If this is the first iteration then we need to adjust the timeout - if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) { - frameTimer.start(1000/rfb::Server::frameRate); - return false; - } + timeout = 1000/rfb::Server::frameRate; - return true; + // If there are no clients, then slow down the clock + if (!desktopStarted) + timeout = 1000; + + frameTimer.repeat(timeout); + + if (!comparer->is_empty() && desktopStarted) + writeUpdate(); + + msc++; + desktop->frameTick(msc); } else if (t == &idleTimer) { slog.info("MaxIdleTime reached, exiting"); desktop->terminate(); @@ -649,8 +673,6 @@ bool VNCServerST::handleTimeout(Timer* t) slog.info("MaxConnectionTime reached, exiting"); desktop->terminate(); } - - return false; } void VNCServerST::queryConnection(VNCSConnectionST* client, @@ -714,7 +736,7 @@ void VNCServerST::startDesktop() { if (!desktopStarted) { slog.debug("starting desktop"); - desktop->start(this); + desktop->start(); if (!pb) throw Exception("SDesktop::start() did not set a valid PixelBuffer"); desktopStarted = true; @@ -722,6 +744,12 @@ void VNCServerST::startDesktop() // stopped, so flush those out if (!comparer->is_empty()) writeUpdate(); + // If the frame clock is running, then it will be running slowly, + // so give it a kick to run at normal speed right away + if (frameTimer.isStarted()) { + stopFrameClock(); + startFrameClock(); + } } } @@ -731,7 +759,6 @@ void VNCServerST::stopDesktop() slog.debug("stopping desktop"); desktopStarted = false; desktop->stop(); - stopFrameClock(); } } @@ -759,9 +786,18 @@ void VNCServerST::startFrameClock() return; if (blockCounter > 0) return; - if (!desktopStarted) + + // Anyone actually interested in frames? + if (comparer->is_empty() && (queuedMsc <= msc)) return; + // Run the frame clock very slowly if there are no clients to actually + // send updates to + if (!desktopStarted) { + frameTimer.start(1000); + return; + } + // The first iteration will be just half a frame as we get a very // unstable update rate if we happen to be perfectly in sync with // the application's update rate diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index d303831e..90c8d753 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -79,6 +79,8 @@ namespace rfb { virtual void blockUpdates(); virtual void unblockUpdates(); + virtual uint64_t getMsc(); + virtual void queueMsc(uint64_t target); virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout); virtual void setPixelBuffer(PixelBuffer* pb); virtual void setScreenLayout(const ScreenSet& layout); @@ -153,7 +155,7 @@ namespace rfb { protected: // Timer callbacks - virtual bool handleTimeout(Timer* t); + virtual void handleTimeout(Timer* t); // - Internal methods @@ -204,6 +206,7 @@ namespace rfb { Timer disconnectTimer; Timer connectTimer; + uint64_t msc, queuedMsc; Timer frameTimer; }; diff --git a/common/rfb/util.h b/common/rfb/util.h index cafea209..b47ac4c9 100644 --- a/common/rfb/util.h +++ b/common/rfb/util.h @@ -73,13 +73,6 @@ namespace rfb { // HELPER functions for timeout handling - // soonestTimeout() is a function to help work out the soonest of several - // timeouts. - inline void soonestTimeout(int* timeout, int newTimeout) { - if (newTimeout && (!*timeout || newTimeout < *timeout)) - *timeout = newTimeout; - } - // secsToMillis() turns seconds into milliseconds, capping the value so it // can't wrap round and become -ve inline int secsToMillis(int secs) { |