diff options
Diffstat (limited to 'vncviewer/CConn.cxx')
-rw-r--r-- | vncviewer/CConn.cxx | 354 |
1 files changed, 176 insertions, 178 deletions
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index f8e80429..da99c8f1 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -27,17 +27,21 @@ #include <unistd.h> #endif -#include <rdr/Exception.h> +#include <core/LogWriter.h> +#include <core/Timer.h> +#include <core/string.h> +#include <core/time.h> + +#include <rdr/FdInStream.h> +#include <rdr/FdOutStream.h> #include <rfb/CMsgWriter.h> #include <rfb/CSecurity.h> #include <rfb/Exception.h> -#include <rfb/Hostname.h> -#include <rfb/LogWriter.h> #include <rfb/Security.h> -#include <rfb/screenTypes.h> #include <rfb/fenceTypes.h> -#include <rfb/Timer.h> +#include <rfb/screenTypes.h> + #include <network/TcpSocket.h> #ifndef WIN32 #include <network/UnixSocket.h> @@ -58,29 +62,27 @@ #include "win32.h" #endif -using namespace rfb; - -static rfb::LogWriter vlog("CConn"); +static core::LogWriter vlog("CConn"); // 8 colours (1 bit per component) -static const PixelFormat verylowColourPF(8, 3,false, true, - 1, 1, 1, 2, 1, 0); +static const rfb::PixelFormat verylowColourPF(8, 3,false, true, + 1, 1, 1, 2, 1, 0); // 64 colours (2 bits per component) -static const PixelFormat lowColourPF(8, 6, false, true, - 3, 3, 3, 4, 2, 0); +static const rfb::PixelFormat lowColourPF(8, 6, false, true, + 3, 3, 3, 4, 2, 0); // 256 colours (2-3 bits per component) -static const PixelFormat mediumColourPF(8, 8, false, true, - 7, 7, 3, 5, 2, 0); +static const rfb::PixelFormat mediumColourPF(8, 8, false, true, + 7, 7, 3, 5, 2, 0); // Time new bandwidth estimates are weighted against (in ms) static const unsigned bpsEstimateWindow = 1000; -CConn::CConn(const char* vncServerName, network::Socket* socket=nullptr) - : serverPort(0), desktop(nullptr), updateCount(0), pixelCount(0), +CConn::CConn() + : serverPort(0), sock(nullptr), desktop(nullptr), + updateCount(0), pixelCount(0), lastServerEncoding((unsigned int)-1), bpsEstimate(20000000) { setShared(::shared); - sock = socket; supportsLocalCursor = true; supportsCursorPosition = true; @@ -93,6 +95,61 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=nullptr) if (!noJpeg) setQualityLevel(::qualityLevel); + OptionsDialog::addCallback(handleOptions, this); +} + +CConn::~CConn() +{ + close(); + + OptionsDialog::removeCallback(handleOptions); + Fl::remove_timeout(handleUpdateTimeout, this); + + if (desktop) + delete desktop; + + if (sock) { + struct timeval now; + + sock->shutdown(); + + // Do a graceful close by waiting for the peer (up to 250 ms) + // FIXME: should do this asynchronously + gettimeofday(&now, nullptr); + while (core::msSince(&now) < 250) { + bool done; + + done = false; + while (true) { + try { + sock->inStream().skip(sock->inStream().avail()); + if (!sock->inStream().hasData(1)) + break; + } catch (std::exception&) { + done = true; + break; + } + } + + if (done) + break; + + #ifdef WIN32 + Sleep(10); + #else + usleep(10000); + #endif + } + + Fl::remove_fd(sock->getFd()); + + delete sock; + } +} + +void CConn::connect(const char* vncServerName, network::Socket* socket) +{ + sock = socket; if(sock == nullptr) { try { #ifndef WIN32 @@ -103,7 +160,7 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=nullptr) } else #endif { - getHostAndPort(vncServerName, &serverHost, &serverPort); + network::getHostAndPort(vncServerName, &serverHost, &serverPort); sock = new network::TcpSocket(serverHost.c_str(), serverPort); vlog.info(_("Connected to host %s port %d"), @@ -123,23 +180,6 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=nullptr) setStreams(&sock->inStream(), &sock->outStream()); initialiseProtocol(); - - OptionsDialog::addCallback(handleOptions, this); -} - -CConn::~CConn() -{ - close(); - - OptionsDialog::removeCallback(handleOptions); - Fl::remove_timeout(handleUpdateTimeout, this); - - if (desktop) - delete desktop; - - if (sock) - Fl::remove_fd(sock->getFd()); - delete sock; } std::string CConn::connectionInfo() @@ -148,46 +188,41 @@ std::string CConn::connectionInfo() char pfStr[100]; - infoText += format(_("Desktop name: %.80s"), server.name()); + infoText += core::format(_("Desktop name: %.80s"), server.name()); infoText += "\n"; - infoText += format(_("Host: %.80s port: %d"), - serverHost.c_str(), serverPort); + infoText += core::format(_("Host: %.80s port: %d"), + serverHost.c_str(), serverPort); infoText += "\n"; - infoText += format(_("Size: %d x %d"), - server.width(), server.height()); + infoText += core::format(_("Size: %d x %d"), + server.width(), server.height()); infoText += "\n"; // TRANSLATORS: Will be filled in with a string describing the // protocol pixel format in a fairly language neutral way server.pf().print(pfStr, 100); - infoText += format(_("Pixel format: %s"), pfStr); + infoText += core::format(_("Pixel format: %s"), pfStr); infoText += "\n"; - // TRANSLATORS: Similar to the earlier "Pixel format" string - serverPF.print(pfStr, 100); - infoText += format(_("(server default %s)"), pfStr); + infoText += core::format(_("Requested encoding: %s"), + rfb::encodingName(getPreferredEncoding())); infoText += "\n"; - infoText += format(_("Requested encoding: %s"), - encodingName(getPreferredEncoding())); + infoText += core::format(_("Last used encoding: %s"), + rfb::encodingName(lastServerEncoding)); infoText += "\n"; - infoText += format(_("Last used encoding: %s"), - encodingName(lastServerEncoding)); + infoText += core::format(_("Line speed estimate: %d kbit/s"), + (int)(bpsEstimate / 1000)); infoText += "\n"; - infoText += format(_("Line speed estimate: %d kbit/s"), - (int)(bpsEstimate / 1000)); + infoText += core::format(_("Protocol version: %d.%d"), + server.majorVersion, server.minorVersion); infoText += "\n"; - infoText += format(_("Protocol version: %d.%d"), - server.majorVersion, server.minorVersion); - infoText += "\n"; - - infoText += format(_("Security method: %s"), - secTypeName(csecurity->getType())); + infoText += core::format(_("Security method: %s"), + rfb::secTypeName(csecurity->getType())); infoText += "\n"; return infoText; @@ -236,7 +271,7 @@ void CConn::socketEvent(FL_SOCKET fd, void *data) // Make sure that the FLTK handling and the timers gets some CPU // time in case of back to back messages Fl::check(); - Timer::checkTimeouts(); + core::Timer::checkTimeouts(); // Also check if we need to stop reading and terminate if (should_disconnect()) @@ -282,7 +317,7 @@ void CConn::resetPassword() ////////////////////// CConnection callback methods ////////////////////// -bool CConn::showMsgBox(MsgBoxFlags flags, const char *title, +bool CConn::showMsgBox(rfb::MsgBoxFlags flags, const char *title, const char *text) { return dlg.showMsgBox(flags, title, text); @@ -304,46 +339,29 @@ void CConn::initDone() if (server.beforeVersion(3, 8) && autoSelect) fullColour.setParam(true); - serverPF = server.pf(); - - desktop = new DesktopWindow(server.width(), server.height(), - server.name(), serverPF, this); + desktop = new DesktopWindow(server.width(), server.height(), this); fullColourPF = desktop->getPreferredPF(); // Force a switch to the format and encoding we'd like + updateEncoding(); updatePixelFormat(); - int encNum = encodingNum(::preferredEncoding); - if (encNum != -1) - setPreferredEncoding(encNum); -} - -// setDesktopSize() is called when the desktop size changes (including when -// it is set initially). -void CConn::setDesktopSize(int w, int h) -{ - CConnection::setDesktopSize(w,h); - resizeFramebuffer(); } -// setExtendedDesktopSize() is a more advanced version of setDesktopSize() void CConn::setExtendedDesktopSize(unsigned reason, unsigned result, - int w, int h, const rfb::ScreenSet& layout) + int w, int h, + const rfb::ScreenSet& layout) { CConnection::setExtendedDesktopSize(reason, result, w, h, layout); - if ((reason == reasonClient) && (result != resultSuccess)) { - vlog.error(_("SetDesktopSize failed: %d"), result); - return; - } - - resizeFramebuffer(); + if (reason == rfb::reasonClient) + desktop->setDesktopSizeDone(result); } // setName() is called when the desktop name changes void CConn::setName(const char* name) { CConnection::setName(name); - desktop->setName(name); + desktop->updateCaption(); } // framebufferUpdateStart() is called at the beginning of an update. @@ -396,28 +414,25 @@ void CConn::framebufferUpdateEnd() desktop->updateWindow(); // Compute new settings based on updated bandwidth values - if (autoSelect) - autoSelectFormatAndEncoding(); + if (autoSelect) { + updateEncoding(); + updateQualityLevel(); + updatePixelFormat(); + } } // The rest of the callbacks are fairly self-explanatory... -void CConn::setColourMapEntries(int /*firstColour*/, int /*nColours*/, - uint16_t* /*rgbs*/) -{ - vlog.error(_("Invalid SetColourMapEntries from server!")); -} - void CConn::bell() { fl_beep(); } -bool CConn::dataRect(const Rect& r, int encoding) +bool CConn::dataRect(const core::Rect& r, int encoding) { bool ret; - if (encoding != encodingCopyRect) + if (encoding != rfb::encodingCopyRect) lastServerEncoding = encoding; ret = CConnection::dataRect(r, encoding); @@ -428,28 +443,17 @@ bool CConn::dataRect(const Rect& r, int encoding) return ret; } -void CConn::setCursor(int width, int height, const Point& hotspot, +void CConn::setCursor(int width, int height, const core::Point& hotspot, const uint8_t* data) { - desktop->setCursor(width, height, hotspot, data); -} + CConnection::setCursor(width, height, hotspot, data); -void CConn::setCursorPos(const Point& pos) -{ - desktop->setCursorPos(pos); + desktop->setCursor(); } -void CConn::fence(uint32_t flags, unsigned len, const uint8_t data[]) +void CConn::setCursorPos(const core::Point& pos) { - CMsgHandler::fence(flags, len, data); - - if (flags & fenceFlagRequest) { - // We handle everything synchronously so we trivially honor these modes - flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter); - - writer()->writeFence(flags, len, data); - return; - } + desktop->setCursorPos(pos); } void CConn::setLEDState(unsigned int state) @@ -482,44 +486,59 @@ void CConn::resizeFramebuffer() desktop->resizeFramebuffer(server.width(), server.height()); } -// autoSelectFormatAndEncoding() chooses the format and encoding appropriate -// to the connection speed: -// -// First we wait for at least one second of bandwidth measurement. -// -// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality, -// which should be perceptually lossless. -// -// If the bandwidth is below that, we choose a more lossy JPEG quality. -// -// If the bandwidth drops below 256 Kbps, we switch to palette mode. -// -// Note: The system here is fairly arbitrary and should be replaced -// with something more intelligent at the server end. -// -void CConn::autoSelectFormatAndEncoding() +void CConn::updateEncoding() +{ + int encNum; + + if (autoSelect) + encNum = rfb::encodingTight; + else + encNum = rfb::encodingNum(::preferredEncoding.getValueStr().c_str()); + + if (encNum != -1) + setPreferredEncoding(encNum); +} + +void CConn::updateCompressLevel() { - bool newFullColour = fullColour; - int newQualityLevel = ::qualityLevel; + if (customCompressLevel) + setCompressLevel(::compressLevel); + else + setCompressLevel(-1); +} - // Always use Tight - setPreferredEncoding(encodingTight); +void CConn::updateQualityLevel() +{ + int newQualityLevel; + + if (noJpeg) + newQualityLevel = -1; + else if (!autoSelect) + newQualityLevel = ::qualityLevel; + else { + // Above 16Mbps (i.e. LAN), we choose the second highest JPEG + // quality, which should be perceptually lossless. If the bandwidth + // is below that, we choose a more lossy JPEG quality. - // Select appropriate quality level - if (!noJpeg) { if (bpsEstimate > 16000000) newQualityLevel = 8; else newQualityLevel = 6; - if (newQualityLevel != ::qualityLevel) { + if (newQualityLevel != getQualityLevel()) { vlog.info(_("Throughput %d kbit/s - changing to quality %d"), (int)(bpsEstimate/1000), newQualityLevel); - ::qualityLevel.setParam(newQualityLevel); - setQualityLevel(newQualityLevel); } } + setQualityLevel(newQualityLevel); +} + +void CConn::updatePixelFormat() +{ + bool useFullColour; + rfb::PixelFormat pf; + if (server.beforeVersion(3, 8)) { // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with // cursors "asynchronously". If this happens in the middle of a @@ -530,28 +549,23 @@ void CConn::autoSelectFormatAndEncoding() // old servers. return; } - - // Select best color level - newFullColour = (bpsEstimate > 256000); - if (newFullColour != fullColour) { - if (newFullColour) - vlog.info(_("Throughput %d kbit/s - full color is now enabled"), - (int)(bpsEstimate/1000)); - else - vlog.info(_("Throughput %d kbit/s - full color is now disabled"), - (int)(bpsEstimate/1000)); - fullColour.setParam(newFullColour); - updatePixelFormat(); - } -} -// requestNewUpdate() requests an update from the server, having set the -// format and encoding appropriately. -void CConn::updatePixelFormat() -{ - PixelFormat pf; + useFullColour = fullColour; + + // If the bandwidth drops below 256 Kbps, we switch to palette mode. + if (autoSelect) { + useFullColour = (bpsEstimate > 256000); + if (useFullColour != (server.pf() == fullColourPF)) { + if (useFullColour) + vlog.info(_("Throughput %d kbit/s - full color is now enabled"), + (int)(bpsEstimate/1000)); + else + vlog.info(_("Throughput %d kbit/s - full color is now disabled"), + (int)(bpsEstimate/1000)); + } + } - if (fullColour) { + if (useFullColour) { pf = fullColourPF; } else { if (lowColourLevel == 0) @@ -562,37 +576,21 @@ void CConn::updatePixelFormat() pf = mediumColourPF; } - char str[256]; - pf.print(str, 256); - vlog.info(_("Using pixel format %s"),str); - setPF(pf); + if (pf != server.pf()) { + char str[256]; + pf.print(str, 256); + vlog.info(_("Using pixel format %s"),str); + setPF(pf); + } } void CConn::handleOptions(void *data) { CConn *self = (CConn*)data; - // Checking all the details of the current set of encodings is just - // a pain. Assume something has changed, as resending the encoding - // list is cheap. Avoid overriding what the auto logic has selected - // though. - if (!autoSelect) { - int encNum = encodingNum(::preferredEncoding); - - if (encNum != -1) - self->setPreferredEncoding(encNum); - } - - if (customCompressLevel) - self->setCompressLevel(::compressLevel); - else - self->setCompressLevel(-1); - - if (!noJpeg && !autoSelect) - self->setQualityLevel(::qualityLevel); - else - self->setQualityLevel(-1); - + self->updateEncoding(); + self->updateCompressLevel(); + self->updateQualityLevel(); self->updatePixelFormat(); } |