Pierre Ossman 6 years ago
parent
commit
105177fc07

+ 12
- 3
common/rfb/Congestion.cxx View File

@@ -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

+ 7
- 1
common/rfb/Congestion.h View File

@@ -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;

+ 114
- 26
common/rfb/EncodeManager.cxx View File

@@ -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)

+ 19
- 4
common/rfb/EncodeManager.h View File

@@ -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;

+ 2
- 0
common/rfb/Encoder.h View File

@@ -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 {

+ 0
- 4
common/rfb/Region.cxx View File

@@ -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);
}

+ 0
- 1
common/rfb/Region.h View File

@@ -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);

+ 1
- 1
common/rfb/TightJPEGEncoder.cxx View File

@@ -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)
{
}

+ 8
- 2
common/rfb/Timer.cxx View File

@@ -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) &&

+ 6
- 0
common/rfb/Timer.h View File

@@ -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);

+ 60
- 34
common/rfb/VNCSConnectionST.cxx View File

@@ -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();


+ 37
- 16
common/rfb/VNCServerST.cxx View File

@@ -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()

+ 3
- 1
common/rfb/VNCServerST.h View File

@@ -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);

Loading…
Cancel
Save