Rewrite server on top of TigerVNC

TigerVNC provides a much more robust server implementation vs.
libvncserver and yields higher performance and lower CPU
usage.
diff --git a/src/main.cpp b/src/main.cpp
index 29178ff..c22f075 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,148 +1,168 @@
-//
-// vncflinger - Copyright (C) 2017 Steve Kondik
-//
-// 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 3 of the License, 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-#include <getopt.h>
+#define LOG_TAG "VNCFlinger"
+#include <utils/Log.h>
 
-#include <csignal>
-#include <iostream>
+#include <fcntl.h>
 
-#include <binder/IServiceManager.h>
+#include "AndroidDesktop.h"
 
-#include "VNCFlinger.h"
-#include "VNCService.h"
+#include <network/Socket.h>
+#include <network/TcpSocket.h>
+#include <rfb/Configuration.h>
+#include <rfb/Logger_android.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/util.h>
 
-using namespace android;
+using namespace vncflinger;
 
-static sp<VNCFlinger> gVNC;
+static char* gProgramName;
+static bool gCaughtSignal = false;
 
-static const char* const shortOpts = "4:6:p:cs:l:vh";
-static const option longOpts[] = {
-    {"listen", 1, nullptr, '4'},
-    {"listen6", 1, nullptr, '6'},
-    {"port", 1, nullptr, 'p'},
-    {"password", 1, nullptr, 's'},
-    {"clear-password", 0, nullptr, 'c'},
-    {"scale", 1, nullptr, 'l'},
-    {"version", 0, nullptr, 'v'},
-    {"help", 0, nullptr, 'h'},
-};
+static rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol", 5900);
 
-static void onSignal(int signal) {
-    ALOGV("Shutting down on signal %d", signal);
-    gVNC->stop();
+static void printVersion(FILE* fp) {
+    fprintf(fp, "VNCFlinger 1.0");
 }
 
-static void printVersion() {
-    std::cout << "VNCFlinger-" << VNCFLINGER_VERSION << std::endl;
-    exit(0);
-}
-
-static void printHelp() {
-    std::cout << "Usage: vncflinger [OPTIONS]\n\n"
-              << "  -4 <addr>        IPv4 address to listen (default: localhost)\n"
-              << "  -6 <addr>        IPv6 address to listen (default: localhost)\n"
-              << "  -p <num>         Port to listen on (default: 5900)\n"
-              << "  -s <pass>        Store server password\n"
-              << "  -c               Clear server password\n"
-              << "  -l <scale>       Scaling value (default: 1.0)\n"
-              << "  -v               Show server version\n"
-              << "  -h               Show help\n\n";
+static void usage() {
+    printVersion(stderr);
+    fprintf(stderr, "\nUsage: %s [<parameters>]\n", gProgramName);
+    fprintf(stderr, "       %s --version\n", gProgramName);
+    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");
+    rfb::Configuration::listParams(79, 14);
     exit(1);
 }
 
-static void parseArgs(int argc, char** argv) {
-    String8 arg;
-
-    while (true) {
-        const auto opt = getopt_long(argc, argv, shortOpts, longOpts, nullptr);
-
-        if (opt < 0) {
-            break;
-        }
-
-        switch (opt) {
-            case 's':
-                arg = optarg;
-                if (gVNC->setPassword(arg) != OK) {
-                    std::cerr << "Failed to set password\n";
-                    exit(1);
-                }
-                exit(0);
-
-            case 'c':
-                if (gVNC->clearPassword() != OK) {
-                    std::cerr << "Failed to clear password\n";
-                    exit(1);
-                }
-                exit(0);
-
-            case '4':
-                arg = optarg;
-                if (gVNC->setV4Address(arg) != OK) {
-                    std::cerr << "Failed to set IPv4 address\n";
-                    exit(1);
-                }
-                break;
-
-            case '6':
-                arg = optarg;
-                if (gVNC->setV6Address(arg) != OK) {
-                    std::cerr << "Failed to set IPv6 address\n";
-                    exit(1);
-                }
-                break;
-
-            case 'p':
-                if (gVNC->setPort(std::stoi(optarg)) != OK) {
-                    std::cerr << "Failed to set port\n";
-                    exit(1);
-                }
-                break;
-
-            case 'l':
-                if (gVNC->setScale(std::stof(optarg)) != OK) {
-                    std::cerr << "Invalid scaling value (must be between 0.0 and 2.0)\n";
-                    exit(1);
-                }
-                break;
-
-            case 'v':
-                printVersion();
-                break;
-
-            case 'h':
-            case '?':
-            default:
-                printHelp();
-                break;
-        }
-    }
-}
-
 int main(int argc, char** argv) {
-    std::signal(SIGINT, onSignal);
-    std::signal(SIGHUP, onSignal);
+    rfb::initAndroidLogger();
+    rfb::LogWriter::setLogParams("*:android:30");
 
-    gVNC = new VNCFlinger();
+    gProgramName = argv[0];
 
-    parseArgs(argc, argv);
+    rfb::Configuration::enableServerParams();
 
-    // binder interface
-    defaultServiceManager()->addService(String16("vnc"), new VNCService(gVNC));
+    for (int i = 1; i < argc; i++) {
+        if (rfb::Configuration::setParam(argv[i])) continue;
 
-    gVNC->start();
-    gVNC.clear();
+        if (argv[i][0] == '-') {
+            if (i + 1 < argc) {
+                if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
+                    i++;
+                    continue;
+                }
+            }
+            if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "-version") == 0 ||
+                strcmp(argv[i], "--version") == 0) {
+                printVersion(stdout);
+                return 0;
+            }
+            usage();
+        }
+        usage();
+    }
+
+    std::list<network::TcpListener*> listeners;
+
+    try {
+        sp<AndroidDesktop> desktop = new AndroidDesktop();
+        rfb::VNCServerST server("vncflinger", desktop.get());
+        network::createTcpListeners(&listeners, 0, (int)rfbport);
+
+        int eventFd = desktop->getEventFd();
+        fcntl(eventFd, F_SETFL, O_NONBLOCK);
+
+        ALOGI("Listening on port %d", (int)rfbport);
+
+        while (!gCaughtSignal) {
+            int wait_ms;
+            struct timeval tv;
+            fd_set rfds, wfds;
+            std::list<network::Socket*> sockets;
+            std::list<network::Socket*>::iterator i;
+
+            FD_ZERO(&rfds);
+            FD_ZERO(&wfds);
+
+            FD_SET(eventFd, &rfds);
+            for (std::list<network::TcpListener*>::iterator i = listeners.begin();
+                 i != listeners.end(); i++)
+                FD_SET((*i)->getFd(), &rfds);
+
+            server.getSockets(&sockets);
+            int clients_connected = 0;
+            for (i = sockets.begin(); i != sockets.end(); i++) {
+                if ((*i)->isShutdown()) {
+                    server.removeSocket(*i);
+                    delete (*i);
+                } else {
+                    FD_SET((*i)->getFd(), &rfds);
+                    if ((*i)->outStream().bufferUsage() > 0) FD_SET((*i)->getFd(), &wfds);
+                    clients_connected++;
+                }
+            }
+
+            wait_ms = 0;
+
+            rfb::soonestTimeout(&wait_ms, server.checkTimeouts());
+
+            tv.tv_sec = wait_ms / 1000;
+            tv.tv_usec = (wait_ms % 1000) * 1000;
+
+            int n = select(FD_SETSIZE, &rfds, &wfds, 0, wait_ms ? &tv : NULL);
+
+            if (n < 0) {
+                if (errno == EINTR) {
+                    ALOGV("Interrupted select() system call");
+                    continue;
+                } else {
+                    throw rdr::SystemException("select", errno);
+                }
+            }
+
+            // Accept new VNC connections
+            for (std::list<network::TcpListener*>::iterator i = listeners.begin();
+                 i != listeners.end(); i++) {
+                if (FD_ISSET((*i)->getFd(), &rfds)) {
+                    network::Socket* sock = (*i)->accept();
+                    if (sock) {
+                        sock->outStream().setBlocking(false);
+                        server.addSocket(sock);
+                    } else {
+                        ALOGW("Client connection rejected");
+                    }
+                }
+            }
+
+            server.checkTimeouts();
+
+            // Client list could have been changed.
+            server.getSockets(&sockets);
+
+            // Nothing more to do if there are no client connections.
+            if (sockets.empty()) continue;
+
+            // Process events on existing VNC connections
+            for (i = sockets.begin(); i != sockets.end(); i++) {
+                if (FD_ISSET((*i)->getFd(), &rfds)) server.processSocketReadEvent(*i);
+                if (FD_ISSET((*i)->getFd(), &wfds)) server.processSocketWriteEvent(*i);
+            }
+
+            // Process events from the display
+            uint64_t eventVal;
+            int status = read(eventFd, &eventVal, sizeof(eventVal));
+            if (status > 0 && eventVal > 0) {
+                desktop->processFrames();
+            }
+        }
+
+    } catch (rdr::Exception& e) {
+        ALOGE("%s", e.str());
+        return 1;
+    }
 }