VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/xclient/seamless-x11.cpp@ 7446

Last change on this file since 7446 was 7446, checked in by vboxsync, 17 years ago

Additions/x11: added a check for a null pointer which should prevent a segfault

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.7 KB
Line 
1/** @file
2 *
3 * Seamless mode:
4 * Linux guest.
5 */
6
7/*
8 * Copyright (C) 2008 innotek GmbH
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
19/*****************************************************************************
20* Header files *
21*****************************************************************************/
22
23#include <iprt/log.h>
24#include <iprt/err.h>
25#include <iprt/assert.h>
26#include <VBox/VBoxGuest.h>
27
28#include "seamless-guest.h"
29
30#include <X11/Xatom.h>
31#include <X11/Xmu/WinUtil.h>
32
33/* This is defined wrong in my X11 header files! */
34#define VBoxShapeNotify 64
35
36/*****************************************************************************
37* Static functions *
38*****************************************************************************/
39
40static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType,
41 const char *aPropName, unsigned long *nItems)
42{
43 Atom propNameAtom = XInternAtom (aDpy, aPropName,
44 True /* only_if_exists */);
45 if (propNameAtom == None)
46 {
47 return NULL;
48 }
49
50 Atom actTypeAtom = None;
51 int actFmt = 0;
52 unsigned long nBytesAfter = 0;
53 unsigned char *propVal = 0;
54 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
55 0, LONG_MAX, False /* delete */,
56 aPropType, &actTypeAtom, &actFmt,
57 nItems, &nBytesAfter, &propVal);
58 if (rc != Success)
59 return NULL;
60
61 return propVal;
62}
63
64/**
65 * Initialise the guest and ensure that it is capable of handling seamless mode
66 *
67 * @returns true if it can handle seamless, false otherwise
68 */
69int VBoxGuestSeamlessX11::init(VBoxGuestSeamlessObserver *pObserver)
70{
71 int rc = VINF_SUCCESS;
72
73 if (0 != mObserver) /* Assertion */
74 {
75 LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n"));
76 return VERR_INTERNAL_ERROR;
77 }
78 if (!mDisplay.init())
79 {
80 LogRel(("VBoxClient: seamless guest object failed to acquire a connection to the display.\n"));
81 return VERR_ACCESS_DENIED;
82 }
83 mObserver = pObserver;
84 return rc;
85}
86
87/**
88 * Read information about currently visible windows in the guest and subscribe to X11
89 * events about changes to this information.
90 *
91 * @note This class does not contain its own event thread, so an external thread must
92 * call nextEvent() for as long as events are wished.
93 * @todo This function should switch the guest to fullscreen mode.
94 */
95int VBoxGuestSeamlessX11::start(void)
96{
97 int rc = VINF_SUCCESS;
98 /** Dummy values for XShapeQueryExtension */
99 int error, event;
100
101 mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
102 mEnabled = true;
103 monitorClientList();
104 rebuildWindowTree();
105 return rc;
106}
107
108/** Stop reporting seamless events to the host. Free information about guest windows
109 and stop requesting updates. */
110void VBoxGuestSeamlessX11::stop(void)
111{
112 mEnabled = false;
113 unmonitorClientList();
114 freeWindowTree();
115}
116
117void VBoxGuestSeamlessX11::monitorClientList(void)
118{
119 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), SubstructureNotifyMask);
120}
121
122void VBoxGuestSeamlessX11::unmonitorClientList(void)
123{
124 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), 0);
125}
126
127/**
128 * Recreate the table of toplevel windows of clients on the default root window of the
129 * X server.
130 */
131void VBoxGuestSeamlessX11::rebuildWindowTree(void)
132{
133 freeWindowTree();
134 addClients(DefaultRootWindow(mDisplay.get()));
135}
136
137
138/**
139 * Look at the list of children of a virtual root window and add them to the list of clients
140 * if they belong to a client which is not a virtual root.
141 *
142 * @param hRoot the virtual root window to be examined
143 */
144void VBoxGuestSeamlessX11::addClients(const Window hRoot)
145{
146 /** Unused out parameters of XQueryTree */
147 Window hRealRoot, hParent;
148 /** The list of children of the root supplied, raw pointer */
149 Window *phChildrenRaw;
150 /** The list of children of the root supplied, auto-pointer */
151 VBoxGuestX11Pointer<Window> phChildren;
152 /** The number of children of the root supplied */
153 unsigned cChildren;
154
155 if (!XQueryTree(mDisplay.get(), hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
156 return;
157 phChildren = phChildrenRaw;
158 for (unsigned i = 0; i < cChildren; ++i)
159 addClientWindow(phChildren.get()[i]);
160}
161
162
163void VBoxGuestSeamlessX11::addClientWindow(const Window hWin)
164{
165 XWindowAttributes winAttrib;
166 bool fAddWin = true;
167 char *pszWinName = NULL;
168 Window hClient = XmuClientWindow(mDisplay, hWin);
169
170 if (isVirtualRoot(hClient))
171 fAddWin = false;
172 if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
173 {
174 LogRelFunc(("VBoxClient: Failed to get the window attributes for window %d\n", hWin));
175 fAddWin = false;
176 }
177 if (fAddWin && (winAttrib.map_state == IsUnmapped))
178 fAddWin = false;
179 if (fAddWin && (XFetchName(mDisplay, hClient, &pszWinName) != 0) && (pszWinName != NULL))
180 XFree(pszWinName);
181 else
182 /* kwin sometimes creates temporary fullscreen windows with no name. */
183 fAddWin = false;
184 if (fAddWin)
185 {
186 VBoxGuestX11Pointer<XRectangle> rects;
187 int cRects = 0, iOrdering;
188 bool hasShape = false;
189
190 if (mSupportsShape)
191 {
192 XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
193 rects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
194 if (0 == rects.get())
195 cRects = 0;
196 else
197 {
198 if ( (cRects > 1)
199 || (rects.get()[0].x != 0)
200 || (rects.get()[0].y != 0)
201 || (rects.get()[0].width != winAttrib.width)
202 || (rects.get()[0].height != winAttrib.height)
203 )
204 hasShape = true;
205 }
206 }
207 mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
208 winAttrib.width, winAttrib.height, cRects, rects);
209 }
210}
211
212
213/**
214 * Checks whether a window is a virtual root.
215 * @returns true if it is, false otherwise
216 * @param hWin the window to be examined
217 */
218bool VBoxGuestSeamlessX11::isVirtualRoot(Window hWin)
219{
220 unsigned char *windowTypeRaw;
221 VBoxGuestX11Pointer<Atom> windowType;
222 unsigned long ulCount;
223 bool rc = false;
224
225 windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
226 if (windowTypeRaw != NULL)
227 {
228 windowType = reinterpret_cast<Atom *>(windowTypeRaw);
229 if ( (ulCount != 0)
230 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
231 rc = true;
232 }
233 return rc;
234}
235
236
237/**
238 * Free all information in the tree of visible windows
239 */
240void VBoxGuestSeamlessX11::freeWindowTree(void)
241{
242 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
243 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
244 mGuestWindows.removeWindow(it++))
245 {
246 XShapeSelectInput(mDisplay, it->first, 0);
247 }
248}
249
250
251/**
252 * Waits for a position or shape-related event from guest windows
253 *
254 * @note Called from the guest event thread.
255 */
256void VBoxGuestSeamlessX11::nextEvent(void)
257{
258 XEvent event;
259
260 /* Start by sending information about the current window setup to the host. We do this
261 here because we want to send all such information from a single thread. */
262 mObserver->notify();
263 XNextEvent(mDisplay, &event);
264 switch (event.type)
265 {
266 case ConfigureNotify:
267 doConfigureEvent(&event.xconfigure);
268 break;
269 case MapNotify:
270 doMapEvent(&event.xmap);
271 break;
272 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
273 doShapeEvent(reinterpret_cast<XShapeEvent *>(&event));
274 break;
275 case UnmapNotify:
276 doUnmapEvent(&event.xunmap);
277 break;
278 default:
279 break;
280 }
281}
282
283/**
284 * Handle a configuration event in the seamless event thread by setting the new position.
285 *
286 * @param event the X11 event structure
287 */
288void VBoxGuestSeamlessX11::doConfigureEvent(const XConfigureEvent *event)
289{
290 VBoxGuestWindowList::iterator iter;
291
292 iter = mGuestWindows.find(event->window);
293 if (iter != mGuestWindows.end())
294 {
295 XWindowAttributes winAttrib;
296
297 if (XGetWindowAttributes(mDisplay, event->window, &winAttrib))
298 {
299 iter->second->mX = winAttrib.x;
300 iter->second->mY = winAttrib.y;
301 iter->second->mWidth = winAttrib.width;
302 iter->second->mHeight = winAttrib.height;
303 }
304 }
305}
306
307/**
308 * Handle a map event in the seamless event thread.
309 *
310 * @param event the X11 event structure
311 */
312void VBoxGuestSeamlessX11::doMapEvent(const XMapEvent *event)
313{
314 VBoxGuestWindowList::iterator iter;
315
316 iter = mGuestWindows.find(event->window);
317 if (mGuestWindows.end() == iter)
318 {
319 addClientWindow(event->window);
320 }
321}
322
323
324/**
325 * Handle a window shape change event in the seamless event thread.
326 *
327 * @param event the X11 event structure
328 */
329void VBoxGuestSeamlessX11::doShapeEvent(const XShapeEvent *event)
330{
331 VBoxGuestWindowList::iterator iter;
332
333 iter = mGuestWindows.find(event->window);
334 if (iter != mGuestWindows.end())
335 {
336 VBoxGuestX11Pointer<XRectangle> rects;
337 int cRects = 0, iOrdering;
338
339 rects = XShapeGetRectangles(mDisplay, event->window, ShapeBounding, &cRects, &iOrdering);
340 iter->second->mhasShape = true;
341 iter->second->mcRects = cRects;
342 iter->second->mapRects = rects;
343 }
344}
345
346/**
347 * Handle an unmap event in the seamless event thread.
348 *
349 * @param event the X11 event structure
350 */
351void VBoxGuestSeamlessX11::doUnmapEvent(const XUnmapEvent *event)
352{
353 VBoxGuestWindowList::iterator iter;
354
355 iter = mGuestWindows.find(event->window);
356 if (mGuestWindows.end() != iter)
357 {
358 mGuestWindows.removeWindow(iter);
359 }
360}
361
362/**
363 * Sends an updated list of visible rectangles to the host
364 */
365std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
366{
367 unsigned cRects = 0;
368 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
369
370 if (0 != mcRects)
371 {
372 apRects.get()->reserve(mcRects * 2);
373 }
374 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
375 it != mGuestWindows.end(); ++it)
376 {
377 if (it->second->mhasShape)
378 {
379 for (int i = 0; i < it->second->mcRects; ++i)
380 {
381 RTRECT rect;
382 rect.xLeft = it->second->mX
383 + it->second->mapRects.get()[i].x;
384 rect.yBottom = it->second->mY
385 + it->second->mapRects.get()[i].y
386 + it->second->mapRects.get()[i].height;
387 rect.xRight = it->second->mX
388 + it->second->mapRects.get()[i].x
389 + it->second->mapRects.get()[i].width;
390 rect.yTop = it->second->mY
391 + it->second->mapRects.get()[i].y;
392 apRects.get()->push_back(rect);
393 }
394 cRects += it->second->mcRects;
395 }
396 else
397 {
398 RTRECT rect;
399 rect.xLeft = it->second->mX;
400 rect.yBottom = it->second->mY
401 + it->second->mHeight;
402 rect.xRight = it->second->mX
403 + it->second->mWidth;
404 rect.yTop = it->second->mY;
405 apRects.get()->push_back(rect);
406 ++cRects;
407 }
408 }
409 mcRects = cRects;
410 return apRects;
411}
412
413/**
414 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
415 *
416 * @note This function should only be called from the host event thread.
417 */
418bool VBoxGuestSeamlessX11::interruptEvent(void)
419{
420 /* Message contents set to zero. */
421 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
422
423 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay.get()), false, PropertyChangeMask,
424 reinterpret_cast<XEvent *>(&clientMessage)))
425 {
426 XFlush(mDisplay);
427 return true;
428 }
429 return false;
430}
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