VirtualBox

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

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

re-export x11

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.3 KB
Line 
1/** @file
2 *
3 * Seamless mode:
4 * Linux guest.
5 */
6
7/*
8 * Copyright (C) 2006-2007 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
32/*****************************************************************************
33* Static functions *
34*****************************************************************************/
35
36static VBoxGuestX11Pointer<unsigned char> XXGetProperty (Display *aDpy, Window aWnd,
37 Atom aPropType, const char *aPropName,
38 unsigned long *nItems)
39{
40 Atom propNameAtom = XInternAtom (aDpy, aPropName,
41 True /* only_if_exists */);
42 if (propNameAtom == None)
43 {
44 return VBoxGuestX11Pointer<unsigned char>(0);
45 }
46
47 Atom actTypeAtom = None;
48 int actFmt = 0;
49 unsigned long nBytesAfter = 0;
50 unsigned char *propVal = 0;
51 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
52 0, LONG_MAX, False /* delete */,
53 aPropType, &actTypeAtom, &actFmt,
54 nItems, &nBytesAfter, &propVal);
55 if (rc != Success)
56 return VBoxGuestX11Pointer<unsigned char>(0);
57
58 return VBoxGuestX11Pointer<unsigned char>(propVal);
59}
60
61/**
62 * Initialise the guest and ensure that it is capable of handling seamless mode
63 *
64 * @returns true if it can handle seamless, false otherwise
65 */
66int VBoxGuestSeamlessX11::init(VBoxGuestSeamlessObserver *pObserver)
67{
68 int rc = VINF_SUCCESS;
69 /** Dummy values for XShapeQueryExtension */
70 int error, event;
71
72 if (0 != mObserver) /* Assertion */
73 {
74 LogRelThisFunc(("ERROR: attempt to initialise service twice!\n"));
75 return VERR_INTERNAL_ERROR;
76 }
77 if (!mDisplay.isValid())
78 {
79 LogRelThisFunc(("Failed to acquire a connection to the display.\n"));
80 return VERR_ACCESS_DENIED;
81 }
82 if (!XShapeQueryExtension(mDisplay, &event, &error))
83 {
84 LogFlowFunc(("X11 shape extension not supported, returning.\n"));
85 return VERR_NOT_SUPPORTED;
86 }
87 mObserver = pObserver;
88 return rc;
89}
90
91/**
92 * Read information about currently visible windows in the guest and subscribe to X11
93 * events about changes to this information.
94 *
95 * @note This class does not contain its own event thread, so an external thread must
96 * call nextEvent() for as long as events are wished.
97 * @todo This function should switch the guest to fullscreen mode.
98 */
99int VBoxGuestSeamlessX11::start(void)
100{
101 int rc = VINF_SUCCESS;
102
103 isEnabled = true;
104 monitorDesktopWindows();
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 isEnabled = false;
113 freeWindowTree();
114}
115
116void VBoxGuestSeamlessX11::monitorDesktopWindows(void)
117{
118 VBoxGuestX11Pointer<unsigned char> virtualRoots;
119 unsigned long nItems;
120
121 XSelectInput(mDisplay, DefaultRootWindow(mDisplay.get()), PropertyChangeMask);
122 virtualRoots = XXGetProperty(mDisplay, DefaultRootWindow(mDisplay.get()), XA_CARDINAL,
123 VIRTUAL_ROOTS_PROP, &nItems);
124 if ((0 != virtualRoots.get()) && (0 != nItems))
125 {
126 rebuildWindowTree();
127 }
128}
129
130void VBoxGuestSeamlessX11::rebuildWindowTree(void)
131{
132 VBoxGuestX11Pointer<unsigned char> virtualRoots;
133 Window *desktopWindows;
134 unsigned long nItems;
135
136 freeWindowTree();
137 virtualRoots = XXGetProperty(mDisplay, DefaultRootWindow(mDisplay.get()), XA_CARDINAL,
138 VIRTUAL_ROOTS_PROP, &nItems);
139 desktopWindows = reinterpret_cast<Window *>(virtualRoots.get());
140 for (unsigned i = 0; i < nItems; ++i)
141 {
142 addDesktopWindow(desktopWindows[i]);
143 }
144 /* This must be done last, so that we do not treat the desktop windows just added as
145 child windows. */
146 addDesktopWindow(DefaultRootWindow(mDisplay.get()));
147}
148
149/**
150 * Store information about a desktop window and register for structure events on it.
151 * If it is mapped, go through the list of it's children and add information about
152 * mapped children to the tree of visible windows, making sure that those windows are
153 * not already in our list of desktop windows.
154 *
155 * @param hWin the window concerned - should be a "desktop" window
156 */
157void VBoxGuestSeamlessX11::addDesktopWindow(Window hWin)
158{
159 unsigned int cChildren = 0;
160 VBoxGuestX11Pointer<Window> children;
161 XWindowAttributes winAttrib;
162 /** Dummies */
163 Window root, parent, *pChildren;
164
165 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
166 {
167 LogRelFunc(("Failed to get the window attributes for window %d\n", hWin));
168 return;
169 }
170 mDesktopWindows.push_back(VBoxGuestDesktopInfo(hWin, winAttrib.x, winAttrib.y,
171 (IsUnmapped != winAttrib.map_state)));
172 XSelectInput(mDisplay, hWin,
173 StructureNotifyMask | SubstructureNotifyMask | PropertyChangeMask);
174 XQueryTree(mDisplay, hWin, &root, &parent, &pChildren, &cChildren);
175 children = pChildren;
176 if (0 == children.get())
177 {
178 LogRelFunc(("Failed to get the tree of active X11 windows.\n"));
179 }
180 else
181 {
182 for (unsigned int i = 0; i < cChildren; ++i)
183 {
184 bool found = false;
185
186 for (unsigned int j = 0; j < mDesktopWindows.size() && !found; ++j)
187 {
188 if (children.get()[i] == mDesktopWindows[j].mWin)
189 {
190 found = true;
191 }
192 }
193 if (!found)
194 {
195 addWindowToList(children.get()[i], hWin);
196 }
197 }
198 }
199}
200
201void VBoxGuestSeamlessX11::addWindowToList(const Window hWin, const Window hParent)
202{
203 XWindowAttributes winAttrib;
204 VBoxGuestX11Pointer<XRectangle> rects;
205 int cRects, iOrdering;
206 unsigned iParent;
207 bool isVisible = false, found = false;
208
209 if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
210 {
211 LogRelFunc(("Failed to get the window attributes for window %d\n", hWin));
212 return;
213 }
214 XShapeSelectInput(mDisplay, hWin, ShapeNotify);
215 rects = XShapeGetRectangles(mDisplay, hWin, ShapeClip, &cRects, &iOrdering);
216 if (0 == rects.get())
217 {
218 cRects = 0;
219 }
220 if (IsViewable == winAttrib.map_state)
221 {
222 isVisible = true;
223 }
224 for (iParent = 0; iParent < mDesktopWindows.size() && !found; ++iParent)
225 {
226 if (hParent == mDesktopWindows[iParent].mWin)
227 {
228 found = true;
229 }
230 }
231 if (found)
232 {
233 mGuestWindows.addWindow(hWin, isVisible, winAttrib.x, winAttrib.y,
234 winAttrib.width, winAttrib.height, cRects, rects, hParent);
235 }
236}
237
238/**
239 * Free all information in the tree of visible windows
240 */
241void VBoxGuestSeamlessX11::freeWindowTree(void)
242{
243 for (unsigned int i = 0; i != mDesktopWindows.size(); ++i)
244 {
245 XSelectInput(mDisplay, mDesktopWindows[i].mWin, 0);
246 }
247 mDesktopWindows.clear();
248 /* We use post-increment in the operation to prevent the iterator from being invalidated. */
249 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin(); it != mGuestWindows.end();
250 mGuestWindows.removeWindow(it++))
251 {
252 XShapeSelectInput(mDisplay, it->first, 0);
253 }
254}
255
256/**
257 * Waits for a position or shape-related event from guest windows
258 *
259 * @note Called from the guest event thread.
260 */
261void VBoxGuestSeamlessX11::nextEvent(void)
262{
263 XEvent event;
264
265 /* Start by sending information about the current window setup to the host. We do this
266 here because we want to send all such information from a single thread. */
267 mObserver->notify();
268 XNextEvent(mDisplay, &event);
269 switch (event.type)
270 {
271 case ConfigureNotify:
272 doConfigureEvent(&event.xconfigure);
273 break;
274 case MapNotify:
275 doMapEvent(&event.xmap);
276 break;
277 case PropertyNotify:
278 doPropertyEvent(&event.xproperty);
279 break;
280 case ShapeNotify:
281 doShapeEvent(reinterpret_cast<XShapeEvent *>(&event));
282 break;
283 case UnmapNotify:
284 doUnmapEvent(&event.xunmap);
285 break;
286 default:
287 break;
288 }
289}
290
291/**
292 * Handle a configuration event in the seamless event thread by setting the new position and
293 * updating the host's rectangle information.
294 *
295 * @param event the X11 event structure
296 */
297void VBoxGuestSeamlessX11::doConfigureEvent(const XConfigureEvent *event)
298{
299 bool found = false;
300 unsigned i = 0;
301
302 for (i = 0; i < mDesktopWindows.size() && !found; ++i)
303 {
304 if (event->window == mDesktopWindows[i].mWin)
305 {
306 found = true;
307 }
308 }
309 if (found)
310 {
311 mDesktopWindows[i].mx = event->x;
312 mDesktopWindows[i].my = event->y;
313 }
314 else
315 {
316 VBoxGuestWindowList::iterator iter;
317
318 iter = mGuestWindows.find(event->window);
319 if (iter != mGuestWindows.end())
320 {
321 iter->second->mx = event->x;
322 iter->second->my = event->y;
323 }
324 }
325}
326
327/**
328 * Handle a map event in the seamless event thread by adding the guest window to the list of
329 * visible windows and updating the host's rectangle information.
330 *
331 * @param event the X11 event structure
332 */
333void VBoxGuestSeamlessX11::doMapEvent(const XMapEvent *event)
334{
335 VBoxGuestWindowList::iterator iter;
336 bool found = false;
337
338 /* Is one of our desktop windows? */
339 for (unsigned i = 0; i < mDesktopWindows.size() && !found; ++i)
340 {
341 if (event->window == mDesktopWindows[i].mWin)
342 {
343 mDesktopWindows[i].mMapped = true;
344 found = true;
345 }
346 }
347 if (!found)
348 {
349 /* Make sure that the window is not already present in the tree */
350 iter = mGuestWindows.find(event->window);
351 if (iter != mGuestWindows.end())
352 {
353 LogRelFunc(("Warning: MapNotify event received for a window listed as mapped\n"));
354 mGuestWindows.removeWindow(event->window);
355 }
356 addWindowToList(event->window, event->event);
357 }
358}
359
360/**
361 * If the list of virtual root windows changes, completely rescan visible windows.
362 *
363 * @param event the X11 event structure
364 */
365void VBoxGuestSeamlessX11::doPropertyEvent(const XPropertyEvent *event)
366{
367 if (XInternAtom(mDisplay, VIRTUAL_ROOTS_PROP, true) == event->atom)
368 {
369 rebuildWindowTree();
370 }
371}
372
373/**
374 * Handle a window shape change event in the seamless event thread by changing the set of
375 * visible rectangles for the window in the list of visible guest windows and updating the
376 * host's rectangle information.
377 *
378 * @param event the X11 event structure
379 */
380void VBoxGuestSeamlessX11::doShapeEvent(const XShapeEvent *event)
381{
382 VBoxGuestWindowList::iterator iter;
383 VBoxGuestX11Pointer<XRectangle> rects;
384 int cRects, iOrdering;
385
386 iter = mGuestWindows.find(event->window);
387 if (iter != mGuestWindows.end())
388 {
389 rects = XShapeGetRectangles(mDisplay, iter->first, ShapeClip, &cRects, &iOrdering);
390 if (0 == rects.get())
391 {
392 cRects = 0;
393 }
394 iter->second->mapRects = rects;
395 iter->second->mcRects = cRects;
396 }
397}
398
399/**
400 * Handle an unmap event in the seamless event thread by removing the guest window from the
401 * list of visible windows and updating the host's rectangle information.
402 *
403 * @param event the X11 event structure
404 */
405void VBoxGuestSeamlessX11::doUnmapEvent(const XUnmapEvent *event)
406{
407 VBoxGuestWindowList::iterator iter;
408 bool found = false;
409
410 /* Is this is one of our desktop windows? */
411 for (unsigned i = 0; i < mDesktopWindows.size() && !found; ++i)
412 {
413 if (event->window == mDesktopWindows[i].mWin)
414 {
415 mDesktopWindows[i].mMapped = false;
416 found = true;
417 }
418 }
419 if (!found)
420 {
421 /* Make sure that the window is not already present in the tree */
422 iter = mGuestWindows.find(event->window);
423 if (iter != mGuestWindows.end())
424 {
425 mGuestWindows.removeWindow(event->window);
426 }
427 else
428 {
429 LogRelFunc(("Warning: UnmapNotify event received for a window not listed as mapped\n"));
430 }
431 }
432}
433
434/**
435 * Sends an updated list of visible rectangles to the host
436 */
437std::auto_ptr<std::vector<RTRECT> > VBoxGuestSeamlessX11::getRects(void)
438{
439 unsigned cRects = 0;
440 std::auto_ptr<std::vector<RTRECT> > apRects(new std::vector<RTRECT>);
441
442 if (0 != mcRects)
443 {
444 apRects.get()->reserve(mcRects * 2);
445 }
446 for (VBoxGuestWindowList::iterator it = mGuestWindows.begin();
447 it != mGuestWindows.end(); ++it)
448 {
449 Window hParent = it->second->mParent;
450 unsigned iParent;
451 bool found = false;
452
453 for (iParent = 0; iParent < mDesktopWindows.size() && !found; ++iParent)
454 {
455 if (mDesktopWindows[iParent].mWin == hParent)
456 {
457 found = true;
458 }
459 }
460 if (found && mDesktopWindows[iParent - 1].mMapped && it->second->mMapped)
461 {
462 for (int i = 0; i < it->second->mcRects; ++i)
463 {
464 RTRECT rect;
465 rect.xLeft = it->second->mx
466 + it->second->mapRects.get()[i].x
467 + mDesktopWindows[iParent].mx;
468 rect.yBottom = it->second->my
469 + it->second->mapRects.get()[i].y
470 + it->second->mapRects.get()[i].height
471 + mDesktopWindows[iParent].my;
472 rect.xRight = it->second->mx
473 + it->second->mapRects.get()[i].x
474 + it->second->mapRects.get()[i].width
475 + mDesktopWindows[iParent].mx;
476 rect.yTop = it->second->my
477 + it->second->mapRects.get()[i].y
478 + mDesktopWindows[iParent].my;
479 apRects.get()->push_back(rect);
480 }
481 cRects += it->second->mcRects;
482 }
483 }
484 mcRects = cRects;
485 return apRects;
486}
487
488/**
489 * Send a client event to wake up the X11 seamless event loop prior to stopping it.
490 *
491 * @note This function should only be called from the host event thread.
492 */
493bool VBoxGuestSeamlessX11::interruptEvent(void)
494{
495 XClientMessageEvent clientMessage = { ClientMessage }; /* Other members set to zero. */
496 XSendEvent(mDisplay, DefaultRootWindow(mDisplay.get()), false, StructureNotifyMask,
497 reinterpret_cast<XEvent *>(&clientMessage));
498 return true;
499}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette