VirtualBox

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

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

Rewrote seamless notification handling.

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 = 1000;
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