VirtualBox

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

Last change on this file since 19015 was 8155, checked in by vboxsync, 16 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.1 KB
Line 
1/** @file
2 *
3 * Seamless mode:
4 * Linux guest.
5 */
6
7/*
8 * Copyright (C) 2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/*****************************************************************************
24* Header files *
25*****************************************************************************/
26
27#include <iprt/err.h>
28#include <iprt/assert.h>
29#include <VBox/log.h>
30#include <VBox/VBoxGuest.h>
31
32#include "seamless-guest.h"
33
34#include <X11/Xatom.h>
35#include <X11/Xmu/WinUtil.h>
36
37#include <limits.h>
38
39/* This is defined wrong in my X11 header files! */
40#define VBoxShapeNotify 64
41
42/*****************************************************************************
43* Static functions *
44*****************************************************************************/
45
46static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType,
47 const char *aPropName, unsigned long *nItems)
48{
49 LogFlowFunc(("\n"));
50 Atom propNameAtom = XInternAtom (aDpy, aPropName,
51 True /* only_if_exists */);
52 if (propNameAtom == None)
53 {
54 return NULL;
55 }
56
57 Atom actTypeAtom = None;
58 int actFmt = 0;
59 unsigned long nBytesAfter = 0;
60 unsigned char *propVal = 0;
61 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
62 0, LONG_MAX, False /* delete */,
63 aPropType, &actTypeAtom, &actFmt,
64 nItems, &nBytesAfter, &propVal);
65 if (rc != Success)
66 return NULL;
67
68 LogFlowFunc(("returning\n"));
69 return propVal;
70}
71
72/**
73 * Initialise the guest and ensure that it is capable of handling seamless mode
74 *
75 * @returns true if it can handle seamless, false otherwise
76 */
77int VBoxGuestSeamlessX11::init(VBoxGuestSeamlessObserver *pObserver)
78{
79 int rc = VINF_SUCCESS;
80
81 LogFlowThisFunc(("\n"));
82 if (0 != mObserver) /* Assertion */
83 {
84 LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n"));
85 return VERR_INTERNAL_ERROR;
86 }
87 if (!mDisplay.init())
88 {
89 LogRel(("VBoxClient: seamless guest object failed to acquire a connection to the display.\n"));
90 return VERR_ACCESS_DENIED;
91 }
92 mObserver = pObserver;
93 LogFlowThisFunc(("returning %Rrc\n", rc));
94 return rc;
95}
96
97/**
98 * Read information about currently visible windows in the guest and subscribe to X11
99 * events about changes to this information.
100 *
101 * @note This class does not contain its own event thread, so an external thread must
102 * call nextEvent() for as long as events are wished.
103 * @todo This function should switch the guest to fullscreen mode.
104 */
105int VBoxGuestSeamlessX11::start(void)
106{
107 int rc = VINF_SUCCESS;
108 /** Dummy values for XShapeQueryExtension */
109 int error, event;
110
111 LogFlowThisFunc(("\n"));
112 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
113 mEnabled = true;
114 monitorClientList();
115 rebuildWindowTree();
116 LogFlowThisFunc(("returning %Rrc\n", rc));
117 return rc;
118}
119
120/** Stop reporting seamless events to the host. Free information about guest windows
121 and stop requesting updates. */
122void VBoxGuestSeamlessX11::stop(void)
123{
124 LogFlowThisFunc(("\n"));
125 mEnabled = false;
126 unmonitorClientList();
127 freeWindowTree();
128 LogFlowThisFunc(("returning\n"));
129}
130
131void VBoxGuestSeamlessX11::monitorClientList(void)
132{
133 LogFlowThisFunc(("called\n"));
134 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), SubstructureNotifyMask);
135}
136
137void VBoxGuestSeamlessX11::unmonitorClientList(void)
138{
139 LogFlowThisFunc(("called\n"));
140 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), 0);
141}
142
143/**
144 * Recreate the table of toplevel windows of clients on the default root window of the
145 * X server.
146 */
147void VBoxGuestSeamlessX11::rebuildWindowTree(void)
148{
149 LogFlowThisFunc(("called\n"));
150 freeWindowTree();
151 addClients(DefaultRootWindow(mDisplay.get()));
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 if (fAddWin && (XFetchName(mDisplay, hClient, &pszWinName) != 0) && (pszWinName != NULL))
200 XFree(pszWinName);
201 else
202 /* kwin sometimes creates temporary fullscreen windows with no name. */
203 fAddWin = false;
204 if (fAddWin)
205 {
206 VBoxGuestX11Pointer<XRectangle> rects;
207 int cRects = 0, iOrdering;
208 bool hasShape = false;
209
210 if (mSupportsShape)
211 {
212 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
213 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
214 if (0 == rects.get())
215 cRects = 0;
216 else
217 {
218 if ( (cRects > 1)
219 || (rects.get()[0].x != 0)
220 || (rects.get()[0].y != 0)
221 || (rects.get()[0].width != winAttrib.width)
222 || (rects.get()[0].height != winAttrib.height)
223 )
224 hasShape = true;
225 }
226 }
227 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
228 winAttrib.width, winAttrib.height, cRects, rects);
229 }
230 LogFlowThisFunc(("returning\n"));
231}
232
233
234/**
235 * Checks whether a window is a virtual root.
236 * @returns true if it is, false otherwise
237 * @param hWin the window to be examined
238 */
239bool VBoxGuestSeamlessX11::isVirtualRoot(Window hWin)
240{
241 unsigned char *windowTypeRaw;
242 VBoxGuestX11Pointer<Atom> windowType;
243 unsigned long ulCount;
244 bool rc = false;
245
246 LogFlowThisFunc(("\n"));
247 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
248 if (windowTypeRaw != NULL)
249 {
250 windowType = reinterpret_cast<Atom *>(windowTypeRaw);
251 if ( (ulCount != 0)
252 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
253 rc = true;
254 }
255 LogFlowThisFunc(("returning %s\n", rc ? "true" : "false"));
256 return rc;
257}
258
259
260/**
261 * Free all information in the tree of visible windows
262 */
263void VBoxGuestSeamlessX11::freeWindowTree(void)
264{
265 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
266 LogFlowThisFunc(("\n"));
267 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
268 mGuestWindows.removeWindow(it++))
269 {
270 XShapeSelectInput(mDisplay, it->first, 0);
271 }
272 LogFlowThisFunc(("returning\n"));
273}
274
275
276/**
277 * Waits for a position or shape-related event from guest windows
278 *
279 * @note Called from the guest event thread.
280 */
281void VBoxGuestSeamlessX11::nextEvent(void)
282{
283 XEvent event;
284
285 LogFlowThisFunc(("\n"));
286 /* Start by sending information about the current window setup to the host. We do this
287 here because we want to send all such information from a single thread. */
288 mObserver->notify();
289 XNextEvent(mDisplay, &event);
290 switch (event.type)
291 {
292 case ConfigureNotify:
293 doConfigureEvent(&event.xconfigure);
294 break;
295 case MapNotify:
296 doMapEvent(&event.xmap);
297 break;
298 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
299 doShapeEvent(reinterpret_cast<XShapeEvent *>(&event));
300 break;
301 case UnmapNotify:
302 doUnmapEvent(&event.xunmap);
303 break;
304 default:
305 break;
306 }
307 LogFlowThisFunc(("returning\n"));
308}
309
310/**
311 * Handle a configuration event in the seamless event thread by setting the new position.
312 *
313 * @param event the X11 event structure
314 */
315void VBoxGuestSeamlessX11::doConfigureEvent(const XConfigureEvent *event)
316{
317 LogFlowThisFunc(("\n"));
318 VBoxGuestWindowList::iterator iter;
319
320 iter = mGuestWindows.find(event->window);
321 if (iter != mGuestWindows.end())
322 {
323 XWindowAttributes winAttrib;
324
325 if (XGetWindowAttributes(mDisplay, event->window, &winAttrib))
326 {
327 iter->second->mX = winAttrib.x;
328 iter->second->mY = winAttrib.y;
329 iter->second->mWidth = winAttrib.width;
330 iter->second->mHeight = winAttrib.height;
331 }
332 }
333 LogFlowThisFunc(("returning\n"));
334}
335
336/**
337 * Handle a map event in the seamless event thread.
338 *
339 * @param event the X11 event structure
340 */
341void VBoxGuestSeamlessX11::doMapEvent(const XMapEvent *event)
342{
343 LogFlowThisFunc(("\n"));
344 VBoxGuestWindowList::iterator iter;
345
346 iter = mGuestWindows.find(event->window);
347 if (mGuestWindows.end() == iter)
348 {
349 addClientWindow(event->window);
350 }
351 LogFlowThisFunc(("returning\n"));
352}
353
354
355/**
356 * Handle a window shape change event in the seamless event thread.
357 *
358 * @param event the X11 event structure
359 */
360void VBoxGuestSeamlessX11::doShapeEvent(const XShapeEvent *event)
361{
362 LogFlowThisFunc(("\n"));
363 VBoxGuestWindowList::iterator iter;
364
365 iter = mGuestWindows.find(event->window);
366 if (iter != mGuestWindows.end())
367 {
368 VBoxGuestX11Pointer<XRectangle> rects;
369 int cRects = 0, iOrdering;
370
371 rects = XShapeGetRectangles(mDisplay, event->window, ShapeBounding, &cRects, &iOrdering);
372 iter->second->mhasShape = true;
373 iter->second->mcRects = cRects;
374 iter->second->mapRects = rects;
375 }
376 LogFlowThisFunc(("returning\n"));
377}
378
379/**
380 * Handle an unmap event in the seamless event thread.
381 *
382 * @param event the X11 event structure
383 */
384void VBoxGuestSeamlessX11::doUnmapEvent(const XUnmapEvent *event)
385{
386 LogFlowThisFunc(("\n"));
387 VBoxGuestWindowList::iterator iter;
388
389 iter = mGuestWindows.find(event->window);
390 if (mGuestWindows.end() != iter)
391 {
392 mGuestWindows.removeWindow(iter);
393 }
394 LogFlowThisFunc(("returning\n"));
395}
396
397/**
398 * Sends an updated list of visible rectangles to the host
399 */
400std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
401{
402 LogFlowThisFunc(("\n"));
403 unsigned cRects = 0;
404 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
405
406 if (0 != mcRects)
407 {
408 apRects.get()->reserve(mcRects * 2);
409 }
410 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
411 it != mGuestWindows.end(); ++it)
412 {
413 if (it->second->mhasShape)
414 {
415 for (int i = 0; i < it->second->mcRects; ++i)
416 {
417 RTRECT rect;
418 rect.xLeft = it->second->mX
419 + it->second->mapRects.get()[i].x;
420 rect.yBottom = it->second->mY
421 + it->second->mapRects.get()[i].y
422 + it->second->mapRects.get()[i].height;
423 rect.xRight = it->second->mX
424 + it->second->mapRects.get()[i].x
425 + it->second->mapRects.get()[i].width;
426 rect.yTop = it->second->mY
427 + it->second->mapRects.get()[i].y;
428 apRects.get()->push_back(rect);
429 }
430 cRects += it->second->mcRects;
431 }
432 else
433 {
434 RTRECT rect;
435 rect.xLeft = it->second->mX;
436 rect.yBottom = it->second->mY
437 + it->second->mHeight;
438 rect.xRight = it->second->mX
439 + it->second->mWidth;
440 rect.yTop = it->second->mY;
441 apRects.get()->push_back(rect);
442 ++cRects;
443 }
444 }
445 mcRects = cRects;
446 LogFlowThisFunc(("returning\n"));
447 return apRects;
448}
449
450/**
451 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
452 *
453 * @note This function should only be called from the host event thread.
454 */
455bool VBoxGuestSeamlessX11::interruptEvent(void)
456{
457 bool rc = false;
458
459 LogFlowThisFunc(("\n"));
460 /* Message contents set to zero. */
461 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
462
463 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay.get()), false, PropertyChangeMask,
464 reinterpret_cast<XEvent *>(&clientMessage)))
465 {
466 XFlush(mDisplay);
467 rc = true;
468 }
469 LogFlowThisFunc(("returning %s\n", rc ? "true" : "false"));
470 return rc;
471}
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