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/AndroidDesktop.cpp b/src/AndroidDesktop.cpp
new file mode 100644
index 0000000..a37500c
--- /dev/null
+++ b/src/AndroidDesktop.cpp
@@ -0,0 +1,340 @@
+#define LOG_TAG "AndroidDesktop"
+#include <utils/Log.h>
+
+#include <fcntl.h>
+#include <sys/eventfd.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <ui/DisplayInfo.h>
+
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb/ScreenSet.h>
+#include <rfb/VNCServerST.h>
+
+#include "AndroidDesktop.h"
+#include "InputDevice.h"
+
+using namespace vncflinger;
+using namespace android;
+
+const rfb::PixelFormat AndroidDesktop::sRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
+
+AndroidDesktop::AndroidDesktop() {
+    mListener = new FrameListener(this);
+    mInputDevice = new InputDevice();
+
+    mEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+    if (mEventFd < 0) {
+        ALOGE("Failed to create event notifier");
+        return;
+    }
+}
+
+AndroidDesktop::~AndroidDesktop() {
+    mInputDevice->stop();
+    close(mEventFd);
+}
+
+void AndroidDesktop::start(rfb::VNCServer* vs) {
+    Mutex::Autolock _l(mMutex);
+
+    sp<ProcessState> self = ProcessState::self();
+    self->startThreadPool();
+
+    mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+    if (updateDisplayProjection() == NO_INIT) {
+        ALOGE("Failed to query display!");
+        return;
+    }
+    mProjectionChanged = false;
+
+    status_t err = createVirtualDisplay();
+    if (err != NO_ERROR) {
+        ALOGE("Failed to create virtual display: err=%d", err);
+        return;
+    }
+
+    mInputDevice->start_async(mWidth, mHeight);
+
+    mServer = (rfb::VNCServerST*)vs;
+
+    updateFBSize(mWidth, mHeight);
+
+    mServer->setPixelBuffer(mPixels.get());
+
+    ALOGV("Desktop is running");
+}
+
+void AndroidDesktop::stop() {
+    Mutex::Autolock _L(mMutex);
+
+    ALOGV("Shutting down");
+
+    mServer->setPixelBuffer(0);
+    destroyVirtualDisplay();
+    mWidth = 0;
+    mHeight = 0;
+}
+
+status_t AndroidDesktop::createVirtualDisplay() {
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&mProducer, &consumer);
+    mCpuConsumer = new CpuConsumer(consumer, 1);
+    mCpuConsumer->setName(String8("vds-to-cpu"));
+    mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
+    mProducer->setMaxDequeuedBufferCount(4);
+    consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBX_8888);
+
+    mCpuConsumer->setFrameAvailableListener(mListener);
+
+    mDpy = SurfaceComposerClient::createDisplay(String8("VNC-VirtualDisplay"), false /*secure*/);
+
+    // aspect ratio
+    float displayAspect = (float) mSourceHeight / (float) mSourceWidth;
+
+    uint32_t outWidth, outHeight;
+    if (mWidth > (uint32_t)(mWidth * displayAspect)) {
+        // limited by narrow width; reduce height
+        outWidth = mWidth;
+        outHeight = (uint32_t)(mWidth * displayAspect);
+    } else {
+        // limited by short height; restrict width
+        outHeight = mHeight;
+        outWidth = (uint32_t)(mHeight / displayAspect);
+    }
+
+    // position the desktop in the viewport while preserving
+    // the source aspect ratio. we do this in case the client
+    // has resized the window and to deal with orientation
+    // changes set up by updateDisplayProjection
+    uint32_t offX, offY;
+    offX = (mWidth - outWidth) / 2;
+    offY = (mHeight - outHeight) / 2;
+    mDisplayRect = Rect(offX, offY, offX + outWidth, offY + outHeight);
+    Rect sourceRect(0, 0, mSourceWidth, mSourceHeight);
+
+    SurfaceComposerClient::openGlobalTransaction();
+    SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
+    SurfaceComposerClient::setDisplayProjection(mDpy, 0, sourceRect, mDisplayRect);
+    SurfaceComposerClient::setDisplayLayerStack(mDpy, 0);  // default stack
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    mVDSActive = true;
+
+    ALOGV("Virtual display (%lux%lu [viewport=%ux%u] created", mWidth, mHeight,
+            outWidth, outHeight);
+
+    return NO_ERROR;
+}
+
+status_t AndroidDesktop::destroyVirtualDisplay() {
+    if (!mVDSActive) {
+        return NO_INIT;
+    }
+
+    mCpuConsumer.clear();
+    mProducer.clear();
+    SurfaceComposerClient::destroyDisplay(mDpy);
+
+    mVDSActive = false;
+
+    ALOGV("Virtual display destroyed");
+
+    return NO_ERROR;
+}
+
+void AndroidDesktop::processDesktopResize() {
+    if (mProjectionChanged) {
+        destroyVirtualDisplay();
+        createVirtualDisplay();
+        updateFBSize(mWidth, mHeight);
+        mInputDevice->reconfigure(mDisplayRect.getWidth(), mDisplayRect.getHeight());
+        rfb::ScreenSet screens;
+        screens.add_screen(rfb::Screen(0, 0, 0, mWidth, mHeight, 0));
+        mServer->setScreenLayout(screens);
+
+        mProjectionChanged = false;
+    }
+}
+
+void AndroidDesktop::processFrames() {
+    Mutex::Autolock _l(mMutex);
+
+    // do any pending resize
+    processDesktopResize();
+
+    if (!mFrameAvailable) {
+        return;
+    }
+
+    // get a frame from the virtual display
+    CpuConsumer::LockedBuffer imgBuffer;
+    status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
+    if (res != OK) {
+        ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
+        return;
+    }
+
+    mFrameNumber = imgBuffer.frameNumber;
+    ALOGV("processFrame: [%lu] format: %x (%dx%d, stride=%d)", mFrameNumber, imgBuffer.format,
+          imgBuffer.width, imgBuffer.height, imgBuffer.stride);
+
+    // we don't know if there was a stride change until we get
+    // a buffer from the queue. if it changed, we need to resize
+
+    rfb::Rect bufRect(0, 0, imgBuffer.width, imgBuffer.height);
+
+    // performance is extremely bad if the gpu memory is used
+    // directly without copying because it is likely uncached
+    mPixels->imageRect(bufRect, imgBuffer.data, imgBuffer.stride);
+
+    mCpuConsumer->unlockBuffer(imgBuffer);
+
+    // update clients
+    mServer->add_changed(bufRect);
+    mFrameAvailable = false;
+}
+
+// notifies the server loop that we have changes
+void AndroidDesktop::notify() {
+    static uint64_t notify = 1;
+    write(mEventFd, &notify, sizeof(notify));
+}
+
+// called when a client resizes the window
+unsigned int AndroidDesktop::setScreenLayout(int reqWidth, int reqHeight,
+                                             const rfb::ScreenSet& layout) {
+    Mutex::Autolock _l(mMutex);
+
+    char* dbg = new char[1024];
+    layout.print(dbg, 1024);
+
+    ALOGD("setScreenLayout: cur: %lux%lu  new: %dx%d %s", mWidth, mHeight, reqWidth, reqHeight, dbg);
+    delete[] dbg;
+
+    if (reqWidth == (int)mWidth && reqHeight == (int)mHeight) {
+        return rfb::resultInvalid;
+    }
+
+    if (reqWidth > 0 && reqHeight > 0) {
+        mWidth = reqWidth;
+        mHeight = reqHeight;
+
+        if (updateDisplayProjection() == NO_ERROR) {
+            // resize immediately
+            processDesktopResize();
+            notify();
+            return rfb::resultSuccess;
+        }
+    }
+    return rfb::resultInvalid;
+}
+
+// updates the pixelbuffer dimensions
+bool AndroidDesktop::updateFBSize(uint64_t width, uint64_t height) {
+    if (mPixels == nullptr || mPixels->height() != (int)height || mPixels->width() != (int)width) {
+        if (mPixels != nullptr) {
+            ALOGD("updateFBSize: old=[%dx%d] new=[%lux%lu]", mPixels->width(), mPixels->height(),
+                  width, height);
+        }
+        if (mPixels != nullptr && (int)width <= mPixels->width() &&
+            (int)height <= mPixels->height()) {
+            mPixels->setSize(width, height);
+        } else {
+            mPixels = new AndroidPixelBuffer(width, height);
+            mServer->setPixelBuffer(mPixels.get());
+        }
+        return true;
+    }
+    return false;
+}
+
+// cpuconsumer frame listener, called from binder thread
+void AndroidDesktop::FrameListener::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock _l(mDesktop->mMutex);
+    mDesktop->updateDisplayProjection();
+    mDesktop->mFrameAvailable = true;
+    mDesktop->notify();
+    ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
+}
+
+rfb::Point AndroidDesktop::getFbSize() {
+    return rfb::Point(mPixels->width(), mPixels->height());
+}
+
+void AndroidDesktop::keyEvent(rdr::U32 key, bool down) {
+    mInputDevice->keyEvent(down, key);
+}
+
+void AndroidDesktop::pointerEvent(const rfb::Point& pos, int buttonMask) {
+    if (pos.x < mDisplayRect.left || pos.x > mDisplayRect.right ||
+            pos.y < mDisplayRect.top || pos.y > mDisplayRect.bottom) {
+        // outside viewport
+        return;
+    }
+    uint32_t x = pos.x * ((float)(mDisplayRect.getWidth()) / (float)mWidth);
+    uint32_t y = pos.y * ((float)(mDisplayRect.getHeight()) / (float)mHeight);
+
+    ALOGD("pointer xlate x1=%d y1=%d x2=%d y2=%d", pos.x, pos.y, x, y);
+
+    mServer->setCursorPos(rfb::Point(x, y));
+    mInputDevice->pointerEvent(buttonMask, x, y);
+}
+
+// figure out the dimensions of the display. deal with orientation
+// changes, client-side window resize, server-side scaling, and
+// maintaining aspect ratio.
+status_t AndroidDesktop::updateDisplayProjection() {
+    DisplayInfo info;
+    status_t err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &info);
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get display characteristics\n");
+        return err;
+    }
+
+    bool deviceRotated =
+        info.orientation != DISPLAY_ORIENTATION_0 && info.orientation != DISPLAY_ORIENTATION_180;
+
+    // if orientation changed, swap width/height
+    uint32_t sourceWidth, sourceHeight;
+    if (!deviceRotated) {
+        sourceWidth = info.w;
+        sourceHeight = info.h;
+    } else {
+        sourceHeight = info.w;
+        sourceWidth = info.h;
+    }
+
+    if (mWidth == 0 && mHeight == 0) {
+        mWidth = sourceWidth;
+        mHeight = sourceHeight;
+    }
+
+    if (deviceRotated != mRotated) {
+        std::swap(mWidth, mHeight);
+        mRotated = deviceRotated;
+    }
+
+    // if nothing changed, we're done
+    if (mSourceWidth == sourceWidth && mSourceHeight == sourceHeight &&
+        (int)mWidth == mPixels->width() && (int)mHeight == mPixels->height()) {
+        return NO_ERROR;
+    }
+
+    // update all the values and flag for an update
+    mSourceWidth = sourceWidth;
+    mSourceHeight = sourceHeight;
+    mProjectionChanged = true;
+
+    ALOGV("Dimensions: %lux%lu [out: %lux%lu] rotated=%d", mSourceWidth, mSourceHeight, mWidth,
+          mHeight, mRotated);
+
+    return NO_ERROR;
+}
diff --git a/src/AndroidDesktop.h b/src/AndroidDesktop.h
new file mode 100644
index 0000000..0d3f8e4
--- /dev/null
+++ b/src/AndroidDesktop.h
@@ -0,0 +1,124 @@
+#ifndef ANDROID_DESKTOP_H_
+#define ANDROID_DESKTOP_H_
+
+#include <memory>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Thread.h>
+
+#include <gui/CpuConsumer.h>
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/SDesktop.h>
+#include <rfb/VNCServerST.h>
+
+#include "InputDevice.h"
+
+
+using namespace android;
+
+namespace vncflinger {
+
+static const rfb::PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
+
+class AndroidDesktop : public rfb::SDesktop, public RefBase {
+  public:
+    AndroidDesktop();
+
+    virtual ~AndroidDesktop();
+
+    virtual void start(rfb::VNCServer* vs);
+    virtual void stop();
+
+    virtual rfb::Point getFbSize();
+    virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout);
+
+    virtual void keyEvent(rdr::U32 key, bool down);
+    virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
+
+    virtual void processFrames();
+
+    virtual int getEventFd() {
+        return mEventFd;
+    }
+
+  private:
+    class FrameListener : public CpuConsumer::FrameAvailableListener {
+      public:
+        FrameListener(AndroidDesktop* desktop) : mDesktop(desktop) {
+        }
+
+        virtual void onFrameAvailable(const BufferItem& item);
+
+      private:
+        FrameListener(FrameListener&) {
+        }
+        AndroidDesktop* mDesktop;
+    };
+
+    class AndroidPixelBuffer : public RefBase, public rfb::ManagedPixelBuffer {
+      public:
+        AndroidPixelBuffer(uint64_t width, uint64_t height)
+            : rfb::ManagedPixelBuffer(sRGBX, width, height) {
+        }
+    };
+
+    virtual status_t createVirtualDisplay();
+    virtual status_t destroyVirtualDisplay();
+
+    virtual void notify();
+
+    virtual status_t updateDisplayProjection();
+    virtual bool updateFBSize(uint64_t width, uint64_t height);
+    virtual void processDesktopResize();
+
+    uint64_t mSourceWidth, mSourceHeight;
+    uint64_t mWidth, mHeight;
+    Rect mDisplayRect;
+
+    bool mRotated;
+
+    Mutex mMutex;
+
+    bool mFrameAvailable;
+    bool mProjectionChanged;
+    bool mRotate;
+    bool mVDSActive;
+
+    uint64_t mFrameNumber;
+    nsecs_t mFrameStartWhen;
+
+    int mEventFd;
+
+    // Android virtual display is always 32-bit
+    static const rfb::PixelFormat sRGBX;
+
+    // Server instance
+    rfb::VNCServerST* mServer;
+
+    // Pixel buffer
+    sp<AndroidPixelBuffer> mPixels;
+
+    // Primary display
+    sp<IBinder> mMainDpy;
+
+    // Virtual display
+    sp<IBinder> mDpy;
+
+    // Producer side of queue, passed into the virtual display.
+    sp<IGraphicBufferProducer> mProducer;
+
+    // This receives frames from the virtual display and makes them available
+    sp<CpuConsumer> mCpuConsumer;
+
+    // Listener for virtual display buffers
+    sp<FrameListener> mListener;
+
+    // Virtual input device
+    sp<InputDevice> mInputDevice;
+};
+};
+
+#endif
diff --git a/src/InputDevice.cpp b/src/InputDevice.cpp
index a4b6d7c..04bad97 100644
--- a/src/InputDevice.cpp
+++ b/src/InputDevice.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "VNC-InputDevice"
 #include <utils/Log.h>
 
+#include <future>
+
 #include "InputDevice.h"
 
 #include <fcntl.h>
@@ -28,11 +30,8 @@
 #include <linux/input.h>
 #include <linux/uinput.h>
 
-#include <rfb/keysym.h>
-
 using namespace android;
 
-ANDROID_SINGLETON_STATIC_INSTANCE(InputDevice)
 
 static const struct UInputOptions {
     int cmd;
@@ -51,11 +50,16 @@
     {UI_SET_PROPBIT, INPUT_PROP_DIRECT},
 };
 
+status_t InputDevice::start_async(uint32_t width, uint32_t height) {
+    // don't block the caller since this can take a few seconds
+    std::async(&InputDevice::start, this, width, height);
+
+    return NO_ERROR;
+}
+
 status_t InputDevice::start(uint32_t width, uint32_t height) {
     Mutex::Autolock _l(mLock);
 
-    status_t err = OK;
-
     mLeftClicked = mMiddleClicked = mRightClicked = false;
 
     struct input_id id = {
@@ -111,6 +115,8 @@
         goto err_ioctl;
     }
 
+    mOpened = true;
+
     ALOGD("Virtual input device created successfully (%dx%d)", width, height);
     return NO_ERROR;
 
@@ -124,11 +130,14 @@
 
 status_t InputDevice::reconfigure(uint32_t width, uint32_t height) {
     stop();
-    return start(width, height);
+    return start_async(width, height);
 }
 
 status_t InputDevice::stop() {
     Mutex::Autolock _l(mLock);
+
+    mOpened = false;
+
     if (mFD < 0) {
         return OK;
     }
@@ -187,20 +196,15 @@
     return release(code);
 }
 
-void InputDevice::onKeyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl) {
-    InputDevice::getInstance().keyEvent(down, key, cl);
-}
-
-void InputDevice::keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl) {
+void InputDevice::keyEvent(bool down, uint32_t key) {
     int code;
     int sh = 0;
     int alt = 0;
 
-    if (mFD < 0) return;
-
     Mutex::Autolock _l(mLock);
+    if (!mOpened) return;
 
-    if ((code = keysym2scancode(key, cl, &sh, &alt))) {
+    if ((code = keysym2scancode(key, &sh, &alt))) {
         int ret = 0;
 
         if (key && down) {
@@ -231,17 +235,12 @@
     }
 }
 
-void InputDevice::onPointerEvent(int buttonMask, int x, int y, rfbClientPtr cl) {
-    InputDevice::getInstance().pointerEvent(buttonMask, x, y, cl);
-}
-
-void InputDevice::pointerEvent(int buttonMask, int x, int y, rfbClientPtr /* cl  */) {
-    if (mFD < 0) return;
+void InputDevice::pointerEvent(int buttonMask, int x, int y) {
+    Mutex::Autolock _l(mLock);
+    if (!mOpened) return;
 
     ALOGV("pointerEvent: buttonMask=%x x=%d y=%d", buttonMask, x, y);
 
-    Mutex::Autolock _l(mLock);
-
     if ((buttonMask & 1) && mLeftClicked) {  // left btn clicked and moving
         inject(EV_ABS, ABS_X, x);
         inject(EV_ABS, ABS_Y, y);
@@ -314,7 +313,7 @@
 static const int spec4[] = {26, 43, 27, 215, 14};
 static const int spec4sh[] = {1, 1, 1, 1, 0};
 
-int InputDevice::keysym2scancode(rfbKeySym c, rfbClientPtr cl, int* sh, int* alt) {
+int InputDevice::keysym2scancode(uint32_t c, int* sh, int* alt) {
     int real = 1;
     if ('a' <= c && c <= 'z') return qwerty[c - 'a'];
     if ('A' <= c && c <= 'Z') {
@@ -372,9 +371,6 @@
         //	return 232;// end -> DPAD_CENTER (ball click)
         case 0xff50:
             return KEY_HOME;  // home
-        case 0xFFC8:
-            rfbShutdownServer(cl->screen, TRUE);
-            return 0;  // F11 disconnect
         case 0xffff:
             return 158;  // del -> back
         case 0xff55:
diff --git a/src/InputDevice.h b/src/InputDevice.h
index 1744478..be4367f 100644
--- a/src/InputDevice.h
+++ b/src/InputDevice.h
@@ -20,32 +20,33 @@
 
 #include <utils/Errors.h>
 #include <utils/Mutex.h>
-#include <utils/Singleton.h>
+#include <utils/RefBase.h>
 
 #include <linux/uinput.h>
-#include <rfb/rfb.h>
+
 
 #define UINPUT_DEVICE "/dev/uinput"
 
 namespace android {
 
-class InputDevice : public Singleton<InputDevice> {
-    friend class Singleton;
-
+class InputDevice : public RefBase {
   public:
     virtual status_t start(uint32_t width, uint32_t height);
+    virtual status_t start_async(uint32_t width, uint32_t height);
     virtual status_t stop();
     virtual status_t reconfigure(uint32_t width, uint32_t height);
 
-    static void onKeyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
-    static void onPointerEvent(int buttonMask, int x, int y, rfbClientPtr cl);
+    virtual void keyEvent(bool down, uint32_t key);
+    virtual void pointerEvent(int buttonMask, int x, int y);
 
     InputDevice() : mFD(-1) {
     }
     virtual ~InputDevice() {
+        stop();
     }
 
   private:
+
     status_t inject(uint16_t type, uint16_t code, int32_t value);
     status_t injectSyn(uint16_t type, uint16_t code, int32_t value);
     status_t movePointer(int32_t x, int32_t y);
@@ -54,14 +55,12 @@
     status_t release(uint16_t code);
     status_t click(uint16_t code);
 
-    void keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
-    void pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl);
-
-    int keysym2scancode(rfbKeySym c, rfbClientPtr cl, int* sh, int* alt);
+    int keysym2scancode(uint32_t c, int* sh, int* alt);
 
     Mutex mLock;
 
     int mFD;
+    bool mOpened;
 
     struct uinput_user_dev mUserDev;
 
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
deleted file mode 100644
index b910453..0000000
--- a/src/VNCFlinger.cpp
+++ /dev/null
@@ -1,473 +0,0 @@
-//
-// 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/>.
-//
-
-#define LOG_TAG "VNCFlinger"
-#include <utils/Log.h>
-
-#include <fstream>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <ui/PixelFormat.h>
-
-#include "InputDevice.h"
-#include "VNCFlinger.h"
-
-using namespace android;
-
-VNCFlinger::VNCFlinger() {
-    mOrientation = -1;
-    mScale = 1.0f;
-    mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
-    updateDisplayProjection();
-
-    createVNCServer();
-
-    String8 v4("127.0.0.1");
-    String8 v6("::1");
-
-    setV4Address(v4);
-    setV6Address(v6);
-}
-
-status_t VNCFlinger::setV4Address(const String8& address) {
-    if (!rfbStringToAddr(const_cast<char *>(address.string()), &(mVNCScreen->listenInterface))) {
-        return BAD_VALUE;
-    }
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::setV6Address(const String8& address) {
-    mVNCScreen->listen6Interface = const_cast<char *>(address.string());
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::setPort(unsigned int port) {
-    if (port > 65535) {
-        port = 0;
-    }
-    if (port == 0) {
-        mVNCScreen->autoPort = 1;
-    } else {
-        mVNCScreen->autoPort = 0;
-        mVNCScreen->port = port;
-        mVNCScreen->ipv6port = port;
-    }
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::setScale(float scale) {
-    if (scale <= 0.0f || scale > 2.0f) {
-        return BAD_VALUE;
-    }
-    mScale = scale;
-    updateDisplayProjection();
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::clearPassword() {
-    std::remove(VNC_AUTH_FILE);
-    ALOGW("Password authentication disabled");
-    mVNCScreen->authPasswdData = NULL;
-    return OK;
-}
-
-status_t VNCFlinger::setPassword(const String8& passwd) {
-    String8 path(VNC_AUTH_FILE);
-    if (rfbEncryptAndStorePasswd(const_cast<char *>(passwd.string()),
-                                 const_cast<char *>(path.string())) != 0) {
-        ALOGE("Failed to set password");
-        return BAD_VALUE;
-    }
-    ALOGI("Password has been set");
-    mVNCScreen->authPasswdData = (void *)VNC_AUTH_FILE;
-    return OK;
-}
-
-status_t VNCFlinger::start() {
-    sp<ProcessState> self = ProcessState::self();
-    self->startThreadPool();
-
-    status_t err = startVNCServer();
-    if (err != NO_ERROR) {
-        ALOGE("Failed to start VNCFlinger: err=%d", err);
-        return err;
-    }
-
-    rfbRunEventLoop(mVNCScreen, -1, true);
-
-    ALOGD("VNCFlinger ready to fling");
-
-    eventLoop();
-
-    ALOGI("VNCFlinger has left the building");
-
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::stop() {
-    Mutex::Autolock _L(mEventMutex);
-    ALOGV("Shutting down");
-
-    rfbShutdownServer(mVNCScreen, false);
-
-    destroyVirtualDisplayLocked();
-
-    mClientCount = 0;
-    mRunning = false;
-
-    mEventCond.signal();
-    delete[] mVNCScreen->frameBuffer;
-    return NO_ERROR;
-}
-
-void VNCFlinger::eventLoop() {
-    mRunning = true;
-
-    Mutex::Autolock _l(mEventMutex);
-    while (mRunning) {
-        mEventCond.wait(mEventMutex);
-
-        // spurious wakeup? never.
-        if (mClientCount == 0) {
-            continue;
-        }
-
-        // this is a new client, so fire everything up
-        status_t err = createVirtualDisplay();
-        if (err != NO_ERROR) {
-            ALOGE("Failed to create virtual display: err=%d", err);
-        }
-
-        // loop while clients are connected and process frames
-        // on the main thread when signalled
-        while (mClientCount > 0) {
-            mEventCond.wait(mEventMutex);
-            if (mFrameAvailable) {
-                if (!updateDisplayProjection()) {
-                    processFrame();
-                }
-                mFrameAvailable = false;
-            }
-        }
-        Mutex::Autolock _l(mUpdateMutex);
-        memset(mVNCScreen->frameBuffer, 0, mFrameSize);
-        destroyVirtualDisplayLocked();
-    }
-}
-
-status_t VNCFlinger::createVirtualDisplay() {
-    sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&mProducer, &consumer);
-    mCpuConsumer = new CpuConsumer(consumer, NUM_BUFS);
-    mCpuConsumer->setName(String8("vds-to-cpu"));
-    mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
-    mProducer->setMaxDequeuedBufferCount(1);
-    consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBX_8888);
-
-    mListener = new FrameListener(this);
-    mCpuConsumer->setFrameAvailableListener(mListener);
-
-    mDpy = SurfaceComposerClient::createDisplay(String8("VNC-VirtualDisplay"), false /*secure*/);
-
-    SurfaceComposerClient::openGlobalTransaction();
-    SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
-    Rect displayRect(0, 0, mSourceWidth, mSourceHeight);
-    Rect outRect(0, 0, mWidth, mHeight);
-    SurfaceComposerClient::setDisplayProjection(mDpy, 0, displayRect, outRect);
-    SurfaceComposerClient::setDisplayLayerStack(mDpy, 0);  // default stack
-    SurfaceComposerClient::closeGlobalTransaction();
-
-    mVDSActive = true;
-
-    ALOGV("Virtual display (%dx%d) created", mWidth, mHeight);
-
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::destroyVirtualDisplayLocked() {
-    if (!mVDSActive) {
-        return NO_INIT;
-    }
-
-    mCpuConsumer.clear();
-    mProducer.clear();
-    SurfaceComposerClient::destroyDisplay(mDpy);
-
-    mVDSActive = false;
-
-    ALOGV("Virtual display destroyed");
-
-    return NO_ERROR;
-}
-
-status_t VNCFlinger::createVNCServer() {
-    status_t err = NO_ERROR;
-
-    rfbLog = VNCFlinger::rfbLogger;
-    rfbErr = VNCFlinger::rfbLogger;
-
-    // 32-bit color
-    mVNCScreen = rfbGetScreen(0, NULL, mWidth, mHeight, 8, 3, 4);
-    if (mVNCScreen == NULL) {
-        ALOGE("Unable to create VNCScreen");
-        return NO_INIT;
-    }
-
-    mFrameNumber = 0;
-    mFrameSize = mWidth * mHeight * 4;
-    mVNCScreen->frameBuffer = (char*)new uint8_t[mFrameSize];
-    memset(mVNCScreen->frameBuffer, 0, mFrameSize);
-
-    mVNCScreen->desktopName = "VNCFlinger";
-    mVNCScreen->alwaysShared = TRUE;
-    mVNCScreen->httpDir = NULL;
-    mVNCScreen->newClientHook = (rfbNewClientHookPtr)VNCFlinger::onNewClient;
-    mVNCScreen->kbdAddEvent = InputDevice::onKeyEvent;
-    mVNCScreen->ptrAddEvent = InputDevice::onPointerEvent;
-    mVNCScreen->displayHook = (rfbDisplayHookPtr)VNCFlinger::onFrameStart;
-    mVNCScreen->displayFinishedHook = (rfbDisplayFinishedHookPtr)VNCFlinger::onFrameDone;
-    mVNCScreen->serverFormat.trueColour = true;
-    mVNCScreen->serverFormat.bitsPerPixel = 32;
-    mVNCScreen->serverFormat.depth = 24;
-    mVNCScreen->handleEventsEagerly = true;
-    mVNCScreen->deferUpdateTime = 0;
-    mVNCScreen->screenData = this;
-
-    std::ifstream authFile(VNC_AUTH_FILE);
-    if ((bool)authFile) {
-        mVNCScreen->authPasswdData = (void *)VNC_AUTH_FILE;
-    }
-
-    return err;
-}
-
-status_t VNCFlinger::startVNCServer() {
-    rfbInitServer(mVNCScreen);
-
-    /* Mark as dirty since we haven't sent any updates at all yet. */
-    rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
-
-    return NO_ERROR;
-}
-
-size_t VNCFlinger::addClient() {
-    Mutex::Autolock _l(mEventMutex);
-    if (mClientCount == 0) {
-        mClientCount++;
-        InputDevice::getInstance().start(mWidth, mHeight);
-        mEventCond.signal();
-    }
-
-    ALOGI("Client connected (%zu)", mClientCount);
-
-    return mClientCount;
-}
-
-size_t VNCFlinger::removeClient() {
-    Mutex::Autolock _l(mEventMutex);
-    if (mClientCount > 0) {
-        mClientCount--;
-        if (mClientCount == 0) {
-            InputDevice::getInstance().stop();
-            mEventCond.signal();
-        }
-    }
-
-    ALOGI("Client disconnected (%zu)", mClientCount);
-
-    return mClientCount;
-}
-
-void VNCFlinger::processFrame() {
-    // Take the update mutex. This ensures that we don't dequeue
-    // a new buffer and blow away the one being sent to a client.
-    // The BufferQueue is self-regulating and will drop frames
-    // automatically for us.
-    Mutex::Autolock _l(mUpdateMutex);
-
-    // get a frame from the virtual display
-    CpuConsumer::LockedBuffer imgBuffer;
-    status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
-    if (res != OK) {
-        ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
-        return;
-    }
-
-    mFrameNumber = imgBuffer.frameNumber;
-    ALOGV("processFrame: [%lu] format: %x (%dx%d, stride=%d)", mFrameNumber, imgBuffer.format,
-          imgBuffer.width, imgBuffer.height, imgBuffer.stride);
-
-    // we don't know if there was a stride change until we get
-    // a buffer from the queue. if it changed, we need to resize
-    updateFBSize(imgBuffer);
-
-    // performance is extremely bad if the gpu memory is used
-    // directly without copying because it is likely uncached
-    memcpy(mVNCScreen->frameBuffer, imgBuffer.data, mFrameSize);
-
-    // update clients
-    rfbMarkRectAsModified(mVNCScreen, 0, 0, imgBuffer.width, imgBuffer.height);
-
-    mCpuConsumer->unlockBuffer(imgBuffer);
-}
-
-/*
- * Returns "true" if the device is rotated 90 degrees.
- */
-bool VNCFlinger::isDeviceRotated(int orientation) {
-    return orientation != DISPLAY_ORIENTATION_0 && orientation != DISPLAY_ORIENTATION_180;
-}
-
-/*
- * Sets the display projection, based on the display dimensions, video size,
- * and device orientation.
- */
-bool VNCFlinger::updateDisplayProjection() {
-    DisplayInfo info;
-    status_t err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &info);
-    if (err != NO_ERROR) {
-        ALOGE("Failed to get display characteristics\n");
-        return true;
-    }
-
-    // Set the region of the layer stack we're interested in, which in our
-    // case is "all of it".  If the app is rotated (so that the width of the
-    // app is based on the height of the display), reverse width/height.
-    bool deviceRotated = isDeviceRotated(info.orientation);
-    uint32_t sourceWidth, sourceHeight;
-    if (!deviceRotated) {
-        sourceWidth = info.w;
-        sourceHeight = info.h;
-    } else {
-        ALOGV("using rotated width/height");
-        sourceHeight = info.w;
-        sourceWidth = info.h;
-    }
-
-    uint32_t width = sourceWidth * mScale;
-    uint32_t height = sourceHeight * mScale;
-
-    if (mSourceWidth == sourceWidth && mSourceHeight == sourceHeight &&
-            mWidth == width && mHeight == height &&
-            mOrientation == info.orientation) {
-        return false;
-    }
-
-    // orientation / resolution change
-    mSourceWidth = sourceWidth;
-    mSourceHeight = sourceHeight;
-    mWidth = width;
-    mHeight = height;
-    mOrientation = info.orientation;
-
-    ALOGV("Dimensions: %dx%d [out: %dx%d, scale: %f] orientation=%d",
-            mSourceWidth, mSourceHeight, mWidth, mHeight, mScale, mOrientation);
-
-    if (!mVDSActive) {
-        return true;
-    }
-
-    // it does not appear to be possible to reconfigure the virtual display
-    // on the fly without forcing surfaceflinger to tear it down
-    destroyVirtualDisplayLocked();
-    createVirtualDisplay();
-
-    return false;
-}
-
-bool VNCFlinger::updateFBSize(CpuConsumer::LockedBuffer& buf) {
-    uint32_t stride = (uint32_t)mVNCScreen->paddedWidthInBytes / 4;
-    uint32_t width = (uint32_t)mVNCScreen->width;
-    uint32_t height = (uint32_t)mVNCScreen->height;
-
-    uint64_t newSize = buf.stride * buf.height * 4;
-
-    if (stride != buf.stride || height != buf.height || width != buf.width) {
-        ALOGD("updateFBSize: old=[%dx%d %d] new=[%dx%d %d]", width, height, stride, buf.width,
-              buf.height, buf.stride);
-
-        if (mFrameSize != newSize) {
-            mFrameSize = newSize;
-            delete[] mVNCScreen->frameBuffer;
-            rfbNewFramebuffer(mVNCScreen, (char*)new uint8_t[newSize], buf.width, buf.height, 8, 3,
-                              4);
-        }
-        mVNCScreen->paddedWidthInBytes = buf.stride * 4;
-    }
-    return NO_ERROR;
-}
-
-// ------------------------------------------------------------------------ //
-
-// libvncserver logger
-void VNCFlinger::rfbLogger(const char* format, ...) {
-    va_list args;
-    char buf[256];
-
-    va_start(args, format);
-    vsprintf(buf, format, args);
-    ALOGI("%s", buf);
-    va_end(args);
-}
-
-// libvncserver callbacks
-ClientGoneHookPtr VNCFlinger::onClientGone(rfbClientPtr cl) {
-    ALOGV("onClientGone");
-    VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
-    vf->removeClient();
-    return 0;
-}
-
-enum rfbNewClientAction VNCFlinger::onNewClient(rfbClientPtr cl) {
-    ALOGV("onNewClient");
-    cl->clientGoneHook = (ClientGoneHookPtr)VNCFlinger::onClientGone;
-    VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
-    vf->addClient();
-    return RFB_CLIENT_ACCEPT;
-}
-
-void VNCFlinger::onFrameStart(rfbClientPtr cl) {
-    VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
-    vf->mUpdateMutex.lock();
-
-    vf->mFrameStartWhen = systemTime(CLOCK_MONOTONIC);
-    ALOGV("frame start [%lu]", vf->mFrameNumber);
-}
-
-void VNCFlinger::onFrameDone(rfbClientPtr cl, int /* status */) {
-    VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
-
-    float timing = (systemTime(CLOCK_MONOTONIC) - vf->mFrameStartWhen) / 1000000.0;
-    ALOGV("onFrameDone [%lu] (%.3f ms)", vf->mFrameNumber, timing);
-
-    vf->mUpdateMutex.unlock();
-}
-
-// cpuconsumer frame listener
-void VNCFlinger::FrameListener::onFrameAvailable(const BufferItem& item) {
-    Mutex::Autolock _l(mVNC->mEventMutex);
-    mVNC->mFrameAvailable = true;
-    mVNC->mEventCond.signal();
-    ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
-}
diff --git a/src/VNCFlinger.h b/src/VNCFlinger.h
deleted file mode 100644
index 3f9bf62..0000000
--- a/src/VNCFlinger.h
+++ /dev/null
@@ -1,130 +0,0 @@
-//
-// 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/>.
-//
-
-#ifndef VNCFLINGER_H
-#define VNCFLINGER_H
-
-#include <gui/CpuConsumer.h>
-#include <ui/DisplayInfo.h>
-#include <utils/String8.h>
-
-#include <rfb/rfb.h>
-#undef max
-
-#define VNC_AUTH_FILE "/data/system/vncauth"
-#define NUM_BUFS 1
-
-namespace android {
-
-class VNCFlinger : public RefBase {
-  public:
-    VNCFlinger();
-
-    virtual ~VNCFlinger() {
-    }
-
-    virtual status_t start();
-    virtual status_t stop();
-
-    virtual size_t addClient();
-    virtual size_t removeClient();
-
-    virtual status_t setPort(unsigned int port);
-    virtual status_t setV4Address(const String8& address);
-    virtual status_t setV6Address(const String8& address);
-    virtual status_t setScale(float scale);
-
-    virtual status_t clearPassword();
-    virtual status_t setPassword(const String8& passwd);
-
-  private:
-    class FrameListener : public CpuConsumer::FrameAvailableListener {
-      public:
-        FrameListener(VNCFlinger* vnc) : mVNC(vnc) {
-        }
-
-        virtual void onFrameAvailable(const BufferItem& item);
-
-      private:
-        FrameListener(FrameListener&) {
-        }
-        VNCFlinger* mVNC;
-    };
-
-    virtual void eventLoop();
-
-    virtual status_t createVirtualDisplay();
-    virtual status_t destroyVirtualDisplayLocked();
-
-    virtual status_t createVNCServer();
-    virtual status_t startVNCServer();
-
-    virtual void processFrame();
-
-    virtual bool isDeviceRotated(int orientation);
-    virtual bool updateDisplayProjection();
-    virtual bool updateFBSize(CpuConsumer::LockedBuffer& buf);
-
-    // vncserver callbacks
-    static ClientGoneHookPtr onClientGone(rfbClientPtr cl);
-    static enum rfbNewClientAction onNewClient(rfbClientPtr cl);
-    static void onFrameStart(rfbClientPtr cl);
-    static void onFrameDone(rfbClientPtr cl, int result);
-    static void rfbLogger(const char* format, ...);
-
-    bool mRunning;
-    bool mFrameAvailable;
-    bool mRotate;
-    bool mVDSActive;
-
-    Mutex mEventMutex;
-    Mutex mUpdateMutex;
-
-    Condition mEventCond;
-
-    uint32_t mWidth, mHeight;
-    uint32_t mSourceWidth, mSourceHeight;
-    int32_t mOrientation;
-    float mScale;
-
-    size_t mClientCount;
-
-    // Framebuffers
-    uint64_t mFrameNumber;
-    uint64_t mFrameSize;
-    nsecs_t mFrameStartWhen;
-
-    // Server instance
-    rfbScreenInfoPtr mVNCScreen;
-
-    // Primary display
-    sp<IBinder> mMainDpy;
-
-    // Virtual display
-    sp<IBinder> mDpy;
-
-    // Producer side of queue, passed into the virtual display.
-    sp<IGraphicBufferProducer> mProducer;
-
-    // This receives frames from the virtual display and makes them available
-    sp<CpuConsumer> mCpuConsumer;
-
-    // Consumer callback
-    sp<FrameListener> mListener;
-};
-};
-#endif
diff --git a/src/VirtualDisplay.h b/src/VirtualDisplay.h
new file mode 100644
index 0000000..f23d32d
--- /dev/null
+++ b/src/VirtualDisplay.h
@@ -0,0 +1,26 @@
+#include <gui/CpuConsumer.h>
+
+namespace vncflinger {
+
+class VirtualDisplay {
+  public:
+    VirtualDisplay();
+
+    virtual void onFrameAvailable(const BufferItem& item);
+
+  private:
+    Mutex mEventMutex;
+    Condition mEventCond;
+
+    bool mFrameAvailable;
+
+    // Virtual display
+    sp<IBinder> mDpy;
+
+    // Producer side of queue, passed into the virtual display.
+    sp<IGraphicBufferProducer> mProducer;
+
+    // This receives frames from the virtual display and makes them available
+    sp<CpuConsumer> mCpuConsumer;
+};
+};
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;
+    }
 }