VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxDisplay.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: 21.9 KB
Line 
1/** @file
2 *
3 * VBoxSeamless - Display notifications
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#include <malloc.h>
28
29typedef struct _VBOXDISPLAYCONTEXT
30{
31 const VBOXSERVICEENV *pEnv;
32
33 /* ChangeDisplaySettingsEx does not exist in NT. ResizeDisplayDevice uses the function. */
34 LONG (WINAPI * pfnChangeDisplaySettingsEx)(LPCTSTR lpszDeviceName, LPDEVMODE lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam);
35} VBOXDISPLAYCONTEXT;
36
37static VBOXDISPLAYCONTEXT gCtx = {0};
38
39int VBoxDisplayInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
40{
41 HMODULE hUser = GetModuleHandle("USER32");
42
43 gCtx.pEnv = pEnv;
44
45 if (hUser)
46 {
47 *(uintptr_t *)&gCtx.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
48 dprintf(("VBoxService: pChangeDisplaySettingsEx = %p\n", gCtx.pfnChangeDisplaySettingsEx));
49 }
50
51 *pfStartThread = true;
52 *ppInstance = (void *)&gCtx;
53 return VINF_SUCCESS;
54}
55
56void VBoxDisplayDestroy (const VBOXSERVICEENV *pEnv, void *pInstance)
57{
58 return;
59}
60
61static bool isVBoxDisplayDriverActive (void)
62{
63 bool result = false;
64
65 DISPLAY_DEVICE dispDevice;
66
67 FillMemory(&dispDevice, sizeof(DISPLAY_DEVICE), 0);
68
69 dispDevice.cb = sizeof(DISPLAY_DEVICE);
70
71 INT devNum = 0;
72
73 while (EnumDisplayDevices(NULL,
74 devNum,
75 &dispDevice,
76 0))
77 {
78 dprintf(("DevNum:%d\nName:%s\nString:%s\nID:%s\nKey:%s\nFlags=%08X\n\n",
79 devNum,
80 &dispDevice.DeviceName[0],
81 &dispDevice.DeviceString[0],
82 &dispDevice.DeviceID[0],
83 &dispDevice.DeviceKey[0],
84 dispDevice.StateFlags));
85
86 if (dispDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
87 {
88 dprintf(("Primary device.\n"));
89
90 if (strcmp(&dispDevice.DeviceString[0], "VirtualBox Graphics Adapter") == 0)
91 {
92 dprintf(("VBox display driver is active.\n"));
93 result = true;
94 }
95
96 break;
97 }
98
99 FillMemory(&dispDevice, sizeof(DISPLAY_DEVICE), 0);
100
101 dispDevice.cb = sizeof(DISPLAY_DEVICE);
102
103 devNum++;
104 }
105
106 return result;
107}
108
109/* Returns TRUE to try again. */
110static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
111{
112 BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0);
113
114 DISPLAY_DEVICE DisplayDevice;
115
116 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
117 DisplayDevice.cb = sizeof(DisplayDevice);
118
119 /* Find out how many display devices the system has */
120 DWORD NumDevices = 0;
121 DWORD i = 0;
122 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
123 {
124 dprintf(("[%d] %s\n", i, DisplayDevice.DeviceName));
125
126 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
127 {
128 dprintf(("Found primary device. err %d\n", GetLastError ()));
129 NumDevices++;
130 }
131 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
132 {
133
134 dprintf(("Found secondary device. err %d\n", GetLastError ()));
135 NumDevices++;
136 }
137
138 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
139 DisplayDevice.cb = sizeof(DisplayDevice);
140 i++;
141 }
142
143 dprintf(("Found total %d devices. err %d\n", NumDevices, GetLastError ()));
144
145 if (NumDevices == 0 || Id >= NumDevices)
146 {
147 dprintf(("Requested identifier %d is invalid. err %d\n", Id, GetLastError ()));
148 return FALSE;
149 }
150
151 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
152 DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
153 RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices);
154
155 /* Fetch information about current devices and modes. */
156 DWORD DevNum = 0;
157 DWORD DevPrimaryNum = 0;
158
159 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
160 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
161
162 i = 0;
163 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
164 {
165 dprintf(("[%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
166
167 BOOL bFetchDevice = FALSE;
168
169 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
170 {
171 dprintf(("Found primary device. err %d\n", GetLastError ()));
172 DevPrimaryNum = DevNum;
173 bFetchDevice = TRUE;
174 }
175 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
176 {
177
178 dprintf(("Found secondary device. err %d\n", GetLastError ()));
179 bFetchDevice = TRUE;
180 }
181
182 if (bFetchDevice)
183 {
184 if (DevNum >= NumDevices)
185 {
186 dprintf(("%d >= %d\n", NumDevices, DevNum));
187 return FALSE;
188 }
189
190 paDisplayDevices[DevNum] = DisplayDevice;
191
192 ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE));
193 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
194 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
195 ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
196 {
197 dprintf(("EnumDisplaySettings err %d\n", GetLastError ()));
198 return FALSE;
199 }
200
201 dprintf(("%dx%d at %d,%d\n",
202 paDeviceModes[DevNum].dmPelsWidth,
203 paDeviceModes[DevNum].dmPelsHeight,
204 paDeviceModes[DevNum].dmPosition.x,
205 paDeviceModes[DevNum].dmPosition.y));
206
207 paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x;
208 paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y;
209 paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth;
210 paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight;
211 DevNum++;
212 }
213
214 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
215 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
216 i++;
217 }
218
219 if (Width == 0)
220 {
221 Width = paRects[Id].right - paRects[Id].left;
222 }
223
224 if (Height == 0)
225 {
226 Height = paRects[Id].bottom - paRects[Id].top;
227 }
228
229 /* Check whether a mode reset or a change is requested. */
230 if ( !fModeReset
231 && paRects[Id].right - paRects[Id].left == Width
232 && paRects[Id].bottom - paRects[Id].top == Height
233 && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
234 {
235 dprintf(("VBoxDisplayThread : already at desired resolution.\n"));
236 return FALSE;
237 }
238
239 resizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height);
240#ifdef dprintf
241 for (i = 0; i < NumDevices; i++)
242 {
243 dprintf(("[%d]: %d,%d %dx%d\n",
244 i, paRects[i].left, paRects[i].top,
245 paRects[i].right - paRects[i].left,
246 paRects[i].bottom - paRects[i].top));
247 }
248#endif /* dprintf */
249
250 /* Without this, Windows will not ask the miniport for its
251 * mode table but uses an internal cache instead.
252 */
253 DEVMODE tempDevMode;
254 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
255 tempDevMode.dmSize = sizeof(DEVMODE);
256 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
257
258 /* Assign the new rectangles to displays. */
259 for (i = 0; i < NumDevices; i++)
260 {
261 paDeviceModes[i].dmPosition.x = paRects[i].left;
262 paDeviceModes[i].dmPosition.y = paRects[i].top;
263 paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
264 paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
265
266 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
267
268 if ( i == Id
269 && BitsPerPixel != 0)
270 {
271 paDeviceModes[i].dmFields |= DM_BITSPERPEL;
272 paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
273 }
274dprintf(("calling pfnChangeDisplaySettingsEx %x\n", gCtx.pfnChangeDisplaySettingsEx));
275 gCtx.pfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
276 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
277 dprintf(("ChangeDisplaySettings position err %d\n", GetLastError ()));
278 }
279
280 /* A second call to ChangeDisplaySettings updates the monitor. */
281 LONG status = ChangeDisplaySettings(NULL, 0);
282 dprintf(("ChangeDisplaySettings update status %d\n", status));
283 if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE)
284 {
285 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
286 return FALSE;
287 }
288
289 /* Retry the request. */
290 return TRUE;
291}
292
293/**
294 * Thread function to wait for and process display change
295 * requests
296 */
297unsigned __stdcall VBoxDisplayThread (void *pInstance)
298{
299 VBOXDISPLAYCONTEXT *pCtx = (VBOXDISPLAYCONTEXT *)pInstance;
300 HANDLE gVBoxDriver = pCtx->pEnv->hDriver;
301 bool fTerminate = false;
302 VBoxGuestFilterMaskInfo maskInfo;
303 DWORD cbReturned;
304
305 maskInfo.u32OrMask = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
306 maskInfo.u32NotMask = 0;
307 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
308 {
309 dprintf(("VBoxDisplayThread : DeviceIOControl(CtlMask - or) succeeded\n"));
310 }
311 else
312 {
313 dprintf(("VBoxDisplayThread : DeviceIOControl(CtlMask) failed, DisplayChangeThread exited\n"));
314 return -1;
315 }
316
317 do
318 {
319 /* wait for a display change event */
320 VBoxGuestWaitEventInfo waitEvent;
321 waitEvent.u32TimeoutIn = 1000;
322 waitEvent.u32EventMaskIn = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
323 if (DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_WAITEVENT, &waitEvent, sizeof(waitEvent), &waitEvent, sizeof(waitEvent), &cbReturned, NULL))
324 {
325 dprintf(("VBoxDisplayThread : DeviceIOControl succeded\n"));
326
327 /* are we supposed to stop? */
328 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0)
329 break;
330
331 dprintf(("VBoxDisplayThread : checking event\n"));
332
333 /* did we get the right event? */
334 if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
335 {
336 dprintf(("VBoxDisplayThread : going to get display change information.\n"));
337
338 /* We got at least one event. Read the requested resolution
339 * and try to set it until success. New events will not be seen
340 * but a new resolution will be read in this poll loop.
341 */
342 for (;;)
343 {
344 /* get the display change request */
345 VMMDevDisplayChangeRequest2 displayChangeRequest = {0};
346 displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest2);
347 displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION;
348 displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequest2;
349 displayChangeRequest.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
350 BOOL fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2),
351 &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2), &cbReturned, NULL);
352 if (!fDisplayChangeQueried)
353 {
354 /* Try the old version of the request for old VBox hosts. */
355 displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest);
356 displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION;
357 displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequest;
358 displayChangeRequest.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
359 fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, IOCTL_VBOXGUEST_VMMREQUEST, &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest),
360 &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest), &cbReturned, NULL);
361 displayChangeRequest.display = 0;
362 }
363
364 if (fDisplayChangeQueried)
365 {
366 dprintf(("VBoxDisplayThread : VMMDevReq_GetDisplayChangeRequest2: %dx%dx%d at %d\n", displayChangeRequest.xres, displayChangeRequest.yres, displayChangeRequest.bpp, displayChangeRequest.display));
367
368 /* Horizontal resolution must be a multiple of 8, round down. */
369 displayChangeRequest.xres &= 0xfff8;
370
371 /*
372 * Only try to change video mode if the active display driver is VBox additions.
373 */
374 if (isVBoxDisplayDriverActive ())
375 {
376 if (pCtx->pfnChangeDisplaySettingsEx != 0)
377 {
378 /* W2K or later. */
379 if (!ResizeDisplayDevice(displayChangeRequest.display,
380 displayChangeRequest.xres,
381 displayChangeRequest.yres,
382 displayChangeRequest.bpp))
383 {
384 break;
385 }
386 }
387 else
388 {
389 /* Single monitor NT. */
390 DEVMODE devMode;
391 memset (&devMode, 0, sizeof (devMode));
392 devMode.dmSize = sizeof(DEVMODE);
393
394 /* get the current screen setup */
395 if (EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode))
396 {
397 dprintf(("VBoxDisplayThread : Current mode: %dx%dx%d at %d,%d\n", devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel, devMode.dmPosition.x, devMode.dmPosition.y));
398
399 /* Check whether a mode reset or a change is requested. */
400 if (displayChangeRequest.xres || displayChangeRequest.yres || displayChangeRequest.bpp)
401 {
402 /* A change is requested.
403 * Set values which are not to be changed to the current values.
404 */
405 if (!displayChangeRequest.xres)
406 displayChangeRequest.xres = devMode.dmPelsWidth;
407 if (!displayChangeRequest.yres)
408 displayChangeRequest.yres = devMode.dmPelsHeight;
409 if (!displayChangeRequest.bpp)
410 displayChangeRequest.bpp = devMode.dmBitsPerPel;
411 }
412 else
413 {
414 /* All zero values means a forced mode reset. Do nothing. */
415 }
416
417 /* Verify that the mode is indeed changed. */
418 if ( devMode.dmPelsWidth == displayChangeRequest.xres
419 && devMode.dmPelsHeight == displayChangeRequest.yres
420 && devMode.dmBitsPerPel == displayChangeRequest.bpp)
421 {
422 dprintf(("VBoxDisplayThread : already at desired resolution.\n"));
423 break;
424 }
425
426 // without this, Windows will not ask the miniport for its
427 // mode table but uses an internal cache instead
428 DEVMODE tempDevMode = {0};
429 tempDevMode.dmSize = sizeof(DEVMODE);
430 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
431
432 /* adjust the values that are supposed to change */
433 if (displayChangeRequest.xres)
434 devMode.dmPelsWidth = displayChangeRequest.xres;
435 if (displayChangeRequest.yres)
436 devMode.dmPelsHeight = displayChangeRequest.yres;
437 if (displayChangeRequest.bpp)
438 devMode.dmBitsPerPel = displayChangeRequest.bpp;
439
440 dprintf(("VBoxDisplayThread : setting the new mode %dx%dx%d\n", devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel));
441
442 /* set the new mode */
443 LONG status = ChangeDisplaySettings(&devMode, CDS_UPDATEREGISTRY);
444 if (status != DISP_CHANGE_SUCCESSFUL)
445 {
446 dprintf(("VBoxDisplayThread : error from ChangeDisplaySettings: %d\n", status));
447
448 if (status == DISP_CHANGE_BADMODE)
449 {
450 /* Our driver can not set the requested mode. Stop trying. */
451 break;
452 }
453 }
454 else
455 {
456 /* Successfully set new video mode. */
457 break;
458 }
459 }
460 else
461 {
462 dprintf(("VBoxDisplayThread : error from EnumDisplaySettings: %d\n", GetLastError ()));
463 break;
464 }
465 }
466 }
467 else
468 {
469 dprintf(("VBoxDisplayThread : vboxDisplayDriver is not active.\n"));
470 }
471
472 /* Retry the change a bit later. */
473 /* are we supposed to stop? */
474 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 1000) == WAIT_OBJECT_0)
475 {
476 fTerminate = true;
477 break;
478 }
479 }
480 else
481 {
482 dprintf(("VBoxDisplayThread : error from DeviceIoControl IOCTL_VBOXGUEST_VMMREQUEST\n"));
483 /* sleep a bit to not eat too much CPU while retrying */
484 /* are we supposed to stop? */
485 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 50) == WAIT_OBJECT_0)
486 {
487 fTerminate = true;
488 break;
489 }
490 }
491 }
492 }
493 } else
494 {
495 dprintf(("VBoxDisplayThread : error 0 from DeviceIoControl IOCTL_VBOXGUEST_WAITEVENT\n"));
496 /* sleep a bit to not eat too much CPU in case the above call always fails */
497 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0)
498 {
499 fTerminate = true;
500 break;
501 }
502 }
503 } while (!fTerminate);
504
505 maskInfo.u32OrMask = 0;
506 maskInfo.u32NotMask = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST;
507 if (DeviceIoControl (gVBoxDriver, IOCTL_VBOXGUEST_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
508 {
509 dprintf(("VBoxDisplayThread : DeviceIOControl(CtlMask - not) succeeded\n"));
510 }
511 else
512 {
513 dprintf(("VBoxDisplayThread : DeviceIOControl(CtlMask) failed\n"));
514 }
515
516 dprintf(("VBoxDisplayThread : finished display change request thread\n"));
517 return 0;
518}
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