[55401] | 1 | /* $Id: seamless-x11.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
|
---|
[6202] | 2 | /** @file
|
---|
[21227] | 3 | * X11 Seamless mode.
|
---|
[6202] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2008-2023 Oracle and/or its affiliates.
|
---|
[6202] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[6202] | 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
[57358] | 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[76474] | 32 | #include <iprt/errcore.h>
|
---|
[6202] | 33 | #include <iprt/assert.h>
|
---|
[37721] | 34 | #include <iprt/vector.h>
|
---|
[90057] | 35 | #include <iprt/thread.h>
|
---|
[7449] | 36 | #include <VBox/log.h>
|
---|
[6202] | 37 |
|
---|
[99600] | 38 | #include "seamless.h"
|
---|
[83135] | 39 | #include "seamless-x11.h"
|
---|
| 40 | #include "VBoxClient.h"
|
---|
| 41 |
|
---|
[83045] | 42 | #include <X11/Xatom.h>
|
---|
| 43 | #include <X11/Xmu/WinUtil.h>
|
---|
| 44 |
|
---|
[7595] | 45 | #include <limits.h>
|
---|
| 46 |
|
---|
[98535] | 47 |
|
---|
| 48 | /*********************************************************************************************************************************
|
---|
| 49 | * Defined Constants And Macros *
|
---|
| 50 | *********************************************************************************************************************************/
|
---|
[21207] | 51 | #ifdef TESTCASE
|
---|
[98535] | 52 | # undef DefaultRootWindow
|
---|
| 53 | # define DefaultRootWindow XDefaultRootWindow
|
---|
[21207] | 54 | #endif
|
---|
[7106] | 55 |
|
---|
[6202] | 56 |
|
---|
[98535] | 57 | /*********************************************************************************************************************************
|
---|
| 58 | * Internal Functions *
|
---|
| 59 | *********************************************************************************************************************************/
|
---|
| 60 |
|
---|
[99739] | 61 | static unsigned char *XXGetProperty(Display *aDpy, Window aWnd, Atom aPropType,
|
---|
[7048] | 62 | const char *aPropName, unsigned long *nItems)
|
---|
[6202] | 63 | {
|
---|
[81040] | 64 | LogRelFlowFuncEnter();
|
---|
[6202] | 65 | Atom propNameAtom = XInternAtom (aDpy, aPropName,
|
---|
| 66 | True /* only_if_exists */);
|
---|
| 67 | if (propNameAtom == None)
|
---|
| 68 | {
|
---|
[7048] | 69 | return NULL;
|
---|
[6202] | 70 | }
|
---|
| 71 |
|
---|
| 72 | Atom actTypeAtom = None;
|
---|
| 73 | int actFmt = 0;
|
---|
| 74 | unsigned long nBytesAfter = 0;
|
---|
| 75 | unsigned char *propVal = 0;
|
---|
| 76 | int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
|
---|
| 77 | 0, LONG_MAX, False /* delete */,
|
---|
| 78 | aPropType, &actTypeAtom, &actFmt,
|
---|
| 79 | nItems, &nBytesAfter, &propVal);
|
---|
| 80 | if (rc != Success)
|
---|
[7048] | 81 | return NULL;
|
---|
[6202] | 82 |
|
---|
[81040] | 83 | LogRelFlowFuncLeave();
|
---|
[7048] | 84 | return propVal;
|
---|
[6202] | 85 | }
|
---|
| 86 |
|
---|
[99600] | 87 | int VBClX11SeamlessMonitor::init(PFNSENDREGIONUPDATE pHostCallback)
|
---|
[6202] | 88 | {
|
---|
| 89 | int rc = VINF_SUCCESS;
|
---|
| 90 |
|
---|
[81040] | 91 | LogRelFlowFuncEnter();
|
---|
[52564] | 92 | if (mHostCallback != NULL) /* Assertion */
|
---|
[6202] | 93 | {
|
---|
[81040] | 94 | VBClLogError("Attempting to initialise seamless guest object twice!\n");
|
---|
[6202] | 95 | return VERR_INTERNAL_ERROR;
|
---|
| 96 | }
|
---|
[36662] | 97 | if (!(mDisplay = XOpenDisplay(NULL)))
|
---|
[6202] | 98 | {
|
---|
[81040] | 99 | VBClLogError("Seamless guest object failed to acquire a connection to the display\n");
|
---|
[6202] | 100 | return VERR_ACCESS_DENIED;
|
---|
| 101 | }
|
---|
[52564] | 102 | mHostCallback = pHostCallback;
|
---|
[57486] | 103 | mEnabled = false;
|
---|
[57344] | 104 | unmonitorClientList();
|
---|
[81040] | 105 | LogRelFlowFuncLeaveRC(rc);
|
---|
[6202] | 106 | return rc;
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | /**
|
---|
[86394] | 110 | * Shutdown seamless event monitoring.
|
---|
| 111 | */
|
---|
[99600] | 112 | void VBClX11SeamlessMonitor::uninit(void)
|
---|
[86394] | 113 | {
|
---|
| 114 | if (mHostCallback)
|
---|
| 115 | stop();
|
---|
| 116 | mHostCallback = NULL;
|
---|
[90057] | 117 |
|
---|
| 118 | /* Before closing a Display, make sure X11 is still running. The indicator
|
---|
| 119 | * that is when XOpenDisplay() returns non NULL. If it is not a
|
---|
| 120 | * case, XCloseDisplay() will hang on internal X11 mutex forever. */
|
---|
| 121 | Display *pDisplay = XOpenDisplay(NULL);
|
---|
| 122 | if (pDisplay)
|
---|
| 123 | {
|
---|
| 124 | XCloseDisplay(pDisplay);
|
---|
| 125 | if (mDisplay)
|
---|
| 126 | {
|
---|
| 127 | XCloseDisplay(mDisplay);
|
---|
| 128 | mDisplay = NULL;
|
---|
| 129 | }
|
---|
| 130 | }
|
---|
| 131 |
|
---|
[86394] | 132 | if (mpRects)
|
---|
| 133 | {
|
---|
| 134 | RTMemFree(mpRects);
|
---|
| 135 | mpRects = NULL;
|
---|
| 136 | }
|
---|
| 137 | }
|
---|
| 138 |
|
---|
| 139 | /**
|
---|
[6202] | 140 | * Read information about currently visible windows in the guest and subscribe to X11
|
---|
| 141 | * events about changes to this information.
|
---|
| 142 | *
|
---|
| 143 | * @note This class does not contain its own event thread, so an external thread must
|
---|
[50346] | 144 | * call nextConfigurationEvent() for as long as events are wished.
|
---|
[6202] | 145 | * @todo This function should switch the guest to fullscreen mode.
|
---|
| 146 | */
|
---|
[99600] | 147 | int VBClX11SeamlessMonitor::start(void)
|
---|
[6202] | 148 | {
|
---|
| 149 | int rc = VINF_SUCCESS;
|
---|
[6290] | 150 | /** Dummy values for XShapeQueryExtension */
|
---|
| 151 | int error, event;
|
---|
[6202] | 152 |
|
---|
[81040] | 153 | LogRelFlowFuncEnter();
|
---|
[57344] | 154 | if (mEnabled)
|
---|
| 155 | return VINF_SUCCESS;
|
---|
[6290] | 156 | mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error);
|
---|
| 157 | mEnabled = true;
|
---|
| 158 | monitorClientList();
|
---|
| 159 | rebuildWindowTree();
|
---|
[81040] | 160 | LogRelFlowFuncLeaveRC(rc);
|
---|
[6202] | 161 | return rc;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | /** Stop reporting seamless events to the host. Free information about guest windows
|
---|
| 165 | and stop requesting updates. */
|
---|
[99600] | 166 | void VBClX11SeamlessMonitor::stop(void)
|
---|
[6202] | 167 | {
|
---|
[81040] | 168 | LogRelFlowFuncEnter();
|
---|
[57344] | 169 | if (!mEnabled)
|
---|
| 170 | return;
|
---|
[6290] | 171 | mEnabled = false;
|
---|
| 172 | unmonitorClientList();
|
---|
[6202] | 173 | freeWindowTree();
|
---|
[81040] | 174 | LogRelFlowFuncLeave();
|
---|
[6202] | 175 | }
|
---|
| 176 |
|
---|
[99600] | 177 | void VBClX11SeamlessMonitor::monitorClientList(void)
|
---|
[6202] | 178 | {
|
---|
[81040] | 179 | LogRelFlowFuncEnter();
|
---|
[57265] | 180 | XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask);
|
---|
[6202] | 181 | }
|
---|
| 182 |
|
---|
[99600] | 183 | void VBClX11SeamlessMonitor::unmonitorClientList(void)
|
---|
[6290] | 184 | {
|
---|
[81040] | 185 | LogRelFlowFuncEnter();
|
---|
[57344] | 186 | XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask);
|
---|
[6290] | 187 | }
|
---|
| 188 |
|
---|
[7048] | 189 | /**
|
---|
| 190 | * Recreate the table of toplevel windows of clients on the default root window of the
|
---|
| 191 | * X server.
|
---|
| 192 | */
|
---|
[99600] | 193 | void VBClX11SeamlessMonitor::rebuildWindowTree(void)
|
---|
[6202] | 194 | {
|
---|
[81040] | 195 | LogRelFlowFuncEnter();
|
---|
[7048] | 196 | freeWindowTree();
|
---|
[36662] | 197 | addClients(DefaultRootWindow(mDisplay));
|
---|
[21441] | 198 | mChanged = true;
|
---|
[7048] | 199 | }
|
---|
[6202] | 200 |
|
---|
[7048] | 201 |
|
---|
| 202 | /**
|
---|
| 203 | * Look at the list of children of a virtual root window and add them to the list of clients
|
---|
| 204 | * if they belong to a client which is not a virtual root.
|
---|
| 205 | *
|
---|
| 206 | * @param hRoot the virtual root window to be examined
|
---|
| 207 | */
|
---|
[99600] | 208 | void VBClX11SeamlessMonitor::addClients(const Window hRoot)
|
---|
[7048] | 209 | {
|
---|
| 210 | /** Unused out parameters of XQueryTree */
|
---|
| 211 | Window hRealRoot, hParent;
|
---|
| 212 | /** The list of children of the root supplied, raw pointer */
|
---|
[36662] | 213 | Window *phChildrenRaw = NULL;
|
---|
[7048] | 214 | /** The list of children of the root supplied, auto-pointer */
|
---|
[36662] | 215 | Window *phChildren;
|
---|
[7048] | 216 | /** The number of children of the root supplied */
|
---|
| 217 | unsigned cChildren;
|
---|
| 218 |
|
---|
[81040] | 219 | LogRelFlowFuncEnter();
|
---|
[36662] | 220 | if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
|
---|
[7048] | 221 | return;
|
---|
| 222 | phChildren = phChildrenRaw;
|
---|
| 223 | for (unsigned i = 0; i < cChildren; ++i)
|
---|
[57264] | 224 | addClientWindow(phChildren[i]);
|
---|
[36662] | 225 | XFree(phChildrenRaw);
|
---|
[81040] | 226 | LogRelFlowFuncLeave();
|
---|
[7106] | 227 | }
|
---|
| 228 |
|
---|
| 229 |
|
---|
[99600] | 230 | void VBClX11SeamlessMonitor::addClientWindow(const Window hWin)
|
---|
[7106] | 231 | {
|
---|
[81040] | 232 | LogRelFlowFuncEnter();
|
---|
[7106] | 233 | XWindowAttributes winAttrib;
|
---|
| 234 | bool fAddWin = true;
|
---|
[57264] | 235 | Window hClient = XmuClientWindow(mDisplay, hWin);
|
---|
[7106] | 236 |
|
---|
[57264] | 237 | if (isVirtualRoot(hClient))
|
---|
| 238 | fAddWin = false;
|
---|
[7106] | 239 | if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib))
|
---|
[6202] | 240 | {
|
---|
[81040] | 241 | VBClLogError("Failed to get the window attributes for window %d\n", hWin);
|
---|
[7106] | 242 | fAddWin = false;
|
---|
[6202] | 243 | }
|
---|
[7106] | 244 | if (fAddWin && (winAttrib.map_state == IsUnmapped))
|
---|
| 245 | fAddWin = false;
|
---|
[21638] | 246 | XSizeHints dummyHints;
|
---|
| 247 | long dummyLong;
|
---|
[57265] | 248 | /* Apparently (?) some old kwin versions had unwanted client windows
|
---|
| 249 | * without normal hints. */
|
---|
[21638] | 250 | if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints,
|
---|
| 251 | &dummyLong)))
|
---|
| 252 | {
|
---|
[81040] | 253 | LogRelFlowFunc(("window %lu, client window %lu has no size hints\n", hWin, hClient));
|
---|
[21638] | 254 | fAddWin = false;
|
---|
| 255 | }
|
---|
[7106] | 256 | if (fAddWin)
|
---|
| 257 | {
|
---|
[36672] | 258 | XRectangle *pRects = NULL;
|
---|
[7106] | 259 | int cRects = 0, iOrdering;
|
---|
| 260 | bool hasShape = false;
|
---|
| 261 |
|
---|
[27709] | 262 | LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin,
|
---|
[21638] | 263 | hClient));
|
---|
[7106] | 264 | if (mSupportsShape)
|
---|
| 265 | {
|
---|
| 266 | XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask);
|
---|
[36662] | 267 | pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering);
|
---|
| 268 | if (!pRects)
|
---|
[7106] | 269 | cRects = 0;
|
---|
| 270 | else
|
---|
| 271 | {
|
---|
| 272 | if ( (cRects > 1)
|
---|
[36662] | 273 | || (pRects[0].x != 0)
|
---|
| 274 | || (pRects[0].y != 0)
|
---|
| 275 | || (pRects[0].width != winAttrib.width)
|
---|
| 276 | || (pRects[0].height != winAttrib.height)
|
---|
[7106] | 277 | )
|
---|
| 278 | hasShape = true;
|
---|
| 279 | }
|
---|
| 280 | }
|
---|
| 281 | mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y,
|
---|
[36662] | 282 | winAttrib.width, winAttrib.height, cRects,
|
---|
| 283 | pRects);
|
---|
[7106] | 284 | }
|
---|
[81040] | 285 | LogRelFlowFuncLeave();
|
---|
[6202] | 286 | }
|
---|
| 287 |
|
---|
[7048] | 288 |
|
---|
| 289 | /**
|
---|
| 290 | * Checks whether a window is a virtual root.
|
---|
| 291 | * @returns true if it is, false otherwise
|
---|
| 292 | * @param hWin the window to be examined
|
---|
| 293 | */
|
---|
[99600] | 294 | bool VBClX11SeamlessMonitor::isVirtualRoot(Window hWin)
|
---|
[7048] | 295 | {
|
---|
[36662] | 296 | unsigned char *windowTypeRaw = NULL;
|
---|
| 297 | Atom *windowType;
|
---|
[7048] | 298 | unsigned long ulCount;
|
---|
| 299 | bool rc = false;
|
---|
| 300 |
|
---|
[81040] | 301 | LogRelFlowFuncEnter();
|
---|
[7048] | 302 | windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount);
|
---|
[7446] | 303 | if (windowTypeRaw != NULL)
|
---|
| 304 | {
|
---|
[36662] | 305 | windowType = (Atom *)(windowTypeRaw);
|
---|
[7446] | 306 | if ( (ulCount != 0)
|
---|
| 307 | && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True)))
|
---|
| 308 | rc = true;
|
---|
| 309 | }
|
---|
[36662] | 310 | if (windowTypeRaw)
|
---|
| 311 | XFree(windowTypeRaw);
|
---|
[37719] | 312 | LogRelFlowFunc(("returning %RTbool\n", rc));
|
---|
[7048] | 313 | return rc;
|
---|
| 314 | }
|
---|
| 315 |
|
---|
[36685] | 316 | DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam)
|
---|
| 317 | {
|
---|
| 318 | Display *pDisplay = (Display *)pvParam;
|
---|
[7048] | 319 |
|
---|
[57344] | 320 | XShapeSelectInput(pDisplay, pInfo->Core.Key, 0);
|
---|
[36685] | 321 | delete pInfo;
|
---|
| 322 | return VINF_SUCCESS;
|
---|
| 323 | }
|
---|
| 324 |
|
---|
[6202] | 325 | /**
|
---|
| 326 | * Free all information in the tree of visible windows
|
---|
| 327 | */
|
---|
[99600] | 328 | void VBClX11SeamlessMonitor::freeWindowTree(void)
|
---|
[6202] | 329 | {
|
---|
| 330 | /* We use post-increment in the operation to prevent the iterator from being invalidated. */
|
---|
[81040] | 331 | LogRelFlowFuncEnter();
|
---|
[36685] | 332 | mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay);
|
---|
[81040] | 333 | LogRelFlowFuncLeave();
|
---|
[6202] | 334 | }
|
---|
| 335 |
|
---|
[7106] | 336 |
|
---|
[6202] | 337 | /**
|
---|
[21227] | 338 | * Waits for a position or shape-related event from guest windows
|
---|
[6202] | 339 | *
|
---|
| 340 | * @note Called from the guest event thread.
|
---|
| 341 | */
|
---|
[99600] | 342 | void VBClX11SeamlessMonitor::nextConfigurationEvent(void)
|
---|
[6202] | 343 | {
|
---|
| 344 | XEvent event;
|
---|
| 345 |
|
---|
[81040] | 346 | LogRelFlowFuncEnter();
|
---|
[6202] | 347 | /* Start by sending information about the current window setup to the host. We do this
|
---|
| 348 | here because we want to send all such information from a single thread. */
|
---|
[57486] | 349 | if (mChanged && mEnabled)
|
---|
[36806] | 350 | {
|
---|
| 351 | updateRects();
|
---|
[52564] | 352 | mHostCallback(mpRects, mcRects);
|
---|
[36806] | 353 | }
|
---|
[21441] | 354 | mChanged = false;
|
---|
[90057] | 355 |
|
---|
| 356 | if (XPending(mDisplay) > 0)
|
---|
| 357 | {
|
---|
| 358 | /* We execute this even when seamless is disabled, as it also waits for
|
---|
| 359 | * enable and disable notification. */
|
---|
| 360 | XNextEvent(mDisplay, &event);
|
---|
| 361 | } else
|
---|
| 362 | {
|
---|
| 363 | /* This function is called in a loop by upper layer. In order to
|
---|
| 364 | * prevent CPU spinning, sleep a bit before returning. */
|
---|
| 365 | RTThreadSleep(300 /* ms */);
|
---|
| 366 | return;
|
---|
| 367 | }
|
---|
| 368 |
|
---|
[57486] | 369 | if (!mEnabled)
|
---|
| 370 | return;
|
---|
[6202] | 371 | switch (event.type)
|
---|
| 372 | {
|
---|
| 373 | case ConfigureNotify:
|
---|
[37719] | 374 | {
|
---|
| 375 | XConfigureEvent *pConf = &event.xconfigure;
|
---|
| 376 | LogRelFlowFunc(("configure event, window=%lu, x=%i, y=%i, w=%i, h=%i, send_event=%RTbool\n",
|
---|
| 377 | (unsigned long) pConf->window, (int) pConf->x,
|
---|
| 378 | (int) pConf->y, (int) pConf->width,
|
---|
| 379 | (int) pConf->height, pConf->send_event));
|
---|
| 380 | }
|
---|
[21216] | 381 | doConfigureEvent(event.xconfigure.window);
|
---|
[6202] | 382 | break;
|
---|
| 383 | case MapNotify:
|
---|
[37719] | 384 | LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n",
|
---|
| 385 | (unsigned long) event.xmap.window,
|
---|
| 386 | event.xmap.send_event));
|
---|
[57265] | 387 | rebuildWindowTree();
|
---|
[6202] | 388 | break;
|
---|
[57265] | 389 | case PropertyNotify:
|
---|
| 390 | if ( event.xproperty.atom != XInternAtom(mDisplay, "_NET_CLIENT_LIST", True /* only_if_exists */)
|
---|
| 391 | || event.xproperty.window != DefaultRootWindow(mDisplay))
|
---|
| 392 | break;
|
---|
[81040] | 393 | LogRelFlowFunc(("_NET_CLIENT_LIST property event on root window\n"));
|
---|
[57265] | 394 | rebuildWindowTree();
|
---|
| 395 | break;
|
---|
[7106] | 396 | case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */
|
---|
[37719] | 397 | LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n",
|
---|
| 398 | (unsigned long) event.xany.window,
|
---|
| 399 | event.xany.send_event));
|
---|
[21216] | 400 | /* the window member in xany is in the same place as in the shape event */
|
---|
| 401 | doShapeEvent(event.xany.window);
|
---|
[6202] | 402 | break;
|
---|
| 403 | case UnmapNotify:
|
---|
[37719] | 404 | LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n",
|
---|
| 405 | (unsigned long) event.xunmap.window,
|
---|
| 406 | event.xunmap.send_event));
|
---|
[57265] | 407 | rebuildWindowTree();
|
---|
[6202] | 408 | break;
|
---|
| 409 | default:
|
---|
| 410 | break;
|
---|
| 411 | }
|
---|
[57486] | 412 | LogRelFlowFunc(("processed event\n"));
|
---|
[6202] | 413 | }
|
---|
| 414 |
|
---|
| 415 | /**
|
---|
[6290] | 416 | * Handle a configuration event in the seamless event thread by setting the new position.
|
---|
[6202] | 417 | *
|
---|
[65099] | 418 | * @param hWin the window to be examined
|
---|
[6202] | 419 | */
|
---|
[99600] | 420 | void VBClX11SeamlessMonitor::doConfigureEvent(Window hWin)
|
---|
[6202] | 421 | {
|
---|
[36685] | 422 | VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
|
---|
| 423 | if (pInfo)
|
---|
[6202] | 424 | {
|
---|
[7106] | 425 | XWindowAttributes winAttrib;
|
---|
| 426 |
|
---|
[21940] | 427 | if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib))
|
---|
| 428 | return;
|
---|
[36685] | 429 | pInfo->mX = winAttrib.x;
|
---|
| 430 | pInfo->mY = winAttrib.y;
|
---|
| 431 | pInfo->mWidth = winAttrib.width;
|
---|
| 432 | pInfo->mHeight = winAttrib.height;
|
---|
[21441] | 433 | mChanged = true;
|
---|
[6202] | 434 | }
|
---|
| 435 | }
|
---|
| 436 |
|
---|
| 437 | /**
|
---|
[6290] | 438 | * Handle a window shape change event in the seamless event thread.
|
---|
[6202] | 439 | *
|
---|
[65099] | 440 | * @param hWin the window to be examined
|
---|
[6202] | 441 | */
|
---|
[99600] | 442 | void VBClX11SeamlessMonitor::doShapeEvent(Window hWin)
|
---|
[6202] | 443 | {
|
---|
[81040] | 444 | LogRelFlowFuncEnter();
|
---|
[36685] | 445 | VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin);
|
---|
| 446 | if (pInfo)
|
---|
[6202] | 447 | {
|
---|
[36662] | 448 | XRectangle *pRects;
|
---|
[7106] | 449 | int cRects = 0, iOrdering;
|
---|
| 450 |
|
---|
[36662] | 451 | pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects,
|
---|
| 452 | &iOrdering);
|
---|
| 453 | if (!pRects)
|
---|
[21940] | 454 | cRects = 0;
|
---|
[36685] | 455 | pInfo->mhasShape = true;
|
---|
| 456 | if (pInfo->mpRects)
|
---|
| 457 | XFree(pInfo->mpRects);
|
---|
| 458 | pInfo->mcRects = cRects;
|
---|
| 459 | pInfo->mpRects = pRects;
|
---|
[21441] | 460 | mChanged = true;
|
---|
[6202] | 461 | }
|
---|
[81040] | 462 | LogRelFlowFuncLeave();
|
---|
[6202] | 463 | }
|
---|
| 464 |
|
---|
| 465 | /**
|
---|
[36806] | 466 | * Gets the list of visible rectangles
|
---|
| 467 | */
|
---|
[99600] | 468 | RTRECT *VBClX11SeamlessMonitor::getRects(void)
|
---|
[36806] | 469 | {
|
---|
| 470 | return mpRects;
|
---|
| 471 | }
|
---|
| 472 |
|
---|
| 473 | /**
|
---|
| 474 | * Gets the number of rectangles in the visible rectangle list
|
---|
| 475 | */
|
---|
[99600] | 476 | size_t VBClX11SeamlessMonitor::getRectCount(void)
|
---|
[36806] | 477 | {
|
---|
| 478 | return mcRects;
|
---|
| 479 | }
|
---|
| 480 |
|
---|
| 481 | RTVEC_DECL(RectList, RTRECT)
|
---|
| 482 |
|
---|
[85121] | 483 | static DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo, struct RectList *pRects)
|
---|
[36685] | 484 | {
|
---|
| 485 | if (pInfo->mhasShape)
|
---|
[6202] | 486 | {
|
---|
[36685] | 487 | for (int i = 0; i < pInfo->mcRects; ++i)
|
---|
| 488 | {
|
---|
[36806] | 489 | RTRECT *pRect;
|
---|
[37423] | 490 |
|
---|
[36806] | 491 | pRect = RectListPushBack(pRects);
|
---|
| 492 | if (!pRect)
|
---|
| 493 | return VERR_NO_MEMORY;
|
---|
| 494 | pRect->xLeft = pInfo->mX
|
---|
| 495 | + pInfo->mpRects[i].x;
|
---|
| 496 | pRect->yBottom = pInfo->mY
|
---|
| 497 | + pInfo->mpRects[i].y
|
---|
| 498 | + pInfo->mpRects[i].height;
|
---|
| 499 | pRect->xRight = pInfo->mX
|
---|
| 500 | + pInfo->mpRects[i].x
|
---|
| 501 | + pInfo->mpRects[i].width;
|
---|
| 502 | pRect->yTop = pInfo->mY
|
---|
| 503 | + pInfo->mpRects[i].y;
|
---|
[36685] | 504 | }
|
---|
[7079] | 505 | }
|
---|
[36685] | 506 | else
|
---|
| 507 | {
|
---|
[36806] | 508 | RTRECT *pRect;
|
---|
| 509 |
|
---|
| 510 | pRect = RectListPushBack(pRects);
|
---|
| 511 | if (!pRect)
|
---|
| 512 | return VERR_NO_MEMORY;
|
---|
| 513 | pRect->xLeft = pInfo->mX;
|
---|
| 514 | pRect->yBottom = pInfo->mY
|
---|
| 515 | + pInfo->mHeight;
|
---|
| 516 | pRect->xRight = pInfo->mX
|
---|
| 517 | + pInfo->mWidth;
|
---|
| 518 | pRect->yTop = pInfo->mY;
|
---|
[36685] | 519 | }
|
---|
| 520 | return VINF_SUCCESS;
|
---|
[6202] | 521 | }
|
---|
| 522 |
|
---|
| 523 | /**
|
---|
[36806] | 524 | * Updates the list of seamless rectangles
|
---|
[6202] | 525 | */
|
---|
[99600] | 526 | int VBClX11SeamlessMonitor::updateRects(void)
|
---|
[6202] | 527 | {
|
---|
[81040] | 528 | LogRelFlowFuncEnter();
|
---|
[36806] | 529 | struct RectList rects = RTVEC_INITIALIZER;
|
---|
[6202] | 530 |
|
---|
[62883] | 531 | if (mcRects != 0)
|
---|
[6202] | 532 | {
|
---|
[36806] | 533 | int rc = RectListReserve(&rects, mcRects * 2);
|
---|
| 534 | if (RT_FAILURE(rc))
|
---|
| 535 | return rc;
|
---|
[6202] | 536 | }
|
---|
[85121] | 537 | mGuestWindows.doWithAll((PFNVBOXGUESTWINCALLBACK)getRectsCallback, &rects);
|
---|
[36806] | 538 | if (mpRects)
|
---|
| 539 | RTMemFree(mpRects);
|
---|
| 540 | mcRects = RectListSize(&rects);
|
---|
| 541 | mpRects = RectListDetach(&rects);
|
---|
[81040] | 542 | LogRelFlowFuncLeave();
|
---|
[36806] | 543 | return VINF_SUCCESS;
|
---|
[6202] | 544 | }
|
---|
| 545 |
|
---|
| 546 | /**
|
---|
| 547 | * Send a client event to wake up the X11 seamless event loop prior to stopping it.
|
---|
| 548 | *
|
---|
| 549 | * @note This function should only be called from the host event thread.
|
---|
| 550 | */
|
---|
[99600] | 551 | bool VBClX11SeamlessMonitor::interruptEventWait(void)
|
---|
[6202] | 552 | {
|
---|
[98534] | 553 | LogRelFlowFuncEnter();
|
---|
| 554 |
|
---|
[57344] | 555 | Display *pDisplay = XOpenDisplay(NULL);
|
---|
| 556 | if (pDisplay == NULL)
|
---|
[89766] | 557 | {
|
---|
[90057] | 558 | VBClLogError("Failed to open X11 display\n");
|
---|
[89766] | 559 | return false;
|
---|
| 560 | }
|
---|
| 561 |
|
---|
[6290] | 562 | /* Message contents set to zero. */
|
---|
[97170] | 563 | XClientMessageEvent clientMessage =
|
---|
[98534] | 564 | {
|
---|
| 565 | /* .type = */ ClientMessage,
|
---|
| 566 | /* .serial = */ 0,
|
---|
| 567 | /* .send_event = */ 0,
|
---|
| 568 | /* .display = */ 0,
|
---|
| 569 | /* .window = */ 0,
|
---|
| 570 | /* .message_type = */ XInternAtom(pDisplay, "VBOX_CLIENT_SEAMLESS_HEARTBEAT", false),
|
---|
| 571 | /* .format = */ 8,
|
---|
| 572 | /* .data ... */
|
---|
| 573 | };
|
---|
[6290] | 574 |
|
---|
[98534] | 575 | bool rc = false;
|
---|
[57344] | 576 | if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false,
|
---|
| 577 | PropertyChangeMask, (XEvent *)&clientMessage))
|
---|
[7449] | 578 | rc = true;
|
---|
[98534] | 579 |
|
---|
[57344] | 580 | XCloseDisplay(pDisplay);
|
---|
[37719] | 581 | LogRelFlowFunc(("returning %RTbool\n", rc));
|
---|
[7449] | 582 | return rc;
|
---|
[6202] | 583 | }
|
---|
[99600] | 584 |
|
---|
| 585 |
|
---|
| 586 | /*********************************************************************************************************************************
|
---|
| 587 | * VBClX11SeamlessSvc implementation *
|
---|
| 588 | ********************************************************************************************************************************/
|
---|
| 589 |
|
---|
| 590 | VBClX11SeamlessSvc::VBClX11SeamlessSvc(void)
|
---|
| 591 | {
|
---|
| 592 | mX11MonitorThread = NIL_RTTHREAD;
|
---|
| 593 | mX11MonitorThreadStopping = false;
|
---|
| 594 |
|
---|
| 595 | mMode = VMMDev_Seamless_Disabled;
|
---|
| 596 | mfPaused = true;
|
---|
| 597 | }
|
---|
| 598 |
|
---|
| 599 | VBClX11SeamlessSvc::~VBClX11SeamlessSvc()
|
---|
| 600 | {
|
---|
| 601 | /* Stopping will be done via main.cpp. */
|
---|
| 602 | }
|
---|
| 603 |
|
---|
| 604 | /** @copydoc VBCLSERVICE::pfnInit */
|
---|
| 605 | int VBClX11SeamlessSvc::init(void)
|
---|
| 606 | {
|
---|
| 607 | int rc;
|
---|
| 608 | const char *pcszStage;
|
---|
| 609 |
|
---|
| 610 | do
|
---|
| 611 | {
|
---|
| 612 | pcszStage = "Connecting to the X server";
|
---|
[99601] | 613 | rc = mX11Monitor.init(VBClSeamlessSendRegionUpdate);
|
---|
[99600] | 614 | if (RT_FAILURE(rc))
|
---|
| 615 | break;
|
---|
| 616 | pcszStage = "Setting guest IRQ filter mask";
|
---|
| 617 | rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0);
|
---|
| 618 | if (RT_FAILURE(rc))
|
---|
| 619 | break;
|
---|
| 620 | pcszStage = "Reporting support for seamless capability";
|
---|
| 621 | rc = VbglR3SeamlessSetCap(true);
|
---|
| 622 | if (RT_FAILURE(rc))
|
---|
| 623 | break;
|
---|
| 624 | rc = startX11MonitorThread();
|
---|
| 625 | if (RT_FAILURE(rc))
|
---|
| 626 | break;
|
---|
| 627 |
|
---|
| 628 | } while(0);
|
---|
| 629 |
|
---|
| 630 | if (RT_FAILURE(rc))
|
---|
| 631 | VBClLogError("Failed to start in stage '%s' -- error %Rrc\n", pcszStage, rc);
|
---|
| 632 |
|
---|
| 633 | return rc;
|
---|
| 634 | }
|
---|
| 635 |
|
---|
| 636 | /** @copydoc VBCLSERVICE::pfnWorker */
|
---|
| 637 | int VBClX11SeamlessSvc::worker(bool volatile *pfShutdown)
|
---|
| 638 | {
|
---|
| 639 | int rc = VINF_SUCCESS;
|
---|
| 640 |
|
---|
| 641 | /* Let the main thread know that it can continue spawning services. */
|
---|
| 642 | RTThreadUserSignal(RTThreadSelf());
|
---|
| 643 |
|
---|
| 644 | /* This will only exit if something goes wrong. */
|
---|
| 645 | for (;;)
|
---|
| 646 | {
|
---|
| 647 | if (ASMAtomicReadBool(pfShutdown))
|
---|
| 648 | break;
|
---|
| 649 |
|
---|
| 650 | rc = nextStateChangeEvent();
|
---|
| 651 |
|
---|
| 652 | if (rc == VERR_TRY_AGAIN)
|
---|
| 653 | rc = VINF_SUCCESS;
|
---|
| 654 |
|
---|
| 655 | if (RT_FAILURE(rc))
|
---|
| 656 | break;
|
---|
| 657 |
|
---|
| 658 | if (ASMAtomicReadBool(pfShutdown))
|
---|
| 659 | break;
|
---|
| 660 |
|
---|
| 661 | /* If we are not stopping, sleep for a bit to avoid using up too
|
---|
| 662 | much CPU while retrying. */
|
---|
| 663 | RTThreadYield();
|
---|
| 664 | }
|
---|
| 665 |
|
---|
| 666 | return rc;
|
---|
| 667 | }
|
---|
| 668 |
|
---|
| 669 | /** @copydoc VBCLSERVICE::pfnStop */
|
---|
| 670 | void VBClX11SeamlessSvc::stop(void)
|
---|
| 671 | {
|
---|
| 672 | VbglR3SeamlessSetCap(false);
|
---|
| 673 | VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
|
---|
| 674 | stopX11MonitorThread();
|
---|
| 675 | }
|
---|
| 676 |
|
---|
| 677 | /** @copydoc VBCLSERVICE::pfnTerm */
|
---|
| 678 | int VBClX11SeamlessSvc::term(void)
|
---|
| 679 | {
|
---|
| 680 | mX11Monitor.uninit();
|
---|
| 681 | return VINF_SUCCESS;
|
---|
| 682 | }
|
---|
| 683 |
|
---|
| 684 | /**
|
---|
| 685 | * Waits for a seamless state change events from the host and dispatch it.
|
---|
| 686 | *
|
---|
| 687 | * @returns VBox return code, or
|
---|
| 688 | * VERR_TRY_AGAIN if no new status is available and we have to try it again
|
---|
| 689 | * at some later point in time.
|
---|
| 690 | */
|
---|
| 691 | int VBClX11SeamlessSvc::nextStateChangeEvent(void)
|
---|
| 692 | {
|
---|
| 693 | VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled;
|
---|
| 694 |
|
---|
| 695 | int rc = VbglR3SeamlessWaitEvent(&newMode);
|
---|
| 696 | if (RT_SUCCESS(rc))
|
---|
| 697 | {
|
---|
| 698 | mMode = newMode;
|
---|
| 699 | switch (newMode)
|
---|
| 700 | {
|
---|
| 701 | case VMMDev_Seamless_Visible_Region:
|
---|
| 702 | /* A simplified seamless mode, obtained by making the host VM window
|
---|
| 703 | * borderless and making the guest desktop transparent. */
|
---|
| 704 | VBClLogVerbose(2, "\"Visible region\" mode requested\n");
|
---|
| 705 | break;
|
---|
| 706 | case VMMDev_Seamless_Disabled:
|
---|
| 707 | VBClLogVerbose(2, "\"Disabled\" mode requested\n");
|
---|
| 708 | break;
|
---|
| 709 | case VMMDev_Seamless_Host_Window:
|
---|
| 710 | /* One host window represents one guest window. Not yet implemented. */
|
---|
| 711 | VBClLogVerbose(2, "Unsupported \"host window\" mode requested\n");
|
---|
| 712 | return VERR_NOT_SUPPORTED;
|
---|
| 713 | default:
|
---|
| 714 | VBClLogError("Unsupported mode %d requested\n", newMode);
|
---|
| 715 | return VERR_NOT_SUPPORTED;
|
---|
| 716 | }
|
---|
| 717 | }
|
---|
| 718 | if ( RT_SUCCESS(rc)
|
---|
| 719 | || rc == VERR_TRY_AGAIN)
|
---|
| 720 | {
|
---|
| 721 | if (mMode == VMMDev_Seamless_Visible_Region)
|
---|
| 722 | mfPaused = false;
|
---|
| 723 | else
|
---|
| 724 | mfPaused = true;
|
---|
| 725 | mX11Monitor.interruptEventWait();
|
---|
| 726 | }
|
---|
| 727 | else
|
---|
| 728 | VBClLogError("VbglR3SeamlessWaitEvent returned %Rrc\n", rc);
|
---|
| 729 |
|
---|
| 730 | return rc;
|
---|
| 731 | }
|
---|
| 732 |
|
---|
| 733 | /**
|
---|
| 734 | * The actual X11 window configuration change monitor thread function.
|
---|
| 735 | */
|
---|
| 736 | int VBClX11SeamlessSvc::x11MonitorThread(RTTHREAD hThreadSelf, void *pvUser)
|
---|
| 737 | {
|
---|
| 738 | RT_NOREF(hThreadSelf);
|
---|
| 739 |
|
---|
| 740 | VBClX11SeamlessSvc *pThis = (VBClX11SeamlessSvc *)pvUser;
|
---|
| 741 | AssertPtrReturn(pThis, VERR_INVALID_POINTER);
|
---|
| 742 |
|
---|
| 743 | int rc = VINF_SUCCESS;
|
---|
| 744 |
|
---|
| 745 | RTThreadUserSignal(hThreadSelf);
|
---|
| 746 |
|
---|
| 747 | VBClLogVerbose(2, "X11 monitor thread started\n");
|
---|
| 748 |
|
---|
| 749 | while (!pThis->mX11MonitorThreadStopping)
|
---|
| 750 | {
|
---|
| 751 | if (!pThis->mfPaused)
|
---|
| 752 | {
|
---|
| 753 | rc = pThis->mX11Monitor.start();
|
---|
| 754 | if (RT_FAILURE(rc))
|
---|
| 755 | VBClLogFatalError("Failed to change the X11 seamless service state, mfPaused=%RTbool, rc=%Rrc\n",
|
---|
| 756 | pThis->mfPaused, rc);
|
---|
| 757 | }
|
---|
| 758 |
|
---|
| 759 | pThis->mX11Monitor.nextConfigurationEvent();
|
---|
| 760 |
|
---|
| 761 | if ( pThis->mfPaused
|
---|
| 762 | || pThis->mX11MonitorThreadStopping)
|
---|
| 763 | {
|
---|
| 764 | pThis->mX11Monitor.stop();
|
---|
| 765 | }
|
---|
| 766 | }
|
---|
| 767 |
|
---|
| 768 | VBClLogVerbose(2, "X11 monitor thread ended\n");
|
---|
| 769 |
|
---|
| 770 | return rc;
|
---|
| 771 | }
|
---|
| 772 |
|
---|
| 773 | /**
|
---|
| 774 | * Start the X11 window configuration change monitor thread.
|
---|
| 775 | */
|
---|
| 776 | int VBClX11SeamlessSvc::startX11MonitorThread(void)
|
---|
| 777 | {
|
---|
| 778 | mX11MonitorThreadStopping = false;
|
---|
| 779 |
|
---|
| 780 | if (isX11MonitorThreadRunning())
|
---|
| 781 | return VINF_SUCCESS;
|
---|
| 782 |
|
---|
| 783 | int rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThread, this, 0,
|
---|
| 784 | RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
|
---|
| 785 | "seamless x11");
|
---|
| 786 | if (RT_SUCCESS(rc))
|
---|
| 787 | rc = RTThreadUserWait(mX11MonitorThread, RT_MS_30SEC);
|
---|
| 788 |
|
---|
| 789 | if (RT_FAILURE(rc))
|
---|
| 790 | VBClLogError("Failed to start X11 monitor thread, rc=%Rrc\n", rc);
|
---|
| 791 |
|
---|
| 792 | return rc;
|
---|
| 793 | }
|
---|
| 794 |
|
---|
| 795 | /**
|
---|
| 796 | * Stops the monitor thread.
|
---|
| 797 | */
|
---|
| 798 | int VBClX11SeamlessSvc::stopX11MonitorThread(void)
|
---|
| 799 | {
|
---|
| 800 | if (!isX11MonitorThreadRunning())
|
---|
| 801 | return VINF_SUCCESS;
|
---|
| 802 |
|
---|
| 803 | mX11MonitorThreadStopping = true;
|
---|
| 804 | if (!mX11Monitor.interruptEventWait())
|
---|
| 805 | {
|
---|
| 806 | VBClLogError("Unable to notify X11 monitor thread\n");
|
---|
| 807 | return VERR_INVALID_STATE;
|
---|
| 808 | }
|
---|
| 809 |
|
---|
| 810 | int rcThread;
|
---|
| 811 | int rc = RTThreadWait(mX11MonitorThread, RT_MS_30SEC, &rcThread);
|
---|
| 812 | if (RT_SUCCESS(rc))
|
---|
| 813 | rc = rcThread;
|
---|
| 814 |
|
---|
| 815 | if (RT_SUCCESS(rc))
|
---|
| 816 | {
|
---|
| 817 | mX11MonitorThread = NIL_RTTHREAD;
|
---|
| 818 | }
|
---|
| 819 | else
|
---|
| 820 | VBClLogError("Waiting for X11 monitor thread to stop failed, rc=%Rrc\n", rc);
|
---|
| 821 |
|
---|
| 822 | return rc;
|
---|
| 823 | }
|
---|
| 824 |
|
---|