VirtualBox

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

Last change on this file since 25988 was 21940, checked in by vboxsync, 16 years ago

Additions/X11/VBoxClient: attempt to fix seamless Additions crashes

  • 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 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21/*****************************************************************************
22* Header files *
23*****************************************************************************/
24
25#include <iprt/err.h>
26#include <iprt/assert.h>
27#include <VBox/log.h>
28
29#include "seamless-guest.h"
30
31#include <X11/Xatom.h>
32#include <X11/Xmu/WinUtil.h>
33
34#include <limits.h>
35
36#ifdef TESTCASE
37#undef DefaultRootWindow
38#define DefaultRootWindow XDefaultRootWindow
39#endif
40
41/*****************************************************************************
42* Static functions *
43*****************************************************************************/
44
45static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType,
46 const char *aPropName, unsigned long *nItems)
47{
48 LogFlowFunc(("\n"));
49 Atom propNameAtom = XInternAtom (aDpy, aPropName,
50 True /* only_if_exists */);
51 if (propNameAtom == None)
52 {
53 return NULL;
54 }
55
56 Atom actTypeAtom = None;
57 int actFmt = 0;
58 unsigned long nBytesAfter = 0;
59 unsigned char *propVal = 0;
60 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
61 0, LONG_MAX, False /* delete */,
62 aPropType, &actTypeAtom, &actFmt,
63 nItems, &nBytesAfter, &propVal);
64 if (rc != Success)
65 return NULL;
66
67 LogFlowFunc(("returning\n"));
68 return propVal;
69}
70
71/**
72 * Initialise the guest and ensure that it is capable of handling seamless mode
73 *
74 * @returns true if it can handle seamless, false otherwise
75 */
76int VBoxGuestSeamlessX11::init(VBoxGuestSeamlessObserver *pObserver)
77{
78 int rc = VINF_SUCCESS;
79
80 LogFlowThisFunc(("\n"));
81 if (0 != mObserver) /* Assertion */
82 {
83 LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n"));
84 return VERR_INTERNAL_ERROR;
85 }
86 if (!mDisplay.init())
87 {
88 LogRel(("VBoxClient: seamless guest object failed to acquire a connection to the display.\n"));
89 return VERR_ACCESS_DENIED;
90 }
91 mObserver = pObserver;
92 LogFlowThisFunc(("returning %Rrc\n", rc));
93 return rc;
94}
95
96/**
97 * Read information about currently visible windows in the guest and subscribe to X11
98 * events about changes to this information.
99 *
100 * @note This class does not contain its own event thread, so an external thread must
101 * call nextEvent() for as long as events are wished.
102 * @todo This function should switch the guest to fullscreen mode.
103 */
104int VBoxGuestSeamlessX11::start(void)
105{
106 int rc = VINF_SUCCESS;
107 /** Dummy values for XShapeQueryExtension */
108 int error, event;
109
110 LogFlowThisFunc(("\n"));
111 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
112 mEnabled = true;
113 monitorClientList();
114 rebuildWindowTree();
115 LogFlowThisFunc(("returning %Rrc\n", rc));
116 return rc;
117}
118
119/** Stop reporting seamless events to the host. Free information about guest windows
120 and stop requesting updates. */
121void VBoxGuestSeamlessX11::stop(void)
122{
123 LogFlowThisFunc(("\n"));
124 mEnabled = false;
125 unmonitorClientList();
126 freeWindowTree();
127 LogFlowThisFunc(("returning\n"));
128}
129
130void VBoxGuestSeamlessX11::monitorClientList(void)
131{
132 LogFlowThisFunc(("called\n"));
133 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), SubstructureNotifyMask);
134}
135
136void VBoxGuestSeamlessX11::unmonitorClientList(void)
137{
138 LogFlowThisFunc(("called\n"));
139 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), 0);
140}
141
142/**
143 * Recreate the table of toplevel windows of clients on the default root window of the
144 * X server.
145 */
146void VBoxGuestSeamlessX11::rebuildWindowTree(void)
147{
148 LogFlowThisFunc(("called\n"));
149 freeWindowTree();
150 addClients(DefaultRootWindow(mDisplay.get()));
151 mChanged = true;
152}
153
154
155/**
156 * Look at the list of children of a virtual root window and add them to the list of clients
157 * if they belong to a client which is not a virtual root.
158 *
159 * @param hRoot the virtual root window to be examined
160 */
161void VBoxGuestSeamlessX11::addClients(const Window hRoot)
162{
163 /** Unused out parameters of XQueryTree */
164 Window hRealRoot, hParent;
165 /** The list of children of the root supplied, raw pointer */
166 Window *phChildrenRaw;
167 /** The list of children of the root supplied, auto-pointer */
168 VBoxGuestX11Pointer<Window> phChildren;
169 /** The number of children of the root supplied */
170 unsigned cChildren;
171
172 LogFlowThisFunc(("\n"));
173 if (!XQueryTree(mDisplay.get(), hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
174 return;
175 phChildren = phChildrenRaw;
176 for (unsigned i = 0; i < cChildren; ++i)
177 addClientWindow(phChildren.get()[i]);
178 LogFlowThisFunc(("returning\n"));
179}
180
181
182void VBoxGuestSeamlessX11::addClientWindow(const Window hWin)
183{
184 LogFlowThisFunc(("\n"));
185 XWindowAttributes winAttrib;
186 bool fAddWin = true;
187 char *pszWinName = NULL;
188 Window hClient = XmuClientWindow(mDisplay, hWin);
189
190 if (isVirtualRoot(hClient))
191 fAddWin = false;
192 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
193 {
194 LogRelFunc(("VBoxClient: Failed to get the window attributes for window %d\n", hWin));
195 fAddWin = false;
196 }
197 if (fAddWin && (winAttrib.map_state == IsUnmapped))
198 fAddWin = false;
199 XSizeHints dummyHints;
200 long dummyLong;
201 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
202 &dummyLong)))
203 {
204 LogFlowFunc(("window %lu, client window %lu has no size hints\n",
205 hWin, hClient));
206 fAddWin = false;
207 }
208 if (fAddWin)
209 {
210 VBoxGuestX11Pointer<XRectangle> rects;
211 int cRects = 0, iOrdering;
212 bool hasShape = false;
213
214 LogFlowFunc(("adding window %lu, client window %lu\n", hWin,
215 hClient));
216 if (mSupportsShape)
217 {
218 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
219 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
220 if (0 == rects.get())
221 cRects = 0;
222 else
223 {
224 if ( (cRects > 1)
225 || (rects.get()[0].x != 0)
226 || (rects.get()[0].y != 0)
227 || (rects.get()[0].width != winAttrib.width)
228 || (rects.get()[0].height != winAttrib.height)
229 )
230 hasShape = true;
231 }
232 }
233 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
234 winAttrib.width, winAttrib.height, cRects, rects);
235 }
236 LogFlowThisFunc(("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;
248 VBoxGuestX11Pointer<Atom> windowType;
249 unsigned long ulCount;
250 bool rc = false;
251
252 LogFlowThisFunc(("\n"));
253 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
254 if (windowTypeRaw != NULL)
255 {
256 windowType = reinterpret_cast<Atom *>(windowTypeRaw);
257 if ( (ulCount != 0)
258 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
259 rc = true;
260 }
261 LogFlowThisFunc(("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 LogFlowThisFunc(("\n"));
273 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
274 mGuestWindows.removeWindow(it++))
275 {
276 XShapeSelectInput(mDisplay, it->first, 0);
277 }
278 LogFlowThisFunc(("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 LogFlowThisFunc(("\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 LogFlowThisFunc(("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 LogFlowThisFunc(("\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 VBoxGuestX11Pointer<XRectangle> rects;
343 int cRects = 0, iOrdering;
344
345 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding,
346 &cRects, &iOrdering);
347 if (rects.get() == NULL)
348 cRects = 0;
349 iter->second->mcRects = cRects;
350 iter->second->mapRects = rects;
351 }
352 mChanged = true;
353 }
354 LogFlowThisFunc(("returning\n"));
355}
356
357/**
358 * Handle a map event in the seamless event thread.
359 *
360 * @param event the X11 event structure
361 */
362void VBoxGuestSeamlessX11::doMapEvent(Window hWin)
363{
364 LogFlowThisFunc(("\n"));
365 VBoxGuestWindowList::iterator iter;
366
367 iter = mGuestWindows.find(hWin);
368 if (mGuestWindows.end() == iter)
369 {
370 addClientWindow(hWin);
371 mChanged = true;
372 }
373 LogFlowThisFunc(("returning\n"));
374}
375
376
377/**
378 * Handle a window shape change event in the seamless event thread.
379 *
380 * @param event the X11 event structure
381 */
382void VBoxGuestSeamlessX11::doShapeEvent(Window hWin)
383{
384 LogFlowThisFunc(("\n"));
385 VBoxGuestWindowList::iterator iter;
386
387 iter = mGuestWindows.find(hWin);
388 if (iter != mGuestWindows.end())
389 {
390 VBoxGuestX11Pointer<XRectangle> rects;
391 int cRects = 0, iOrdering;
392
393 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
394 &iOrdering);
395 if (rects.get() == NULL)
396 cRects = 0;
397 iter->second->mhasShape = true;
398 iter->second->mcRects = cRects;
399 iter->second->mapRects = rects;
400 mChanged = true;
401 }
402 LogFlowThisFunc(("returning\n"));
403}
404
405/**
406 * Handle an unmap event in the seamless event thread.
407 *
408 * @param event the X11 event structure
409 */
410void VBoxGuestSeamlessX11::doUnmapEvent(Window hWin)
411{
412 LogFlowThisFunc(("\n"));
413 VBoxGuestWindowList::iterator iter;
414
415 iter = mGuestWindows.find(hWin);
416 if (mGuestWindows.end() != iter)
417 {
418 mGuestWindows.removeWindow(iter);
419 mChanged = true;
420 }
421 LogFlowThisFunc(("returning\n"));
422}
423
424/**
425 * Sends an updated list of visible rectangles to the host
426 */
427std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
428{
429 LogFlowThisFunc(("\n"));
430 unsigned cRects = 0;
431 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
432
433 if (0 != mcRects)
434 {
435 apRects.get()->reserve(mcRects * 2);
436 }
437 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
438 it != mGuestWindows.end(); ++it)
439 {
440 if (it->second->mhasShape)
441 {
442 for (int i = 0; i < it->second->mcRects; ++i)
443 {
444 RTRECT rect;
445 rect.xLeft = it->second->mX
446 + it->second->mapRects.get()[i].x;
447 rect.yBottom = it->second->mY
448 + it->second->mapRects.get()[i].y
449 + it->second->mapRects.get()[i].height;
450 rect.xRight = it->second->mX
451 + it->second->mapRects.get()[i].x
452 + it->second->mapRects.get()[i].width;
453 rect.yTop = it->second->mY
454 + it->second->mapRects.get()[i].y;
455 apRects.get()->push_back(rect);
456 }
457 cRects += it->second->mcRects;
458 }
459 else
460 {
461 RTRECT rect;
462 rect.xLeft = it->second->mX;
463 rect.yBottom = it->second->mY
464 + it->second->mHeight;
465 rect.xRight = it->second->mX
466 + it->second->mWidth;
467 rect.yTop = it->second->mY;
468 apRects.get()->push_back(rect);
469 ++cRects;
470 }
471 }
472 mcRects = cRects;
473 LogFlowThisFunc(("returning\n"));
474 return apRects;
475}
476
477/**
478 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
479 *
480 * @note This function should only be called from the host event thread.
481 */
482bool VBoxGuestSeamlessX11::interruptEvent(void)
483{
484 bool rc = false;
485
486 LogFlowThisFunc(("\n"));
487 /* Message contents set to zero. */
488 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
489
490 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay.get()), false, PropertyChangeMask,
491 reinterpret_cast<XEvent *>(&clientMessage)))
492 {
493 XFlush(mDisplay);
494 rc = true;
495 }
496 LogFlowThisFunc(("returning %s\n", rc ? "true" : "false"));
497 return rc;
498}
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