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);
+}
diff --git a/src/AndroidDesktop.h b/src/AndroidDesktop.h
index 0d3f8e4..768c27c 100644
--- a/src/AndroidDesktop.h
+++ b/src/AndroidDesktop.h
@@ -10,20 +10,23 @@
#include <gui/CpuConsumer.h>
+#include <ui/DisplayInfo.h>
+
#include <rfb/PixelBuffer.h>
#include <rfb/SDesktop.h>
#include <rfb/VNCServerST.h>
+#include "AndroidPixelBuffer.h"
#include "InputDevice.h"
-
+#include "VirtualDisplay.h"
using namespace android;
namespace vncflinger {
-static const rfb::PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
-
-class AndroidDesktop : public rfb::SDesktop, public RefBase {
+class AndroidDesktop : public rfb::SDesktop,
+ public CpuConsumer::FrameAvailableListener,
+ public AndroidPixelBuffer::BufferDimensionsListener {
public:
AndroidDesktop();
@@ -44,77 +47,35 @@
return mEventFd;
}
+ virtual void onBufferDimensionsChanged(uint32_t width, uint32_t height);
+
+ virtual void onFrameAvailable(const BufferItem& item);
+
private:
- class FrameListener : public CpuConsumer::FrameAvailableListener {
- public:
- FrameListener(AndroidDesktop* desktop) : mDesktop(desktop) {
- }
-
- virtual void onFrameAvailable(const BufferItem& item);
-
- private:
- FrameListener(FrameListener&) {
- }
- AndroidDesktop* mDesktop;
- };
-
- class AndroidPixelBuffer : public RefBase, public rfb::ManagedPixelBuffer {
- public:
- AndroidPixelBuffer(uint64_t width, uint64_t height)
- : rfb::ManagedPixelBuffer(sRGBX, width, height) {
- }
- };
-
- virtual status_t createVirtualDisplay();
- virtual status_t destroyVirtualDisplay();
-
virtual void notify();
- virtual status_t updateDisplayProjection();
- virtual bool updateFBSize(uint64_t width, uint64_t height);
- virtual void processDesktopResize();
+ virtual status_t updateDisplayInfo();
- uint64_t mSourceWidth, mSourceHeight;
- uint64_t mWidth, mHeight;
Rect mDisplayRect;
- bool mRotated;
-
- Mutex mMutex;
-
- bool mFrameAvailable;
- bool mProjectionChanged;
- bool mRotate;
- bool mVDSActive;
+ Mutex mLock;
uint64_t mFrameNumber;
- nsecs_t mFrameStartWhen;
int mEventFd;
- // Android virtual display is always 32-bit
- static const rfb::PixelFormat sRGBX;
-
// Server instance
rfb::VNCServerST* mServer;
// Pixel buffer
sp<AndroidPixelBuffer> mPixels;
+ // Virtual display controller
+ sp<VirtualDisplay> mVirtualDisplay;
+
// Primary display
sp<IBinder> mMainDpy;
-
- // Virtual display
- sp<IBinder> mDpy;
-
- // Producer side of queue, passed into the virtual display.
- sp<IGraphicBufferProducer> mProducer;
-
- // This receives frames from the virtual display and makes them available
- sp<CpuConsumer> mCpuConsumer;
-
- // Listener for virtual display buffers
- sp<FrameListener> mListener;
+ DisplayInfo mDisplayInfo;
// Virtual input device
sp<InputDevice> mInputDevice;
diff --git a/src/AndroidPixelBuffer.cpp b/src/AndroidPixelBuffer.cpp
new file mode 100644
index 0000000..7cbb9a7
--- /dev/null
+++ b/src/AndroidPixelBuffer.cpp
@@ -0,0 +1,119 @@
+//
+// vncflinger - Copyright (C) 2017 Steve Kondik
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#define LOG_TAG "AndroidPixelBuffer"
+#include <utils/Log.h>
+
+#include <ui/DisplayInfo.h>
+
+#include "AndroidPixelBuffer.h"
+
+using namespace vncflinger;
+using namespace android;
+
+const rfb::PixelFormat AndroidPixelBuffer::sRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
+
+AndroidPixelBuffer::AndroidPixelBuffer()
+ : ManagedPixelBuffer(), mRotated(false), mScaleX(1.0f), mScaleY(1.0f) {
+ setPF(sRGBX);
+ setSize(0, 0);
+}
+
+AndroidPixelBuffer::~AndroidPixelBuffer() {
+ mListener = nullptr;
+}
+
+bool AndroidPixelBuffer::isDisplayRotated(uint8_t orientation) {
+ return orientation != DISPLAY_ORIENTATION_0 && orientation != DISPLAY_ORIENTATION_180;
+}
+
+void AndroidPixelBuffer::setBufferRotation(bool rotated) {
+ if (rotated != mRotated) {
+ ALOGV("Orientation changed, swap width/height");
+ mRotated = rotated;
+ setSize(height_, width_);
+ std::swap(mScaleX, mScaleY);
+ stride = width_;
+
+ if (mListener != nullptr) {
+ mListener->onBufferDimensionsChanged(width_, height_);
+ }
+ }
+}
+
+void AndroidPixelBuffer::updateBufferSize(bool fromDisplay) {
+ uint32_t w = 0, h = 0;
+
+ // if this was caused by the source size changing (doesn't really
+ // happen on most Android hardware), then we need to consider
+ // a previous window size set by the client
+ if (fromDisplay) {
+ w = (uint32_t)((float)mSourceWidth * mScaleX);
+ h = (uint32_t)((float)mSourceHeight * mScaleY);
+ mClientWidth = w;
+ mClientHeight = h;
+ } else {
+ w = mClientWidth;
+ h = mClientHeight;
+ }
+
+ mScaleX = (float)mClientWidth / (float)mSourceWidth;
+ mScaleY = (float)mClientHeight / (float)mSourceHeight;
+
+ if (w == (uint32_t)width_ && h == (uint32_t)height_) {
+ return;
+ }
+
+ ALOGV("Buffer dimensions changed: old=(%dx%d) new=(%dx%d) scaleX=%f scaleY=%f", width_, height_,
+ w, h, mScaleX, mScaleY);
+
+ setSize(w, h);
+
+ if (mListener != nullptr) {
+ mListener->onBufferDimensionsChanged(width_, height_);
+ }
+}
+
+void AndroidPixelBuffer::setWindowSize(uint32_t width, uint32_t height) {
+ if (mClientWidth != width || mClientHeight != height) {
+ ALOGV("Client window size changed: old=(%dx%d) new=(%dx%d)", mClientWidth, mClientHeight,
+ width, height);
+ mClientWidth = width;
+ mClientHeight = height;
+ updateBufferSize();
+ }
+}
+
+void AndroidPixelBuffer::setDisplayInfo(DisplayInfo* info) {
+ bool rotated = isDisplayRotated(info->orientation);
+ setBufferRotation(rotated);
+
+ uint32_t w = rotated ? info->h : info->w;
+ uint32_t h = rotated ? info->w : info->h;
+
+ if (w != mSourceWidth || h != mSourceHeight) {
+ ALOGV("Display dimensions changed: old=(%dx%d) new=(%dx%d)", mSourceWidth, mSourceHeight, w,
+ h);
+ mSourceWidth = w;
+ mSourceHeight = h;
+ updateBufferSize(true);
+ }
+}
+
+Rect AndroidPixelBuffer::getSourceRect() {
+ return Rect(mSourceWidth, mSourceHeight);
+}
diff --git a/src/AndroidPixelBuffer.h b/src/AndroidPixelBuffer.h
new file mode 100644
index 0000000..5cd314c
--- /dev/null
+++ b/src/AndroidPixelBuffer.h
@@ -0,0 +1,90 @@
+//
+// vncflinger - Copyright (C) 2017 Steve Kondik
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef ANDROID_PIXEL_BUFFER_H
+#define ANDROID_PIXEL_BUFFER_H
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+#include <ui/DisplayInfo.h>
+#include <ui/Rect.h>
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/PixelFormat.h>
+
+using namespace android;
+
+namespace vncflinger {
+
+class AndroidPixelBuffer : public RefBase, public rfb::ManagedPixelBuffer {
+ public:
+ AndroidPixelBuffer();
+
+ virtual void setDisplayInfo(DisplayInfo* info);
+
+ virtual void setWindowSize(uint32_t width, uint32_t height);
+
+ virtual ~AndroidPixelBuffer();
+
+ class BufferDimensionsListener {
+ public:
+ virtual void onBufferDimensionsChanged(uint32_t width, uint32_t height) = 0;
+ virtual ~BufferDimensionsListener() {
+ }
+ };
+
+ void setDimensionsChangedListener(BufferDimensionsListener* listener) {
+ mListener = listener;
+ }
+
+ bool isRotated() {
+ return mRotated;
+ }
+
+ Rect getSourceRect();
+
+ private:
+ static bool isDisplayRotated(uint8_t orientation);
+
+ virtual void setBufferRotation(bool rotated);
+
+ virtual void updateBufferSize(bool fromDisplay = false);
+
+ Mutex mLock;
+
+ // width/height is swapped due to display orientation
+ bool mRotated;
+
+ // preferred size of the client's window
+ uint32_t mClientWidth, mClientHeight;
+
+ // size of the display
+ uint32_t mSourceWidth, mSourceHeight;
+
+ // current ratio between server and client
+ float mScaleX, mScaleY;
+
+ // callback when buffer size changes
+ BufferDimensionsListener* mListener;
+
+ // Android virtual display is always 32-bit
+ static const rfb::PixelFormat sRGBX;
+};
+};
+
+#endif
diff --git a/src/VirtualDisplay.cpp b/src/VirtualDisplay.cpp
new file mode 100644
index 0000000..8000f8f
--- /dev/null
+++ b/src/VirtualDisplay.cpp
@@ -0,0 +1,94 @@
+//
+// vncflinger - Copyright (C) 2017 Steve Kondik
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#define LOG_TAG "VirtualDisplay"
+#include <utils/Log.h>
+
+#include <gui/BufferQueue.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include "VirtualDisplay.h"
+
+using namespace vncflinger;
+
+VirtualDisplay::VirtualDisplay(DisplayInfo* info, uint32_t width, uint32_t height,
+ sp<CpuConsumer::FrameAvailableListener> listener) {
+ mWidth = width;
+ mHeight = height;
+
+ if (info->orientation == DISPLAY_ORIENTATION_0 || info->orientation == DISPLAY_ORIENTATION_180) {
+ mSourceRect = Rect(info->w, info->h);
+ } else {
+ mSourceRect = Rect(info->h, info->w);
+ }
+
+ Rect displayRect = getDisplayRect();
+
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ mCpuConsumer = new CpuConsumer(consumer, 1);
+ mCpuConsumer->setName(String8("vds-to-cpu"));
+ mCpuConsumer->setDefaultBufferSize(width, height);
+ mProducer->setMaxDequeuedBufferCount(4);
+ consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBX_8888);
+
+ mCpuConsumer->setFrameAvailableListener(listener);
+
+ mDpy = SurfaceComposerClient::createDisplay(String8("VNC-VirtualDisplay"), false /*secure*/);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
+
+ SurfaceComposerClient::setDisplayProjection(mDpy, 0, mSourceRect, displayRect);
+ SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ ALOGV("Virtual display (%ux%u [viewport=%ux%u] created", width, height, displayRect.getWidth(),
+ displayRect.getHeight());
+}
+
+VirtualDisplay::~VirtualDisplay() {
+ mCpuConsumer.clear();
+ mProducer.clear();
+ SurfaceComposerClient::destroyDisplay(mDpy);
+
+ ALOGV("Virtual display destroyed");
+}
+
+Rect VirtualDisplay::getDisplayRect() {
+ uint32_t outWidth, outHeight;
+ if (mWidth > (uint32_t)((float)mWidth * aspectRatio())) {
+ // limited by narrow width; reduce height
+ outWidth = mWidth;
+ outHeight = (uint32_t)((float)mWidth * aspectRatio());
+ } else {
+ // limited by short height; restrict width
+ outHeight = mHeight;
+ outWidth = (uint32_t)((float)mHeight / aspectRatio());
+ }
+
+ // 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;
+ return Rect(offX, offY, offX + outWidth, offY + outHeight);
+}
diff --git a/src/VirtualDisplay.h b/src/VirtualDisplay.h
index f23d32d..002d87d 100644
--- a/src/VirtualDisplay.h
+++ b/src/VirtualDisplay.h
@@ -1,26 +1,70 @@
+//
+// vncflinger - Copyright (C) 2017 Steve Kondik
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef VIRTUAL_DISPLAY_H_
+#define VIRTUAL_DISPLAY_H_
+
+#include <utils/RefBase.h>
+
#include <gui/CpuConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+
+#include <ui/DisplayInfo.h>
+#include <ui/Rect.h>
+
+using namespace android;
namespace vncflinger {
-class VirtualDisplay {
+class VirtualDisplay : public RefBase {
public:
- VirtualDisplay();
+ VirtualDisplay(DisplayInfo* info, uint32_t width, uint32_t height,
+ sp<CpuConsumer::FrameAvailableListener> listener);
- virtual void onFrameAvailable(const BufferItem& item);
+ virtual ~VirtualDisplay();
+
+ virtual Rect getDisplayRect();
+
+ virtual Rect getSourceRect() {
+ return mSourceRect;
+ }
+
+ CpuConsumer* getConsumer() {
+ return mCpuConsumer.get();
+ }
private:
- Mutex mEventMutex;
- Condition mEventCond;
-
- bool mFrameAvailable;
-
- // Virtual display
- sp<IBinder> mDpy;
+ float aspectRatio() {
+ return (float)mSourceRect.getHeight() / (float)mSourceRect.getWidth();
+ }
// Producer side of queue, passed into the virtual display.
sp<IGraphicBufferProducer> mProducer;
// This receives frames from the virtual display and makes them available
sp<CpuConsumer> mCpuConsumer;
+
+ // Virtual display
+ sp<IBinder> mDpy;
+
+ sp<CpuConsumer::FrameAvailableListener> mListener;
+
+ uint32_t mWidth, mHeight;
+ Rect mSourceRect;
};
};
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index c22f075..831268d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,10 @@
#include "AndroidDesktop.h"
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
#include <network/Socket.h>
#include <network/TcpSocket.h>
#include <rfb/Configuration.h>
@@ -13,6 +17,7 @@
#include <rfb/util.h>
using namespace vncflinger;
+using namespace android;
static char* gProgramName;
static bool gCaughtSignal = false;
@@ -67,6 +72,9 @@
usage();
}
+ sp<ProcessState> self = ProcessState::self();
+ self->startThreadPool();
+
std::list<network::TcpListener*> listeners;
try {
@@ -157,6 +165,7 @@
uint64_t eventVal;
int status = read(eventFd, &eventVal, sizeof(eventVal));
if (status > 0 && eventVal > 0) {
+ ALOGV("status=%d eventval=%lu", status, eventVal);
desktop->processFrames();
}
}