From: Pierre Ossman Date: Wed, 9 Mar 2011 09:42:34 +0000 (+0000) Subject: Initial commit of new FLTK based vncviewer. Most of the code comes from the X-Git-Tag: v1.1.90~410 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5156d5e49b88d9a5c0ede2c81897e8a2d3c52523;p=tigervnc.git Initial commit of new FLTK based vncviewer. Most of the code comes from the current Unix vncviewer. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4345 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 83926e7a..b8507ab0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ set(VERSION 1.0.90) # The RC version must always be four comma-separated numbers set(RCVERSION 1,0,90,0) +# Manual toggle until we can deprecate the old viewers +option(BUILD_NEW_VNCVIEWER "Build the new FLTK based vncviewer instead of the old ones") + # Compatibility variables for the migration from autotools add_definitions(-DPACKAGE_NAME="${CMAKE_PROJECT_NAME}") add_definitions(-DPACKAGE_VERSION="${VERSION}") @@ -128,6 +131,13 @@ if(NOT FOUND_JPEG_TURBO) message(STATUS "WARNING: You are not using libjpeg-turbo. Performance will suffer.") endif() +# Check for FLTK +if(BUILD_NEW_VNCVIEWER) + set(FLTK_SKIP_FLUID TRUE) + set(FLTK_SKIP_OPENGL TRUE) + find_package(FLTK COMPONENTS REQUIRED) +endif() + # Check for GNUTLS library find_package(GnuTLS) if(GNUTLS_FOUND) @@ -179,3 +189,7 @@ if(WIN32) else() add_subdirectory(unix) endif() + +if(BUILD_NEW_VNCVIEWER) + add_subdirectory(vncviewer) +endif() diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 176fad36..599f26f9 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -2,4 +2,7 @@ add_subdirectory(tx) add_subdirectory(vncconfig) add_subdirectory(vncpasswd) + +if(NOT BUILD_NEW_VNCVIEWER) add_subdirectory(vncviewer) +endif() diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx new file mode 100644 index 00000000..e9032840 --- /dev/null +++ b/vncviewer/CConn.cxx @@ -0,0 +1,450 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "CConn.h" +#include "i18n.h" +#include "parameters.h" + +using namespace rdr; +using namespace rfb; +using namespace std; + +extern void exit_vncviewer(); + +static rfb::LogWriter vlog("CConn"); + +CConn::CConn(const char* vncServerName) + : serverHost(0), serverPort(0), sock(NULL), desktop(NULL), + currentEncoding(encodingTight), lastServerEncoding((unsigned int)-1), + formatChange(false), encodingChange(false), + firstUpdate(true), pendingUpdate(false) +{ + setShared(::shared); + + int encNum = encodingNum(preferredEncoding); + if (encNum != -1) + currentEncoding = encNum; + + cp.supportsDesktopResize = true; + cp.supportsExtendedDesktopSize = true; + cp.supportsDesktopRename = true; + cp.supportsLocalCursor = useLocalCursor; + + cp.customCompressLevel = customCompressLevel; + cp.compressLevel = compressLevel; + + cp.noJpeg = noJpeg; + cp.qualityLevel = qualityLevel; + + try { + getHostAndPort(vncServerName, &serverHost, &serverPort); + + sock = new network::TcpSocket(serverHost, serverPort); + vlog.info(_("connected to host %s port %d"), serverHost, serverPort); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + fl_alert(e.str()); + exit_vncviewer(); + return; + } + + Fl::add_fd(sock->getFd(), FL_READ | FL_EXCEPT, socketEvent, this); + + // See callback below + sock->inStream().setBlockCallback(this); + + setServerName(serverHost); + setStreams(&sock->inStream(), &sock->outStream()); + + initialiseProtocol(); +} + +CConn::~CConn() +{ + free(serverHost); + if (sock) + Fl::remove_fd(sock->getFd()); + delete sock; +} + +// The RFB core is not properly asynchronous, so it calls this callback +// whenever it needs to block to wait for more data. Since FLTK is +// monitoring the socket, we just make sure FLTK gets to run. + +void CConn::blockCallback() +{ + int next_timer; + + next_timer = Timer::checkTimeouts(); + if (next_timer == 0) + next_timer = INT_MAX; + + Fl::wait((double)next_timer / 1000.0); +} + +void CConn::socketEvent(int fd, void *data) +{ + CConn *cc; + static bool recursing = false; + + assert(data); + cc = (CConn*)data; + + // I don't think processMsg() is recursion safe, so add this check + if (recursing) + return; + + recursing = true; + + try { + // processMsg() only processes one message, so we need to loop + // until the buffers are empty or things will stall. + do { + cc->processMsg(); + } while (cc->sock->inStream().checkNoWait(1)); + } catch (rdr::EndOfStream& e) { + vlog.info(e.str()); + exit_vncviewer(); + } catch (rdr::Exception& e) { + vlog.error(e.str()); + fl_alert(e.str()); + exit_vncviewer(); + } + + recursing = false; +} + +////////////////////// CConnection callback methods ////////////////////// + +// serverInit() is called when the serverInit message has been received. At +// this point we create the desktop window and display it. We also tell the +// server the pixel format and encodings to use and request the first update. +void CConn::serverInit() +{ + CConnection::serverInit(); + + // If using AutoSelect with old servers, start in FullColor + // mode. See comment in autoSelectFormatAndEncoding. + if (cp.beforeVersion(3, 8) && autoSelect) + fullColour.setParam(true); + + serverPF = cp.pf(); + + desktop = new DesktopWindow(cp.width, cp.height, cp.name(), serverPF, this); + fullColourPF = desktop->getPreferredPF(); + + formatChange = encodingChange = true; + requestNewUpdate(); +} + +// 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(int reason, int result, 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(); +} + +// setName() is called when the desktop name changes +void CConn::setName(const char* name) +{ + CConnection::setName(name); + if (desktop) + desktop->setName(name); +} + +// framebufferUpdateStart() is called at the beginning of an update. +// Here we try to send out a new framebuffer update request so that the +// next update can be sent out in parallel with us decoding the current +// one. We cannot do this if we're in the middle of a format change +// though. +void CConn::framebufferUpdateStart() +{ + if (!formatChange) { + pendingUpdate = true; + requestNewUpdate(); + } else + pendingUpdate = false; +} + +// framebufferUpdateEnd() is called at the end of an update. +// For each rectangle, the FdInStream will have timed the speed +// of the connection, allowing us to select format and encoding +// appropriately, and then request another incremental update. +void CConn::framebufferUpdateEnd() +{ + desktop->updateWindow(); + + if (firstUpdate) { + int width, height; + + if (cp.supportsSetDesktopSize && + sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) == 2) { + ScreenSet layout; + + layout = cp.screenLayout; + + if (layout.num_screens() == 0) + layout.add_screen(rfb::Screen()); + else if (layout.num_screens() != 1) { + ScreenSet::iterator iter; + + while (true) { + iter = layout.begin(); + ++iter; + + if (iter == layout.end()) + break; + + layout.remove_screen(iter->id); + } + } + + layout.begin()->dimensions.tl.x = 0; + layout.begin()->dimensions.tl.y = 0; + layout.begin()->dimensions.br.x = width; + layout.begin()->dimensions.br.y = height; + + writer()->writeSetDesktopSize(width, height, layout); + } + + firstUpdate = false; + } + + // A format change prevented us from sending this before the update, + // so make sure to send it now. + if (formatChange && !pendingUpdate) + requestNewUpdate(); + + // Compute new settings based on updated bandwidth values + if (autoSelect) + autoSelectFormatAndEncoding(); + + // Make sure that the FLTK handling and the timers gets some CPU time + // in case of back to back framebuffer updates. + Fl::check(); + Timer::checkTimeouts(); +} + +// The rest of the callbacks are fairly self-explanatory... + +void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs) +{ + desktop->setColourMapEntries(firstColour, nColours, rgbs); +} + +void CConn::bell() +{ + fl_beep(); +} + +void CConn::serverCutText(const char* str, rdr::U32 len) +{ +// desktop->serverCutText(str,len); +} + +// We start timing on beginRect and stop timing on endRect, to +// avoid skewing the bandwidth estimation as a result of the server +// being slow or the network having high latency +void CConn::beginRect(const Rect& r, int encoding) +{ + sock->inStream().startTiming(); + if (encoding != encodingCopyRect) { + lastServerEncoding = encoding; + } +} + +void CConn::endRect(const Rect& r, int encoding) +{ + sock->inStream().stopTiming(); +} + +void CConn::fillRect(const rfb::Rect& r, rfb::Pixel p) +{ + desktop->fillRect(r,p); +} +void CConn::imageRect(const rfb::Rect& r, void* p) +{ + desktop->imageRect(r,p); +} +void CConn::copyRect(const rfb::Rect& r, int sx, int sy) +{ + desktop->copyRect(r,sx,sy); +} +void CConn::setCursor(int width, int height, const Point& hotspot, + void* data, void* mask) +{ +// desktop->setCursor(width, height, hotspot, data, mask); +} + +////////////////////// Internal methods ////////////////////// + +void CConn::resizeFramebuffer() +{ +/* + if (!desktop) + return; + if ((desktop->width() == cp.width) && (desktop->height() == cp.height)) + return; + + desktop->resize(cp.width, cp.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() +{ + int kbitsPerSecond = sock->inStream().kbitsPerSecond(); + unsigned int timeWaited = sock->inStream().timeWaited(); + bool newFullColour = fullColour; + int newQualityLevel = qualityLevel; + + // Always use Tight + if (currentEncoding != encodingTight) { + currentEncoding = encodingTight; + encodingChange = true; + } + + // Check that we have a decent bandwidth measurement + if ((kbitsPerSecond == 0) || (timeWaited < 10000)) + return; + + // Select appropriate quality level + if (!noJpeg) { + if (kbitsPerSecond > 16000) + newQualityLevel = 8; + else + newQualityLevel = 6; + + if (newQualityLevel != qualityLevel) { + vlog.info(_("Throughput %d kbit/s - changing to quality %d"), + kbitsPerSecond, newQualityLevel); + cp.qualityLevel = newQualityLevel; + qualityLevel.setParam(newQualityLevel); + encodingChange = true; + } + } + + if (cp.beforeVersion(3, 8)) { + // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with + // cursors "asynchronously". If this happens in the middle of a + // pixel format change, the server will encode the cursor with + // the old format, but the client will try to decode it + // according to the new format. This will lead to a + // crash. Therefore, we do not allow automatic format change for + // old servers. + return; + } + + // Select best color level + newFullColour = (kbitsPerSecond > 256); + if (newFullColour != fullColour) { + vlog.info(_("Throughput %d kbit/s - full color is now %s"), + kbitsPerSecond, + newFullColour ? _("enabled") : _("disabled")); + fullColour.setParam(newFullColour); + formatChange = true; + } +} + +// checkEncodings() sends a setEncodings message if one is needed. +void CConn::checkEncodings() +{ + if (encodingChange && writer()) { + vlog.info(_("Using %s encoding"),encodingName(currentEncoding)); + writer()->writeSetEncodings(currentEncoding, true); + encodingChange = false; + } +} + +// requestNewUpdate() requests an update from the server, having set the +// format and encoding appropriately. +void CConn::requestNewUpdate() +{ + if (formatChange) { + PixelFormat pf; + + /* Catch incorrect requestNewUpdate calls */ + assert(pendingUpdate == false); + + if (fullColour) { + pf = fullColourPF; + } else { + if (lowColourLevel == 0) + pf = PixelFormat(8,3,0,1,1,1,1,2,1,0); + else if (lowColourLevel == 1) + pf = PixelFormat(8,6,0,1,3,3,3,4,2,0); + else + pf = PixelFormat(8,8,0,0); + } + char str[256]; + pf.print(str, 256); + vlog.info(_("Using pixel format %s"),str); + desktop->setServerPF(pf); + cp.setPF(pf); + writer()->writeSetPixelFormat(pf); + } + checkEncodings(); + writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height), + !formatChange); + formatChange = false; +} diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h new file mode 100644 index 00000000..0f5be621 --- /dev/null +++ b/vncviewer/CConn.h @@ -0,0 +1,96 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __CCONN_H__ +#define __CCONN_H__ + +#include +#include + +#include "DesktopWindow.h" + +class CConn : public rfb::CConnection, + public rdr::FdInStreamBlockCallback +{ +public: + CConn(const char* vncServerName); + ~CConn(); + + // FdInStreamBlockCallback methods + void blockCallback(); + + // Callback when socket is ready (or broken) + static void socketEvent(int fd, void *data); + + // CConnection callback methods + void serverInit(); + + void setDesktopSize(int w, int h); + void setExtendedDesktopSize(int reason, int result, int w, int h, + const rfb::ScreenSet& layout); + + void setName(const char* name); + + void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); + + void bell(); + + void serverCutText(const char* str, rdr::U32 len); + + void framebufferUpdateStart(); + void framebufferUpdateEnd(); + + void beginRect(const rfb::Rect& r, int encoding); + void endRect(const rfb::Rect& r, int encoding); + + void fillRect(const rfb::Rect& r, rfb::Pixel p); + void imageRect(const rfb::Rect& r, void* p); + void copyRect(const rfb::Rect& r, int sx, int sy); + + void setCursor(int width, int height, const rfb::Point& hotspot, + void* data, void* mask); + +private: + + void resizeFramebuffer(); + + void autoSelectFormatAndEncoding(); + void checkEncodings(); + void requestNewUpdate(); + +private: + char* serverHost; + int serverPort; + network::Socket* sock; + + DesktopWindow *desktop; + + rfb::PixelFormat serverPF; + rfb::PixelFormat fullColourPF; + + int currentEncoding, lastServerEncoding; + + bool formatChange; + bool encodingChange; + + bool firstUpdate; + bool pendingUpdate; +}; + +#endif diff --git a/vncviewer/CMakeLists.txt b/vncviewer/CMakeLists.txt new file mode 100644 index 00000000..742ba131 --- /dev/null +++ b/vncviewer/CMakeLists.txt @@ -0,0 +1,12 @@ +include_directories(${FLTK_INCLUDE_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}/common) + +add_executable(vncviewer + CConn.cxx + DesktopWindow.cxx + UserDialog.cxx + parameters.cxx + vncviewer.cxx) + +target_link_libraries(vncviewer rfb network rdr os Xregion ${FLTK_LIBRARIES}) diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx new file mode 100644 index 00000000..28f6c09a --- /dev/null +++ b/vncviewer/DesktopWindow.cxx @@ -0,0 +1,186 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +#include + +#include +#include + +#include "DesktopWindow.h" +#include "CConn.h" +#include "i18n.h" +#include "parameters.h" + +using namespace rfb; + +extern void exit_vncviewer(); + +static rfb::LogWriter vlog("DesktopWindow"); + +DesktopWindow::DesktopWindow(int w, int h, const char *name, + const rfb::PixelFormat& serverPF, + CConn* cc_) + : Fl_Window(w, h), cc(cc_), frameBuffer(NULL), pixelTrans(NULL) +{ + callback(handleClose, this); + + setName(name); + + frameBuffer = new ManagedPixelBuffer(getPreferredPF(), w, h); + assert(frameBuffer); + + setServerPF(serverPF); + + show(); +} + + +DesktopWindow::~DesktopWindow() +{ + delete frameBuffer; + + if (pixelTrans) + delete pixelTrans; +} + + +void DesktopWindow::setServerPF(const rfb::PixelFormat& pf) +{ + if (pixelTrans) + delete pixelTrans; + pixelTrans = NULL; + + if (pf.equal(getPreferredPF())) + return; + + pixelTrans = new PixelTransformer(); + pixelTrans->init(pf, &colourMap, getPreferredPF()); +} + + +const rfb::PixelFormat &DesktopWindow::getPreferredPF() +{ + static PixelFormat prefPF(32, 24, false, true, 255, 255, 255, 0, 8, 16); + + return prefPF; +} + + +// Cursor stuff + +void DesktopWindow::setCursor(int width, int height, const Point& hotspot, + void* data, void* mask) +{ +} + + +void DesktopWindow::setName(const char *name) +{ + CharArray windowNameStr; + windowNameStr.replaceBuf(new char[256]); + + snprintf(windowNameStr.buf, 256, _("TigerVNC: %.240s"), name); + + copy_label(windowNameStr.buf); +} + +// setColourMapEntries() changes some of the entries in the colourmap. +// Unfortunately these messages are often sent one at a time, so we delay the +// settings taking effect by 100ms. This is because recalculating the internal +// translation table can be expensive. +void DesktopWindow::setColourMapEntries(int firstColour, int nColours, + rdr::U16* rgbs) +{ + for (int i = 0; i < nColours; i++) + colourMap.set(firstColour+i, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); + + if (!Fl::has_timeout(handleColourMap, this)) + Fl::add_timeout(0.100, handleColourMap, this); +} + + +// Copy the areas of the framebuffer that have been changed (damaged) +// to the displayed window. + +void DesktopWindow::updateWindow() +{ + Rect r; + + Fl::remove_timeout(handleUpdateTimeout, this); + + r = damage.get_bounding_rect(); + Fl_Window::damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height()); + + damage.clear(); +} + + +void DesktopWindow::draw() +{ + int X, Y, W, H; + + int pixel_bytes, stride_bytes; + const uchar *buf_start; + + // Check what actually needs updating + fl_clip_box(0, 0, w(), h(), X, Y, W, H); + if ((W == 0) || (H == 0)) + return; + + pixel_bytes = frameBuffer->getPF().bpp/8; + stride_bytes = pixel_bytes * frameBuffer->getStride(); + buf_start = frameBuffer->data + + pixel_bytes * X + + stride_bytes * Y; + + // FIXME: Check how efficient this thing really is + fl_draw_image(buf_start, X, Y, W, H, pixel_bytes, stride_bytes); +} + + +void DesktopWindow::handleUpdateTimeout(void *data) +{ + DesktopWindow *self = (DesktopWindow *)data; + + assert(self); + + self->updateWindow(); +} + + +void DesktopWindow::handleColourMap(void *data) +{ + DesktopWindow *self = (DesktopWindow *)data; + + assert(self); + + if (self->pixelTrans != NULL) + self->pixelTrans->setColourMapEntries(0, 0); + + self->Fl_Window::damage(FL_DAMAGE_ALL); +} + +void DesktopWindow::handleClose(Fl_Widget *wnd, void *data) +{ + exit_vncviewer(); +} diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h new file mode 100644 index 00000000..de85adde --- /dev/null +++ b/vncviewer/DesktopWindow.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __DESKTOPWINDOW_H__ +#define __DESKTOPWINDOW_H__ + +#include +#include + +#include +#include +#include +#include +#include + +class CConn; + +class DesktopWindow : public Fl_Window { +public: + + DesktopWindow(int w, int h, const char *name, + const rfb::PixelFormat& serverPF, CConn* cc_); + ~DesktopWindow(); + + // PixelFormat of incoming write operations + void setServerPF(const rfb::PixelFormat& pf); + // Most efficient format (from DesktopWindow's point of view) + const rfb::PixelFormat &getPreferredPF(); + + // setCursor() sets the shape of the local cursor + void setCursor(int width, int height, const rfb::Point& hotspot, + void* data, void* mask); + + // Flush updates to screen + void updateWindow(); + + // Methods forwarded from CConn + void setName(const char *name); + + void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); + + void fillRect(const rfb::Rect& r, rfb::Pixel pix) { + if (pixelTrans) { + rfb::Pixel pix2; + pixelTrans->translatePixels(&pix, &pix2, 1); + pix = pix2; + } + + frameBuffer->fillRect(r, pix); + damageRect(r); + } + void imageRect(const rfb::Rect& r, void* pixels) { + if (pixelTrans) + pixelTrans->translateRect(pixels, r.width(), + rfb::Rect(0, 0, r.width(), r.height()), + frameBuffer->data, frameBuffer->getStride(), + r.tl); + else + frameBuffer->imageRect(r, pixels); + damageRect(r); + } + void copyRect(const rfb::Rect& r, int srcX, int srcY) { + frameBuffer->copyRect(r, rfb::Point(r.tl.x-srcX, r.tl.y-srcY)); + damageRect(r); + } + + // Fl_Window callback methods + void draw(); + +private: + + void damageRect(const rfb::Rect& r) { + damage.assign_union(rfb::Region(r)); + if (!Fl::has_timeout(handleUpdateTimeout, this)) + Fl::add_timeout(0.100, handleUpdateTimeout, this); + }; + + static void handleUpdateTimeout(void *data); + static void handleColourMap(void *data); + + static void handleClose(Fl_Widget *wnd, void *data); + +private: + CConn* cc; + + rfb::ManagedPixelBuffer* frameBuffer; + + rfb::PixelTransformer *pixelTrans; + rfb::SimpleColourMap colourMap; + + rfb::Region damage; +}; + +#endif diff --git a/vncviewer/UserDialog.cxx b/vncviewer/UserDialog.cxx new file mode 100644 index 00000000..992ea785 --- /dev/null +++ b/vncviewer/UserDialog.cxx @@ -0,0 +1,86 @@ +/* Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include "i18n.h" +#include "parameters.h" +#include "UserDialog.h" + +using namespace rfb; + +UserDialog::UserDialog() +{ +} + +UserDialog::~UserDialog() +{ +} + +void UserDialog::getUserPasswd(char** user, char** password) +{ + CharArray passwordFileStr(passwordFile.getData()); + + assert(password); + + if (!user && passwordFileStr.buf[0]) { + ObfuscatedPasswd obfPwd(256); + FILE* fp; + + fp = fopen(passwordFileStr.buf, "r"); + if (!fp) + throw rfb::Exception(_("Opening password file failed")); + + obfPwd.length = fread(obfPwd.buf, 1, obfPwd.length, fp); + fclose(fp); + + PlainPasswd passwd(obfPwd); + *password = passwd.takeBuf(); + + return; + } + + if (!user) { + *password = strDup(fl_password(_("VNC authentication"), "")); + if (!*password) + throw rfb::Exception(_("Authentication cancelled")); + + return; + } + + fl_alert(_("NOT IMPLEMENTED!")); + + *user = strDup(""); + *password = strDup(""); +} + +bool UserDialog::showMsgBox(int flags, const char* title, const char* text) +{ + fl_message_title(title); + fl_message(text); + + return false; +} diff --git a/vncviewer/UserDialog.h b/vncviewer/UserDialog.h new file mode 100644 index 00000000..c6756a8e --- /dev/null +++ b/vncviewer/UserDialog.h @@ -0,0 +1,41 @@ +/* Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __USERDIALOG_H__ +#define __USERDIALOG_H__ + +#include +#include + +class UserDialog : public rfb::UserPasswdGetter, + public rfb::UserMsgBox +{ +public: + UserDialog(); + ~UserDialog(); + + // UserPasswdGetter callbacks + + void getUserPasswd(char** user, char** password); + + // UserMsgBox callbacks + + bool showMsgBox(int flags, const char* title, const char* text); +}; + +#endif diff --git a/vncviewer/gettext.h b/vncviewer/gettext.h new file mode 100644 index 00000000..209921e6 --- /dev/null +++ b/vncviewer/gettext.h @@ -0,0 +1,271 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + , which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include +# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H +# include +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/vncviewer/i18n.h b/vncviewer/i18n.h new file mode 100644 index 00000000..bf5a50c9 --- /dev/null +++ b/vncviewer/i18n.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef _I18N_H +#define _I18N_H 1 + +#include "gettext.h" + +#define _(String) gettext (String) +#define gettext_noop(String) String +#define N_(String) gettext_noop (String) + +#endif /* _I18N_H */ diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx new file mode 100644 index 00000000..ff0e49c2 --- /dev/null +++ b/vncviewer/parameters.cxx @@ -0,0 +1,90 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "parameters.h" + +using namespace rfb; + +IntParameter pointerEventInterval("PointerEventInterval", + "Time in milliseconds to rate-limit" + " successive pointer events", 0); +BoolParameter useLocalCursor("UseLocalCursor", + "Render the mouse cursor locally", true); +BoolParameter dotWhenNoCursor("DotWhenNoCursor", + "Show the dot cursor when the server sends an " + "invisible cursor", true); + +StringParameter passwordFile("PasswordFile", + "Password file for VNC authentication", ""); +AliasParameter passwd("passwd", "Alias for PasswordFile", &passwordFile); + +BoolParameter autoSelect("AutoSelect", + "Auto select pixel format and encoding. " + "Default if PreferredEncoding and FullColor are not specified.", + true); +BoolParameter fullColour("FullColor", + "Use full color", true); +AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour); +IntParameter lowColourLevel("LowColorLevel", + "Color level to use on slow connections. " + "0 = Very Low (8 colors), 1 = Low (64 colors), " + "2 = Medium (256 colors)", 2); +AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel); +StringParameter preferredEncoding("PreferredEncoding", + "Preferred encoding to use (Tight, ZRLE, Hextile or" + " Raw)", "Tight"); +BoolParameter customCompressLevel("CustomCompressLevel", + "Use custom compression level. " + "Default if CompressLevel is specified.", false); +IntParameter compressLevel("CompressLevel", + "Use specified compression level 0 = Low, 9 = High", + 6); +BoolParameter noJpeg("NoJPEG", + "Disable lossy JPEG compression in Tight encoding.", + false); +IntParameter qualityLevel("QualityLevel", + "JPEG quality level. 0 = Low, 9 = High", + 8); + +BoolParameter fullScreen("FullScreen", "Full screen mode", false); +StringParameter desktopSize("DesktopSize", + "Reconfigure desktop size on the server on " + "connect (if possible)", ""); + +BoolParameter viewOnly("ViewOnly", + "Don't send any mouse or keyboard events to the server", + false); +BoolParameter shared("Shared", + "Don't disconnect other viewers upon connection - " + "share the desktop instead", + false); + +BoolParameter acceptClipboard("AcceptClipboard", + "Accept clipboard changes from the server", + true); +BoolParameter sendClipboard("SendClipboard", + "Send clipboard changes to the server", true); +BoolParameter sendPrimary("SendPrimary", + "Send the primary selection and cut buffer to the " + "server as well as the clipboard selection", + true); + +StringParameter menuKey("MenuKey", "The key which brings up the popup menu", + "F8"); + diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h new file mode 100644 index 00000000..df4b0807 --- /dev/null +++ b/vncviewer/parameters.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +#ifndef __PARAMETERS_H__ +#define __PARAMETERS_H__ + +#include + +extern rfb::IntParameter pointerEventInterval; +extern rfb::BoolParameter useLocalCursor; +extern rfb::BoolParameter dotWhenNoCursor; + +extern rfb::StringParameter passwordFile; + +extern rfb::BoolParameter autoSelect; +extern rfb::BoolParameter fullColour; +extern rfb::AliasParameter fullColourAlias; +extern rfb::IntParameter lowColourLevel; +extern rfb::AliasParameter lowColourLevelAlias; +extern rfb::StringParameter preferredEncoding; +extern rfb::BoolParameter customCompressLevel; +extern rfb::IntParameter compressLevel; +extern rfb::BoolParameter noJpeg; +extern rfb::IntParameter qualityLevel; + +extern rfb::BoolParameter fullScreen; +extern rfb::StringParameter desktopSize; + +extern rfb::BoolParameter viewOnly; +extern rfb::BoolParameter shared; + +extern rfb::BoolParameter acceptClipboard; +extern rfb::BoolParameter sendClipboard; +extern rfb::BoolParameter sendPrimary; + +extern rfb::StringParameter menuKey; + +#endif diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx new file mode 100644 index 00000000..a6244cba --- /dev/null +++ b/vncviewer/vncviewer.cxx @@ -0,0 +1,250 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#define mkdir(path, mode) _mkdir(path) +#endif + +#include +#include +#include +#include +#ifdef HAVE_GNUTLS +#include +#endif +#include +#include +#include + +#include +#include +#include + +#include "i18n.h" +#include "parameters.h" +#include "CConn.h" +#include "UserDialog.h" + +rfb::LogWriter vlog("main"); + +using namespace network; +using namespace rfb; +using namespace std; + +char aboutText[1024]; + +static bool exitMainloop = false; + +void exit_vncviewer() +{ + exitMainloop = true; +} + +static void CleanupSignalHandler(int sig) +{ + // CleanupSignalHandler allows C++ object cleanup to happen because it calls + // exit() rather than the default which is to abort. + vlog.info("CleanupSignalHandler called"); + exit(1); +} + +static void init_fltk() +{ + // Basic text size (10pt @ 96 dpi => 13px) + FL_NORMAL_SIZE = 13; + +#ifndef __APPLE__ + // Select a FLTK scheme and background color that looks somewhat + // close to modern Linux and Windows. + Fl::scheme("gtk+"); + Fl::background(220, 220, 220); +#else + // On Mac OS X there is another scheme that fits better though. + Fl::scheme("plastic"); +#endif + + // This makes the "icon" in dialogs rounded, which fits better + // with the above schemes. + fl_message_icon()->box(FL_UP_BOX); + + // Turn off the annoying behaviour where popups track the mouse. + fl_message_hotspot(false); + + // Avoid empty titles for popups + fl_message_title_default(_("TigerVNC Viewer")); + +#ifdef WIN32 + // Most "normal" Windows apps use this font for UI elements. + Fl::set_font(FL_HELVETICA, "Tahoma"); +#endif + + // FLTK exposes these so that we can translate them. + fl_no = _("No"); + fl_yes = _("Yes"); + fl_ok = _("OK"); + fl_cancel = _("Cancel"); + fl_close = _("Close"); +} + +static void mkvnchomedir() +{ + // Create .vnc in the user's home directory if it doesn't already exist + char* homeDir = NULL; + + if (getvnchomedir(&homeDir) == -1) { + vlog.error(_("Could not create VNC home directory: can't obtain home " + "directory path.")); + } else { + int result = mkdir(homeDir, 0755); + if (result == -1 && errno != EEXIST) + vlog.error(_("Could not create VNC home directory: %s."), strerror(errno)); + delete [] homeDir; + } +} + +static void usage(const char *programName) +{ + fprintf(stderr, + "\nusage: %s [parameters] [host:displayNum] [parameters]\n" + " %s [parameters] -listen [port] [parameters]\n", + programName, programName); + fprintf(stderr,"\n" + "Parameters can be turned on with - or off with -=0\n" + "Parameters which take a value can be specified as " + "- \n" + "Other valid forms are = -= " + "--=\n" + "Parameter names are case-insensitive. The parameters are:\n\n"); + Configuration::listParams(79, 14); + exit(1); +} + +int main(int argc, char** argv) +{ + char* vncServerName = 0; + UserDialog dlg; + + const char englishAbout[] = N_("TigerVNC Viewer version %s\n" + "Copyright (C) 2002-2005 RealVNC Ltd.\n" + "Copyright (C) 2000-2006 TightVNC Group\n" + "Copyright (C) 2004-2009 Peter Astrand for Cendio AB\n" + "See http://www.tigervnc.org for information on TigerVNC."); + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE_NAME, LOCALEDIR); + textdomain(PACKAGE_NAME); + + rfb::SecurityClient::setDefaults(); + + // Write about text to console, still using normal locale codeset + snprintf(aboutText, sizeof(aboutText), + gettext(englishAbout), PACKAGE_VERSION); + fprintf(stderr,"\n%s\n", aboutText); + + // Set gettext codeset to what our GUI toolkit uses. Since we are + // passing strings from strerror/gai_strerror to the GUI, these must + // be in GUI codeset as well. + bind_textdomain_codeset(PACKAGE_NAME, "UTF-8"); + bind_textdomain_codeset("libc", "UTF-8"); + + // Re-create the aboutText for the GUI, now using GUI codeset + snprintf(aboutText, sizeof(aboutText), + gettext(englishAbout), PACKAGE_VERSION); + + rfb::initStdIOLoggers(); + rfb::LogWriter::setLogParams("*:stderr:30"); + +#ifdef SIGHUP + signal(SIGHUP, CleanupSignalHandler); +#endif + signal(SIGINT, CleanupSignalHandler); + signal(SIGTERM, CleanupSignalHandler); + + init_fltk(); + + Configuration::enableViewerParams(); + + for (int i = 1; i < argc; i++) { + if (Configuration::setParam(argv[i])) + continue; + + if (argv[i][0] == '-') { + if (i+1 < argc) { + if (Configuration::setParam(&argv[i][1], argv[i+1])) { + i++; + continue; + } + } + usage(argv[0]); + } + + vncServerName = argv[i]; + } + + if (!::autoSelect.hasBeenSet()) { + // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used + ::autoSelect.setParam(!::preferredEncoding.hasBeenSet() && + !::fullColour.hasBeenSet() && + !::fullColourAlias.hasBeenSet()); + } + if (!::fullColour.hasBeenSet() && !::fullColourAlias.hasBeenSet()) { + // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set + if (!::autoSelect && (::lowColourLevel.hasBeenSet() || + ::lowColourLevelAlias.hasBeenSet())) { + ::fullColour.setParam(false); + } + } + if (!::customCompressLevel.hasBeenSet()) { + // Default to CustomCompressLevel=1 if CompressLevel is used. + ::customCompressLevel.setParam(::compressLevel.hasBeenSet()); + } + + mkvnchomedir(); + + CSecurity::upg = &dlg; +#ifdef HAVE_GNUTLS + CSecurityTLS::msg = &dlg; +#endif + + CConn cc(vncServerName); + + while (!exitMainloop) { + int next_timer; + + next_timer = Timer::checkTimeouts(); + if (next_timer == 0) + next_timer = INT_MAX; + + if (Fl::wait((double)next_timer / 1000.0) < 0.0) { + vlog.error(_("Internal FLTK error. Exiting.")); + break; + } + } + + return 0; +} diff --git a/win/CMakeLists.txt b/win/CMakeLists.txt index ab311aee..0bd5a3a4 100644 --- a/win/CMakeLists.txt +++ b/win/CMakeLists.txt @@ -3,7 +3,10 @@ include_directories(${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/win) configure_file(resdefs.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/resdefs.h) add_subdirectory(rfb_win32) + +if(NOT BUILD_NEW_VNCVIEWER) add_subdirectory(vncviewer) +endif() if(BUILD_WINVNC) add_subdirectory(vncconfig)