VirtualBox

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

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

Additions/X11: more updates to seamless mode

  • 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 windowType = reinterpret_cast<Atom *>(windowTypeRaw);
227 if ( (ulCount != 0)
228 && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
229 rc = true;
230 return rc;
231}
232
233
234/**
235 * Free all information in the tree of visible windows
236 */
237void VBoxGuestSeamlessX11::freeWindowTree(void)
238{
239 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
240 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
241 mGuestWindows.removeWindow(it++))
242 {
243 XShapeSelectInput(mDisplay, it->first, 0);
244 }
245}
246
247
248/**
249 * Waits for a position or shape-related event from guest windows
250 *
251 * @note Called from the guest event thread.
252 */
253void VBoxGuestSeamlessX11::nextEvent(void)
254{
255 XEvent event;
256
257 /* Start by sending information about the current window setup to the host. We do this
258 here because we want to send all such information from a single thread. */
259 mObserver->notify();
260 XNextEvent(mDisplay, &event);
261 switch (event.type)
262 {
263 case ConfigureNotify:
264 doConfigureEvent(&event.xconfigure);
265 break;
266 case MapNotify:
267 doMapEvent(&event.xmap);
268 break;
269 case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
270 doShapeEvent(reinterpret_cast<XShapeEvent *>(&event));
271 break;
272 case UnmapNotify:
273 doUnmapEvent(&event.xunmap);
274 break;
275 default:
276 break;
277 }
278}
279
280/**
281 * Handle a configuration event in the seamless event thread by setting the new position.
282 *
283 * @param event the X11 event structure
284 */
285void VBoxGuestSeamlessX11::doConfigureEvent(const XConfigureEvent *event)
286{
287 VBoxGuestWindowList::iterator iter;
288
289 iter = mGuestWindows.find(event->window);
290 if (iter != mGuestWindows.end())
291 {
292 XWindowAttributes winAttrib;
293
294 if (XGetWindowAttributes(mDisplay, event->window, &winAttrib))
295 {
296 iter->second->mX = winAttrib.x;
297 iter->second->mY = winAttrib.y;
298 iter->second->mWidth = winAttrib.width;
299 iter->second->mHeight = winAttrib.height;
300 }
301 }
302}
303
304/**
305 * Handle a map event in the seamless event thread.
306 *
307 * @param event the X11 event structure
308 */
309void VBoxGuestSeamlessX11::doMapEvent(const XMapEvent *event)
310{
311 VBoxGuestWindowList::iterator iter;
312
313 iter = mGuestWindows.find(event->window);
314 if (mGuestWindows.end() == iter)
315 {
316 addClientWindow(event->window);
317 }
318}
319
320
321/**
322 * Handle a window shape change event in the seamless event thread.
323 *
324 * @param event the X11 event structure
325 */
326void VBoxGuestSeamlessX11::doShapeEvent(const XShapeEvent *event)
327{
328 VBoxGuestWindowList::iterator iter;
329
330 iter = mGuestWindows.find(event->window);
331 if (iter != mGuestWindows.end())
332 {
333 VBoxGuestX11Pointer<XRectangle> rects;
334 int cRects = 0, iOrdering;
335
336 rects = XShapeGetRectangles(mDisplay, event->window, ShapeBounding, &cRects, &iOrdering);
337 iter->second->mhasShape = true;
338 iter->second->mcRects = cRects;
339 iter->second->mapRects = rects;
340 }
341}
342
343/**
344 * Handle an unmap event in the seamless event thread.
345 *
346 * @param event the X11 event structure
347 */
348void VBoxGuestSeamlessX11::doUnmapEvent(const XUnmapEvent *event)
349{
350 VBoxGuestWindowList::iterator iter;
351
352 iter = mGuestWindows.find(event->window);
353 if (mGuestWindows.end() != iter)
354 {
355 mGuestWindows.removeWindow(iter);
356 }
357}
358
359/**
360 * Sends an updated list of visible rectangles to the host
361 */
362std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
363{
364 unsigned cRects = 0;
365 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
366
367 if (0 != mcRects)
368 {
369 apRects.get()->reserve(mcRects * 2);
370 }
371 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
372 it != mGuestWindows.end(); ++it)
373 {
374 if (it->second->mhasShape)
375 {
376 for (int i = 0; i < it->second->mcRects; ++i)
377 {
378 RTRECT rect;
379 rect.xLeft = it->second->mX
380 + it->second->mapRects.get()[i].x;
381 rect.yBottom = it->second->mY
382 + it->second->mapRects.get()[i].y
383 + it->second->mapRects.get()[i].height;
384 rect.xRight = it->second->mX
385 + it->second->mapRects.get()[i].x
386 + it->second->mapRects.get()[i].width;
387 rect.yTop = it->second->mY
388 + it->second->mapRects.get()[i].y;
389 apRects.get()->push_back(rect);
390 }
391 cRects += it->second->mcRects;
392 }
393 else
394 {
395 RTRECT rect;
396 rect.xLeft = it->second->mX;
397 rect.yBottom = it->second->mY
398 + it->second->mHeight;
399 rect.xRight = it->second->mX
400 + it->second->mWidth;
401 rect.yTop = it->second->mY;
402 apRects.get()->push_back(rect);
403 ++cRects;
404 }
405 }
406 mcRects = cRects;
407 return apRects;
408}
409
410/**
411 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
412 *
413 * @note This function should only be called from the host event thread.
414 */
415bool VBoxGuestSeamlessX11::interruptEvent(void)
416{
417 /* Message contents set to zero. */
418 XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 };
419
420 if (0 != XSendEvent(mDisplay, DefaultRootWindow(mDisplay.get()), false, PropertyChangeMask,
421 reinterpret_cast<XEvent *>(&clientMessage)))
422 {
423 XFlush(mDisplay);
424 return true;
425 }
426 return false;
427}
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