VirtualBox

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

Last change on this file since 36670 was 36662, checked in by vboxsync, 14 years ago

Additions/x11/VBoxClient: remove auto-pointer-like stuff from seamless code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.7 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#ifdef TESTCASE
33#undef DefaultRootWindow
34#define DefaultRootWindow XDefaultRootWindow
35#endif
36
37/*****************************************************************************
38* Static functions *
39*****************************************************************************/
40
41static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType,
42 const char *aPropName, unsigned long *nItems)
43{
44 LogRelFlowFunc(("\n"));
45 Atom propNameAtom = XInternAtom (aDpy, aPropName,
46 True /* only_if_exists */);
47 if (propNameAtom == None)
48 {
49 return NULL;
50 }
51
52 Atom actTypeAtom = None;
53 int actFmt = 0;
54 unsigned long nBytesAfter = 0;
55 unsigned char *propVal = 0;
56 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
57 0, LONG_MAX, False /* delete */,
58 aPropType, &actTypeAtom, &actFmt,
59 nItems, &nBytesAfter, &propVal);
60 if (rc != Success)
61 return NULL;
62
63 LogRelFlowFunc(("returning\n"));
64 return propVal;
65}
66
67/**
68 * Initialise the guest and ensure that it is capable of handling seamless mode
69 *
70 * @returns true if it can handle seamless, false otherwise
71 */
72int VBoxGuestSeamlessX11::init(VBoxGuestSeamlessObserver *pObserver)
73{
74 int rc = VINF_SUCCESS;
75
76 LogRelFlowFunc(("\n"));
77 if (0 != mObserver) /* Assertion */
78 {
79 LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n"));
80 return VERR_INTERNAL_ERROR;
81 }
82 if (!(mDisplay = XOpenDisplay(NULL)))
83 {
84 LogRel(("VBoxClient: seamless guest object failed to acquire a connection to the display.\n"));
85 return VERR_ACCESS_DENIED;
86 }
87 mObserver = pObserver;
88 LogRelFlowFunc(("returning %Rrc\n", rc));
89 return rc;
90}
91
92/**
93 * Read information about currently visible windows in the guest and subscribe to X11
94 * events about changes to this information.
95 *
96 * @note This class does not contain its own event thread, so an external thread must
97 * call nextEvent() for as long as events are wished.
98 * @todo This function should switch the guest to fullscreen mode.
99 */
100int VBoxGuestSeamlessX11::start(void)
101{
102 int rc = VINF_SUCCESS;
103 /** Dummy values for XShapeQueryExtension */
104 int error, event;
105
106 LogRelFlowFunc(("\n"));
107 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
108 mEnabled = true;
109 monitorClientList();
110 rebuildWindowTree();
111 LogRelFlowFunc(("returning %Rrc\n", rc));
112 return rc;
113}
114
115/** Stop reporting seamless events to the host. Free information about guest windows
116 and stop requesting updates. */
117void VBoxGuestSeamlessX11::stop(void)
118{
119 LogRelFlowFunc(("\n"));
120 mEnabled = false;
121 unmonitorClientList();
122 freeWindowTree();
123 LogRelFlowFunc(("returning\n"));
124}
125
126void VBoxGuestSeamlessX11::monitorClientList(void)
127{
128 LogRelFlowFunc(("called\n"));
129 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), SubstructureNotifyMask);
130}
131
132void VBoxGuestSeamlessX11::unmonitorClientList(void)
133{
134 LogRelFlowFunc(("called\n"));
135 XSelectInput(mDisplay, DefaultRootWindow(mDisplay), 0);
136}
137
138/**
139 * Recreate the table of toplevel windows of clients on the default root window of the
140 * X server.
141 */
142void VBoxGuestSeamlessX11::rebuildWindowTree(void)
143{
144 LogRelFlowFunc(("called\n"));
145 freeWindowTree();
146 addClients(DefaultRootWindow(mDisplay));
147 mChanged = true;
148}
149
150
151/**
152 * Look at the list of children of a virtual root window and add them to the list of clients
153 * if they belong to a client which is not a virtual root.
154 *
155 * @param hRoot the virtual root window to be examined
156 */
157void VBoxGuestSeamlessX11::addClients(const Window hRoot)
158{
159 /** Unused out parameters of XQueryTree */
160 Window hRealRoot, hParent;
161 /** The list of children of the root supplied, raw pointer */
162 Window *phChildrenRaw = NULL;
163 /** The list of children of the root supplied, auto-pointer */
164 Window *phChildren;
165 /** The number of children of the root supplied */
166 unsigned cChildren;
167
168 LogRelFlowFunc(("\n"));
169 if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
170 return;
171 phChildren = phChildrenRaw;
172 for (unsigned i = 0; i < cChildren; ++i)
173 addClientWindow(phChildren[i]);
174 XFree(phChildrenRaw);
175 LogRelFlowFunc(("returning\n"));
176}
177
178
179void VBoxGuestSeamlessX11::addClientWindow(const Window hWin)
180{
181 LogRelFlowFunc(("\n"));
182 XWindowAttributes winAttrib;
183 bool fAddWin = true;
184 char *pszWinName = NULL;
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;
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 %s\n", rc ? "true" : "false"));
262 return rc;
263}
264
265
266/**
267 * Free all information in the tree of visible windows
268 */
269void VBoxGuestSeamlessX11::freeWindowTree(void)
270{
271 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
272 LogRelFlowFunc(("\n"));
273 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
274 mGuestWindows.removeWindow(it++))
275 {
276 XShapeSelectInput(mDisplay, it->first, 0);
277 }
278 LogRelFlowFunc(("returning\n"));
279}
280
281
282/**
283 * Waits for a position or shape-related event from guest windows
284 *
285 * @note Called from the guest event thread.
286 */
287void VBoxGuestSeamlessX11::nextEvent(void)
288{
289 XEvent event;
290
291 LogRelFlowFunc(("\n"));
292 /* Start by sending information about the current window setup to the host. We do this
293 here because we want to send all such information from a single thread. */
294 if (mChanged)
295 mObserver->notify();
296 mChanged = false;
297 XNextEvent(mDisplay, &event);
298 switch (event.type)
299 {
300 case ConfigureNotify:
301 doConfigureEvent(event.xconfigure.window);
302 break;
303 case MapNotify:
304 doMapEvent(event.xmap.window);
305 break;
306 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
307 /* the window member in xany is in the same place as in the shape event */
308 doShapeEvent(event.xany.window);
309 break;
310 case UnmapNotify:
311 doUnmapEvent(event.xunmap.window);
312 break;
313 default:
314 break;
315 }
316 LogRelFlowFunc(("returning\n"));
317}
318
319/**
320 * Handle a configuration event in the seamless event thread by setting the new position.
321 *
322 * @param event the X11 event structure
323 */
324void VBoxGuestSeamlessX11::doConfigureEvent(Window hWin)
325{
326 LogRelFlowFunc(("\n"));
327 VBoxGuestWindowList::iterator iter;
328
329 iter = mGuestWindows.find(hWin);
330 if (iter != mGuestWindows.end())
331 {
332 XWindowAttributes winAttrib;
333
334 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
335 return;
336 iter->second->mX = winAttrib.x;
337 iter->second->mY = winAttrib.y;
338 iter->second->mWidth = winAttrib.width;
339 iter->second->mHeight = winAttrib.height;
340 if (iter->second->mhasShape)
341 {
342 XRectangle *pRects;
343 int cRects = 0, iOrdering;
344
345 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding,
346 &cRects, &iOrdering);
347 if (!pRects)
348 cRects = 0;
349 if (iter->second->mpRects)
350 XFree(iter->second->mpRects);
351 iter->second->mcRects = cRects;
352 iter->second->mpRects = pRects;
353 }
354 mChanged = true;
355 }
356 LogRelFlowFunc(("returning\n"));
357}
358
359/**
360 * Handle a map event in the seamless event thread.
361 *
362 * @param event the X11 event structure
363 */
364void VBoxGuestSeamlessX11::doMapEvent(Window hWin)
365{
366 LogRelFlowFunc(("\n"));
367 VBoxGuestWindowList::iterator iter;
368
369 iter = mGuestWindows.find(hWin);
370 if (mGuestWindows.end() == iter)
371 {
372 addClientWindow(hWin);
373 mChanged = true;
374 }
375 LogRelFlowFunc(("returning\n"));
376}
377
378
379/**
380 * Handle a window shape change event in the seamless event thread.
381 *
382 * @param event the X11 event structure
383 */
384void VBoxGuestSeamlessX11::doShapeEvent(Window hWin)
385{
386 LogRelFlowFunc(("\n"));
387 VBoxGuestWindowList::iterator iter;
388
389 iter = mGuestWindows.find(hWin);
390 if (iter != mGuestWindows.end())
391 {
392 XRectangle *pRects;
393 int cRects = 0, iOrdering;
394
395 pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
396 &iOrdering);
397 if (!pRects)
398 cRects = 0;
399 iter->second->mhasShape = true;
400 if (iter->second->mpRects)
401 XFree(iter->second->mpRects);
402 iter->second->mcRects = cRects;
403 iter->second->mpRects = pRects;
404 mChanged = true;
405 }
406 LogRelFlowFunc(("returning\n"));
407}
408
409/**
410 * Handle an unmap event in the seamless event thread.
411 *
412 * @param event the X11 event structure
413 */
414void VBoxGuestSeamlessX11::doUnmapEvent(Window hWin)
415{
416 LogRelFlowFunc(("\n"));
417 VBoxGuestWindowList::iterator iter;
418
419 iter = mGuestWindows.find(hWin);
420 if (mGuestWindows.end() != iter)
421 {
422 mGuestWindows.removeWindow(iter);
423 mChanged = true;
424 }
425 LogRelFlowFunc(("returning\n"));
426}
427
428/**
429 * Sends an updated list of visible rectangles to the host
430 */
431std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
432{
433 LogRelFlowFunc(("\n"));
434 unsigned cRects = 0;
435 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
436
437 if (0 != mcRects)
438 {
439 apRects.get()->reserve(mcRects * 2);
440 }
441 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
442 it != mGuestWindows.end(); ++it)
443 {
444 if (it->second->mhasShape)
445 {
446 for (int i = 0; i < it->second->mcRects; ++i)
447 {
448 RTRECT rect;
449 rect.xLeft = it->second->mX
450 + it->second->mpRects[i].x;
451 rect.yBottom = it->second->mY
452 + it->second->mpRects[i].y
453 + it->second->mpRects[i].height;
454 rect.xRight = it->second->mX
455 + it->second->mpRects[i].x
456 + it->second->mpRects[i].width;
457 rect.yTop = it->second->mY
458 + it->second->mpRects[i].y;
459 apRects.get()->push_back(rect);
460 }
461 cRects += it->second->mcRects;
462 }
463 else
464 {
465 RTRECT rect;
466 rect.xLeft = it->second->mX;
467 rect.yBottom = it->second->mY
468 + it->second->mHeight;
469 rect.xRight = it->second->mX
470 + it->second->mWidth;
471 rect.yTop = it->second->mY;
472 apRects.get()->push_back(rect);
473 ++cRects;
474 }
475 }
476 mcRects = cRects;
477 LogRelFlowFunc(("returning\n"));
478 return apRects;
479}
480
481/**
482 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
483 *
484 * @note This function should only be called from the host event thread.
485 */
486bool VBoxGuestSeamlessX11::interruptEvent(void)
487{
488 bool rc = false;
489
490 LogRelFlowFunc(("\n"));
491 /* Message contents set to zero. */
492 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
493
494 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay), false, PropertyChangeMask,
495 reinterpret_cast<XEvent *>(&clientMessage)))
496 {
497 XFlush(mDisplay);
498 rc = true;
499 }
500 LogRelFlowFunc(("returning %s\n", rc ? "true" : "false"));
501 return rc;
502}
Note: See TracBrowser for help on using the repository browser.

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