vnc: Simplify everything

Previous design was using extra threads needlessly and had some
hacky data sharing going on. Slim this down to just two classes
and utilize threads appropriately.
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
index efd6cbc..4775d0b 100644
--- a/src/VNCFlinger.cpp
+++ b/src/VNCFlinger.cpp
@@ -12,14 +12,26 @@
 #include "InputDevice.h"
 #include "VNCFlinger.h"
 
+
 using namespace android;
 
-Mutex VNCFlinger::sUpdateMutex;
-
 status_t VNCFlinger::start() {
-    Mutex::Autolock _l(mMutex);
+    sp<ProcessState> self = ProcessState::self();
+    self->startThreadPool();
 
-    status_t err = setup_l();
+    status_t err = NO_ERROR;
+
+    mMainDpy = SurfaceComposerClient::getBuiltInDisplay(
+            ISurfaceComposer::eDisplayIdMain);
+    err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &mMainDpyInfo);
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get display characteristics\n");
+        return err;
+    }
+    mHeight = mMainDpyInfo.h;
+    mWidth = mMainDpyInfo.w;
+
+    err = createVNCServer();
     if (err != NO_ERROR) {
         ALOGE("Failed to start VNCFlinger: err=%d", err);
         return err;
@@ -29,34 +41,78 @@
 
     rfbRunEventLoop(mVNCScreen, -1, true);
 
-    mCondition.wait(mMutex);
+    eventLoop();
 
-    release_l();
     return NO_ERROR;
 }
 
-status_t VNCFlinger::setup_l() {
+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) {
+                processFrame();
+                mFrameAvailable = false;
+            }
+        }
+
+        destroyVirtualDisplay();
+    }
+}
+
+status_t VNCFlinger::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);
+
+    mListener = new FrameListener(this);
+    mCpuConsumer->setFrameAvailableListener(mListener);
+
+    mDpy = SurfaceComposerClient::createDisplay(
+            String8("VNC-VirtualDisplay"), false /*secure*/);
+
+    SurfaceComposerClient::openGlobalTransaction();
+    SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
+    //setDisplayProjection(mDpy, mainDpyInfo);
+    SurfaceComposerClient::setDisplayLayerStack(mDpy, 0);    // default stack
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    ALOGV("Virtual display created");
+    return NO_ERROR;
+}
+
+status_t VNCFlinger::destroyVirtualDisplay() {
+    mCpuConsumer.clear();
+    mProducer.clear();
+    SurfaceComposerClient::destroyDisplay(mDpy);
+    return NO_ERROR;
+}
+
+status_t VNCFlinger::createVNCServer() {
 
     status_t err = NO_ERROR;
 
-    mMainDpy = SurfaceComposerClient::getBuiltInDisplay(
-            ISurfaceComposer::eDisplayIdMain);
-    err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &mMainDpyInfo);
-    if (err != NO_ERROR) {
-        ALOGE("Unable to get display characteristics\n");
-        return err;
-    }
-
-    bool rotated = VirtualDisplay::isDeviceRotated(mMainDpyInfo.orientation);
-    if (mWidth == 0) {
-        mWidth = rotated ? mMainDpyInfo.h : mMainDpyInfo.w;
-    }
-    if (mHeight == 0) {
-        mHeight = rotated ? mMainDpyInfo.w : mMainDpyInfo.h;
-    }
-
-    ALOGD("Display dimensions: %dx%d rotated=%d", mWidth, mHeight, rotated);
-
     rfbLog = VNCFlinger::rfbLogger;
     rfbErr = VNCFlinger::rfbLogger;
 
@@ -88,32 +144,27 @@
     /* Mark as dirty since we haven't sent any updates at all yet. */
     rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
 
-
-    mVirtualDisplay = new VirtualDisplay(mVNCScreen, &sUpdateMutex);
-
     return err;
 }
 
-void VNCFlinger::release_l() {
-    mVirtualDisplay.clear();
-
-    ALOGD("VNCFlinger released");
-}
-
 status_t VNCFlinger::stop() {
-    Mutex::Autolock _l(mMutex);
-    mCondition.signal();
+    Mutex::Autolock _L(mEventMutex);
+
+    mClientCount = 0;
+    mRunning = false;
+
+    mEventCond.signal();
 
     return NO_ERROR;
 }
 
 size_t VNCFlinger::addClient() {
-    Mutex::Autolock _l(mMutex);
+    Mutex::Autolock _l(mEventMutex);
     if (mClientCount == 0) {
+        mClientCount++;
         InputDevice::start(mWidth, mHeight);
-        mVirtualDisplay->start(mMainDpyInfo);
+        mEventCond.signal();
     }
-    mClientCount++;
 
     ALOGI("Client connected (%zu)", mClientCount);
 
@@ -121,12 +172,12 @@
 }
 
 size_t VNCFlinger::removeClient() {
-    Mutex::Autolock _l(mMutex);
+    Mutex::Autolock _l(mEventMutex);
     if (mClientCount > 0) {
         mClientCount--;
         if (mClientCount == 0) {
-            mVirtualDisplay->stop();
             InputDevice::stop();
+            mEventCond.signal();
         }
     }
 
@@ -150,23 +201,18 @@
     return RFB_CLIENT_ACCEPT;
 }
 
-void VNCFlinger::onFrameStart(rfbClientPtr /* cl */) {
-    sUpdateMutex.lock();
+void VNCFlinger::onFrameStart(rfbClientPtr cl) {
+    VNCFlinger *vf = (VNCFlinger *)cl->screen->screenData;
+    vf->mUpdateMutex.lock();
     ALOGV("frame start");
 }
 
-void VNCFlinger::onFrameDone(rfbClientPtr /* cl */, int status) {
-    sUpdateMutex.unlock();
+void VNCFlinger::onFrameDone(rfbClientPtr cl, int status) {
+    VNCFlinger *vf = (VNCFlinger *)cl->screen->screenData;
+    vf->mUpdateMutex.unlock();
     ALOGV("frame done! %d", status);
 }
 
-void VNCFlinger::markFrame(void* frame, size_t stride) {
-    Mutex::Autolock _l(sUpdateMutex);
-    mVNCScreen->frameBuffer = (char *)frame;
-    mVNCScreen->paddedWidthInBytes = stride * 4;
-    rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
-}
-
 void VNCFlinger::rfbLogger(const char *format, ...) {
     va_list args;
     char buf[256];
@@ -176,3 +222,51 @@
     ALOGI("%s", buf);
     va_end(args);
 }
+
+void VNCFlinger::FrameListener::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock _l(mVNC->mEventMutex);
+    mVNC->mFrameAvailable = true;
+    mVNC->mEventCond.signal();
+    ALOGV("onFrameAvailable: mTimestamp=%ld mFrameNumber=%ld",
+            item.mTimestamp, item.mFrameNumber);
+}
+
+void VNCFlinger::processFrame() {
+    ALOGV("processFrame\n");
+
+    // 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);
+
+    CpuConsumer::LockedBuffer imgBuffer;
+    status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
+    if (res != OK) {
+        ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
+        return;
+    }
+
+    ALOGV("processFrame: ptr: %p format: %x (%dx%d, stride=%d)",
+            imgBuffer.data, imgBuffer.format, imgBuffer.width,
+            imgBuffer.height, imgBuffer.stride);
+
+    void* vncbuf = mVNCScreen->frameBuffer;
+    void* imgbuf = imgBuffer.data;
+
+    // Copy the frame to the server's buffer
+    if (imgBuffer.stride > mWidth) {
+        // Image has larger stride, so we need to copy row by row
+        for (size_t y = 0; y < mHeight; y++) {
+            memcpy(vncbuf, imgbuf, mWidth * 4);
+            vncbuf = (void *)((char *)vncbuf + mWidth * 4);
+            imgbuf = (void *)((char *)imgbuf + imgBuffer.stride * 4);
+        }
+    } else {
+        memcpy(vncbuf, imgbuf, mWidth * mHeight * 4);
+    }
+
+    rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
+
+    mCpuConsumer->unlockBuffer(imgBuffer);
+}