Initial import of VNCFlinger
diff --git a/src/VirtualDisplay.cpp b/src/VirtualDisplay.cpp
new file mode 100644
index 0000000..369d271
--- /dev/null
+++ b/src/VirtualDisplay.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VNC-VirtualDisplay"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <ui/Rect.h>
+
+#include "VirtualDisplay.h"
+
+using namespace android;
+
+static const int kGlBytesPerPixel = 4;      // GL_RGBA
+
+
+/*
+ * Returns "true" if the device is rotated 90 degrees.
+ */
+bool VirtualDisplay::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.
+ */
+status_t VirtualDisplay::setDisplayProjection(const sp<IBinder>& dpy,
+        const DisplayInfo& mMainDpyInfo) {
+    status_t err;
+
+    // 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(mMainDpyInfo.orientation);
+    uint32_t sourceWidth, sourceHeight;
+    if (!deviceRotated) {
+        sourceWidth = mMainDpyInfo.w;
+        sourceHeight = mMainDpyInfo.h;
+    } else {
+        ALOGV("using rotated width/height");
+        sourceHeight = mMainDpyInfo.w;
+        sourceWidth = mMainDpyInfo.h;
+    }
+    Rect layerStackRect(sourceWidth, sourceHeight);
+
+    // We need to preserve the aspect ratio of the display.
+    float displayAspect = (float) sourceHeight / (float) sourceWidth;
+
+
+    // Set the way we map the output onto the display surface (which will
+    // be e.g. 1280x720 for a 720p video).  The rect is interpreted
+    // post-rotation, so if the display is rotated 90 degrees we need to
+    // "pre-rotate" it by flipping width/height, so that the orientation
+    // adjustment changes it back.
+    //
+    // We might want to encode a portrait display as landscape to use more
+    // of the screen real estate.  (If players respect a 90-degree rotation
+    // hint, we can essentially get a 720x1280 video instead of 1280x720.)
+    // In that case, we swap the configured video width/height and then
+    // supply a rotation value to the display projection.
+    uint32_t videoWidth, videoHeight;
+    uint32_t outWidth, outHeight;
+   if (!mRotate) {
+        videoWidth = mWidth;
+        videoHeight = mHeight;
+    } else {
+        videoWidth = mHeight;
+        videoHeight = mWidth;
+    }
+    if (videoHeight > (uint32_t)(videoWidth * displayAspect)) {
+        // limited by narrow width; reduce height
+        outWidth = videoWidth;
+        outHeight = (uint32_t)(videoWidth * displayAspect);
+    } else {
+        // limited by short height; restrict width
+        outHeight = videoHeight;
+        outWidth = (uint32_t)(videoHeight / displayAspect);
+    }
+    uint32_t offX, offY;
+    offX = (videoWidth - outWidth) / 2;
+    offY = (videoHeight - outHeight) / 2;
+    Rect displayRect(offX, offY, offX + outWidth, offY + outHeight);
+
+    if (mRotate) {
+        printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
+                outHeight, outWidth, offY, offX);
+    } else {
+        printf("Content area is %ux%u at offset x=%d y=%d\n",
+                outWidth, outHeight, offX, offY);
+    }
+
+    SurfaceComposerClient::setDisplayProjection(dpy,
+            mRotate ? DISPLAY_ORIENTATION_90 : DISPLAY_ORIENTATION_0,
+            layerStackRect, displayRect);
+    return NO_ERROR;
+}
+
+status_t VirtualDisplay::start(const DisplayInfo& mainDpyInfo, EventQueue *queue) {
+
+    Mutex::Autolock _l(mMutex);
+
+    mQueue = queue;
+
+    mRotate = isDeviceRotated(mainDpyInfo.orientation);
+    mWidth = mRotate ? mainDpyInfo.h : mainDpyInfo.w;
+    mHeight = mRotate ? mainDpyInfo.w : mainDpyInfo.h;
+
+    sp<ProcessState> self = ProcessState::self();
+    self->startThreadPool();
+
+    run("vnc-virtualdisplay");
+    
+    mState = INIT;
+    while (mState == INIT) {
+        mStartCond.wait(mMutex);
+    }
+  
+    if (mThreadResult != NO_ERROR) {
+        ALOGE("Failed to start VDS thread: err=%d", mThreadResult);
+        return mThreadResult;
+    }
+    assert(mState == RUNNING);
+  
+    mDpy = SurfaceComposerClient::createDisplay(
+            String8("VNCFlinger"), false /*secure*/);
+
+    SurfaceComposerClient::openGlobalTransaction();
+    SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
+    setDisplayProjection(mDpy, mainDpyInfo);
+    SurfaceComposerClient::setDisplayLayerStack(mDpy, 0);    // default stack
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    ALOGV("VirtualDisplay::start successful");
+    return NO_ERROR;
+}
+
+status_t VirtualDisplay::stop() {
+    Mutex::Autolock _l(mMutex);
+    mState = STOPPING;
+    mEventCond.signal();
+    return NO_ERROR;
+}
+  
+bool VirtualDisplay::threadLoop() {
+    Mutex::Autolock _l(mMutex);
+
+    mThreadResult = setup_l();
+
+    if (mThreadResult != NO_ERROR) {
+        ALOGW("Aborting VDS thread");
+        mState = STOPPED;
+        release_l();
+        mStartCond.broadcast();
+        return false;
+    }
+
+    ALOGV("VDS thread running");
+    mState = RUNNING;
+    mStartCond.broadcast();
+
+    while (mState == RUNNING) {
+        mEventCond.wait(mMutex);
+        ALOGD("Awake, frame available");
+        void* ptr = processFrame_l();
+        const Event ev(EVENT_BUFFER_READY, ptr);
+        mQueue->enqueue(ev);
+    }
+
+    ALOGV("VDS thread stopping");
+    release_l();
+    mState = STOPPED;
+    return false;       // stop
+}
+
+status_t VirtualDisplay::setup_l() {
+    status_t err;
+
+    err = mEglWindow.createPbuffer(mWidth, mHeight);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    mEglWindow.makeCurrent();
+
+    glViewport(0, 0, mWidth, mHeight);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+
+    // Shader for rendering the external texture.
+    err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    // Input side (buffers from virtual display).
+    glGenTextures(1, &mExtTextureName);
+    if (mExtTextureName == 0) {
+        ALOGE("glGenTextures failed: %#x", glGetError());
+        return UNKNOWN_ERROR;
+    }
+
+    mBufSize = mWidth * mHeight * kGlBytesPerPixel;
+    
+    // pixel buffer for image copy
+    mPBO = new GLuint[NUM_PBO];
+    glGenBuffers(NUM_PBO, mPBO);
+
+    for (unsigned int i = 0; i < NUM_PBO; i++) {
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[i]);
+        glBufferData(GL_PIXEL_PACK_BUFFER, mBufSize, 0, GL_DYNAMIC_READ);
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+    }
+
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&mProducer, &consumer);
+    mGlConsumer = new GLConsumer(consumer, mExtTextureName,
+                GL_TEXTURE_EXTERNAL_OES, true, false);
+    mGlConsumer->setName(String8("virtual display"));
+    mGlConsumer->setDefaultBufferSize(mWidth, mHeight);
+    mProducer->setMaxDequeuedBufferCount(4);
+    mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
+
+    mGlConsumer->setFrameAvailableListener(this);
+
+    ALOGD("VirtualDisplay::setup_l OK");
+    return NO_ERROR;
+}
+
+void* VirtualDisplay::processFrame_l() {
+    ALOGD("processFrame_l\n");
+
+    float texMatrix[16];
+    mGlConsumer->updateTexImage();
+    mGlConsumer->getTransformMatrix(texMatrix);
+
+    // The data is in an external texture, so we need to render it to the
+    // pbuffer to get access to RGB pixel data.  We also want to flip it
+    // upside-down for easy conversion to a bitmap.
+    int width = mEglWindow.getWidth();
+    int height = mEglWindow.getHeight();
+    mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0, mWidth, mHeight, true);
+
+    GLenum glErr;
+    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
+    glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+    if ((glErr = glGetError()) != GL_NO_ERROR) {
+        ALOGE("glReadPixels failed: %#x", glErr);
+        return NULL;
+    }
+
+    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO[mIndex]);
+    void* ptr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, mBufSize, GL_MAP_READ_BIT);
+    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
+    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+
+    mIndex = (mIndex + 1) % NUM_PBO; 
+    return ptr;
+}
+
+void VirtualDisplay::release_l() {
+    ALOGD("release_l");
+    mGlConsumer.clear();
+    mProducer.clear();
+    mExtTexProgram.release();
+    mEglWindow.release();
+    SurfaceComposerClient::destroyDisplay(mDpy);
+}
+
+// Callback; executes on arbitrary thread.
+void VirtualDisplay::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock _l(mMutex);
+    mEventCond.signal();
+    ALOGD("mTimestamp=%ld mFrameNumber=%ld", item.mTimestamp, item.mFrameNumber);
+}