vnc: Static scaling support

 * Add new commandline flag to apply a static scaling multiplier.
 * The scaling is applied in hardware for best performance.
 * This does not yet apply as a result of rfbSetScale calls
   (window resize) from clients.
 * Fix server shutdown too.
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
index cd7da0f..b9b5da4 100644
--- a/src/VNCFlinger.cpp
+++ b/src/VNCFlinger.cpp
@@ -26,6 +26,8 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 
+#include <ui/PixelFormat.h>
+
 #include "InputDevice.h"
 #include "VNCFlinger.h"
 
@@ -33,6 +35,7 @@
 
 VNCFlinger::VNCFlinger() {
     mOrientation = -1;
+    mScale = 1.0f;
     mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
     updateDisplayProjection();
 
@@ -53,7 +56,6 @@
 }
 
 status_t VNCFlinger::setV6Address(const String8& address) {
-    ALOGD("v6: %s", const_cast<char *>(address.string()));
     mVNCScreen->listen6Interface = const_cast<char *>(address.string());
     return NO_ERROR;
 }
@@ -72,6 +74,15 @@
     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");
@@ -112,12 +123,12 @@
 
 status_t VNCFlinger::stop() {
     Mutex::Autolock _L(mEventMutex);
-
     ALOGV("Shutting down");
 
-    rfbShutdownServer(mVNCScreen, true);
+    rfbShutdownServer(mVNCScreen, false);
 
     destroyVirtualDisplayLocked();
+
     mClientCount = 0;
     mRunning = false;
 
@@ -167,7 +178,8 @@
     mCpuConsumer = new CpuConsumer(consumer, NUM_BUFS);
     mCpuConsumer->setName(String8("vds-to-cpu"));
     mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
-    mProducer->setMaxDequeuedBufferCount(4);
+    mProducer->setMaxDequeuedBufferCount(1);
+    consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBX_8888);
 
     mListener = new FrameListener(this);
     mCpuConsumer->setFrameAvailableListener(mListener);
@@ -176,7 +188,9 @@
 
     SurfaceComposerClient::openGlobalTransaction();
     SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
-    // setDisplayProjection(mDpy, mainDpyInfo);
+    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();
 
@@ -232,8 +246,9 @@
     mVNCScreen->displayFinishedHook = (rfbDisplayFinishedHookPtr)VNCFlinger::onFrameDone;
     mVNCScreen->serverFormat.trueColour = true;
     mVNCScreen->serverFormat.bitsPerPixel = 32;
+    mVNCScreen->serverFormat.depth = 24;
     mVNCScreen->handleEventsEagerly = true;
-    mVNCScreen->deferUpdateTime = 1;
+    mVNCScreen->deferUpdateTime = 0;
     mVNCScreen->screenData = this;
 
     return err;
@@ -342,17 +357,25 @@
         sourceWidth = info.h;
     }
 
-    if (mWidth == sourceWidth && mHeight == sourceHeight && mOrientation == info.orientation) {
+    uint32_t width = sourceWidth * mScale;
+    uint32_t height = sourceHeight * mScale;
+
+    if (mSourceWidth == sourceWidth && mSourceHeight == sourceHeight &&
+            mWidth == width && mHeight == height &&
+            mOrientation == info.orientation) {
         return false;
     }
 
-    ALOGD("Display dimensions: %dx%d orientation=%d", sourceWidth, sourceHeight, info.orientation);
-
-    // orientation change
-    mWidth = sourceWidth;
-    mHeight = sourceHeight;
+    // 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;
     }
@@ -361,6 +384,7 @@
     // on the fly without forcing surfaceflinger to tear it down
     destroyVirtualDisplayLocked();
     createVirtualDisplay();
+
     return false;
 }
 
diff --git a/src/VNCFlinger.h b/src/VNCFlinger.h
index 856bbb7..3f9bf62 100644
--- a/src/VNCFlinger.h
+++ b/src/VNCFlinger.h
@@ -46,6 +46,7 @@
     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);
@@ -96,7 +97,9 @@
     Condition mEventCond;
 
     uint32_t mWidth, mHeight;
+    uint32_t mSourceWidth, mSourceHeight;
     int32_t mOrientation;
+    float mScale;
 
     size_t mClientCount;
 
diff --git a/src/main.cpp b/src/main.cpp
index e1e8ac1..322fcc0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -28,17 +28,19 @@
 
 static sp<VNCFlinger> gVNC;
 
-static const char* const shortOpts = "4:6:p:s:vh";
+static const char* const shortOpts = "4:6:p:s:l:vh";
 static const option longOpts[] = {
     {"listen", 1, nullptr, '4'},
     {"listen6", 1, nullptr, '6'},
     {"port", 1, nullptr, 'p'},
     {"password", 1, nullptr, 's'},
+    {"scale", 1, nullptr, 'l'},
     {"version", 0, nullptr, 'v'},
     {"help", 0, nullptr, 'h'},
 };
 
-static void onSignal(int /* signal */) {
+static void onSignal(int signal) {
+    ALOGV("Shutting down on signal %d", signal);
     gVNC->stop();
 }
 
@@ -54,6 +56,7 @@
               << "  -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";
     exit(1);
@@ -108,6 +111,13 @@
                 }
                 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;
@@ -133,4 +143,5 @@
     defaultServiceManager()->addService(String16("vnc"), new VNCService(gVNC));
 
     gVNC->start();
+    gVNC.clear();
 }