VirtualBox

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

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

Guest Additions NT4: Dynamic display resizing works now.

Guest Additions NT4: Bugfix: VBoxService was eating up 99% of CPU time.

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