VirtualBox

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

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

Guest Additions: update for the Linux seamless additions

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