VirtualBox

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

Last change on this file since 36810 was 36806, checked in by vboxsync, 14 years ago

Additions/x11/seamless: no more std::autoptr or std::vector, use the new STL-vector-in-C instead

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