VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp@ 39548

Last change on this file since 39548 was 37721, checked in by vboxsync, 13 years ago

Runtime: add C++-like vector implementation in C, burn fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
1/** @file
2 * X11 Seamless mode.
3 */
4
5/*
6 * Copyright (C) 2008 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17/*****************************************************************************
18* Header files *
19*****************************************************************************/
20
21#include <iprt/err.h>
22#include <iprt/assert.h>
23#include <iprt/vector.h>
24#include <VBox/log.h>
25
26#include "seamless-guest.h"
27
28#include <X11/Xatom.h>
29#include <X11/Xmu/WinUtil.h>
30
31#include <limits.h>
32
33#ifdef TESTCASE
34#undef DefaultRootWindow
35#define DefaultRootWindow XDefaultRootWindow
36#endif
37
38/*****************************************************************************
39* Static functions *
40*****************************************************************************/
41
42static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType,
43 const char *aPropName, unsigned long *nItems)
44{
45 LogRelFlowFunc(("\n"));
46 Atom propNameAtom = XInternAtom (aDpy, aPropName,
47 True /* only_if_exists */);
48 if (propNameAtom == None)
49 {
50 return NULL;
51 }
52
53 Atom actTypeAtom = None;
54 int actFmt = 0;
55 unsigned long nBytesAfter = 0;
56 unsigned char *propVal = 0;
57 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
58 0, LONG_MAX, False /* delete */,
59 aPropType, &actTypeAtom, &actFmt,
60 nItems, &nBytesAfter, &propVal);
61 if (rc != Success)
62 return NULL;
63
64 LogRelFlowFunc(("returning\n"));
65 return propVal;
66}
67
68/**
69 * Initialise the guest and ensure that it is capable of handling seamless mode
70 *
71 * @returns true if it can handle seamless, false otherwise
72 */
73int VBoxGuestSeamlessX11::init(VBoxGuestSeamlessObserver *pObserver)
74{
75 int rc = VINF_SUCCESS;
76
77 LogRelFlowFunc(("\n"));
78 if (0 != mObserver) /* Assertion */
79 {
80 LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n"));
81 return VERR_INTERNAL_ERROR;
82 }
83 if (!(mDisplay = XOpenDisplay(NULL)))
84 {
85 LogRel(("VBoxClient: seamless guest object failed to acquire a connection to the display.\n"));
86 return VERR_ACCESS_DENIED;
87 }
88 mObserver = pObserver;
89 LogRelFlowFunc(("returning %Rrc\n", rc));
90 return rc;
91}
92
93/**
94 * Read information about currently visible windows in the guest and subscribe to X11
95 * events about changes to this information.
96 *
97 * @note This class does not contain its own event thread, so an external thread must
98 * call nextEvent() for as long as events are wished.
99 * @todo This function should switch the guest to fullscreen mode.
100 */
101int VBoxGuestSeamlessX11::start(void)
102{
103 int rc = VINF_SUCCESS;
104 /** Dummy values for XShapeQueryExtension */
105 int error, event;
106
107 LogRelFlowFunc(("\n"));
108 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
109 mEnabled = true;
110 monitorClientList();
111 rebuildWindowTree();
112 LogRelFlowFunc(("returning %Rrc\n", rc));
113 return rc;
114}
115
116/** Stop reporting seamless events to the host. Free information about guest windows
117 and stop requesting updates. */
118void VBoxGuestSeamlessX11::stop(void)
119{
120 LogRelFlowFunc(("\n"));
121 mEnabled = false;
122 unmonitorClientList();
123 freeWindowTree();
124 LogRelFlowFunc(("returning\n"));
125}
126
127void VBoxGuestSeamlessX11::monitorClientList(void)
128{
129 LogRelFlowFunc(("called\n"));
130 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), SubstructureNotifyMask);
131}
132
133void VBoxGuestSeamlessX11::unmonitorClientList(void)
134{
135 LogRelFlowFunc(("called\n"));
136 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), 0);
137}
138
139/**
140 * Recreate the table of toplevel windows of clients on the default root window of the
141 * X server.
142 */
143void VBoxGuestSeamlessX11::rebuildWindowTree(void)
144{
145 LogRelFlowFunc(("called\n"));
146 freeWindowTree();
147 addClients(DefaultRootWindow(mDisplay));
148 mChanged = true;
149}
150
151
152/**
153 * Look at the list of children of a virtual root window and add them to the list of clients
154 * if they belong to a client which is not a virtual root.
155 *
156 * @param hRoot the virtual root window to be examined
157 */
158void VBoxGuestSeamlessX11::addClients(const Window hRoot)
159{
160 /** Unused out parameters of XQueryTree */
161 Window hRealRoot, hParent;
162 /** The list of children of the root supplied, raw pointer */
163 Window *phChildrenRaw = NULL;
164 /** The list of children of the root supplied, auto-pointer */
165 Window *phChildren;
166 /** The number of children of the root supplied */
167 unsigned cChildren;
168
169 LogRelFlowFunc(("\n"));
170 if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
171 return;
172 phChildren = phChildrenRaw;
173 for (unsigned i = 0; i < cChildren; ++i)
174 addClientWindow(phChildren[i]);
175 XFree(phChildrenRaw);
176 LogRelFlowFunc(("returning\n"));
177}
178
179
180void VBoxGuestSeamlessX11::addClientWindow(const Window hWin)
181{
182 LogRelFlowFunc(("\n"));
183 XWindowAttributes winAttrib;
184 bool fAddWin = true;
185 Window hClient = XmuClientWindow(mDisplay, hWin);
186
187 if (isVirtualRoot(hClient))
188 fAddWin = false;
189 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
190 {
191 LogRelFunc(("VBoxClient: Failed to get the window attributes for window %d\n", hWin));
192 fAddWin = false;
193 }
194 if (fAddWin && (winAttrib.map_state == IsUnmapped))
195 fAddWin = false;
196 XSizeHints dummyHints;
197 long dummyLong;
198 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
199 &dummyLong)))
200 {
201 LogRelFlowFunc(("window %lu, client window %lu has no size hints\n",
202 hWin, hClient));
203 fAddWin = false;
204 }
205 if (fAddWin)
206 {
207 XRectangle *pRects = NULL;
208 int cRects = 0, iOrdering;
209 bool hasShape = false;
210
211 LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
212 hClient));
213 if (mSupportsShape)
214 {
215 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
216 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
217 if (!pRects)
218 cRects = 0;
219 else
220 {
221 if ( (cRects > 1)
222 || (pRects[0].x != 0)
223 || (pRects[0].y != 0)
224 || (pRects[0].width != winAttrib.width)
225 || (pRects[0].height != winAttrib.height)
226 )
227 hasShape = true;
228 }
229 }
230 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
231 winAttrib.width, winAttrib.height, cRects,
232 pRects);
233 }
234 LogRelFlowFunc(("returning\n"));
235}
236
237
238/**
239 * Checks whether a window is a virtual root.
240 * @returns true if it is, false otherwise
241 * @param hWin the window to be examined
242 */
243bool VBoxGuestSeamlessX11::isVirtualRoot(Window hWin)
244{
245 unsigned char *windowTypeRaw = NULL;
246 Atom *windowType;
247 unsigned long ulCount;
248 bool rc = false;
249
250 LogRelFlowFunc(("\n"));
251 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
252 if (windowTypeRaw != NULL)
253 {
254 windowType = (Atom *)(windowTypeRaw);
255 if ( (ulCount != 0)
256 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
257 rc = true;
258 }
259 if (windowTypeRaw)
260 XFree(windowTypeRaw);
261 LogRelFlowFunc(("returning %RTbool\n", rc));
262 return rc;
263}
264
265DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam)
266{
267 Display *pDisplay = (Display *)pvParam;
268
269 XShapeSelectInput(pDisplay, pInfo->Core.Key, 0);
270 delete pInfo;
271 return VINF_SUCCESS;
272}
273
274/**
275 * Free all information in the tree of visible windows
276 */
277void VBoxGuestSeamlessX11::freeWindowTree(void)
278{
279 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
280 LogRelFlowFunc(("\n"));
281 mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
282 LogRelFlowFunc(("returning\n"));
283}
284
285
286/**
287 * Waits for a position or shape-related event from guest windows
288 *
289 * @note Called from the guest event thread.
290 */
291void VBoxGuestSeamlessX11::nextEvent(void)
292{
293 XEvent event;
294
295 LogRelFlowFunc(("\n"));
296 /* Start by sending information about the current window setup to the host. We do this
297 here because we want to send all such information from a single thread. */
298 if (mChanged)
299 {
300 updateRects();
301 mObserver->notify();
302 }
303 mChanged = false;
304 XNextEvent(mDisplay, &event);
305 switch (event.type)
306 {
307 case ConfigureNotify:
308 {
309 XConfigureEvent *pConf = &event.xconfigure;
310 LogRelFlowFunc(("configure event, window=%lu, x=%i, y=%i, w=%i, h=%i, send_event=%RTbool\n",
311 (unsigned long) pConf->window, (int) pConf->x,
312 (int) pConf->y, (int) pConf->width,
313 (int) pConf->height, pConf->send_event));
314 }
315 doConfigureEvent(event.xconfigure.window);
316 break;
317 case MapNotify:
318 LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n",
319 (unsigned long) event.xmap.window,
320 event.xmap.send_event));
321 doMapEvent(event.xmap.window);
322 break;
323 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
324 LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n",
325 (unsigned long) event.xany.window,
326 event.xany.send_event));
327 /* the window member in xany is in the same place as in the shape event */
328 doShapeEvent(event.xany.window);
329 break;
330 case UnmapNotify:
331 LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n",
332 (unsigned long) event.xunmap.window,
333 event.xunmap.send_event));
334 doUnmapEvent(event.xunmap.window);
335 break;
336 default:
337 break;
338 }
339 LogRelFlowFunc(("returning\n"));
340}
341
342/**
343 * Handle a configuration event in the seamless event thread by setting the new position.
344 *
345 * @param event the X11 event structure
346 */
347void VBoxGuestSeamlessX11::doConfigureEvent(Window hWin)
348{
349 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
350 if (pInfo)
351 {
352 XWindowAttributes winAttrib;
353
354 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
355 return;
356 pInfo->mX = winAttrib.x;
357 pInfo->mY = winAttrib.y;
358 pInfo->mWidth = winAttrib.width;
359 pInfo->mHeight = winAttrib.height;
360 if (pInfo->mhasShape)
361 {
362 XRectangle *pRects;
363 int cRects = 0, iOrdering;
364
365 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding,
366 &cRects, &iOrdering);
367 if (!pRects)
368 cRects = 0;
369 if (pInfo->mpRects)
370 XFree(pInfo->mpRects);
371 pInfo->mcRects = cRects;
372 pInfo->mpRects = pRects;
373 }
374 mChanged = true;
375 }
376}
377
378/**
379 * Handle a map event in the seamless event thread.
380 *
381 * @param event the X11 event structure
382 */
383void VBoxGuestSeamlessX11::doMapEvent(Window hWin)
384{
385 LogRelFlowFunc(("\n"));
386 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
387 if (!pInfo)
388 {
389 addClientWindow(hWin);
390 mChanged = true;
391 }
392 LogRelFlowFunc(("returning\n"));
393}
394
395
396/**
397 * Handle a window shape change event in the seamless event thread.
398 *
399 * @param event the X11 event structure
400 */
401void VBoxGuestSeamlessX11::doShapeEvent(Window hWin)
402{
403 LogRelFlowFunc(("\n"));
404 VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
405 if (pInfo)
406 {
407 XRectangle *pRects;
408 int cRects = 0, iOrdering;
409
410 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
411 &iOrdering);
412 if (!pRects)
413 cRects = 0;
414 pInfo->mhasShape = true;
415 if (pInfo->mpRects)
416 XFree(pInfo->mpRects);
417 pInfo->mcRects = cRects;
418 pInfo->mpRects = pRects;
419 mChanged = true;
420 }
421 LogRelFlowFunc(("returning\n"));
422}
423
424/**
425 * Handle an unmap event in the seamless event thread.
426 *
427 * @param event the X11 event structure
428 */
429void VBoxGuestSeamlessX11::doUnmapEvent(Window hWin)
430{
431 LogRelFlowFunc(("\n"));
432 VBoxGuestWinInfo *pInfo = mGuestWindows.removeWindow(hWin);
433 if (pInfo)
434 {
435 VBoxGuestWinFree(pInfo, mDisplay);
436 mChanged = true;
437 }
438 LogRelFlowFunc(("returning\n"));
439}
440
441/**
442 * Gets the list of visible rectangles
443 */
444RTRECT *VBoxGuestSeamlessX11::getRects(void)
445{
446 return mpRects;
447}
448
449/**
450 * Gets the number of rectangles in the visible rectangle list
451 */
452size_t VBoxGuestSeamlessX11::getRectCount(void)
453{
454 return mcRects;
455}
456
457RTVEC_DECL(RectList, RTRECT)
458
459DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo,
460 struct RectList *pRects)
461{
462 if (pInfo->mhasShape)
463 {
464 for (int i = 0; i < pInfo->mcRects; ++i)
465 {
466 RTRECT *pRect;
467
468 pRect = RectListPushBack(pRects);
469 if (!pRect)
470 return VERR_NO_MEMORY;
471 pRect->xLeft = pInfo->mX
472 + pInfo->mpRects[i].x;
473 pRect->yBottom = pInfo->mY
474 + pInfo->mpRects[i].y
475 + pInfo->mpRects[i].height;
476 pRect->xRight = pInfo->mX
477 + pInfo->mpRects[i].x
478 + pInfo->mpRects[i].width;
479 pRect->yTop = pInfo->mY
480 + pInfo->mpRects[i].y;
481 }
482 }
483 else
484 {
485 RTRECT *pRect;
486
487 pRect = RectListPushBack(pRects);
488 if (!pRect)
489 return VERR_NO_MEMORY;
490 pRect->xLeft = pInfo->mX;
491 pRect->yBottom = pInfo->mY
492 + pInfo->mHeight;
493 pRect->xRight = pInfo->mX
494 + pInfo->mWidth;
495 pRect->yTop = pInfo->mY;
496 }
497 return VINF_SUCCESS;
498}
499
500/**
501 * Updates the list of seamless rectangles
502 */
503int VBoxGuestSeamlessX11::updateRects(void)
504{
505 LogRelFlowFunc(("\n"));
506 unsigned cRects = 0;
507 struct RectList rects = RTVEC_INITIALIZER;
508
509 if (0 != mcRects)
510 {
511 int rc = RectListReserve(&rects, mcRects * 2);
512 if (RT_FAILURE(rc))
513 return rc;
514 }
515 mGuestWindows.doWithAll((PVBOXGUESTWINCALLBACK)getRectsCallback,
516 &rects);
517 if (mpRects)
518 RTMemFree(mpRects);
519 mcRects = RectListSize(&rects);
520 mpRects = RectListDetach(&rects);
521 LogRelFlowFunc(("returning\n"));
522 return VINF_SUCCESS;
523}
524
525/**
526 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
527 *
528 * @note This function should only be called from the host event thread.
529 */
530bool VBoxGuestSeamlessX11::interruptEvent(void)
531{
532 bool rc = false;
533
534 LogRelFlowFunc(("\n"));
535 /* Message contents set to zero. */
536 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
537
538 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay), false, PropertyChangeMask,
539 reinterpret_cast<XEvent *>(&clientMessage)))
540 {
541 XFlush(mDisplay);
542 rc = true;
543 }
544 LogRelFlowFunc(("returning %RTbool\n", rc));
545 return rc;
546}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette