VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxDisplay.cpp@ 4267

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

Updated logging strings

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