vnc: Orientation change support

 * Detect orientation changes and reconfigure appropriately.
 * Clean up the uinput code.
 * FIXME: The virtual mouse isn't reconfiguring correctly yet.
diff --git a/src/InputDevice.cpp b/src/InputDevice.cpp
index 5a3aa9f..1f07f5a 100644
--- a/src/InputDevice.cpp
+++ b/src/InputDevice.cpp
@@ -32,98 +32,105 @@
 
 using namespace android;
 
+ANDROID_SINGLETON_STATIC_INSTANCE(InputDevice)
+
 static const struct UInputOptions {
     int cmd;
     int bit;
 } kOptions[] = {
     {UI_SET_EVBIT, EV_KEY},
-    {UI_SET_EVBIT, EV_REP},
+    {UI_SET_EVBIT, EV_REL},
     {UI_SET_EVBIT, EV_ABS},
     {UI_SET_EVBIT, EV_SYN},
     {UI_SET_ABSBIT, ABS_X},
     {UI_SET_ABSBIT, ABS_Y},
+    {UI_SET_RELBIT, REL_WHEEL},
     {UI_SET_PROPBIT, INPUT_PROP_DIRECT},
 };
 
-int InputDevice::sFD = -1;
-
 status_t InputDevice::start(uint32_t width, uint32_t height) {
+    Mutex::Autolock _l(mLock);
+
     status_t err = OK;
-    struct uinput_user_dev user_dev;
 
     struct input_id id = {
         BUS_VIRTUAL, /* Bus type */
         1,           /* Vendor */
         1,           /* Product */
-        1,           /* Version */
+        4,           /* Version */
     };
 
-    if (sFD >= 0) {
+    if (mFD >= 0) {
         ALOGE("Input device already open!");
         return NO_INIT;
     }
 
-    sFD = open(UINPUT_DEVICE, O_WRONLY | O_NONBLOCK);
-    if (sFD < 0) {
-        ALOGE("Failed to open %s: err=%d", UINPUT_DEVICE, sFD);
+    mFD = open(UINPUT_DEVICE, O_WRONLY | O_NONBLOCK);
+    if (mFD < 0) {
+        ALOGE("Failed to open %s: err=%d", UINPUT_DEVICE, mFD);
         return NO_INIT;
     }
 
     unsigned int idx = 0;
     for (idx = 0; idx < sizeof(kOptions) / sizeof(kOptions[0]); idx++) {
-        if (ioctl(sFD, kOptions[idx].cmd, kOptions[idx].bit) < 0) {
+        if (ioctl(mFD, kOptions[idx].cmd, kOptions[idx].bit) < 0) {
             ALOGE("uinput ioctl failed: %d %d", kOptions[idx].cmd, kOptions[idx].bit);
             goto err_ioctl;
         }
     }
 
     for (idx = 0; idx < KEY_MAX; idx++) {
-        if (ioctl(sFD, UI_SET_KEYBIT, idx) < 0) {
+        if (ioctl(mFD, UI_SET_KEYBIT, idx) < 0) {
             ALOGE("UI_SET_KEYBIT failed");
             goto err_ioctl;
         }
     }
 
-    memset(&user_dev, 0, sizeof(user_dev));
-    strncpy(user_dev.name, "VNC", UINPUT_MAX_NAME_SIZE);
+    memset(&mUserDev, 0, sizeof(mUserDev));
+    strncpy(mUserDev.name, "VNC-RemoteInput", UINPUT_MAX_NAME_SIZE);
 
-    user_dev.id = id;
+    mUserDev.id = id;
 
-    user_dev.absmin[ABS_X] = 0;
-    user_dev.absmax[ABS_X] = width;
-    user_dev.absmin[ABS_Y] = 0;
-    user_dev.absmax[ABS_Y] = height;
+    mUserDev.absmin[ABS_X] = 0;
+    mUserDev.absmax[ABS_X] = width;
+    mUserDev.absmin[ABS_Y] = 0;
+    mUserDev.absmax[ABS_Y] = height;
 
-    if (write(sFD, &user_dev, sizeof(user_dev)) != sizeof(user_dev)) {
+    if (write(mFD, &mUserDev, sizeof(mUserDev)) != sizeof(mUserDev)) {
         ALOGE("Failed to configure uinput device");
         goto err_ioctl;
     }
 
-    if (ioctl(sFD, UI_DEV_CREATE) == -1) {
+    if (ioctl(mFD, UI_DEV_CREATE) == -1) {
         ALOGE("UI_DEV_CREATE failed");
         goto err_ioctl;
     }
 
-    return OK;
+    ALOGD("Virtual input device created successfully (%dx%d)", width, height);
+    return NO_ERROR;
 
 err_ioctl:
     int prev_errno = errno;
-    ::close(sFD);
+    ::close(mFD);
     errno = prev_errno;
-    sFD = -1;
+    mFD = -1;
     return NO_INIT;
 }
 
+status_t InputDevice::reconfigure(uint32_t width, uint32_t height) {
+    stop();
+    return start(width, height);
+}
+
 status_t InputDevice::stop() {
-    if (sFD < 0) {
+    Mutex::Autolock _l(mLock);
+    if (mFD < 0) {
         return OK;
     }
 
-    sleep(2);
-
-    ioctl(sFD, UI_DEV_DESTROY);
-    close(sFD);
-    sFD = -1;
+    ioctl(mFD, UI_DEV_DESTROY);
+    close(mFD);
+    mFD = -1;
 
     return OK;
 }
@@ -135,7 +142,7 @@
     event.type = type;
     event.code = code;
     event.value = value;
-    if (write(sFD, &event, sizeof(event)) != sizeof(event)) return BAD_VALUE;
+    if (write(mFD, &event, sizeof(event)) != sizeof(event)) return BAD_VALUE;
     return OK;
 }
 
@@ -175,12 +182,18 @@
     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) {
     int code;
     int sh = 0;
     int alt = 0;
 
-    if (sFD < 0) return;
+    if (mFD < 0) return;
+
+    Mutex::Autolock _l(mLock);
 
     if ((code = keysym2scancode(key, cl, &sh, &alt))) {
         int ret = 0;
@@ -213,11 +226,19 @@
     }
 }
 
+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) {
     static int leftClicked = 0, rightClicked = 0, middleClicked = 0;
     (void)cl;
 
-    if (sFD < 0) return;
+    if (mFD < 0) return;
+
+    ALOGV("pointerEvent: buttonMask=%x x=%d y=%d", buttonMask, x, y);
+
+    Mutex::Autolock _l(mLock);
 
     if ((buttonMask & 1) && leftClicked) {  // left btn clicked and moving
         static int i = 0;
@@ -229,8 +250,7 @@
             inject(EV_ABS, ABS_Y, y);
             inject(EV_SYN, SYN_REPORT, 0);
         }
-    } else if (buttonMask & 1)  // left btn clicked
-    {
+    } else if (buttonMask & 1) { // left btn clicked
         leftClicked = 1;
 
         inject(EV_ABS, ABS_X, x);
@@ -269,6 +289,14 @@
         release(KEY_END);
         inject(EV_SYN, SYN_REPORT, 0);
     }
+
+    if (buttonMask & 8) {
+        inject(EV_REL, REL_WHEEL, 1);
+    }
+
+    if (buttonMask & 0x10) {
+        inject(EV_REL, REL_WHEEL, -1);
+    }
 }
 
 // q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m
diff --git a/src/InputDevice.h b/src/InputDevice.h
index 19595a2..20f5063 100644
--- a/src/InputDevice.h
+++ b/src/InputDevice.h
@@ -19,37 +19,51 @@
 #define INPUT_DEVICE_H
 
 #include <utils/Errors.h>
-#include <utils/RefBase.h>
+#include <utils/Mutex.h>
+#include <utils/Singleton.h>
 
 #include <rfb/rfb.h>
-
+#include <linux/uinput.h>
 
 #define UINPUT_DEVICE "/dev/uinput"
 
 namespace android {
 
-class InputDevice : public RefBase {
-public:
-    static status_t start(uint32_t width, uint32_t height);
-    static status_t stop();
+class InputDevice : public Singleton<InputDevice> {
 
-    static void keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
-    static void pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl);
+friend class Singleton;
+
+public:
+    virtual status_t start(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);
+
+    InputDevice() : mFD(-1) {}
+    virtual ~InputDevice() {}
 
 private:
 
-    static status_t inject(uint16_t type, uint16_t code, int32_t value);
-    static status_t injectSyn(uint16_t type, uint16_t code, int32_t value);
-    static status_t movePointer(int32_t x, int32_t y);
-    static status_t setPointer(int32_t x, int32_t y);
-    static status_t press(uint16_t code);
-    static status_t release(uint16_t code);
-    static status_t click(uint16_t code);
+    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);
+    status_t setPointer(int32_t x, int32_t y);
+    status_t press(uint16_t code);
+    status_t release(uint16_t code);
+    status_t click(uint16_t code);
 
-    static int keysym2scancode(rfbKeySym c, rfbClientPtr cl, int* sh, int* alt);
+    void keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
+    void pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl);
 
-    static int sFD;
+    int keysym2scancode(rfbKeySym c, rfbClientPtr cl, int* sh, int* alt);
 
+    Mutex mLock;
+
+    int mFD;
+
+    struct uinput_user_dev mUserDev;
 };
 
 };
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
index 4517e35..fa28bbd 100644
--- a/src/VNCFlinger.cpp
+++ b/src/VNCFlinger.cpp
@@ -36,28 +36,21 @@
     sp<ProcessState> self = ProcessState::self();
     self->startThreadPool();
 
-    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();
+    updateDisplayProjection();
+
+    status_t err = createVNCServer();
     if (err != NO_ERROR) {
         ALOGE("Failed to start VNCFlinger: err=%d", err);
         return err;
     }
 
-    ALOGD("VNCFlinger is running!");
-
     rfbRunEventLoop(mVNCScreen, -1, true);
 
+    ALOGD("VNCFlinger is running!");
+
     eventLoop();
 
     return NO_ERROR;
@@ -86,12 +79,14 @@
         while (mClientCount > 0) {
             mEventCond.wait(mEventMutex);
             if (mFrameAvailable) {
-                processFrame();
+                if (!updateDisplayProjection()) {
+                    processFrame();
+                }
                 mFrameAvailable = false;
             }
         }
-
-        destroyVirtualDisplay();
+        Mutex::Autolock _l(mUpdateMutex);
+        destroyVirtualDisplayLocked();
     }
 }
 
@@ -115,14 +110,23 @@
     SurfaceComposerClient::setDisplayLayerStack(mDpy, 0);    // default stack
     SurfaceComposerClient::closeGlobalTransaction();
 
-    ALOGV("Virtual display created");
+    mVDSActive = true;
+
+    ALOGV("Virtual display (%dx%d) created", mWidth, mHeight);
+
     return NO_ERROR;
 }
 
-status_t VNCFlinger::destroyVirtualDisplay() {
+status_t VNCFlinger::destroyVirtualDisplayLocked() {
+
     mCpuConsumer.clear();
     mProducer.clear();
     SurfaceComposerClient::destroyDisplay(mDpy);
+
+    mVDSActive = false;
+
+    ALOGV("Virtual display destroyed");
+
     return NO_ERROR;
 }
 
@@ -147,14 +151,14 @@
     mVNCScreen->httpDir = NULL;
     mVNCScreen->port = VNC_PORT;
     mVNCScreen->newClientHook = (rfbNewClientHookPtr) VNCFlinger::onNewClient;
-    mVNCScreen->kbdAddEvent = InputDevice::keyEvent;
-    mVNCScreen->ptrAddEvent = InputDevice::pointerEvent;
+    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->handleEventsEagerly = true;
-    mVNCScreen->deferUpdateTime = 0;
+    mVNCScreen->deferUpdateTime = 1;
     mVNCScreen->screenData = this;
     rfbInitServer(mVNCScreen);
 
@@ -179,7 +183,8 @@
     Mutex::Autolock _l(mEventMutex);
     if (mClientCount == 0) {
         mClientCount++;
-        InputDevice::start(mWidth, mHeight);
+        updateFBSize(mWidth, mHeight, mWidth);
+        InputDevice::getInstance().start(mWidth, mHeight);
         mEventCond.signal();
     }
 
@@ -193,7 +198,7 @@
     if (mClientCount > 0) {
         mClientCount--;
         if (mClientCount == 0) {
-            InputDevice::stop();
+            InputDevice::getInstance().stop();
             mEventCond.signal();
         }
     }
@@ -226,6 +231,11 @@
 
 void VNCFlinger::onFrameDone(rfbClientPtr cl, int status) {
     VNCFlinger *vf = (VNCFlinger *)cl->screen->screenData;
+
+    if (vf->mInputReconfigPending) {
+        //InputDevice::getInstance().reconfigure(vf->mWidth, vf->mHeight);
+        vf->mInputReconfigPending = false;
+    }
     vf->mUpdateMutex.unlock();
     ALOGV("frame done! %d", status);
 }
@@ -268,22 +278,89 @@
             imgBuffer.data, imgBuffer.format, imgBuffer.width,
             imgBuffer.height, imgBuffer.stride);
 
-    void* vncbuf = mVNCScreen->frameBuffer;
-    void* imgbuf = imgBuffer.data;
+    updateFBSize(imgBuffer.width, imgBuffer.height, imgBuffer.stride);
 
-    // 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);
-    }
+    memcpy(mVNCBuf, imgBuffer.data, imgBuffer.stride * imgBuffer.height * 4);
 
-    rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
+    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;
+    }
+
+    if (info.orientation == mOrientation) {
+        return false;
+    }
+
+    // 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);
+    int sourceWidth, sourceHeight;
+    if (!deviceRotated) {
+        sourceWidth = info.w;
+        sourceHeight = info.h;
+    } else {
+        ALOGV("using rotated width/height");
+        sourceHeight = info.w;
+        sourceWidth = info.h;
+    }
+
+    Mutex::Autolock _l(mUpdateMutex);
+    mWidth = sourceWidth;
+    mHeight = sourceHeight;
+    mOrientation = info.orientation;
+
+    if (!mVDSActive) {
+        return true;
+    }
+
+    destroyVirtualDisplayLocked();
+    createVirtualDisplay();
+    return true;
+}
+
+status_t VNCFlinger::updateFBSize(int width, int height, int stride) {
+    if ((mVNCScreen->paddedWidthInBytes / 4) != stride ||
+            mVNCScreen->height != height ||
+            mVNCScreen->width != width) {
+
+        ALOGD("updateFBSize: old=[%dx%d %d] new=[%dx%d %d]",
+                mVNCScreen->width, mVNCScreen->height, mVNCScreen->paddedWidthInBytes / 4,
+                width, height, stride);
+
+        delete[] mVNCBuf;
+        mVNCBuf = new uint8_t[stride * height * 4];
+        memset(mVNCBuf, 0, stride * height * 4);
+
+        // little dance here to avoid an ugly immediate resize
+        if (mVNCScreen->height != height || mVNCScreen->width != width) {
+            mInputReconfigPending = true;
+            rfbNewFramebuffer(mVNCScreen, (char *)mVNCBuf, width, height, 8, 3, 4);
+        } else {
+            mVNCScreen->frameBuffer = (char *)mVNCBuf;
+        }
+        mVNCScreen->paddedWidthInBytes = stride * 4;
+    }
+    return NO_ERROR;
+}
diff --git a/src/VNCFlinger.h b/src/VNCFlinger.h
index a446c81..0682a2f 100644
--- a/src/VNCFlinger.h
+++ b/src/VNCFlinger.h
@@ -32,7 +32,8 @@
     VNCFlinger(int argc, char **argv) :
             mArgc(argc),
             mArgv(argv),
-            mClientCount(0) {
+            mClientCount(0),
+            mOrientation(-1) {
     }
 
     virtual ~VNCFlinger() {}
@@ -60,11 +61,15 @@
     virtual void eventLoop();
 
     virtual status_t createVirtualDisplay();
-    virtual status_t destroyVirtualDisplay();
+    virtual status_t destroyVirtualDisplayLocked();
     virtual status_t createVNCServer();
 
     virtual void processFrame();
 
+    virtual bool isDeviceRotated(int orientation);
+    virtual bool updateDisplayProjection();
+    virtual status_t updateFBSize(int width, int height, int stride);
+
     // vncserver callbacks
     static ClientGoneHookPtr onClientGone(rfbClientPtr cl);
     static enum rfbNewClientAction onNewClient(rfbClientPtr cl);
@@ -74,6 +79,9 @@
 
     bool mRunning;
     bool mFrameAvailable;
+    bool mRotate;
+    bool mVDSActive;
+    bool mInputReconfigPending;
 
     Mutex mEventMutex;
     Mutex mUpdateMutex;
@@ -83,16 +91,17 @@
     rfbScreenInfoPtr mVNCScreen;
     uint8_t *mVNCBuf;
 
-    uint32_t mWidth, mHeight;
+    int mWidth, mHeight;
 
     sp<IBinder> mMainDpy;
-    DisplayInfo mMainDpyInfo;
 
     int mArgc;
     char **mArgv;
 
     size_t mClientCount;
 
+    int mOrientation;
+
     sp<FrameListener> mListener;
 
     // Producer side of queue, passed into the virtual display.