VirtualBox

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

Last change on this file since 36308 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • 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.init())
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.get()), SubstructureNotifyMask);
130}
131
132void VBoxGuestSeamlessX11::unmonitorClientList(void)
133{
134 LogRelFlowFunc(("called\n"));
135 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), 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.get()));
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;
163 /** The list of children of the root supplied, auto-pointer */
164 VBoxGuestX11Pointer<Window> phChildren;
165 /** The number of children of the root supplied */
166 unsigned cChildren;
167
168 LogRelFlowFunc(("\n"));
169 if (!XQueryTree(mDisplay.get(), hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
170 return;
171 phChildren = phChildrenRaw;
172 for (unsigned i = 0; i < cChildren; ++i)
173 addClientWindow(phChildren.get()[i]);
174 LogRelFlowFunc(("returning\n"));
175}
176
177
178void VBoxGuestSeamlessX11::addClientWindow(const Window hWin)
179{
180 LogRelFlowFunc(("\n"));
181 XWindowAttributes winAttrib;
182 bool fAddWin = true;
183 char *pszWinName = NULL;
184 Window hClient = XmuClientWindow(mDisplay, hWin);
185
186 if (isVirtualRoot(hClient))
187 fAddWin = false;
188 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
189 {
190 LogRelFunc(("VBoxClient: Failed to get the window attributes for window %d\n", hWin));
191 fAddWin = false;
192 }
193 if (fAddWin && (winAttrib.map_state == IsUnmapped))
194 fAddWin = false;
195 XSizeHints dummyHints;
196 long dummyLong;
197 if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
198 &dummyLong)))
199 {
200 LogRelFlowFunc(("window %lu, client window %lu has no size hints\n",
201 hWin, hClient));
202 fAddWin = false;
203 }
204 if (fAddWin)
205 {
206 VBoxGuestX11Pointer<XRectangle> rects;
207 int cRects = 0, iOrdering;
208 bool hasShape = false;
209
210 LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
211 hClient));
212 if (mSupportsShape)
213 {
214 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
215 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
216 if (0 == rects.get())
217 cRects = 0;
218 else
219 {
220 if ( (cRects > 1)
221 || (rects.get()[0].x != 0)
222 || (rects.get()[0].y != 0)
223 || (rects.get()[0].width != winAttrib.width)
224 || (rects.get()[0].height != winAttrib.height)
225 )
226 hasShape = true;
227 }
228 }
229 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
230 winAttrib.width, winAttrib.height, cRects, rects);
231 }
232 LogRelFlowFunc(("returning\n"));
233}
234
235
236/**
237 * Checks whether a window is a virtual root.
238 * @returns true if it is, false otherwise
239 * @param hWin the window to be examined
240 */
241bool VBoxGuestSeamlessX11::isVirtualRoot(Window hWin)
242{
243 unsigned char *windowTypeRaw;
244 VBoxGuestX11Pointer<Atom> windowType;
245 unsigned long ulCount;
246 bool rc = false;
247
248 LogRelFlowFunc(("\n"));
249 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
250 if (windowTypeRaw != NULL)
251 {
252 windowType = reinterpret_cast<Atom *>(windowTypeRaw);
253 if ( (ulCount != 0)
254 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
255 rc = true;
256 }
257 LogRelFlowFunc(("returning %s\n", rc ? "true" : "false"));
258 return rc;
259}
260
261
262/**
263 * Free all information in the tree of visible windows
264 */
265void VBoxGuestSeamlessX11::freeWindowTree(void)
266{
267 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
268 LogRelFlowFunc(("\n"));
269 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
270 mGuestWindows.removeWindow(it++))
271 {
272 XShapeSelectInput(mDisplay, it->first, 0);
273 }
274 LogRelFlowFunc(("returning\n"));
275}
276
277
278/**
279 * Waits for a position or shape-related event from guest windows
280 *
281 * @note Called from the guest event thread.
282 */
283void VBoxGuestSeamlessX11::nextEvent(void)
284{
285 XEvent event;
286
287 LogRelFlowFunc(("\n"));
288 /* Start by sending information about the current window setup to the host. We do this
289 here because we want to send all such information from a single thread. */
290 if (mChanged)
291 mObserver->notify();
292 mChanged = false;
293 XNextEvent(mDisplay, &event);
294 switch (event.type)
295 {
296 case ConfigureNotify:
297 doConfigureEvent(event.xconfigure.window);
298 break;
299 case MapNotify:
300 doMapEvent(event.xmap.window);
301 break;
302 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
303 /* the window member in xany is in the same place as in the shape event */
304 doShapeEvent(event.xany.window);
305 break;
306 case UnmapNotify:
307 doUnmapEvent(event.xunmap.window);
308 break;
309 default:
310 break;
311 }
312 LogRelFlowFunc(("returning\n"));
313}
314
315/**
316 * Handle a configuration event in the seamless event thread by setting the new position.
317 *
318 * @param event the X11 event structure
319 */
320void VBoxGuestSeamlessX11::doConfigureEvent(Window hWin)
321{
322 LogRelFlowFunc(("\n"));
323 VBoxGuestWindowList::iterator iter;
324
325 iter = mGuestWindows.find(hWin);
326 if (iter != mGuestWindows.end())
327 {
328 XWindowAttributes winAttrib;
329
330 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
331 return;
332 iter->second->mX = winAttrib.x;
333 iter->second->mY = winAttrib.y;
334 iter->second->mWidth = winAttrib.width;
335 iter->second->mHeight = winAttrib.height;
336 if (iter->second->mhasShape)
337 {
338 VBoxGuestX11Pointer<XRectangle> rects;
339 int cRects = 0, iOrdering;
340
341 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding,
342 &cRects, &iOrdering);
343 if (rects.get() == NULL)
344 cRects = 0;
345 iter->second->mcRects = cRects;
346 iter->second->mapRects = rects;
347 }
348 mChanged = true;
349 }
350 LogRelFlowFunc(("returning\n"));
351}
352
353/**
354 * Handle a map event in the seamless event thread.
355 *
356 * @param event the X11 event structure
357 */
358void VBoxGuestSeamlessX11::doMapEvent(Window hWin)
359{
360 LogRelFlowFunc(("\n"));
361 VBoxGuestWindowList::iterator iter;
362
363 iter = mGuestWindows.find(hWin);
364 if (mGuestWindows.end() == iter)
365 {
366 addClientWindow(hWin);
367 mChanged = true;
368 }
369 LogRelFlowFunc(("returning\n"));
370}
371
372
373/**
374 * Handle a window shape change event in the seamless event thread.
375 *
376 * @param event the X11 event structure
377 */
378void VBoxGuestSeamlessX11::doShapeEvent(Window hWin)
379{
380 LogRelFlowFunc(("\n"));
381 VBoxGuestWindowList::iterator iter;
382
383 iter = mGuestWindows.find(hWin);
384 if (iter != mGuestWindows.end())
385 {
386 VBoxGuestX11Pointer<XRectangle> rects;
387 int cRects = 0, iOrdering;
388
389 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
390 &iOrdering);
391 if (rects.get() == NULL)
392 cRects = 0;
393 iter->second->mhasShape = true;
394 iter->second->mcRects = cRects;
395 iter->second->mapRects = rects;
396 mChanged = true;
397 }
398 LogRelFlowFunc(("returning\n"));
399}
400
401/**
402 * Handle an unmap event in the seamless event thread.
403 *
404 * @param event the X11 event structure
405 */
406void VBoxGuestSeamlessX11::doUnmapEvent(Window hWin)
407{
408 LogRelFlowFunc(("\n"));
409 VBoxGuestWindowList::iterator iter;
410
411 iter = mGuestWindows.find(hWin);
412 if (mGuestWindows.end() != iter)
413 {
414 mGuestWindows.removeWindow(iter);
415 mChanged = true;
416 }
417 LogRelFlowFunc(("returning\n"));
418}
419
420/**
421 * Sends an updated list of visible rectangles to the host
422 */
423std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
424{
425 LogRelFlowFunc(("\n"));
426 unsigned cRects = 0;
427 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
428
429 if (0 != mcRects)
430 {
431 apRects.get()->reserve(mcRects * 2);
432 }
433 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
434 it != mGuestWindows.end(); ++it)
435 {
436 if (it->second->mhasShape)
437 {
438 for (int i = 0; i < it->second->mcRects; ++i)
439 {
440 RTRECT rect;
441 rect.xLeft = it->second->mX
442 + it->second->mapRects.get()[i].x;
443 rect.yBottom = it->second->mY
444 + it->second->mapRects.get()[i].y
445 + it->second->mapRects.get()[i].height;
446 rect.xRight = it->second->mX
447 + it->second->mapRects.get()[i].x
448 + it->second->mapRects.get()[i].width;
449 rect.yTop = it->second->mY
450 + it->second->mapRects.get()[i].y;
451 apRects.get()->push_back(rect);
452 }
453 cRects += it->second->mcRects;
454 }
455 else
456 {
457 RTRECT rect;
458 rect.xLeft = it->second->mX;
459 rect.yBottom = it->second->mY
460 + it->second->mHeight;
461 rect.xRight = it->second->mX
462 + it->second->mWidth;
463 rect.yTop = it->second->mY;
464 apRects.get()->push_back(rect);
465 ++cRects;
466 }
467 }
468 mcRects = cRects;
469 LogRelFlowFunc(("returning\n"));
470 return apRects;
471}
472
473/**
474 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
475 *
476 * @note This function should only be called from the host event thread.
477 */
478bool VBoxGuestSeamlessX11::interruptEvent(void)
479{
480 bool rc = false;
481
482 LogRelFlowFunc(("\n"));
483 /* Message contents set to zero. */
484 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
485
486 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay.get()), false, PropertyChangeMask,
487 reinterpret_cast<XEvent *>(&clientMessage)))
488 {
489 XFlush(mDisplay);
490 rc = true;
491 }
492 LogRelFlowFunc(("returning %s\n", rc ? "true" : "false"));
493 return rc;
494}
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