Rewrite server on top of TigerVNC
TigerVNC provides a much more robust server implementation vs.
libvncserver and yields higher performance and lower CPU
usage.
diff --git a/src/AndroidDesktop.cpp b/src/AndroidDesktop.cpp
new file mode 100644
index 0000000..a37500c
--- /dev/null
+++ b/src/AndroidDesktop.cpp
@@ -0,0 +1,340 @@
+#define LOG_TAG "AndroidDesktop"
+#include <utils/Log.h>
+
+#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>
+
+#include <ui/DisplayInfo.h>
+
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb/ScreenSet.h>
+#include <rfb/VNCServerST.h>
+
+#include "AndroidDesktop.h"
+#include "InputDevice.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();
+
+ mEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+ if (mEventFd < 0) {
+ ALOGE("Failed to create event notifier");
+ return;
+ }
+}
+
+AndroidDesktop::~AndroidDesktop() {
+ mInputDevice->stop();
+ close(mEventFd);
+}
+
+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);
+
+ mServer->setPixelBuffer(mPixels.get());
+
+ ALOGV("Desktop is running");
+}
+
+void AndroidDesktop::stop() {
+ Mutex::Autolock _L(mMutex);
+
+ 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;
+ }
+}
+
+void AndroidDesktop::processFrames() {
+ Mutex::Autolock _l(mMutex);
+
+ // do any pending resize
+ processDesktopResize();
+
+ if (!mFrameAvailable) {
+ return;
+ }
+
+ // get a frame from the virtual display
+ CpuConsumer::LockedBuffer imgBuffer;
+ status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
+ if (res != OK) {
+ ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
+ return;
+ }
+
+ mFrameNumber = imgBuffer.frameNumber;
+ ALOGV("processFrame: [%lu] format: %x (%dx%d, stride=%d)", mFrameNumber, imgBuffer.format,
+ imgBuffer.width, imgBuffer.height, imgBuffer.stride);
+
+ // we don't know if there was a stride change until we get
+ // a buffer from the queue. if it changed, we need to resize
+
+ rfb::Rect bufRect(0, 0, imgBuffer.width, imgBuffer.height);
+
+ // performance is extremely bad if the gpu memory is used
+ // directly without copying because it is likely uncached
+ mPixels->imageRect(bufRect, imgBuffer.data, imgBuffer.stride);
+
+ mCpuConsumer->unlockBuffer(imgBuffer);
+
+ // update clients
+ mServer->add_changed(bufRect);
+ mFrameAvailable = false;
+}
+
+// notifies the server loop that we have changes
+void AndroidDesktop::notify() {
+ static uint64_t notify = 1;
+ write(mEventFd, ¬ify, sizeof(notify));
+}
+
+// called when a client resizes the window
+unsigned int AndroidDesktop::setScreenLayout(int reqWidth, int reqHeight,
+ const rfb::ScreenSet& layout) {
+ Mutex::Autolock _l(mMutex);
+
+ char* dbg = new char[1024];
+ layout.print(dbg, 1024);
+
+ ALOGD("setScreenLayout: cur: %lux%lu new: %dx%d %s", mWidth, mHeight, reqWidth, reqHeight, dbg);
+ delete[] dbg;
+
+ if (reqWidth == (int)mWidth && reqHeight == (int)mHeight) {
+ return rfb::resultInvalid;
+ }
+
+ if (reqWidth > 0 && reqHeight > 0) {
+ mWidth = reqWidth;
+ mHeight = reqHeight;
+
+ if (updateDisplayProjection() == NO_ERROR) {
+ // resize immediately
+ processDesktopResize();
+ notify();
+ 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();
+ ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
+}
+
+rfb::Point AndroidDesktop::getFbSize() {
+ return rfb::Point(mPixels->width(), mPixels->height());
+}
+
+void AndroidDesktop::keyEvent(rdr::U32 key, bool down) {
+ mInputDevice->keyEvent(down, key);
+}
+
+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) {
+ // outside viewport
+ return;
+ }
+ uint32_t x = pos.x * ((float)(mDisplayRect.getWidth()) / (float)mWidth);
+ uint32_t y = pos.y * ((float)(mDisplayRect.getHeight()) / (float)mHeight);
+
+ ALOGD("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);
+ 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);
+
+ return NO_ERROR;
+}
diff --git a/src/AndroidDesktop.h b/src/AndroidDesktop.h
new file mode 100644
index 0000000..0d3f8e4
--- /dev/null
+++ b/src/AndroidDesktop.h
@@ -0,0 +1,124 @@
+#ifndef ANDROID_DESKTOP_H_
+#define ANDROID_DESKTOP_H_
+
+#include <memory>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Thread.h>
+
+#include <gui/CpuConsumer.h>
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/SDesktop.h>
+#include <rfb/VNCServerST.h>
+
+#include "InputDevice.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 {
+ public:
+ AndroidDesktop();
+
+ virtual ~AndroidDesktop();
+
+ virtual void start(rfb::VNCServer* vs);
+ virtual void stop();
+
+ virtual rfb::Point getFbSize();
+ virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout);
+
+ virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
+
+ virtual void processFrames();
+
+ virtual int getEventFd() {
+ return mEventFd;
+ }
+
+ 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();
+
+ uint64_t mSourceWidth, mSourceHeight;
+ uint64_t mWidth, mHeight;
+ Rect mDisplayRect;
+
+ bool mRotated;
+
+ Mutex mMutex;
+
+ bool mFrameAvailable;
+ bool mProjectionChanged;
+ bool mRotate;
+ bool mVDSActive;
+
+ 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;
+
+ // 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;
+
+ // Virtual input device
+ sp<InputDevice> mInputDevice;
+};
+};
+
+#endif
diff --git a/src/InputDevice.cpp b/src/InputDevice.cpp
index a4b6d7c..04bad97 100644
--- a/src/InputDevice.cpp
+++ b/src/InputDevice.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "VNC-InputDevice"
#include <utils/Log.h>
+#include <future>
+
#include "InputDevice.h"
#include <fcntl.h>
@@ -28,11 +30,8 @@
#include <linux/input.h>
#include <linux/uinput.h>
-#include <rfb/keysym.h>
-
using namespace android;
-ANDROID_SINGLETON_STATIC_INSTANCE(InputDevice)
static const struct UInputOptions {
int cmd;
@@ -51,11 +50,16 @@
{UI_SET_PROPBIT, INPUT_PROP_DIRECT},
};
+status_t InputDevice::start_async(uint32_t width, uint32_t height) {
+ // don't block the caller since this can take a few seconds
+ std::async(&InputDevice::start, this, width, height);
+
+ return NO_ERROR;
+}
+
status_t InputDevice::start(uint32_t width, uint32_t height) {
Mutex::Autolock _l(mLock);
- status_t err = OK;
-
mLeftClicked = mMiddleClicked = mRightClicked = false;
struct input_id id = {
@@ -111,6 +115,8 @@
goto err_ioctl;
}
+ mOpened = true;
+
ALOGD("Virtual input device created successfully (%dx%d)", width, height);
return NO_ERROR;
@@ -124,11 +130,14 @@
status_t InputDevice::reconfigure(uint32_t width, uint32_t height) {
stop();
- return start(width, height);
+ return start_async(width, height);
}
status_t InputDevice::stop() {
Mutex::Autolock _l(mLock);
+
+ mOpened = false;
+
if (mFD < 0) {
return OK;
}
@@ -187,20 +196,15 @@
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) {
+void InputDevice::keyEvent(bool down, uint32_t key) {
int code;
int sh = 0;
int alt = 0;
- if (mFD < 0) return;
-
Mutex::Autolock _l(mLock);
+ if (!mOpened) return;
- if ((code = keysym2scancode(key, cl, &sh, &alt))) {
+ if ((code = keysym2scancode(key, &sh, &alt))) {
int ret = 0;
if (key && down) {
@@ -231,17 +235,12 @@
}
}
-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 */) {
- if (mFD < 0) return;
+void InputDevice::pointerEvent(int buttonMask, int x, int y) {
+ Mutex::Autolock _l(mLock);
+ if (!mOpened) return;
ALOGV("pointerEvent: buttonMask=%x x=%d y=%d", buttonMask, x, y);
- Mutex::Autolock _l(mLock);
-
if ((buttonMask & 1) && mLeftClicked) { // left btn clicked and moving
inject(EV_ABS, ABS_X, x);
inject(EV_ABS, ABS_Y, y);
@@ -314,7 +313,7 @@
static const int spec4[] = {26, 43, 27, 215, 14};
static const int spec4sh[] = {1, 1, 1, 1, 0};
-int InputDevice::keysym2scancode(rfbKeySym c, rfbClientPtr cl, int* sh, int* alt) {
+int InputDevice::keysym2scancode(uint32_t c, int* sh, int* alt) {
int real = 1;
if ('a' <= c && c <= 'z') return qwerty[c - 'a'];
if ('A' <= c && c <= 'Z') {
@@ -372,9 +371,6 @@
// return 232;// end -> DPAD_CENTER (ball click)
case 0xff50:
return KEY_HOME; // home
- case 0xFFC8:
- rfbShutdownServer(cl->screen, TRUE);
- return 0; // F11 disconnect
case 0xffff:
return 158; // del -> back
case 0xff55:
diff --git a/src/InputDevice.h b/src/InputDevice.h
index 1744478..be4367f 100644
--- a/src/InputDevice.h
+++ b/src/InputDevice.h
@@ -20,32 +20,33 @@
#include <utils/Errors.h>
#include <utils/Mutex.h>
-#include <utils/Singleton.h>
+#include <utils/RefBase.h>
#include <linux/uinput.h>
-#include <rfb/rfb.h>
+
#define UINPUT_DEVICE "/dev/uinput"
namespace android {
-class InputDevice : public Singleton<InputDevice> {
- friend class Singleton;
-
+class InputDevice : public RefBase {
public:
virtual status_t start(uint32_t width, uint32_t height);
+ virtual status_t start_async(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);
+ virtual void keyEvent(bool down, uint32_t key);
+ virtual void pointerEvent(int buttonMask, int x, int y);
InputDevice() : mFD(-1) {
}
virtual ~InputDevice() {
+ stop();
}
private:
+
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);
@@ -54,14 +55,12 @@
status_t release(uint16_t code);
status_t click(uint16_t code);
- void keyEvent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
- void pointerEvent(int buttonMask, int x, int y, rfbClientPtr cl);
-
- int keysym2scancode(rfbKeySym c, rfbClientPtr cl, int* sh, int* alt);
+ int keysym2scancode(uint32_t c, int* sh, int* alt);
Mutex mLock;
int mFD;
+ bool mOpened;
struct uinput_user_dev mUserDev;
diff --git a/src/VNCFlinger.cpp b/src/VNCFlinger.cpp
deleted file mode 100644
index b910453..0000000
--- a/src/VNCFlinger.cpp
+++ /dev/null
@@ -1,473 +0,0 @@
-//
-// 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 "VNCFlinger"
-#include <utils/Log.h>
-
-#include <fstream>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <ui/PixelFormat.h>
-
-#include "InputDevice.h"
-#include "VNCFlinger.h"
-
-using namespace android;
-
-VNCFlinger::VNCFlinger() {
- mOrientation = -1;
- mScale = 1.0f;
- mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
- updateDisplayProjection();
-
- createVNCServer();
-
- String8 v4("127.0.0.1");
- String8 v6("::1");
-
- setV4Address(v4);
- setV6Address(v6);
-}
-
-status_t VNCFlinger::setV4Address(const String8& address) {
- if (!rfbStringToAddr(const_cast<char *>(address.string()), &(mVNCScreen->listenInterface))) {
- return BAD_VALUE;
- }
- return NO_ERROR;
-}
-
-status_t VNCFlinger::setV6Address(const String8& address) {
- mVNCScreen->listen6Interface = const_cast<char *>(address.string());
- return NO_ERROR;
-}
-
-status_t VNCFlinger::setPort(unsigned int port) {
- if (port > 65535) {
- port = 0;
- }
- if (port == 0) {
- mVNCScreen->autoPort = 1;
- } else {
- mVNCScreen->autoPort = 0;
- mVNCScreen->port = port;
- mVNCScreen->ipv6port = port;
- }
- return NO_ERROR;
-}
-
-status_t VNCFlinger::setScale(float scale) {
- if (scale <= 0.0f || scale > 2.0f) {
- return BAD_VALUE;
- }
- mScale = scale;
- updateDisplayProjection();
- return NO_ERROR;
-}
-
-status_t VNCFlinger::clearPassword() {
- std::remove(VNC_AUTH_FILE);
- ALOGW("Password authentication disabled");
- mVNCScreen->authPasswdData = NULL;
- return OK;
-}
-
-status_t VNCFlinger::setPassword(const String8& passwd) {
- String8 path(VNC_AUTH_FILE);
- if (rfbEncryptAndStorePasswd(const_cast<char *>(passwd.string()),
- const_cast<char *>(path.string())) != 0) {
- ALOGE("Failed to set password");
- return BAD_VALUE;
- }
- ALOGI("Password has been set");
- mVNCScreen->authPasswdData = (void *)VNC_AUTH_FILE;
- return OK;
-}
-
-status_t VNCFlinger::start() {
- sp<ProcessState> self = ProcessState::self();
- self->startThreadPool();
-
- status_t err = startVNCServer();
- if (err != NO_ERROR) {
- ALOGE("Failed to start VNCFlinger: err=%d", err);
- return err;
- }
-
- rfbRunEventLoop(mVNCScreen, -1, true);
-
- ALOGD("VNCFlinger ready to fling");
-
- eventLoop();
-
- ALOGI("VNCFlinger has left the building");
-
- return NO_ERROR;
-}
-
-status_t VNCFlinger::stop() {
- Mutex::Autolock _L(mEventMutex);
- ALOGV("Shutting down");
-
- rfbShutdownServer(mVNCScreen, false);
-
- destroyVirtualDisplayLocked();
-
- mClientCount = 0;
- mRunning = false;
-
- mEventCond.signal();
- delete[] mVNCScreen->frameBuffer;
- return NO_ERROR;
-}
-
-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) {
- if (!updateDisplayProjection()) {
- processFrame();
- }
- mFrameAvailable = false;
- }
- }
- Mutex::Autolock _l(mUpdateMutex);
- memset(mVNCScreen->frameBuffer, 0, mFrameSize);
- destroyVirtualDisplayLocked();
- }
-}
-
-status_t VNCFlinger::createVirtualDisplay() {
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&mProducer, &consumer);
- mCpuConsumer = new CpuConsumer(consumer, NUM_BUFS);
- mCpuConsumer->setName(String8("vds-to-cpu"));
- mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
- mProducer->setMaxDequeuedBufferCount(1);
- consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBX_8888);
-
- mListener = new FrameListener(this);
- mCpuConsumer->setFrameAvailableListener(mListener);
-
- mDpy = SurfaceComposerClient::createDisplay(String8("VNC-VirtualDisplay"), false /*secure*/);
-
- SurfaceComposerClient::openGlobalTransaction();
- SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
- Rect displayRect(0, 0, mSourceWidth, mSourceHeight);
- Rect outRect(0, 0, mWidth, mHeight);
- SurfaceComposerClient::setDisplayProjection(mDpy, 0, displayRect, outRect);
- SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack
- SurfaceComposerClient::closeGlobalTransaction();
-
- mVDSActive = true;
-
- ALOGV("Virtual display (%dx%d) created", mWidth, mHeight);
-
- return NO_ERROR;
-}
-
-status_t VNCFlinger::destroyVirtualDisplayLocked() {
- if (!mVDSActive) {
- return NO_INIT;
- }
-
- mCpuConsumer.clear();
- mProducer.clear();
- SurfaceComposerClient::destroyDisplay(mDpy);
-
- mVDSActive = false;
-
- ALOGV("Virtual display destroyed");
-
- return NO_ERROR;
-}
-
-status_t VNCFlinger::createVNCServer() {
- status_t err = NO_ERROR;
-
- rfbLog = VNCFlinger::rfbLogger;
- rfbErr = VNCFlinger::rfbLogger;
-
- // 32-bit color
- mVNCScreen = rfbGetScreen(0, NULL, mWidth, mHeight, 8, 3, 4);
- if (mVNCScreen == NULL) {
- ALOGE("Unable to create VNCScreen");
- return NO_INIT;
- }
-
- mFrameNumber = 0;
- mFrameSize = mWidth * mHeight * 4;
- mVNCScreen->frameBuffer = (char*)new uint8_t[mFrameSize];
- memset(mVNCScreen->frameBuffer, 0, mFrameSize);
-
- mVNCScreen->desktopName = "VNCFlinger";
- mVNCScreen->alwaysShared = TRUE;
- mVNCScreen->httpDir = NULL;
- mVNCScreen->newClientHook = (rfbNewClientHookPtr)VNCFlinger::onNewClient;
- 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->serverFormat.depth = 24;
- mVNCScreen->handleEventsEagerly = true;
- mVNCScreen->deferUpdateTime = 0;
- mVNCScreen->screenData = this;
-
- std::ifstream authFile(VNC_AUTH_FILE);
- if ((bool)authFile) {
- mVNCScreen->authPasswdData = (void *)VNC_AUTH_FILE;
- }
-
- return err;
-}
-
-status_t VNCFlinger::startVNCServer() {
- rfbInitServer(mVNCScreen);
-
- /* Mark as dirty since we haven't sent any updates at all yet. */
- rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
-
- return NO_ERROR;
-}
-
-size_t VNCFlinger::addClient() {
- Mutex::Autolock _l(mEventMutex);
- if (mClientCount == 0) {
- mClientCount++;
- InputDevice::getInstance().start(mWidth, mHeight);
- mEventCond.signal();
- }
-
- ALOGI("Client connected (%zu)", mClientCount);
-
- return mClientCount;
-}
-
-size_t VNCFlinger::removeClient() {
- Mutex::Autolock _l(mEventMutex);
- if (mClientCount > 0) {
- mClientCount--;
- if (mClientCount == 0) {
- InputDevice::getInstance().stop();
- mEventCond.signal();
- }
- }
-
- ALOGI("Client disconnected (%zu)", mClientCount);
-
- return mClientCount;
-}
-
-void VNCFlinger::processFrame() {
- // 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);
-
- // get a frame from the virtual display
- CpuConsumer::LockedBuffer imgBuffer;
- status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
- if (res != OK) {
- ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
- return;
- }
-
- mFrameNumber = imgBuffer.frameNumber;
- ALOGV("processFrame: [%lu] format: %x (%dx%d, stride=%d)", mFrameNumber, imgBuffer.format,
- imgBuffer.width, imgBuffer.height, imgBuffer.stride);
-
- // we don't know if there was a stride change until we get
- // a buffer from the queue. if it changed, we need to resize
- updateFBSize(imgBuffer);
-
- // performance is extremely bad if the gpu memory is used
- // directly without copying because it is likely uncached
- memcpy(mVNCScreen->frameBuffer, imgBuffer.data, mFrameSize);
-
- // update clients
- 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;
- }
-
- // 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);
- uint32_t sourceWidth, sourceHeight;
- if (!deviceRotated) {
- sourceWidth = info.w;
- sourceHeight = info.h;
- } else {
- ALOGV("using rotated width/height");
- sourceHeight = info.w;
- sourceWidth = info.h;
- }
-
- uint32_t width = sourceWidth * mScale;
- uint32_t height = sourceHeight * mScale;
-
- if (mSourceWidth == sourceWidth && mSourceHeight == sourceHeight &&
- mWidth == width && mHeight == height &&
- mOrientation == info.orientation) {
- return false;
- }
-
- // orientation / resolution change
- mSourceWidth = sourceWidth;
- mSourceHeight = sourceHeight;
- mWidth = width;
- mHeight = height;
- mOrientation = info.orientation;
-
- ALOGV("Dimensions: %dx%d [out: %dx%d, scale: %f] orientation=%d",
- mSourceWidth, mSourceHeight, mWidth, mHeight, mScale, mOrientation);
-
- if (!mVDSActive) {
- return true;
- }
-
- // it does not appear to be possible to reconfigure the virtual display
- // on the fly without forcing surfaceflinger to tear it down
- destroyVirtualDisplayLocked();
- createVirtualDisplay();
-
- return false;
-}
-
-bool VNCFlinger::updateFBSize(CpuConsumer::LockedBuffer& buf) {
- uint32_t stride = (uint32_t)mVNCScreen->paddedWidthInBytes / 4;
- uint32_t width = (uint32_t)mVNCScreen->width;
- uint32_t height = (uint32_t)mVNCScreen->height;
-
- uint64_t newSize = buf.stride * buf.height * 4;
-
- if (stride != buf.stride || height != buf.height || width != buf.width) {
- ALOGD("updateFBSize: old=[%dx%d %d] new=[%dx%d %d]", width, height, stride, buf.width,
- buf.height, buf.stride);
-
- if (mFrameSize != newSize) {
- mFrameSize = newSize;
- delete[] mVNCScreen->frameBuffer;
- rfbNewFramebuffer(mVNCScreen, (char*)new uint8_t[newSize], buf.width, buf.height, 8, 3,
- 4);
- }
- mVNCScreen->paddedWidthInBytes = buf.stride * 4;
- }
- return NO_ERROR;
-}
-
-// ------------------------------------------------------------------------ //
-
-// libvncserver logger
-void VNCFlinger::rfbLogger(const char* format, ...) {
- va_list args;
- char buf[256];
-
- va_start(args, format);
- vsprintf(buf, format, args);
- ALOGI("%s", buf);
- va_end(args);
-}
-
-// libvncserver callbacks
-ClientGoneHookPtr VNCFlinger::onClientGone(rfbClientPtr cl) {
- ALOGV("onClientGone");
- VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
- vf->removeClient();
- return 0;
-}
-
-enum rfbNewClientAction VNCFlinger::onNewClient(rfbClientPtr cl) {
- ALOGV("onNewClient");
- cl->clientGoneHook = (ClientGoneHookPtr)VNCFlinger::onClientGone;
- VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
- vf->addClient();
- return RFB_CLIENT_ACCEPT;
-}
-
-void VNCFlinger::onFrameStart(rfbClientPtr cl) {
- VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
- vf->mUpdateMutex.lock();
-
- vf->mFrameStartWhen = systemTime(CLOCK_MONOTONIC);
- ALOGV("frame start [%lu]", vf->mFrameNumber);
-}
-
-void VNCFlinger::onFrameDone(rfbClientPtr cl, int /* status */) {
- VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
-
- float timing = (systemTime(CLOCK_MONOTONIC) - vf->mFrameStartWhen) / 1000000.0;
- ALOGV("onFrameDone [%lu] (%.3f ms)", vf->mFrameNumber, timing);
-
- vf->mUpdateMutex.unlock();
-}
-
-// cpuconsumer frame listener
-void VNCFlinger::FrameListener::onFrameAvailable(const BufferItem& item) {
- Mutex::Autolock _l(mVNC->mEventMutex);
- mVNC->mFrameAvailable = true;
- mVNC->mEventCond.signal();
- ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
-}
diff --git a/src/VNCFlinger.h b/src/VNCFlinger.h
deleted file mode 100644
index 3f9bf62..0000000
--- a/src/VNCFlinger.h
+++ /dev/null
@@ -1,130 +0,0 @@
-//
-// 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 VNCFLINGER_H
-#define VNCFLINGER_H
-
-#include <gui/CpuConsumer.h>
-#include <ui/DisplayInfo.h>
-#include <utils/String8.h>
-
-#include <rfb/rfb.h>
-#undef max
-
-#define VNC_AUTH_FILE "/data/system/vncauth"
-#define NUM_BUFS 1
-
-namespace android {
-
-class VNCFlinger : public RefBase {
- public:
- VNCFlinger();
-
- virtual ~VNCFlinger() {
- }
-
- virtual status_t start();
- virtual status_t stop();
-
- virtual size_t addClient();
- virtual size_t removeClient();
-
- virtual status_t setPort(unsigned int port);
- virtual status_t setV4Address(const String8& address);
- virtual status_t setV6Address(const String8& address);
- virtual status_t setScale(float scale);
-
- virtual status_t clearPassword();
- virtual status_t setPassword(const String8& passwd);
-
- private:
- class FrameListener : public CpuConsumer::FrameAvailableListener {
- public:
- FrameListener(VNCFlinger* vnc) : mVNC(vnc) {
- }
-
- virtual void onFrameAvailable(const BufferItem& item);
-
- private:
- FrameListener(FrameListener&) {
- }
- VNCFlinger* mVNC;
- };
-
- virtual void eventLoop();
-
- virtual status_t createVirtualDisplay();
- virtual status_t destroyVirtualDisplayLocked();
-
- virtual status_t createVNCServer();
- virtual status_t startVNCServer();
-
- virtual void processFrame();
-
- virtual bool isDeviceRotated(int orientation);
- virtual bool updateDisplayProjection();
- virtual bool updateFBSize(CpuConsumer::LockedBuffer& buf);
-
- // vncserver callbacks
- static ClientGoneHookPtr onClientGone(rfbClientPtr cl);
- static enum rfbNewClientAction onNewClient(rfbClientPtr cl);
- static void onFrameStart(rfbClientPtr cl);
- static void onFrameDone(rfbClientPtr cl, int result);
- static void rfbLogger(const char* format, ...);
-
- bool mRunning;
- bool mFrameAvailable;
- bool mRotate;
- bool mVDSActive;
-
- Mutex mEventMutex;
- Mutex mUpdateMutex;
-
- Condition mEventCond;
-
- uint32_t mWidth, mHeight;
- uint32_t mSourceWidth, mSourceHeight;
- int32_t mOrientation;
- float mScale;
-
- size_t mClientCount;
-
- // Framebuffers
- uint64_t mFrameNumber;
- uint64_t mFrameSize;
- nsecs_t mFrameStartWhen;
-
- // Server instance
- rfbScreenInfoPtr mVNCScreen;
-
- // 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;
-
- // Consumer callback
- sp<FrameListener> mListener;
-};
-};
-#endif
diff --git a/src/VirtualDisplay.h b/src/VirtualDisplay.h
new file mode 100644
index 0000000..f23d32d
--- /dev/null
+++ b/src/VirtualDisplay.h
@@ -0,0 +1,26 @@
+#include <gui/CpuConsumer.h>
+
+namespace vncflinger {
+
+class VirtualDisplay {
+ public:
+ VirtualDisplay();
+
+ virtual void onFrameAvailable(const BufferItem& item);
+
+ private:
+ Mutex mEventMutex;
+ Condition mEventCond;
+
+ bool mFrameAvailable;
+
+ // 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;
+};
+};
diff --git a/src/main.cpp b/src/main.cpp
index 29178ff..c22f075 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,148 +1,168 @@
-//
-// 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/>.
-//
-#include <getopt.h>
+#define LOG_TAG "VNCFlinger"
+#include <utils/Log.h>
-#include <csignal>
-#include <iostream>
+#include <fcntl.h>
-#include <binder/IServiceManager.h>
+#include "AndroidDesktop.h"
-#include "VNCFlinger.h"
-#include "VNCService.h"
+#include <network/Socket.h>
+#include <network/TcpSocket.h>
+#include <rfb/Configuration.h>
+#include <rfb/Logger_android.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/util.h>
-using namespace android;
+using namespace vncflinger;
-static sp<VNCFlinger> gVNC;
+static char* gProgramName;
+static bool gCaughtSignal = false;
-static const char* const shortOpts = "4:6:p:cs:l:vh";
-static const option longOpts[] = {
- {"listen", 1, nullptr, '4'},
- {"listen6", 1, nullptr, '6'},
- {"port", 1, nullptr, 'p'},
- {"password", 1, nullptr, 's'},
- {"clear-password", 0, nullptr, 'c'},
- {"scale", 1, nullptr, 'l'},
- {"version", 0, nullptr, 'v'},
- {"help", 0, nullptr, 'h'},
-};
+static rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol", 5900);
-static void onSignal(int signal) {
- ALOGV("Shutting down on signal %d", signal);
- gVNC->stop();
+static void printVersion(FILE* fp) {
+ fprintf(fp, "VNCFlinger 1.0");
}
-static void printVersion() {
- std::cout << "VNCFlinger-" << VNCFLINGER_VERSION << std::endl;
- exit(0);
-}
-
-static void printHelp() {
- std::cout << "Usage: vncflinger [OPTIONS]\n\n"
- << " -4 <addr> IPv4 address to listen (default: localhost)\n"
- << " -6 <addr> IPv6 address to listen (default: localhost)\n"
- << " -p <num> Port to listen on (default: 5900)\n"
- << " -s <pass> Store server password\n"
- << " -c Clear server password\n"
- << " -l <scale> Scaling value (default: 1.0)\n"
- << " -v Show server version\n"
- << " -h Show help\n\n";
+static void usage() {
+ printVersion(stderr);
+ fprintf(stderr, "\nUsage: %s [<parameters>]\n", gProgramName);
+ fprintf(stderr, " %s --version\n", gProgramName);
+ fprintf(stderr,
+ "\n"
+ "Parameters can be turned on with -<param> or off with -<param>=0\n"
+ "Parameters which take a value can be specified as "
+ "-<param> <value>\n"
+ "Other valid forms are <param>=<value> -<param>=<value> "
+ "--<param>=<value>\n"
+ "Parameter names are case-insensitive. The parameters are:\n\n");
+ rfb::Configuration::listParams(79, 14);
exit(1);
}
-static void parseArgs(int argc, char** argv) {
- String8 arg;
-
- while (true) {
- const auto opt = getopt_long(argc, argv, shortOpts, longOpts, nullptr);
-
- if (opt < 0) {
- break;
- }
-
- switch (opt) {
- case 's':
- arg = optarg;
- if (gVNC->setPassword(arg) != OK) {
- std::cerr << "Failed to set password\n";
- exit(1);
- }
- exit(0);
-
- case 'c':
- if (gVNC->clearPassword() != OK) {
- std::cerr << "Failed to clear password\n";
- exit(1);
- }
- exit(0);
-
- case '4':
- arg = optarg;
- if (gVNC->setV4Address(arg) != OK) {
- std::cerr << "Failed to set IPv4 address\n";
- exit(1);
- }
- break;
-
- case '6':
- arg = optarg;
- if (gVNC->setV6Address(arg) != OK) {
- std::cerr << "Failed to set IPv6 address\n";
- exit(1);
- }
- break;
-
- case 'p':
- if (gVNC->setPort(std::stoi(optarg)) != OK) {
- std::cerr << "Failed to set port\n";
- exit(1);
- }
- break;
-
- case 'l':
- if (gVNC->setScale(std::stof(optarg)) != OK) {
- std::cerr << "Invalid scaling value (must be between 0.0 and 2.0)\n";
- exit(1);
- }
- break;
-
- case 'v':
- printVersion();
- break;
-
- case 'h':
- case '?':
- default:
- printHelp();
- break;
- }
- }
-}
-
int main(int argc, char** argv) {
- std::signal(SIGINT, onSignal);
- std::signal(SIGHUP, onSignal);
+ rfb::initAndroidLogger();
+ rfb::LogWriter::setLogParams("*:android:30");
- gVNC = new VNCFlinger();
+ gProgramName = argv[0];
- parseArgs(argc, argv);
+ rfb::Configuration::enableServerParams();
- // binder interface
- defaultServiceManager()->addService(String16("vnc"), new VNCService(gVNC));
+ for (int i = 1; i < argc; i++) {
+ if (rfb::Configuration::setParam(argv[i])) continue;
- gVNC->start();
- gVNC.clear();
+ if (argv[i][0] == '-') {
+ if (i + 1 < argc) {
+ if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
+ i++;
+ continue;
+ }
+ }
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "-version") == 0 ||
+ strcmp(argv[i], "--version") == 0) {
+ printVersion(stdout);
+ return 0;
+ }
+ usage();
+ }
+ usage();
+ }
+
+ std::list<network::TcpListener*> listeners;
+
+ try {
+ sp<AndroidDesktop> desktop = new AndroidDesktop();
+ rfb::VNCServerST server("vncflinger", desktop.get());
+ network::createTcpListeners(&listeners, 0, (int)rfbport);
+
+ int eventFd = desktop->getEventFd();
+ fcntl(eventFd, F_SETFL, O_NONBLOCK);
+
+ ALOGI("Listening on port %d", (int)rfbport);
+
+ while (!gCaughtSignal) {
+ int wait_ms;
+ struct timeval tv;
+ fd_set rfds, wfds;
+ std::list<network::Socket*> sockets;
+ std::list<network::Socket*>::iterator i;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ FD_SET(eventFd, &rfds);
+ for (std::list<network::TcpListener*>::iterator i = listeners.begin();
+ i != listeners.end(); i++)
+ FD_SET((*i)->getFd(), &rfds);
+
+ server.getSockets(&sockets);
+ int clients_connected = 0;
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ if ((*i)->isShutdown()) {
+ server.removeSocket(*i);
+ delete (*i);
+ } else {
+ FD_SET((*i)->getFd(), &rfds);
+ if ((*i)->outStream().bufferUsage() > 0) FD_SET((*i)->getFd(), &wfds);
+ clients_connected++;
+ }
+ }
+
+ wait_ms = 0;
+
+ rfb::soonestTimeout(&wait_ms, server.checkTimeouts());
+
+ tv.tv_sec = wait_ms / 1000;
+ tv.tv_usec = (wait_ms % 1000) * 1000;
+
+ int n = select(FD_SETSIZE, &rfds, &wfds, 0, wait_ms ? &tv : NULL);
+
+ if (n < 0) {
+ if (errno == EINTR) {
+ ALOGV("Interrupted select() system call");
+ continue;
+ } else {
+ throw rdr::SystemException("select", errno);
+ }
+ }
+
+ // Accept new VNC connections
+ for (std::list<network::TcpListener*>::iterator i = listeners.begin();
+ i != listeners.end(); i++) {
+ if (FD_ISSET((*i)->getFd(), &rfds)) {
+ network::Socket* sock = (*i)->accept();
+ if (sock) {
+ sock->outStream().setBlocking(false);
+ server.addSocket(sock);
+ } else {
+ ALOGW("Client connection rejected");
+ }
+ }
+ }
+
+ server.checkTimeouts();
+
+ // Client list could have been changed.
+ server.getSockets(&sockets);
+
+ // Nothing more to do if there are no client connections.
+ if (sockets.empty()) continue;
+
+ // Process events on existing VNC connections
+ for (i = sockets.begin(); i != sockets.end(); i++) {
+ if (FD_ISSET((*i)->getFd(), &rfds)) server.processSocketReadEvent(*i);
+ if (FD_ISSET((*i)->getFd(), &wfds)) server.processSocketWriteEvent(*i);
+ }
+
+ // Process events from the display
+ uint64_t eventVal;
+ int status = read(eventFd, &eventVal, sizeof(eventVal));
+ if (status > 0 && eventVal > 0) {
+ desktop->processFrames();
+ }
+ }
+
+ } catch (rdr::Exception& e) {
+ ALOGE("%s", e.str());
+ return 1;
+ }
}