Refactor the code

 * Split out classes for pixel buffer and virtual display
 * Move resize handling to appropriate classes
 * Use callbacks for orientation change and client resize
 * Remove unnecessary locking
diff --git a/src/AndroidDesktop.cpp b/src/AndroidDesktop.cpp
index a37500c..72c5896 100644
--- a/src/AndroidDesktop.cpp
+++ b/src/AndroidDesktop.cpp
@@ -4,10 +4,6 @@
 #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>
 
@@ -19,16 +15,16 @@
 #include <rfb/VNCServerST.h>
 
 #include "AndroidDesktop.h"
+#include "AndroidPixelBuffer.h"
 #include "InputDevice.h"
+#include "VirtualDisplay.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();
+    mDisplayRect = Rect(0, 0);
 
     mEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
     if (mEventFd < 0) {
@@ -43,140 +39,40 @@
 }
 
 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);
+    mPixels = new AndroidPixelBuffer();
+    mPixels->setDimensionsChangedListener(this);
 
-    mServer->setPixelBuffer(mPixels.get());
+    if (updateDisplayInfo() != NO_ERROR) {
+        ALOGE("Failed to query display!");
+        return;
+    }
 
     ALOGV("Desktop is running");
 }
 
 void AndroidDesktop::stop() {
-    Mutex::Autolock _L(mMutex);
+    Mutex::Autolock _L(mLock);
 
     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;
-    }
+    mVirtualDisplay.clear();
+    mPixels.clear();
 }
 
 void AndroidDesktop::processFrames() {
-    Mutex::Autolock _l(mMutex);
+    Mutex::Autolock _l(mLock);
 
-    // do any pending resize
-    processDesktopResize();
-
-    if (!mFrameAvailable) {
-        return;
-    }
+    updateDisplayInfo();
 
     // get a frame from the virtual display
     CpuConsumer::LockedBuffer imgBuffer;
-    status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
+    status_t res = mVirtualDisplay->getConsumer()->lockNextBuffer(&imgBuffer);
     if (res != OK) {
         ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
         return;
@@ -195,11 +91,10 @@
     // directly without copying because it is likely uncached
     mPixels->imageRect(bufRect, imgBuffer.data, imgBuffer.stride);
 
-    mCpuConsumer->unlockBuffer(imgBuffer);
+    mVirtualDisplay->getConsumer()->unlockBuffer(imgBuffer);
 
     // update clients
     mServer->add_changed(bufRect);
-    mFrameAvailable = false;
 }
 
 // notifies the server loop that we have changes
@@ -211,58 +106,35 @@
 // called when a client resizes the window
 unsigned int AndroidDesktop::setScreenLayout(int reqWidth, int reqHeight,
                                              const rfb::ScreenSet& layout) {
-    Mutex::Autolock _l(mMutex);
+    Mutex::Autolock _l(mLock);
 
     char* dbg = new char[1024];
     layout.print(dbg, 1024);
 
-    ALOGD("setScreenLayout: cur: %lux%lu  new: %dx%d %s", mWidth, mHeight, reqWidth, reqHeight, dbg);
+    ALOGD("setScreenLayout: cur: %s  new: %dx%d", dbg, reqWidth, reqHeight);
     delete[] dbg;
 
-    if (reqWidth == (int)mWidth && reqHeight == (int)mHeight) {
+    if (reqWidth == mDisplayRect.getWidth() && reqHeight == mDisplayRect.getHeight()) {
         return rfb::resultInvalid;
     }
 
     if (reqWidth > 0 && reqHeight > 0) {
-        mWidth = reqWidth;
-        mHeight = reqHeight;
+        mPixels->setWindowSize(reqWidth, reqHeight);
 
-        if (updateDisplayProjection() == NO_ERROR) {
-            // resize immediately
-            processDesktopResize();
-            notify();
-            return rfb::resultSuccess;
-        }
+        rfb::ScreenSet screens;
+        screens.add_screen(rfb::Screen(0, 0, 0, mPixels->width(), mPixels->height(), 0));
+        mServer->setScreenLayout(screens);
+        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();
+void AndroidDesktop::onFrameAvailable(const BufferItem& item) {
     ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
+
+    notify();
 }
 
 rfb::Point AndroidDesktop::getFbSize() {
@@ -274,67 +146,47 @@
 }
 
 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) {
+    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);
+    uint32_t x = pos.x * ((float)(mDisplayRect.getWidth()) / (float)mPixels->width());
+    uint32_t y = pos.y * ((float)(mDisplayRect.getHeight()) / (float)mPixels->height());
 
-    ALOGD("pointer xlate x1=%d y1=%d x2=%d y2=%d", pos.x, pos.y, x, y);
+    ALOGV("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);
+// refresh the display dimensions
+status_t AndroidDesktop::updateDisplayInfo() {
+    status_t err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &mDisplayInfo);
     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);
+    mPixels->setDisplayInfo(&mDisplayInfo);
 
     return NO_ERROR;
 }
+
+void AndroidDesktop::onBufferDimensionsChanged(uint32_t width, uint32_t height) {
+    ALOGV("Dimensions changed: old=(%ux%u) new=(%ux%u)", mDisplayRect.getWidth(),
+          mDisplayRect.getHeight(), width, height);
+
+    mVirtualDisplay.clear();
+    mVirtualDisplay = new VirtualDisplay(&mDisplayInfo, mPixels->width(), mPixels->height(), this);
+
+    mDisplayRect = mVirtualDisplay->getDisplayRect();
+
+    mInputDevice->reconfigure(mDisplayRect.getWidth(), mDisplayRect.getHeight());
+
+    mServer->setPixelBuffer(mPixels.get());
+
+    rfb::ScreenSet screens;
+    screens.add_screen(rfb::Screen(0, 0, 0, mPixels->width(), mPixels->height(), 0));
+    mServer->setScreenLayout(screens);
+}