VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxSeamless.cpp@ 5025

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

Adjusted timeout. VBoxGuest used to wait 10 times longer than it should.

File size: 15.2 KB
Line 
1/** @file
2 *
3 * VBoxSeamless - Seamless windows
4 *
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 as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define _WIN32_WINNT 0x0500
19#include <windows.h>
20#include "VBoxService.h"
21#include "VBoxSeamless.h"
22#include <VBoxHook.h>
23#include <VBoxDisplay.h>
24#include <VBox/VBoxDev.h>
25#include <iprt/assert.h>
26#include "helpers.h"
27
28typedef struct _VBOXSEAMLESSCONTEXT
29{
30 const VBOXSERVICEENV *pEnv;
31
32 HMODULE hModule;
33
34 BOOL (* pfnVBoxInstallHook)(HMODULE hDll);
35 BOOL (* pfnVBoxRemoveHook)();
36
37 LPRGNDATA lpRgnData;
38} VBOXSEAMLESSCONTEXT;
39
40typedef struct
41{
42 HDC hdc;
43 HRGN hrgn;
44 RECT rect;
45} VBOX_ENUM_PARAM, *PVBOX_ENUM_PARAM;
46
47static VBOXSEAMLESSCONTEXT gCtx = {0};
48
49void VBoxLogString(HANDLE hDriver, char *pszStr);
50
51int VBoxSeamlessInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
52{
53 dprintf(("VBoxSeamlessInit\n"));
54
55 *pfStartThread = false;
56 gCtx.pEnv = pEnv;
57
58 /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */
59 gCtx.hModule = LoadLibrary(VBOXHOOK_DLL_NAME);
60 if (gCtx.hModule)
61 {
62 *(uintptr_t *)&gCtx.pfnVBoxInstallHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxInstallHook");
63 *(uintptr_t *)&gCtx.pfnVBoxRemoveHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxRemoveHook");
64
65 /* inform the host that we support the seamless window mode */
66 VMMDevReqGuestCapabilities vmmreqGuestCaps = {0};
67 vmmdevInitRequest((VMMDevRequestHeader*)&vmmreqGuestCaps, VMMDevReq_ReportGuestCapabilities);
68 vmmreqGuestCaps.caps = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
69
70 DWORD cbReturned;
71 if (!DeviceIoControl(pEnv->hDriver, IOCTL_VBOXGUEST_VMMREQUEST, &vmmreqGuestCaps, sizeof(vmmreqGuestCaps),
72 &vmmreqGuestCaps, sizeof(vmmreqGuestCaps), &cbReturned, NULL))
73 {
74 dprintf(("VMMDevReq_ReportGuestCapabilities: error doing IOCTL, last error: %d\n", GetLastError()));
75 return VERR_INVALID_PARAMETER;
76 }
77
78 *pfStartThread = true;
79 *ppInstance = &gCtx;
80 return VINF_SUCCESS;
81 }
82 else
83 {
84 dprintf(("VBoxSeamlessInit LoadLibrary failed with %d\n", GetLastError()));
85 return VERR_INVALID_PARAMETER;
86 }
87
88 return VINF_SUCCESS;
89}
90
91
92void VBoxSeamlessDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
93{
94 dprintf(("VBoxSeamlessDestroy\n"));
95 /* inform the host that we no longer support the seamless window mode */
96 VMMDevReqGuestCapabilities vmmreqGuestCaps = {0};
97 vmmdevInitRequest((VMMDevRequestHeader*)&vmmreqGuestCaps, VMMDevReq_ReportGuestCapabilities);
98 vmmreqGuestCaps.caps = 0;
99
100 DWORD cbReturned;
101 if (!DeviceIoControl(pEnv->hDriver, IOCTL_VBOXGUEST_VMMREQUEST, &vmmreqGuestCaps, sizeof(vmmreqGuestCaps),
102 &vmmreqGuestCaps, sizeof(vmmreqGuestCaps), &cbReturned, NULL))
103 {
104 dprintf(("VMMDevReq_ReportGuestCapabilities: error doing IOCTL, last error: %d\n", GetLastError()));
105 }
106
107 if (gCtx.pfnVBoxRemoveHook)
108 gCtx.pfnVBoxRemoveHook();
109 if (gCtx.hModule)
110 FreeLibrary(gCtx.hModule);
111 gCtx.hModule = 0;
112 return;
113}
114
115void VBoxSeamlessInstallHook()
116{
117 if (gCtx.pfnVBoxInstallHook)
118 {
119 /* Check current visible region state */
120 VBoxSeamlessCheckWindows();
121
122 gCtx.pfnVBoxInstallHook(gCtx.hModule);
123 }
124}
125
126void VBoxSeamlessRemoveHook()
127{
128 if (gCtx.pfnVBoxRemoveHook)
129 gCtx.pfnVBoxRemoveHook();
130
131 if (gCtx.lpRgnData)
132 {
133 free(gCtx.lpRgnData);
134 gCtx.lpRgnData = NULL;
135 }
136}
137
138BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam)
139{
140 PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam;
141 DWORD dwStyle, dwExStyle;
142 RECT rectWindow, rectVisible;
143
144 dwStyle = GetWindowLong(hwnd, GWL_STYLE);
145 dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
146 if ( !(dwStyle & WS_VISIBLE)
147 || (dwStyle & WS_CHILD))
148 return TRUE;
149
150 dprintf(("VBoxEnumFunc %x\n", hwnd));
151 /* Only visible windows that are present on the desktop are interesting here */
152 if ( GetWindowRect(hwnd, &rectWindow)
153 && IntersectRect(&rectVisible, &lpParam->rect, &rectWindow))
154 {
155 char szWindowText[256];
156 szWindowText[0] = 0;
157 GetWindowText(hwnd, szWindowText, sizeof(szWindowText));
158
159 /* Filter out Windows XP shadow windows */
160 /** @todo still shows inside the guest */
161 if ( szWindowText[0] == 0
162 && dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS)
163 && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST))
164 {
165 dprintf(("Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle));
166 return TRUE;
167 }
168
169 /** @todo will this suffice? The Program Manager window covers the whole screen */
170 if (strcmp(szWindowText, "Program Manager"))
171 {
172 dprintf(("Enum hwnd=%x rect (%d,%d) (%d,%d)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
173 dprintf(("title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle));
174
175 HRGN hrgn = CreateRectRgn(0,0,0,0);
176
177 int ret = GetWindowRgn(hwnd, hrgn);
178
179 if (ret == ERROR)
180 {
181 dprintf(("GetWindowRgn failed with rc=%d\n", GetLastError()));
182 SetRectRgn(hrgn, rectVisible.left, rectVisible.top, rectVisible.right, rectVisible.bottom);
183 }
184 else
185 {
186 /* this region is relative to the window origin instead of the desktop origin */
187 OffsetRgn(hrgn, rectWindow.left, rectWindow.top);
188 }
189 if (lpParam->hrgn)
190 {
191 /* create a union of the current visible region and the visible rectangle of this window. */
192 CombineRgn(lpParam->hrgn, lpParam->hrgn, hrgn, RGN_OR);
193 DeleteObject(hrgn);
194 }
195 else
196 lpParam->hrgn = hrgn;
197 }
198 else
199 {
200 dprintf(("Enum hwnd=%x rect (%d,%d) (%d,%d) (ignored)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom));
201 dprintf(("title=%s style=%x\n", szWindowText, dwStyle));
202 }
203 }
204 return TRUE; /* continue enumeration */
205}
206
207void VBoxSeamlessCheckWindows()
208{
209 VBOX_ENUM_PARAM param;
210
211 param.hdc = GetDC(HWND_DESKTOP);
212 param.hrgn = 0;
213
214 GetWindowRect(GetDesktopWindow(), &param.rect);
215 dprintf(("VBoxRecheckVisibleWindows desktop=%x rect (%d,%d) (%d,%d)\n", GetDesktopWindow(), param.rect.left, param.rect.top, param.rect.right, param.rect.bottom));
216 EnumWindows(VBoxEnumFunc, (LPARAM)&param);
217
218 if (param.hrgn)
219 {
220 DWORD cbSize;
221
222 cbSize = GetRegionData(param.hrgn, 0, NULL);
223 if (cbSize)
224 {
225 LPRGNDATA lpRgnData = (LPRGNDATA)malloc(cbSize);
226 memset(lpRgnData, 0, cbSize);
227 if (lpRgnData)
228 {
229 cbSize = GetRegionData(param.hrgn, cbSize, lpRgnData);
230 if (cbSize)
231 {
232#ifdef DEBUG
233 RECT *lpRect = (RECT *)&lpRgnData->Buffer[0];
234 dprintf(("New visible region: \n"));
235
236 for (DWORD i=0;i<lpRgnData->rdh.nCount;i++)
237 {
238 dprintf(("visible rect (%d,%d)(%d,%d)\n", lpRect[i].left, lpRect[i].top, lpRect[i].right, lpRect[i].bottom));
239 }
240#endif
241 if ( !gCtx.lpRgnData
242 || (gCtx.lpRgnData->rdh.dwSize + gCtx.lpRgnData->rdh.nRgnSize != cbSize)
243 || memcmp(gCtx.lpRgnData, lpRgnData, cbSize))
244 {
245 /* send to display driver */
246 ExtEscape(param.hdc, VBOXESC_SETVISIBLEREGION, cbSize, (LPCSTR)lpRgnData, 0, NULL);
247
248 if (gCtx.lpRgnData)
249 free(gCtx.lpRgnData);
250 gCtx.lpRgnData = lpRgnData;
251 }
252 else
253 dprintf(("Visible rectangles haven't changed; ignore\n"));
254 }
255 if (lpRgnData != gCtx.lpRgnData)
256 free(lpRgnData);
257 }
258 }
259
260 DeleteObject(param.hrgn);
261 }
262
263 ReleaseDC(HWND_DESKTOP, param.hdc);
264}
265
266/**
267 * Thread function to wait for and process seamless mode change
268 * requests
269 */
270unsigned __stdcall VBoxSeamlessThread(void *pInstance)
271{
272 VBOXSEAMLESSCONTEXT *pCtx = (VBOXSEAMLESSCONTEXT *)pInstance;
273 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
274 bool fTerminate = false;
275 VBoxGuestFilterMaskInfo maskInfo;
276 DWORD cbReturned;
277 BOOL fWasScreenSaverActive = FALSE, ret;
278
279 maskInfo.u32OrMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
280 maskInfo.u32NotMask = 0;
281 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
282 {
283 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask - or) succeeded\n"));
284 }
285 else
286 {
287 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask) failed, SeamlessChangeThread exited\n"));
288 return 0;
289 }
290
291 do
292 {
293 /* wait for a seamless change event */
294 VBoxGuestWaitEventInfo waitEvent;
295 waitEvent.u32TimeoutIn = 5000;
296 waitEvent.u32EventMaskIn = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
297 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
298 {
299 dprintf(("VBoxSeamlessThread: DeviceIOControl succeded\n"));
300
301 /* are we supposed to stop? */
302 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
303 break;
304
305 dprintf(("VBoxSeamlessThread: checking event\n"));
306
307 /* did we get the right event? */
308 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST)
309 {
310 dprintf(("VBoxService: going to get seamless change information.\n"));
311
312 /* We got at least one event. Read the requested resolution
313 * and try to set it until success. New events will not be seen
314 * but a new resolution will be read in this poll loop.
315 */
316 for (;;)
317 {
318 /* get the seamless change request */
319 VMMDevSeamlessChangeRequest seamlessChangeRequest = {0};
320 vmmdevInitRequest((VMMDevRequestHeader*)&seamlessChangeRequest, VMMDevReq_GetSeamlessChangeRequest);
321 seamlessChangeRequest.eventAck = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
322
323 BOOL fSeamlessChangeQueried = DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &seamlessChangeRequest, sizeof(seamlessChangeRequest),
324 &seamlessChangeRequest, sizeof(seamlessChangeRequest), &cbReturned, NULL);
325 if (fSeamlessChangeQueried)
326 {
327 dprintf(("VBoxSeamlessThread: mode change to %d\n", seamlessChangeRequest.mode));
328
329 switch(seamlessChangeRequest.mode)
330 {
331 case VMMDev_Seamless_Disabled:
332 if (fWasScreenSaverActive)
333 {
334 dprintf(("Re-enabling the screensaver\n"));
335 ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
336 if (!ret)
337 dprintf(("SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
338 }
339 PostMessage(gToolWindow, WM_VBOX_REMOVE_SEAMLESS_HOOK, 0, 0);
340 break;
341
342 case VMMDev_Seamless_Visible_Region:
343 ret = SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fWasScreenSaverActive, 0);
344 if (!ret)
345 dprintf(("SystemParametersInfo SPI_GETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
346
347 if (fWasScreenSaverActive)
348 dprintf(("Disabling the screensaver\n"));
349
350 ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
351 if (!ret)
352 dprintf(("SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError()));
353 PostMessage(gToolWindow, WM_VBOX_INSTALL_SEAMLESS_HOOK, 0, 0);
354 break;
355
356 case VMMDev_Seamless_Host_Window:
357 break;
358
359 default:
360 AssertFailed();
361 break;
362 }
363 break;
364 }
365 else
366 {
367 dprintf(("VBoxSeamlessThread: error from DeviceIoControl IOCTL_VBOXGUEST_VMMREQUEST\n"));
368 }
369 /* sleep a bit to not eat too much CPU while retrying */
370 /* are we supposed to stop? */
371 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 50) == WAIT_OBJECT_0)
372 {
373 fTerminate = true;
374 break;
375 }
376 }
377 }
378 }
379 else
380 {
381 dprintf(("VBoxService: error 0 from DeviceIoControl IOCTL_VBOXGUEST_WAITEVENT\n"));
382 /* sleep a bit to not eat too much CPU in case the above call always fails */
383 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
384 {
385 fTerminate = true;
386 break;
387 }
388 }
389 }
390 while (!fTerminate);
391
392 maskInfo.u32OrMask = 0;
393 maskInfo.u32NotMask = VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST;
394 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
395 {
396 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask - not) succeeded\n"));
397 }
398 else
399 {
400 dprintf(("VBoxSeamlessThread: DeviceIOControl(CtlMask) failed\n"));
401 }
402
403 dprintf(("VBoxSeamlessThread: finished seamless change request thread\n"));
404 return 0;
405}
406
407
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