blob: a37500cd314773024e3c97017345344e7535bd4e [file] [log] [blame]
Steve Kondik961b4cc2017-06-22 18:10:50 -07001#define LOG_TAG "AndroidDesktop"
2#include <utils/Log.h>
3
4#include <fcntl.h>
5#include <sys/eventfd.h>
6
7#include <binder/IPCThreadState.h>
8#include <binder/IServiceManager.h>
9#include <binder/ProcessState.h>
10
11#include <gui/ISurfaceComposer.h>
12#include <gui/SurfaceComposerClient.h>
13
14#include <ui/DisplayInfo.h>
15
16#include <rfb/PixelFormat.h>
17#include <rfb/Rect.h>
18#include <rfb/ScreenSet.h>
19#include <rfb/VNCServerST.h>
20
21#include "AndroidDesktop.h"
22#include "InputDevice.h"
23
24using namespace vncflinger;
25using namespace android;
26
27const rfb::PixelFormat AndroidDesktop::sRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
28
29AndroidDesktop::AndroidDesktop() {
30 mListener = new FrameListener(this);
31 mInputDevice = new InputDevice();
32
33 mEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
34 if (mEventFd < 0) {
35 ALOGE("Failed to create event notifier");
36 return;
37 }
38}
39
40AndroidDesktop::~AndroidDesktop() {
41 mInputDevice->stop();
42 close(mEventFd);
43}
44
45void AndroidDesktop::start(rfb::VNCServer* vs) {
46 Mutex::Autolock _l(mMutex);
47
48 sp<ProcessState> self = ProcessState::self();
49 self->startThreadPool();
50
51 mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
52 if (updateDisplayProjection() == NO_INIT) {
53 ALOGE("Failed to query display!");
54 return;
55 }
56 mProjectionChanged = false;
57
58 status_t err = createVirtualDisplay();
59 if (err != NO_ERROR) {
60 ALOGE("Failed to create virtual display: err=%d", err);
61 return;
62 }
63
64 mInputDevice->start_async(mWidth, mHeight);
65
66 mServer = (rfb::VNCServerST*)vs;
67
68 updateFBSize(mWidth, mHeight);
69
70 mServer->setPixelBuffer(mPixels.get());
71
72 ALOGV("Desktop is running");
73}
74
75void AndroidDesktop::stop() {
76 Mutex::Autolock _L(mMutex);
77
78 ALOGV("Shutting down");
79
80 mServer->setPixelBuffer(0);
81 destroyVirtualDisplay();
82 mWidth = 0;
83 mHeight = 0;
84}
85
86status_t AndroidDesktop::createVirtualDisplay() {
87 sp<IGraphicBufferConsumer> consumer;
88 BufferQueue::createBufferQueue(&mProducer, &consumer);
89 mCpuConsumer = new CpuConsumer(consumer, 1);
90 mCpuConsumer->setName(String8("vds-to-cpu"));
91 mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
92 mProducer->setMaxDequeuedBufferCount(4);
93 consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBX_8888);
94
95 mCpuConsumer->setFrameAvailableListener(mListener);
96
97 mDpy = SurfaceComposerClient::createDisplay(String8("VNC-VirtualDisplay"), false /*secure*/);
98
99 // aspect ratio
100 float displayAspect = (float) mSourceHeight / (float) mSourceWidth;
101
102 uint32_t outWidth, outHeight;
103 if (mWidth > (uint32_t)(mWidth * displayAspect)) {
104 // limited by narrow width; reduce height
105 outWidth = mWidth;
106 outHeight = (uint32_t)(mWidth * displayAspect);
107 } else {
108 // limited by short height; restrict width
109 outHeight = mHeight;
110 outWidth = (uint32_t)(mHeight / displayAspect);
111 }
112
113 // position the desktop in the viewport while preserving
114 // the source aspect ratio. we do this in case the client
115 // has resized the window and to deal with orientation
116 // changes set up by updateDisplayProjection
117 uint32_t offX, offY;
118 offX = (mWidth - outWidth) / 2;
119 offY = (mHeight - outHeight) / 2;
120 mDisplayRect = Rect(offX, offY, offX + outWidth, offY + outHeight);
121 Rect sourceRect(0, 0, mSourceWidth, mSourceHeight);
122
123 SurfaceComposerClient::openGlobalTransaction();
124 SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
125 SurfaceComposerClient::setDisplayProjection(mDpy, 0, sourceRect, mDisplayRect);
126 SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack
127 SurfaceComposerClient::closeGlobalTransaction();
128
129 mVDSActive = true;
130
131 ALOGV("Virtual display (%lux%lu [viewport=%ux%u] created", mWidth, mHeight,
132 outWidth, outHeight);
133
134 return NO_ERROR;
135}
136
137status_t AndroidDesktop::destroyVirtualDisplay() {
138 if (!mVDSActive) {
139 return NO_INIT;
140 }
141
142 mCpuConsumer.clear();
143 mProducer.clear();
144 SurfaceComposerClient::destroyDisplay(mDpy);
145
146 mVDSActive = false;
147
148 ALOGV("Virtual display destroyed");
149
150 return NO_ERROR;
151}
152
153void AndroidDesktop::processDesktopResize() {
154 if (mProjectionChanged) {
155 destroyVirtualDisplay();
156 createVirtualDisplay();
157 updateFBSize(mWidth, mHeight);
158 mInputDevice->reconfigure(mDisplayRect.getWidth(), mDisplayRect.getHeight());
159 rfb::ScreenSet screens;
160 screens.add_screen(rfb::Screen(0, 0, 0, mWidth, mHeight, 0));
161 mServer->setScreenLayout(screens);
162
163 mProjectionChanged = false;
164 }
165}
166
167void AndroidDesktop::processFrames() {
168 Mutex::Autolock _l(mMutex);
169
170 // do any pending resize
171 processDesktopResize();
172
173 if (!mFrameAvailable) {
174 return;
175 }
176
177 // get a frame from the virtual display
178 CpuConsumer::LockedBuffer imgBuffer;
179 status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
180 if (res != OK) {
181 ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
182 return;
183 }
184
185 mFrameNumber = imgBuffer.frameNumber;
186 ALOGV("processFrame: [%lu] format: %x (%dx%d, stride=%d)", mFrameNumber, imgBuffer.format,
187 imgBuffer.width, imgBuffer.height, imgBuffer.stride);
188
189 // we don't know if there was a stride change until we get
190 // a buffer from the queue. if it changed, we need to resize
191
192 rfb::Rect bufRect(0, 0, imgBuffer.width, imgBuffer.height);
193
194 // performance is extremely bad if the gpu memory is used
195 // directly without copying because it is likely uncached
196 mPixels->imageRect(bufRect, imgBuffer.data, imgBuffer.stride);
197
198 mCpuConsumer->unlockBuffer(imgBuffer);
199
200 // update clients
201 mServer->add_changed(bufRect);
202 mFrameAvailable = false;
203}
204
205// notifies the server loop that we have changes
206void AndroidDesktop::notify() {
207 static uint64_t notify = 1;
208 write(mEventFd, &notify, sizeof(notify));
209}
210
211// called when a client resizes the window
212unsigned int AndroidDesktop::setScreenLayout(int reqWidth, int reqHeight,
213 const rfb::ScreenSet& layout) {
214 Mutex::Autolock _l(mMutex);
215
216 char* dbg = new char[1024];
217 layout.print(dbg, 1024);
218
219 ALOGD("setScreenLayout: cur: %lux%lu new: %dx%d %s", mWidth, mHeight, reqWidth, reqHeight, dbg);
220 delete[] dbg;
221
222 if (reqWidth == (int)mWidth && reqHeight == (int)mHeight) {
223 return rfb::resultInvalid;
224 }
225
226 if (reqWidth > 0 && reqHeight > 0) {
227 mWidth = reqWidth;
228 mHeight = reqHeight;
229
230 if (updateDisplayProjection() == NO_ERROR) {
231 // resize immediately
232 processDesktopResize();
233 notify();
234 return rfb::resultSuccess;
235 }
236 }
237 return rfb::resultInvalid;
238}
239
240// updates the pixelbuffer dimensions
241bool AndroidDesktop::updateFBSize(uint64_t width, uint64_t height) {
242 if (mPixels == nullptr || mPixels->height() != (int)height || mPixels->width() != (int)width) {
243 if (mPixels != nullptr) {
244 ALOGD("updateFBSize: old=[%dx%d] new=[%lux%lu]", mPixels->width(), mPixels->height(),
245 width, height);
246 }
247 if (mPixels != nullptr && (int)width <= mPixels->width() &&
248 (int)height <= mPixels->height()) {
249 mPixels->setSize(width, height);
250 } else {
251 mPixels = new AndroidPixelBuffer(width, height);
252 mServer->setPixelBuffer(mPixels.get());
253 }
254 return true;
255 }
256 return false;
257}
258
259// cpuconsumer frame listener, called from binder thread
260void AndroidDesktop::FrameListener::onFrameAvailable(const BufferItem& item) {
261 Mutex::Autolock _l(mDesktop->mMutex);
262 mDesktop->updateDisplayProjection();
263 mDesktop->mFrameAvailable = true;
264 mDesktop->notify();
265 ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
266}
267
268rfb::Point AndroidDesktop::getFbSize() {
269 return rfb::Point(mPixels->width(), mPixels->height());
270}
271
272void AndroidDesktop::keyEvent(rdr::U32 key, bool down) {
273 mInputDevice->keyEvent(down, key);
274}
275
276void AndroidDesktop::pointerEvent(const rfb::Point& pos, int buttonMask) {
277 if (pos.x < mDisplayRect.left || pos.x > mDisplayRect.right ||
278 pos.y < mDisplayRect.top || pos.y > mDisplayRect.bottom) {
279 // outside viewport
280 return;
281 }
282 uint32_t x = pos.x * ((float)(mDisplayRect.getWidth()) / (float)mWidth);
283 uint32_t y = pos.y * ((float)(mDisplayRect.getHeight()) / (float)mHeight);
284
285 ALOGD("pointer xlate x1=%d y1=%d x2=%d y2=%d", pos.x, pos.y, x, y);
286
287 mServer->setCursorPos(rfb::Point(x, y));
288 mInputDevice->pointerEvent(buttonMask, x, y);
289}
290
291// figure out the dimensions of the display. deal with orientation
292// changes, client-side window resize, server-side scaling, and
293// maintaining aspect ratio.
294status_t AndroidDesktop::updateDisplayProjection() {
295 DisplayInfo info;
296 status_t err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &info);
297 if (err != NO_ERROR) {
298 ALOGE("Failed to get display characteristics\n");
299 return err;
300 }
301
302 bool deviceRotated =
303 info.orientation != DISPLAY_ORIENTATION_0 && info.orientation != DISPLAY_ORIENTATION_180;
304
305 // if orientation changed, swap width/height
306 uint32_t sourceWidth, sourceHeight;
307 if (!deviceRotated) {
308 sourceWidth = info.w;
309 sourceHeight = info.h;
310 } else {
311 sourceHeight = info.w;
312 sourceWidth = info.h;
313 }
314
315 if (mWidth == 0 && mHeight == 0) {
316 mWidth = sourceWidth;
317 mHeight = sourceHeight;
318 }
319
320 if (deviceRotated != mRotated) {
321 std::swap(mWidth, mHeight);
322 mRotated = deviceRotated;
323 }
324
325 // if nothing changed, we're done
326 if (mSourceWidth == sourceWidth && mSourceHeight == sourceHeight &&
327 (int)mWidth == mPixels->width() && (int)mHeight == mPixels->height()) {
328 return NO_ERROR;
329 }
330
331 // update all the values and flag for an update
332 mSourceWidth = sourceWidth;
333 mSourceHeight = sourceHeight;
334 mProjectionChanged = true;
335
336 ALOGV("Dimensions: %lux%lu [out: %lux%lu] rotated=%d", mSourceWidth, mSourceHeight, mWidth,
337 mHeight, mRotated);
338
339 return NO_ERROR;
340}