summaryrefslogtreecommitdiffstats
path: root/unix/vncviewer/vncviewer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'unix/vncviewer/vncviewer.cxx')
-rw-r--r--unix/vncviewer/vncviewer.cxx396
1 files changed, 396 insertions, 0 deletions
diff --git a/unix/vncviewer/vncviewer.cxx b/unix/vncviewer/vncviewer.cxx
new file mode 100644
index 00000000..e9c9ac4c
--- /dev/null
+++ b/unix/vncviewer/vncviewer.cxx
@@ -0,0 +1,396 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ *
+ * 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.
+ */
+//
+// All-new VNC viewer for X.
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <network/TcpSocket.h>
+#include "TXWindow.h"
+#include "TXMsgBox.h"
+#include "CConn.h"
+
+#include <intl/gettext.h>
+#define _(String) gettext (String)
+#define gettext_noop(String) String
+#define N_(String) gettext_noop (String)
+
+rfb::LogWriter vlog("main");
+
+using namespace network;
+using namespace rfb;
+using namespace std;
+
+IntParameter pointerEventInterval("PointerEventInterval",
+ "Time in milliseconds to rate-limit"
+ " successive pointer events", 0);
+IntParameter wmDecorationWidth("WMDecorationWidth", "Width of window manager "
+ "decoration around a window", 6);
+IntParameter wmDecorationHeight("WMDecorationHeight", "Height of window "
+ "manager decoration around a window", 24);
+StringParameter passwordFile("PasswordFile",
+ "Password file for VNC authentication", "");
+AliasParameter rfbauth("passwd", "Alias for PasswordFile", &passwordFile);
+
+BoolParameter useLocalCursor("UseLocalCursor",
+ "Render the mouse cursor locally", true);
+BoolParameter dotWhenNoCursor("DotWhenNoCursor",
+ "Show the dot cursor when the server sends an "
+ "invisible cursor", true);
+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 fullScreen("FullScreen", "Full screen mode", false);
+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);
+
+BoolParameter listenMode("listen", "Listen for connections from VNC servers",
+ false);
+StringParameter geometry("geometry", "X geometry specification", "");
+StringParameter displayname("display", "The X display", "");
+
+StringParameter via("via", "Gateway to tunnel via", "");
+
+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",
+ 6);
+
+char aboutText[1024];
+char* programName;
+extern char buildtime[];
+
+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);
+}
+
+// XLoginIconifier is a class which iconifies the XDM login window when it has
+// grabbed the keyboard, thus releasing the grab, allowing the viewer to use
+// the keyboard. It remaps the xlogin window on exit.
+class XLoginIconifier {
+public:
+ Display* dpy;
+ Window xlogin;
+ XLoginIconifier() : dpy(0), xlogin(0) {}
+ void iconify(Display* dpy_) {
+ dpy = dpy_;
+ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), False, GrabModeSync,
+ GrabModeSync, CurrentTime) == GrabSuccess) {
+ XUngrabKeyboard(dpy, CurrentTime);
+ } else {
+ xlogin = TXWindow::windowWithName(dpy, DefaultRootWindow(dpy), "xlogin");
+ if (xlogin) {
+ XIconifyWindow(dpy, xlogin, DefaultScreen(dpy));
+ XSync(dpy, False);
+ }
+ }
+ }
+ ~XLoginIconifier() {
+ if (xlogin) {
+ fprintf(stderr,"~XLoginIconifier remapping xlogin\n");
+ XMapWindow(dpy, xlogin);
+ XFlush(dpy);
+ sleep(1);
+ }
+ }
+};
+
+static XLoginIconifier xloginIconifier;
+
+static void usage()
+{
+ 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 -<param> or off with -<param>=0\n"
+ "Parameters which take a value can be specified as "
+ "-<param> <value>\n"
+ "Other valid forms are <param>=<value> -<param>=<value> "
+ "--<param>=<value>\n"
+ "Parameter names are case-insensitive. The parameters are:\n\n");
+ Configuration::listParams(79, 14);
+ exit(1);
+}
+
+/* Tunnelling support. */
+static void
+interpretViaParam (char **gatewayHost, char **remoteHost,
+ int *remotePort, char **vncServerName,
+ int localPort)
+{
+ const int SERVER_PORT_OFFSET = 5900;
+ char *pos = strchr (*vncServerName, ':');
+ if (pos == NULL)
+ *remotePort = SERVER_PORT_OFFSET;
+ else {
+ int portOffset = SERVER_PORT_OFFSET;
+ size_t len;
+ *pos++ = '\0';
+ len = strlen (pos);
+ if (*pos == ':') {
+ /* Two colons is an absolute port number, not an offset. */
+ pos++;
+ len--;
+ portOffset = 0;
+ }
+ if (!len || strspn (pos, "-0123456789") != len )
+ usage ();
+ *remotePort = atoi (pos) + portOffset;
+ }
+
+ if (**vncServerName != '\0')
+ *remoteHost = *vncServerName;
+
+ *gatewayHost = strDup (via.getValueStr ());
+ *vncServerName = new char[50];
+ sprintf (*vncServerName, "localhost::%d", localPort);
+}
+
+#ifndef HAVE_SETENV
+int
+setenv(const char *envname, const char * envval, int overwrite)
+{
+ if (envname && envval) {
+ char * envp = NULL;
+ envp = (char*)malloc(strlen(envname) + strlen(envval) + 2);
+ if (envp) {
+ // The putenv API guarantees memory leaks when
+ // changing environment variables repeatedly.
+ sprintf(envp, "%s=%s", envname, envval);
+
+ // Cannot free envp
+ putenv(envp);
+ return(0);
+ }
+ }
+ return(-1);
+}
+#endif
+
+static void
+createTunnel (const char *gatewayHost, const char *remoteHost,
+ int remotePort, int localPort)
+{
+ char *cmd = getenv ("VNC_VIA_CMD");
+ char *percent;
+ char lport[10], rport[10];
+ sprintf (lport, "%d", localPort);
+ sprintf (rport, "%d", remotePort);
+ setenv ("G", gatewayHost, 1);
+ setenv ("H", remoteHost, 1);
+ setenv ("R", rport, 1);
+ setenv ("L", lport, 1);
+ if (!cmd)
+ cmd = "/usr/bin/ssh -f -L \"$L\":\"$H\":\"$R\" \"$G\" sleep 20";
+ /* Compatibility with TightVNC's method. */
+ while ((percent = strchr (cmd, '%')) != NULL)
+ *percent = '$';
+ system (cmd);
+}
+
+int main(int argc, char** argv)
+{
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ snprintf(aboutText, sizeof(aboutText),
+ _("TightVNC viewer for X version 1.5 - built %s\n"
+ "Copyright (C) 2002-2005 RealVNC Ltd.\n"
+ "Copyright (C) 2000-2004 Constantin Kaplinsky\n"
+ "Copyright (C) 2004-2005 Peter Astrand, Cendio AB\n"
+ "See http://www.tightvnc.com for information on TightVNC."),
+ buildtime);
+ fprintf(stderr,"\n%s\n", aboutText);
+
+ bind_textdomain_codeset(PACKAGE, "iso-8859-1");
+
+ rfb::initStdIOLoggers();
+ rfb::LogWriter::setLogParams("*:stderr:30");
+
+ signal(SIGHUP, CleanupSignalHandler);
+ signal(SIGINT, CleanupSignalHandler);
+ signal(SIGTERM, CleanupSignalHandler);
+
+ programName = argv[0];
+ char* vncServerName = 0;
+ Display* dpy = 0;
+
+ 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();
+ }
+
+ vncServerName = argv[i];
+ }
+
+ // Create .vnc in the user's home directory if it doesn't already exist
+ char* homeDir = getenv("HOME");
+ if (homeDir) {
+ CharArray vncDir(strlen(homeDir)+6);
+ sprintf(vncDir.buf, "%s/.vnc", homeDir);
+ int result = mkdir(vncDir.buf, 0755);
+ if (result == -1 && errno != EEXIST)
+ vlog.error("Could not create .vnc directory: %s.", strerror(errno));
+ } else
+ vlog.error("Could not create .vnc directory: environment variable $HOME not set.");
+
+ if (!::autoSelect.hasBeenSet()) {
+ // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
+ ::autoSelect.setParam(!::preferredEncoding.hasBeenSet()
+ && !::fullColour.hasBeenSet()
+ && !::fullColourAlias.hasBeenSet());
+ }
+ if (!::customCompressLevel.hasBeenSet()) {
+ // Default to CustomCompressLevel=1 if CompressLevel is used.
+ ::customCompressLevel.setParam(::compressLevel.hasBeenSet());
+ }
+
+ try {
+ /* Tunnelling support. */
+ if (strlen (via.getValueStr ()) > 0) {
+ char *gatewayHost = "";
+ char *remoteHost = "localhost";
+ int localPort = findFreeTcpPort ();
+ int remotePort;
+ if (!vncServerName)
+ usage();
+ interpretViaParam (&gatewayHost, &remoteHost, &remotePort,
+ &vncServerName, localPort);
+ createTunnel (gatewayHost, remoteHost, remotePort, localPort);
+ }
+
+ Socket* sock = 0;
+
+ if (listenMode) {
+ int port = 5500;
+ if (vncServerName && isdigit(vncServerName[0]))
+ port = atoi(vncServerName);
+
+ TcpListener listener(port);
+
+ vlog.info("Listening on port %d\n",port);
+
+ while (true) {
+ sock = listener.accept();
+ int pid = fork();
+ if (pid < 0) { perror("fork"); exit(1); }
+ if (pid == 0) break; // child
+ delete sock;
+ int status;
+ while (wait3(&status, WNOHANG, 0) > 0) ;
+ }
+ }
+
+ CharArray displaynameStr(displayname.getData());
+ if (!(dpy = XOpenDisplay(TXWindow::strEmptyToNull(displaynameStr.buf)))) {
+ fprintf(stderr,"%s: unable to open display \"%s\"\n",
+ programName, XDisplayName(displaynameStr.buf));
+ exit(1);
+ }
+
+ TXWindow::init(dpy, "Vncviewer");
+ xloginIconifier.iconify(dpy);
+ CConn cc(dpy, argc, argv, sock, vncServerName, listenMode);
+
+ // X events are processed whenever reading from the socket would block.
+
+ while (true) {
+ cc.getInStream()->check(1);
+ cc.processMsg();
+ }
+
+ } catch (rdr::EndOfStream& e) {
+ vlog.info(e.str());
+ } catch (rdr::Exception& e) {
+ vlog.error(e.str());
+ if (dpy) {
+ TXMsgBox msgBox(dpy, e.str(), MB_OK, "VNC Viewer: Information");
+ msgBox.show();
+ }
+ return 1;
+ }
+
+ return 0;
+}