VirtualBox

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

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

Additions/x11: seamless for Linux guests now works in a limited way on X.org 1.3 and above with KDE

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