aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2018-04-20 13:57:41 +0200
committerPierre Ossman <ossman@cendio.se>2018-04-20 13:57:41 +0200
commit105177fc07d07521eafdcf373c1825a59b9c554d (patch)
treeb88c95aceee02386c05faff686c7f550d901bad4 /common
parentfa646bc6e2fdabae4b8f85829691f33327baea16 (diff)
parent8efc7b47cc744b3ee4b7ae20f88c9e3aa775eefe (diff)
downloadtigervnc-105177fc07d07521eafdcf373c1825a59b9c554d.tar.gz
tigervnc-105177fc07d07521eafdcf373c1825a59b9c554d.zip
Merge branch 'alr' of https://github.com/CendioOssman/tigervnc
Diffstat (limited to 'common')
-rw-r--r--common/rfb/Congestion.cxx15
-rw-r--r--common/rfb/Congestion.h8
-rw-r--r--common/rfb/EncodeManager.cxx140
-rw-r--r--common/rfb/EncodeManager.h23
-rw-r--r--common/rfb/Encoder.h2
-rw-r--r--common/rfb/Region.cxx4
-rw-r--r--common/rfb/Region.h1
-rw-r--r--common/rfb/TightJPEGEncoder.cxx2
-rw-r--r--common/rfb/Timer.cxx10
-rw-r--r--common/rfb/Timer.h6
-rw-r--r--common/rfb/VNCSConnectionST.cxx94
-rw-r--r--common/rfb/VNCServerST.cxx53
-rw-r--r--common/rfb/VNCServerST.h4
13 files changed, 269 insertions, 93 deletions
diff --git a/common/rfb/Congestion.cxx b/common/rfb/Congestion.cxx
index a2f7a256..4d36d9f1 100644
--- a/common/rfb/Congestion.cxx
+++ b/common/rfb/Congestion.cxx
@@ -1,4 +1,4 @@
-/* Copyright 2009-2015 Pierre Ossman for Cendio AB
+/* Copyright 2009-2018 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
@@ -73,7 +73,7 @@ static LogWriter vlog("Congestion");
Congestion::Congestion() :
lastPosition(0), extraBuffer(0),
baseRTT(-1), congWindow(INITIAL_WINDOW), inSlowStart(true),
- measurements(0), minRTT(-1), minCongestedRTT(-1)
+ safeBaseRTT(-1), measurements(0), minRTT(-1), minCongestedRTT(-1)
{
gettimeofday(&lastUpdate, NULL);
gettimeofday(&lastSent, NULL);
@@ -170,7 +170,7 @@ void Congestion::gotPong()
// Try to estimate wire latency by tracking lowest seen latency
if (rtt < baseRTT)
- baseRTT = rtt;
+ safeBaseRTT = baseRTT = rtt;
// Pings sent before the last adjustment aren't interesting as they
// aren't a measurement of the current congestion window
@@ -284,6 +284,15 @@ int Congestion::getUncongestedETA()
}
}
+size_t Congestion::getBandwidth()
+{
+ // No measurements yet? Guess RTT of 60 ms
+ if (safeBaseRTT == (unsigned)-1)
+ return congWindow * 1000 / 60;
+
+ return congWindow * 1000 / safeBaseRTT;
+}
+
void Congestion::debugTrace(const char* filename, int fd)
{
#ifdef CONGESTION_TRACE
diff --git a/common/rfb/Congestion.h b/common/rfb/Congestion.h
index fd57c22e..d2935128 100644
--- a/common/rfb/Congestion.h
+++ b/common/rfb/Congestion.h
@@ -1,4 +1,4 @@
-/* Copyright 2009-2015 Pierre Ossman for Cendio AB
+/* Copyright 2009-2018 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
@@ -47,6 +47,10 @@ namespace rfb {
// longer be congested.
int getUncongestedETA();
+ // getBandwidth() returns the current bandwidth estimation in bytes
+ // per second.
+ size_t getBandwidth();
+
// debugTrace() writes the current congestion window, as well as the
// congestion window of the underlying TCP layer, to the specified
// file
@@ -68,6 +72,8 @@ namespace rfb {
unsigned congWindow;
bool inSlowStart;
+ unsigned safeBaseRTT;
+
struct RTTInfo {
struct timeval tv;
unsigned pos;
diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx
index 0cd52060..0ce611e9 100644
--- a/common/rfb/EncodeManager.cxx
+++ b/common/rfb/EncodeManager.cxx
@@ -1,6 +1,6 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB
+ * Copyright 2014-2018 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
@@ -17,6 +17,9 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
+
+#include <stdlib.h>
+
#include <rfb/EncodeManager.h>
#include <rfb/Encoder.h>
#include <rfb/Palette.h>
@@ -245,52 +248,79 @@ bool EncodeManager::supported(int encoding)
}
}
+bool EncodeManager::needsLosslessRefresh(const Region& req)
+{
+ return !lossyRegion.intersect(req).is_empty();
+}
+
+void EncodeManager::pruneLosslessRefresh(const Region& limits)
+{
+ lossyRegion.assign_intersect(limits);
+}
+
void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor)
{
+ doUpdate(true, ui.changed, ui.copied, ui.copy_delta, pb, renderedCursor);
+}
+
+void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor,
+ size_t maxUpdateSize)
+{
+ doUpdate(false, getLosslessRefresh(req, maxUpdateSize),
+ Region(), Point(), pb, renderedCursor);
+}
+
+void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
+ const Region& copied, const Point& copyDelta,
+ const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor)
+{
int nRects;
- Region changed;
+ Region changed, cursorRegion;
updates++;
- prepareEncoders();
+ prepareEncoders(allowLossy);
+
+ changed = changed_;
+
+ /*
+ * We need to render the cursor seperately as it has its own
+ * magical pixel buffer, so split it out from the changed region.
+ */
+ if (renderedCursor != NULL) {
+ cursorRegion = changed.intersect(renderedCursor->getEffectiveRect());
+ changed.assign_subtract(renderedCursor->getEffectiveRect());
+ }
if (conn->cp.supportsLastRect)
nRects = 0xFFFF;
else {
- nRects = ui.copied.numRects();
- nRects += computeNumRects(ui.changed);
-
- if (renderedCursor != NULL)
- nRects += 1;
+ nRects = copied.numRects();
+ nRects += computeNumRects(changed);
+ nRects += computeNumRects(cursorRegion);
}
conn->writer()->writeFramebufferUpdateStart(nRects);
- writeCopyRects(ui);
+ writeCopyRects(copied, copyDelta);
/*
* We start by searching for solid rects, which are then removed
* from the changed region.
*/
- changed.copyFrom(ui.changed);
-
if (conn->cp.supportsLastRect)
writeSolidRects(&changed, pb);
writeRects(changed, pb);
-
- if (renderedCursor != NULL) {
- Rect renderedCursorRect;
-
- renderedCursorRect = renderedCursor->getEffectiveRect();
- writeSubRect(renderedCursorRect, renderedCursor);
- }
+ writeRects(cursorRegion, renderedCursor);
conn->writer()->writeFramebufferUpdateEnd();
}
-void EncodeManager::prepareEncoders()
+void EncodeManager::prepareEncoders(bool allowLossy)
{
enum EncoderClass solid, bitmap, bitmapRLE;
enum EncoderClass indexed, indexedRLE, fullColour;
@@ -315,7 +345,7 @@ void EncodeManager::prepareEncoders()
break;
case encodingTight:
if (encoders[encoderTightJPEG]->isSupported() &&
- (conn->cp.pf().bpp >= 16))
+ (conn->cp.pf().bpp >= 16) && allowLossy)
fullColour = encoderTightJPEG;
else
fullColour = encoderTight;
@@ -333,7 +363,7 @@ void EncodeManager::prepareEncoders()
if (fullColour == encoderRaw) {
if (encoders[encoderTightJPEG]->isSupported() &&
- (conn->cp.pf().bpp >= 16))
+ (conn->cp.pf().bpp >= 16) && allowLossy)
fullColour = encoderTightJPEG;
else if (encoders[encoderZRLE]->isSupported())
fullColour = encoderZRLE;
@@ -373,7 +403,7 @@ void EncodeManager::prepareEncoders()
// JPEG is the only encoder that can reduce things to grayscale
if ((conn->cp.subsampling == subsampleGray) &&
- encoders[encoderTightJPEG]->isSupported()) {
+ encoders[encoderTightJPEG]->isSupported() && allowLossy) {
solid = bitmap = bitmapRLE = encoderTightJPEG;
indexed = indexedRLE = fullColour = encoderTightJPEG;
}
@@ -397,6 +427,52 @@ void EncodeManager::prepareEncoders()
}
}
+Region EncodeManager::getLosslessRefresh(const Region& req,
+ size_t maxUpdateSize)
+{
+ std::vector<Rect> rects;
+ Region refresh;
+ size_t area;
+
+ // We make a conservative guess at the compression ratio at 2:1
+ maxUpdateSize *= 2;
+
+ area = 0;
+ lossyRegion.intersect(req).get_rects(&rects);
+ while (!rects.empty()) {
+ size_t idx;
+ Rect rect;
+
+ // Grab a random rect so we don't keep damaging and restoring the
+ // same rect over and over
+ idx = rand() % rects.size();
+
+ rect = rects[idx];
+
+ // Add rects until we exceed the threshold, then include as much as
+ // possible of the final rect
+ if ((area + rect.area()) > maxUpdateSize) {
+ // Use the narrowest axis to avoid getting to thin rects
+ if (rect.width() > rect.height()) {
+ int width = (maxUpdateSize - area) / rect.height();
+ rect.br.x = rect.tl.x + __rfbmax(1, width);
+ } else {
+ int height = (maxUpdateSize - area) / rect.width();
+ rect.br.y = rect.tl.y + __rfbmax(1, height);
+ }
+ refresh.assign_union(Region(rect));
+ break;
+ }
+
+ area += rect.area();
+ refresh.assign_union(Region(rect));
+
+ rects.erase(rects.begin() + idx);
+ }
+
+ return refresh;
+}
+
int EncodeManager::computeNumRects(const Region& changed)
{
int numRects;
@@ -449,6 +525,11 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type)
encoder = encoders[klass];
conn->writer()->startRect(rect, encoder->encoding);
+ if (encoder->flags & EncoderLossy)
+ lossyRegion.assign_union(Region(rect));
+ else
+ lossyRegion.assign_subtract(Region(rect));
+
return encoder;
}
@@ -465,14 +546,16 @@ void EncodeManager::endRect()
stats[klass][activeType].bytes += length;
}
-void EncodeManager::writeCopyRects(const UpdateInfo& ui)
+void EncodeManager::writeCopyRects(const Region& copied, const Point& delta)
{
std::vector<Rect> rects;
std::vector<Rect>::const_iterator rect;
+ Region lossyCopy;
+
beforeLength = conn->getOutStream()->length();
- ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
+ copied.get_rects(&rects, delta.x <= 0, delta.y <= 0);
for (rect = rects.begin(); rect != rects.end(); ++rect) {
int equiv;
@@ -481,11 +564,16 @@ void EncodeManager::writeCopyRects(const UpdateInfo& ui)
equiv = 12 + rect->area() * conn->cp.pf().bpp/8;
copyStats.equivalent += equiv;
- conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
- rect->tl.y - ui.copy_delta.y);
+ conn->writer()->writeCopyRect(*rect, rect->tl.x - delta.x,
+ rect->tl.y - delta.y);
}
copyStats.bytes += conn->getOutStream()->length() - beforeLength;
+
+ lossyCopy = lossyRegion;
+ lossyCopy.translate(delta);
+ lossyCopy.assign_intersect(copied);
+ lossyRegion.assign_union(lossyCopy);
}
void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h
index 4319b427..a91c544e 100644
--- a/common/rfb/EncodeManager.h
+++ b/common/rfb/EncodeManager.h
@@ -1,6 +1,6 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB
+ * Copyright 2014-2018 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
@@ -24,6 +24,7 @@
#include <rdr/types.h>
#include <rfb/PixelBuffer.h>
+#include <rfb/Region.h>
namespace rfb {
class SConnection;
@@ -31,7 +32,6 @@ namespace rfb {
class UpdateInfo;
class PixelBuffer;
class RenderedCursor;
- class Region;
struct Rect;
struct RectInfo;
@@ -46,18 +46,31 @@ namespace rfb {
// Hack to let ConnParams calculate the client's preferred encoding
static bool supported(int encoding);
+ bool needsLosslessRefresh(const Region& req);
+ void pruneLosslessRefresh(const Region& limits);
+
void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor);
+ void writeLosslessRefresh(const Region& req, const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor,
+ size_t maxUpdateSize);
+
protected:
- void prepareEncoders();
+ void doUpdate(bool allowLossy, const Region& changed,
+ const Region& copied, const Point& copy_delta,
+ const PixelBuffer* pb,
+ const RenderedCursor* renderedCursor);
+ void prepareEncoders(bool allowLossy);
+
+ Region getLosslessRefresh(const Region& req, size_t maxUpdateSize);
int computeNumRects(const Region& changed);
Encoder *startRect(const Rect& rect, int type);
void endRect();
- void writeCopyRects(const UpdateInfo& ui);
+ void writeCopyRects(const Region& copied, const Point& delta);
void writeSolidRects(Region *changed, const PixelBuffer* pb);
void findSolidRect(const Rect& rect, Region *changed, const PixelBuffer* pb);
void writeRects(const Region& changed, const PixelBuffer* pb);
@@ -103,6 +116,8 @@ namespace rfb {
std::vector<Encoder*> encoders;
std::vector<int> activeEncoders;
+ Region lossyRegion;
+
struct EncoderStats {
unsigned rects;
unsigned long long bytes;
diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h
index a8a447e2..66a10d26 100644
--- a/common/rfb/Encoder.h
+++ b/common/rfb/Encoder.h
@@ -35,6 +35,8 @@ namespace rfb {
// Give us the raw frame buffer, and not something converted to
// the what the client is asking for.
EncoderUseNativePF = 1 << 0,
+ // Encoder does not encode pixels perfectly accurate
+ EncoderLossy = 1 << 1,
};
class Encoder {
diff --git a/common/rfb/Region.cxx b/common/rfb/Region.cxx
index 995f8c5b..c17c5d48 100644
--- a/common/rfb/Region.cxx
+++ b/common/rfb/Region.cxx
@@ -143,10 +143,6 @@ void rfb::Region::setExtentsAndOrderedRects(const ShortRect* extents,
}
}
-void rfb::Region::copyFrom(const rfb::Region& r) {
- XUnionRegion(r.xrgn, r.xrgn, xrgn);
-}
-
void rfb::Region::assign_intersect(const rfb::Region& r) {
XIntersectRegion(xrgn, r.xrgn, xrgn);
}
diff --git a/common/rfb/Region.h b/common/rfb/Region.h
index 93375569..9e53d362 100644
--- a/common/rfb/Region.h
+++ b/common/rfb/Region.h
@@ -52,7 +52,6 @@ namespace rfb {
void setOrderedRects(const std::vector<Rect>& rects);
void setExtentsAndOrderedRects(const ShortRect* extents, int nRects,
const ShortRect* rects);
- void copyFrom(const Region& r);
void assign_intersect(const Region& r);
void assign_union(const Region& r);
diff --git a/common/rfb/TightJPEGEncoder.cxx b/common/rfb/TightJPEGEncoder.cxx
index 7bb61265..385207f7 100644
--- a/common/rfb/TightJPEGEncoder.cxx
+++ b/common/rfb/TightJPEGEncoder.cxx
@@ -64,7 +64,7 @@ static const struct TightJPEGConfiguration conf[10] = {
TightJPEGEncoder::TightJPEGEncoder(SConnection* conn) :
- Encoder(conn, encodingTight, EncoderUseNativePF, -1),
+ Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1),
qualityLevel(-1), fineQuality(-1), fineSubsampling(subsampleUndefined)
{
}
diff --git a/common/rfb/Timer.cxx b/common/rfb/Timer.cxx
index 7179cd87..fd1a87ae 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 Pierre Ossman for Cendio AB
+ * Copyright 2016-2018 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
@@ -94,7 +94,7 @@ int Timer::checkTimeouts() {
int Timer::getNextTimeout() {
timeval now;
gettimeofday(&now, 0);
- int toWait = __rfbmax(1, diffTimeMillis(pending.front()->dueTime, now));
+ int toWait = __rfbmax(1, pending.front()->getRemainingMs());
if (toWait > pending.front()->timeoutMs) {
if (toWait - pending.front()->timeoutMs < 1000) {
vlog.info("gettimeofday is broken...");
@@ -148,6 +148,12 @@ int Timer::getTimeoutMs() {
return timeoutMs;
}
+int Timer::getRemainingMs() {
+ timeval now;
+ gettimeofday(&now, 0);
+ return __rfbmax(0, diffTimeMillis(pending.front()->dueTime, now));
+}
+
bool Timer::isBefore(timeval other) {
return (dueTime.tv_sec < other.tv_sec) ||
((dueTime.tv_sec == other.tv_sec) &&
diff --git a/common/rfb/Timer.h b/common/rfb/Timer.h
index 78687d1a..15b5d03d 100644
--- a/common/rfb/Timer.h
+++ b/common/rfb/Timer.h
@@ -1,4 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright 2018 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
@@ -85,6 +86,11 @@ namespace rfb {
// Usually used with isStarted() to get the _current_ timeout.
int getTimeoutMs();
+ // 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.
bool isBefore(timeval other);
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index e707e492..126fb4e8 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2016 Pierre Ossman for Cendio AB
+ * Copyright 2009-2018 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
@@ -223,6 +223,9 @@ void VNCSConnectionST::pixelBufferChange()
}
}
}
+
+ // Drop any lossy tracking that is now outside the framebuffer
+ encodeManager.pruneLosslessRefresh(Region(server->pb->getRect()));
}
// Just update the whole screen at the moment because we're too lazy to
// work out what's actually changed.
@@ -962,18 +965,13 @@ void VNCSConnectionST::writeNoDataUpdate()
void VNCSConnectionST::writeDataUpdate()
{
- Region req;
+ Region req, pending;
UpdateInfo ui;
bool needNewUpdateInfo;
const RenderedCursor *cursor;
updates.enable_copyrect(cp.useCopyRect);
- // See if we are allowed to send anything right now (the framebuffer
- // might have changed in ways we haven't yet been informed of).
- if (!server->checkUpdate())
- return;
-
// See what the client has requested (if anything)
if (continuousUpdates)
req = cuRegion.union_(requested);
@@ -983,6 +981,9 @@ void VNCSConnectionST::writeDataUpdate()
if (req.is_empty())
return;
+ // Get any framebuffer changes we haven't yet been informed of
+ pending = server->getPendingRegion();
+
// Get the lists of updates. Prior to exporting the data to the `ui' object,
// getUpdateInfo() will normalize the `updates' object such way that its
// `changed' and `copied' regions would not intersect.
@@ -996,7 +997,7 @@ void VNCSConnectionST::writeDataUpdate()
if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) {
Region bogusCopiedCursor;
- bogusCopiedCursor.copyFrom(damagedCursorRegion);
+ bogusCopiedCursor = damagedCursorRegion;
bogusCopiedCursor.translate(ui.copy_delta);
bogusCopiedCursor.assign_intersect(server->pb->getRect());
if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
@@ -1015,53 +1016,78 @@ void VNCSConnectionST::writeDataUpdate()
removeRenderedCursor = false;
}
- // Return if there is nothing to send the client.
+ // If we need a full cursor update then make sure its entire region
+ // is marked as changed.
- if (updates.is_empty() && !writer()->needFakeUpdate() && !updateRenderedCursor)
- return;
+ if (updateRenderedCursor) {
+ updates.add_changed(server->getRenderedCursor()->getEffectiveRect());
+ needNewUpdateInfo = true;
+ updateRenderedCursor = false;
+ }
// The `updates' object could change, make sure we have valid update info.
if (needNewUpdateInfo)
updates.getUpdateInfo(&ui, req);
- // If the client needs a server-side rendered cursor, work out the cursor
- // rectangle. If it's empty then don't bother drawing it, but if it overlaps
- // with the update region, we need to draw the rendered cursor regardless of
- // whether it has changed.
+ // If there are queued updates then we cannot safely send an update
+ // without risking a partially updated screen
+
+ if (!pending.is_empty()) {
+ // However we might still be able to send a lossless refresh
+ req.assign_subtract(pending);
+ req.assign_subtract(ui.changed);
+ req.assign_subtract(ui.copied);
+
+ ui.changed.clear();
+ ui.copied.clear();
+ }
+
+ // Does the client need a server-side rendered cursor?
cursor = NULL;
if (needRenderedCursor()) {
Rect renderedCursorRect;
cursor = server->getRenderedCursor();
-
- renderedCursorRect
- = cursor->getEffectiveRect().intersect(req.get_bounding_rect());
-
- if (renderedCursorRect.is_empty()) {
- cursor = NULL;
- } else if (!updateRenderedCursor &&
- ui.changed.union_(ui.copied)
- .intersect(renderedCursorRect).is_empty()) {
- cursor = NULL;
+ renderedCursorRect = cursor->getEffectiveRect();
+
+ // Check that we don't try to copy over the cursor area, and
+ // if that happens we need to treat it as changed so that we can
+ // re-render it
+ if (!ui.copied.intersect(renderedCursorRect).is_empty()) {
+ ui.changed.assign_union(ui.copied.intersect(renderedCursorRect));
+ ui.copied.assign_subtract(renderedCursorRect);
}
- if (cursor) {
- updates.subtract(renderedCursorRect);
- updates.getUpdateInfo(&ui, req);
- }
-
- damagedCursorRegion.assign_union(renderedCursorRect);
- updateRenderedCursor = false;
+ // Track where we've rendered the cursor
+ damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect));
}
- if (ui.is_empty() && !writer()->needFakeUpdate() && !cursor)
+ // Return if there is nothing to send the client.
+
+ if (ui.is_empty() && !writer()->needFakeUpdate() &&
+ !encodeManager.needsLosslessRefresh(req))
return;
writeRTTPing();
- encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
+ if (!ui.is_empty())
+ encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
+ else {
+ size_t maxUpdateSize;
+
+ // FIXME: If continuous updates aren't used then the client might
+ // be slower than frameRate in its requests and we could
+ // afford a larger update size
+
+ // FIXME: Bandwidth estimation without congestion control
+ maxUpdateSize = congestion.getBandwidth() *
+ server->msToNextUpdate() / 1000;
+
+ encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
+ cursor, maxUpdateSize);
+ }
writeRTTPing();
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index 0008dc41..95870c92 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-2017 Pierre Ossman for Cendio AB
+ * Copyright 2009-2018 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
@@ -105,10 +105,7 @@ VNCServerST::~VNCServerST()
}
// Stop the desktop object if active, *only* after deleting all clients!
- if (desktopStarted) {
- desktopStarted = false;
- desktop->stop();
- }
+ stopDesktop();
if (comparer)
comparer->logStats();
@@ -154,12 +151,8 @@ void VNCServerST::removeSocket(network::Socket* sock) {
delete *ci;
// - Check that the desktop object is still required
- if (authClientCount() == 0 && desktopStarted) {
- slog.debug("no authenticated clients - stopping desktop");
- desktopStarted = false;
- desktop->stop();
- stopFrameClock();
- }
+ if (authClientCount() == 0)
+ stopDesktop();
if (comparer)
comparer->logStats();
@@ -552,6 +545,16 @@ void VNCServerST::startDesktop()
}
}
+void VNCServerST::stopDesktop()
+{
+ if (desktopStarted) {
+ slog.debug("stopping desktop");
+ desktopStarted = false;
+ desktop->stop();
+ stopFrameClock();
+ }
+}
+
int VNCServerST::authClientCount() {
int count = 0;
std::list<VNCSConnectionST*>::iterator ci;
@@ -576,6 +579,8 @@ void VNCServerST::startFrameClock()
return;
if (blockCounter > 0)
return;
+ if (!desktopStarted)
+ 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
@@ -588,6 +593,17 @@ void VNCServerST::stopFrameClock()
frameTimer.stop();
}
+int VNCServerST::msToNextUpdate()
+{
+ // FIXME: If the application is updating slower than frameRate then
+ // we could allow the clients more time here
+
+ if (!frameTimer.isStarted())
+ return 1000/rfb::Server::frameRate/2;
+ else
+ return frameTimer.getRemainingMs();
+}
+
// writeUpdate() is called on a regular interval in order to see what
// updates are pending and propagates them to the update tracker for
// each client. It uses the ComparingUpdateTracker's compare() method
@@ -603,6 +619,7 @@ void VNCServerST::writeUpdate()
std::list<VNCSConnectionST*>::iterator ci, ci_next;
assert(blockCounter == 0);
+ assert(desktopStarted);
comparer->getUpdateInfo(&ui, pb->getRect());
toCheck = ui.changed.union_(ui.copied);
@@ -639,17 +656,21 @@ void VNCServerST::writeUpdate()
// checkUpdate() is called by clients to see if it is safe to read from
// the framebuffer at this time.
-bool VNCServerST::checkUpdate()
+Region VNCServerST::getPendingRegion()
{
+ UpdateInfo ui;
+
// Block clients as the frame buffer cannot be safely accessed
if (blockCounter > 0)
- return false;
+ return pb->getRect();
// Block client from updating if there are pending updates
- if (!comparer->is_empty())
- return false;
+ if (comparer->is_empty())
+ return Region();
+
+ comparer->getUpdateInfo(&ui, pb->getRect());
- return true;
+ return ui.changed.union_(ui.copied);
}
const RenderedCursor* VNCServerST::getRenderedCursor()
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index e00a1f7e..b7845ddd 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -195,6 +195,7 @@ namespace rfb {
// - Internal methods
void startDesktop();
+ void stopDesktop();
static LogWriter connectionsLog;
Blacklist blacklist;
@@ -226,8 +227,9 @@ namespace rfb {
bool needRenderedCursor();
void startFrameClock();
void stopFrameClock();
+ int msToNextUpdate();
void writeUpdate();
- bool checkUpdate();
+ Region getPendingRegion();
const RenderedCursor* getRenderedCursor();
void notifyScreenLayoutChange(VNCSConnectionST *requester);