VirtualBox

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

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

Additions/X11: more seamless fixes

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