VirtualBox

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

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

Additions/x11: seamless fixes for Linux guests

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