VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.cpp@ 95962

最後變更 在這個檔案從95962是 95962,由 vboxsync 提交於 3 年 前

Additions/VBoxTray: Got rid of static function isVBoxDisplayDriverActive() defined in a header, which makes no sense (and is gone anyway).

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 38.0 KB
 
1/* $Id: VBoxDisplay.cpp 95962 2022-08-01 14:11:45Z vboxsync $ */
2/** @file
3 * VBoxSeamless - Display notifications.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "VBoxTray.h"
23#include "VBoxHelpers.h"
24#include "VBoxSeamless.h"
25
26#include <iprt/alloca.h>
27#include <iprt/assert.h>
28#ifdef VBOX_WITH_WDDM
29# include <iprt/asm.h>
30#endif
31#include <iprt/log.h>
32#include <iprt/system.h>
33
34#include <VBoxDisplay.h>
35#include <VBoxHook.h>
36
37
38/*********************************************************************************************************************************
39* Structures and Typedefs *
40*********************************************************************************************************************************/
41typedef struct _VBOXDISPLAYCONTEXT
42{
43 const VBOXSERVICEENV *pEnv;
44 BOOL fAnyX;
45 /** ChangeDisplaySettingsEx does not exist in NT. ResizeDisplayDevice uses the function. */
46 DECLCALLBACKMEMBER_EX(LONG,WINAPI, pfnChangeDisplaySettingsEx,(LPCTSTR lpszDeviceName, LPDEVMODE lpDevMode, HWND hwnd,
47 DWORD dwflags, LPVOID lParam));
48 /** EnumDisplayDevices does not exist in NT. */
49 DECLCALLBACKMEMBER_EX(BOOL, WINAPI, pfnEnumDisplayDevices,(IN LPCSTR lpDevice, IN DWORD iDevNum,
50 OUT PDISPLAY_DEVICEA lpDisplayDevice, IN DWORD dwFlags));
51 /** Display driver interface, XPDM - WDDM abstraction see VBOXDISPIF** definitions above */
52 VBOXDISPIF dispIf;
53} VBOXDISPLAYCONTEXT, *PVBOXDISPLAYCONTEXT;
54
55typedef enum
56{
57 VBOXDISPLAY_DRIVER_TYPE_UNKNOWN = 0,
58 VBOXDISPLAY_DRIVER_TYPE_XPDM = 1,
59 VBOXDISPLAY_DRIVER_TYPE_WDDM = 2
60} VBOXDISPLAY_DRIVER_TYPE;
61
62
63/*********************************************************************************************************************************
64* Global Variables *
65*********************************************************************************************************************************/
66static VBOXDISPLAYCONTEXT g_Ctx = { 0 };
67
68
69/*********************************************************************************************************************************
70* Internal Functions *
71*********************************************************************************************************************************/
72static VBOXDISPLAY_DRIVER_TYPE getVBoxDisplayDriverType(VBOXDISPLAYCONTEXT *pCtx);
73
74
75static DECLCALLBACK(int) VBoxDisplayInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
76{
77 LogFlowFuncEnter();
78
79 PVBOXDISPLAYCONTEXT pCtx = &g_Ctx; /** @todo r=andy Use instance data via service lookup (add void *pInstance). */
80 AssertPtr(pCtx);
81
82 int rc;
83 HMODULE hUser = GetModuleHandle("user32.dll"); /** @todo r=andy Use RTLdrXXX and friends. */
84
85 pCtx->pEnv = pEnv;
86
87 uint64_t const uNtVersion = RTSystemGetNtVersion();
88
89 if (NULL == hUser)
90 {
91 LogFlowFunc(("Could not get module handle of USER32.DLL!\n"));
92 rc = VERR_NOT_IMPLEMENTED;
93 }
94 else if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* APIs available only on W2K and up. */
95 {
96 /** @todo r=andy Use RTLdrXXX and friends. */
97 /** @todo r=andy No unicode version available? */
98 *(uintptr_t *)&pCtx->pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
99 LogFlowFunc(("pfnChangeDisplaySettingsEx = %p\n", pCtx->pfnChangeDisplaySettingsEx));
100
101 *(uintptr_t *)&pCtx->pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA");
102 LogFlowFunc(("pfnEnumDisplayDevices = %p\n", pCtx->pfnEnumDisplayDevices));
103
104#ifdef VBOX_WITH_WDDM
105 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
106 {
107 /* This is Vista and up, check if we need to switch the display driver if to WDDM mode. */
108 LogFlowFunc(("this is Windows Vista and up\n"));
109 VBOXDISPLAY_DRIVER_TYPE enmType = getVBoxDisplayDriverType(pCtx);
110 if (enmType == VBOXDISPLAY_DRIVER_TYPE_WDDM)
111 {
112 LogFlowFunc(("WDDM driver is installed, switching display driver if to WDDM mode\n"));
113 /* This is hacky, but the most easiest way. */
114 VBOXDISPIF_MODE enmMode = uNtVersion < RTSYSTEM_MAKE_NT_VERSION(6, 1, 0)
115 ? VBOXDISPIF_MODE_WDDM : VBOXDISPIF_MODE_WDDM_W7;
116 DWORD dwErr = VBoxDispIfSwitchMode(const_cast<PVBOXDISPIF>(&pEnv->dispIf), enmMode, NULL /* old mode, we don't care about it */);
117 if (dwErr == NO_ERROR)
118 {
119 LogFlowFunc(("DispIf successfully switched to WDDM mode\n"));
120 rc = VINF_SUCCESS;
121 }
122 else
123 {
124 LogFlowFunc(("Failed to switch DispIf to WDDM mode, error (%d)\n", dwErr));
125 rc = RTErrConvertFromWin32(dwErr);
126 }
127 }
128 else
129 rc = VINF_SUCCESS;
130 }
131 else
132 rc = VINF_SUCCESS;
133#endif
134 }
135 else if (uNtVersion < RTSYSTEM_MAKE_NT_VERSION(5, 0, 0)) /* Windows NT 4.0. */
136 {
137 /* Nothing to do here yet. */
138 /** @todo r=andy Has this been tested? */
139 rc = VINF_SUCCESS;
140 }
141 else /* Unsupported platform. */
142 {
143 LogFlowFunc(("Warning: Display for platform not handled yet!\n"));
144 rc = VERR_NOT_IMPLEMENTED;
145 }
146
147 if (RT_SUCCESS(rc))
148 {
149 VBOXDISPIFESCAPE_ISANYX IsAnyX = { {0} };
150 IsAnyX.EscapeHdr.escapeCode = VBOXESC_ISANYX;
151 DWORD err = VBoxDispIfEscapeInOut(&pEnv->dispIf, &IsAnyX.EscapeHdr, sizeof(uint32_t));
152 if (err == NO_ERROR)
153 pCtx->fAnyX = !!IsAnyX.u32IsAnyX;
154 else
155 pCtx->fAnyX = TRUE;
156
157 *ppInstance = pCtx;
158 }
159
160 LogFlowFuncLeaveRC(rc);
161 return rc;
162}
163
164static DECLCALLBACK(void) VBoxDisplayDestroy(void *pInstance)
165{
166 RT_NOREF(pInstance);
167 return;
168}
169
170static VBOXDISPLAY_DRIVER_TYPE getVBoxDisplayDriverType(PVBOXDISPLAYCONTEXT pCtx)
171{
172 VBOXDISPLAY_DRIVER_TYPE enmType = VBOXDISPLAY_DRIVER_TYPE_UNKNOWN;
173
174 if (pCtx->pfnEnumDisplayDevices)
175 {
176 INT devNum = 0;
177 DISPLAY_DEVICE dispDevice;
178 FillMemory(&dispDevice, sizeof(DISPLAY_DEVICE), 0);
179 dispDevice.cb = sizeof(DISPLAY_DEVICE);
180
181 LogFlowFunc(("getVBoxDisplayDriverType: Checking for active VBox display driver (W2K+) ...\n"));
182
183 while (EnumDisplayDevices(NULL,
184 devNum,
185 &dispDevice,
186 0))
187 {
188 LogFlowFunc(("getVBoxDisplayDriverType: DevNum:%d\nName:%s\nString:%s\nID:%s\nKey:%s\nFlags=%08X\n\n",
189 devNum,
190 &dispDevice.DeviceName[0],
191 &dispDevice.DeviceString[0],
192 &dispDevice.DeviceID[0],
193 &dispDevice.DeviceKey[0],
194 dispDevice.StateFlags));
195
196 if (dispDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
197 {
198 LogFlowFunc(("getVBoxDisplayDriverType: Primary device\n"));
199
200 /* WDDM driver can now have multiple incarnations,
201 * if the driver name contains VirtualBox, and does NOT match the XPDM name,
202 * assume it to be WDDM */
203 if (strcmp(&dispDevice.DeviceString[0], "VirtualBox Graphics Adapter") == 0)
204 enmType = VBOXDISPLAY_DRIVER_TYPE_XPDM;
205 else if (strstr(&dispDevice.DeviceString[0], "VirtualBox"))
206 enmType = VBOXDISPLAY_DRIVER_TYPE_WDDM;
207
208 break;
209 }
210
211 FillMemory(&dispDevice, sizeof(DISPLAY_DEVICE), 0);
212
213 dispDevice.cb = sizeof(DISPLAY_DEVICE);
214
215 devNum++;
216 }
217 }
218 else /* This must be NT 4 or something really old, so don't use EnumDisplayDevices() here ... */
219 {
220 LogFlowFunc(("getVBoxDisplayDriverType: Checking for active VBox display driver (NT or older) ...\n"));
221
222 DEVMODE tempDevMode;
223 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
224 tempDevMode.dmSize = sizeof(DEVMODE);
225 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &tempDevMode); /* Get current display device settings */
226
227 /* Check for the short name, because all long stuff would be truncated */
228 if (strcmp((char*)&tempDevMode.dmDeviceName[0], "VBoxDisp") == 0)
229 enmType = VBOXDISPLAY_DRIVER_TYPE_XPDM;
230 }
231
232 return enmType;
233}
234
235/** @todo r=andy The "display", "seamless" (and VBoxCaps facility in VBoxTray.cpp indirectly) is using this.
236 * Add a PVBOXDISPLAYCONTEXT here for properly getting the display (XPDM/WDDM) abstraction interfaces. */
237DWORD EnableAndResizeDispDev(DEVMODE *paDeviceModes, DISPLAY_DEVICE *paDisplayDevices,
238 DWORD totalDispNum, UINT Id, DWORD aWidth, DWORD aHeight,
239 DWORD aBitsPerPixel, LONG aPosX, LONG aPosY, BOOL fEnabled, BOOL fExtDispSup)
240{
241 DISPLAY_DEVICE displayDeviceTmp;
242 DISPLAY_DEVICE displayDevice;
243 DEVMODE deviceMode;
244 DWORD dwStatus = DISP_CHANGE_SUCCESSFUL;
245 DWORD iter ;
246
247 PVBOXDISPLAYCONTEXT pCtx = &g_Ctx; /* See todo above. */
248
249 deviceMode = paDeviceModes[Id];
250 displayDevice = paDisplayDevices[Id];
251
252 for (iter = 0; iter < totalDispNum; iter++)
253 {
254 if (iter != 0 && iter != Id && !(paDisplayDevices[iter].StateFlags & DISPLAY_DEVICE_ACTIVE))
255 {
256 LogRel(("Display: Initially disabling monitor with ID=%ld; total monitor count is %ld\n", iter, totalDispNum));
257 DEVMODE deviceModeTmp;
258 ZeroMemory(&deviceModeTmp, sizeof(DEVMODE));
259 deviceModeTmp.dmSize = sizeof(DEVMODE);
260 deviceModeTmp.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION
261 | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS ;
262 displayDeviceTmp = paDisplayDevices[iter];
263 pCtx->pfnChangeDisplaySettingsEx(displayDeviceTmp.DeviceName, &deviceModeTmp, NULL,
264 (CDS_UPDATEREGISTRY | CDS_NORESET), NULL);
265 }
266 }
267
268 if (fExtDispSup) /* Extended Display Support possible*/
269 {
270 if (fEnabled)
271 {
272 /* Special case for enabling the secondary monitor. */
273 if(!(displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE))
274 {
275 LogRel(("Display [ID=%ld, name='%s']: Is a secondary monitor and disabled -- enabling it\n", Id, displayDevice.DeviceName));
276 deviceMode.dmPosition.x = paDeviceModes[0].dmPelsWidth;
277 deviceMode.dmPosition.y = 0;
278 deviceMode.dmBitsPerPel = 32;
279
280 uint64_t const uNtVersion = RTSystemGetNtVersion();
281 if (uNtVersion < RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
282 /* dont any more flags here as, only DM_POISITON is used to enable the secondary display */
283 deviceMode.dmFields = DM_POSITION;
284 else /* for win 7 and above */
285 /* for vista and above DM_BITSPERPEL is necessary */
286 deviceMode.dmFields = DM_BITSPERPEL | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY | DM_POSITION;
287
288 dwStatus = pCtx->pfnChangeDisplaySettingsEx((LPSTR)displayDevice.DeviceName,&deviceMode, NULL, (CDS_UPDATEREGISTRY | CDS_NORESET), NULL);
289 /* A second call to ChangeDisplaySettings updates the monitor.*/
290 pCtx->pfnChangeDisplaySettingsEx(NULL, NULL, NULL,0, NULL);
291 }
292 else /* secondary monitor already enabled. Request to change the resolution or position. */
293 {
294 if (aWidth !=0 && aHeight != 0)
295 {
296 LogRel(("Display [ID=%ld, name='%s']: Changing resolution to %ldx%ld\n", Id, displayDevice.DeviceName, aWidth, aHeight));
297 deviceMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL
298 | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
299 deviceMode.dmPelsWidth = aWidth;
300 deviceMode.dmPelsHeight = aHeight;
301 deviceMode.dmBitsPerPel = aBitsPerPixel;
302 }
303 if (aPosX != 0 || aPosY != 0)
304 {
305 LogRel(("Display [ID=%ld, name='%s']: Changing position to %ld,%ld\n", Id, displayDevice.DeviceName, aPosX, aPosY));
306 deviceMode.dmFields |= DM_POSITION;
307 deviceMode.dmPosition.x = aPosX;
308 deviceMode.dmPosition.y = aPosY;
309 }
310 dwStatus = pCtx->pfnChangeDisplaySettingsEx((LPSTR)displayDevice.DeviceName,
311 &deviceMode, NULL, CDS_NORESET|CDS_UPDATEREGISTRY, NULL);
312 /* A second call to ChangeDisplaySettings updates the monitor. */
313 pCtx->pfnChangeDisplaySettingsEx(NULL, NULL, NULL,0, NULL);
314 }
315 }
316 else /* Request is there to disable the monitor with ID = Id*/
317 {
318 LogRel(("Display [ID=%ld, name='%s']: Disalbing\n", Id, displayDevice.DeviceName));
319
320 DEVMODE deviceModeTmp;
321 ZeroMemory(&deviceModeTmp, sizeof(DEVMODE));
322 deviceModeTmp.dmSize = sizeof(DEVMODE);
323 deviceModeTmp.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION
324 | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS ;
325 displayDeviceTmp = paDisplayDevices[Id];
326 dwStatus = pCtx->pfnChangeDisplaySettingsEx(displayDeviceTmp.DeviceName, &deviceModeTmp, NULL,
327 (CDS_UPDATEREGISTRY | CDS_NORESET), NULL);
328 pCtx->pfnChangeDisplaySettingsEx(NULL, NULL, NULL,0, NULL);
329 }
330 }
331 return dwStatus;
332}
333
334DWORD VBoxDisplayGetCount(void)
335{
336 DISPLAY_DEVICE DisplayDevice;
337
338 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
339 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
340
341 /* Find out how many display devices the system has */
342 DWORD NumDevices = 0;
343 DWORD i = 0;
344 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
345 {
346 LogFlowFunc(("ResizeDisplayDevice: [%d] %s\n", i, DisplayDevice.DeviceName));
347
348 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
349 {
350 LogFlowFunc(("ResizeDisplayDevice: Found primary device. err %d\n", GetLastError ()));
351 NumDevices++;
352 }
353 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
354 {
355
356 LogFlowFunc(("ResizeDisplayDevice: Found secondary device. err %d\n", GetLastError ()));
357 NumDevices++;
358 }
359
360 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
361 DisplayDevice.cb = sizeof(DisplayDevice);
362 i++;
363 }
364
365 return NumDevices;
366}
367
368DWORD VBoxDisplayGetConfig(const DWORD NumDevices, DWORD *pDevPrimaryNum, DWORD *pNumDevices,
369 DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes)
370{
371 /* Fetch information about current devices and modes. */
372 DWORD DevNum = 0;
373 DWORD DevPrimaryNum = 0;
374
375 DISPLAY_DEVICE DisplayDevice;
376
377 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
378 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
379
380 DWORD i = 0;
381 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
382 {
383 LogFlowFunc(("ResizeDisplayDevice: [%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
384
385 BOOL bFetchDevice = FALSE;
386
387 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
388 {
389 LogFlowFunc(("ResizeDisplayDevice: Found primary device. err %d\n", GetLastError ()));
390 DevPrimaryNum = DevNum;
391 bFetchDevice = TRUE;
392 }
393 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
394 {
395
396 LogFlowFunc(("ResizeDisplayDevice: Found secondary device. err %d\n", GetLastError ()));
397 bFetchDevice = TRUE;
398 }
399
400 if (bFetchDevice)
401 {
402 if (DevNum >= NumDevices)
403 {
404 LogFlowFunc(("ResizeDisplayDevice: %d >= %d\n", NumDevices, DevNum));
405 return ERROR_BUFFER_OVERFLOW;
406 }
407
408 paDisplayDevices[DevNum] = DisplayDevice;
409
410 /* First try to get the video mode stored in registry (ENUM_REGISTRY_SETTINGS).
411 * A secondary display could be not active at the moment and would not have
412 * a current video mode (ENUM_CURRENT_SETTINGS).
413 */
414 ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE));
415 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
416 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
417 ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
418 {
419 LogFlowFunc(("ResizeDisplayDevice: EnumDisplaySettings error %d\n", GetLastError ()));
420 }
421
422 if ( paDeviceModes[DevNum].dmPelsWidth == 0
423 || paDeviceModes[DevNum].dmPelsHeight == 0)
424 {
425 /* No ENUM_REGISTRY_SETTINGS yet. Seen on Vista after installation.
426 * Get the current video mode then.
427 */
428 ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE));
429 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
430 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
431 ENUM_CURRENT_SETTINGS, &paDeviceModes[DevNum]))
432 {
433 /* ENUM_CURRENT_SETTINGS returns FALSE when the display is not active:
434 * for example a disabled secondary display.
435 * Do not return here, ignore the error and set the display info to 0x0x0.
436 */
437 LogFlowFunc(("ResizeDisplayDevice: EnumDisplaySettings(ENUM_CURRENT_SETTINGS) error %d\n", GetLastError ()));
438 }
439 }
440
441
442 DevNum++;
443 }
444
445 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
446 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
447 i++;
448 }
449
450 *pNumDevices = DevNum;
451 *pDevPrimaryNum = DevPrimaryNum;
452
453 return NO_ERROR;
454}
455
456static void ResizeDisplayDeviceNT4(DWORD dwNewXRes, DWORD dwNewYRes, DWORD dwNewBpp)
457{
458 DEVMODE devMode;
459
460 RT_ZERO(devMode);
461 devMode.dmSize = sizeof(DEVMODE);
462
463 /* get the current screen setup */
464 if (!EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode))
465 {
466 LogFlowFunc(("error from EnumDisplaySettings: %d\n", GetLastError()));
467 return;
468 }
469
470 LogFlowFunc(("Current mode: %d x %d x %d at %d,%d\n",
471 devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel, devMode.dmPosition.x, devMode.dmPosition.y));
472
473 /* Check whether a mode reset or a change is requested. */
474 if (dwNewXRes || dwNewYRes || dwNewBpp)
475 {
476 /* A change is requested.
477 * Set values which are not to be changed to the current values.
478 */
479 if (!dwNewXRes)
480 dwNewXRes = devMode.dmPelsWidth;
481 if (!dwNewYRes)
482 dwNewYRes = devMode.dmPelsHeight;
483 if (!dwNewBpp)
484 dwNewBpp = devMode.dmBitsPerPel;
485 }
486 else
487 {
488 /* All zero values means a forced mode reset. Do nothing. */
489 LogFlowFunc(("Forced mode reset\n"));
490 }
491
492 /* Verify that the mode is indeed changed. */
493 if (devMode.dmPelsWidth == dwNewXRes
494 && devMode.dmPelsHeight == dwNewYRes
495 && devMode.dmBitsPerPel == dwNewBpp)
496 {
497 LogFlowFunc(("already at desired resolution\n"));
498 return;
499 }
500
501 // without this, Windows will not ask the miniport for its
502 // mode table but uses an internal cache instead
503 DEVMODE tempDevMode = { 0 };
504 tempDevMode.dmSize = sizeof(DEVMODE);
505 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
506
507 /* adjust the values that are supposed to change */
508 if (dwNewXRes)
509 devMode.dmPelsWidth = dwNewXRes;
510 if (dwNewYRes)
511 devMode.dmPelsHeight = dwNewYRes;
512 if (dwNewBpp)
513 devMode.dmBitsPerPel = dwNewBpp;
514
515 LogFlowFunc(("setting new mode %d x %d, %d BPP\n",
516 devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel));
517
518 /* set the new mode */
519 LONG status = ChangeDisplaySettings(&devMode, CDS_UPDATEREGISTRY);
520 if (status != DISP_CHANGE_SUCCESSFUL)
521 {
522 LogFlowFunc(("error from ChangeDisplaySettings: %d\n", status));
523
524 if (status == DISP_CHANGE_BADMODE)
525 {
526 /* Our driver can not set the requested mode. Stop trying. */
527 return;
528 }
529 }
530}
531
532/* Returns TRUE to try again. */
533/** @todo r=andy Why not using the VMMDevDisplayChangeRequestEx structure for all those parameters here? */
534static BOOL ResizeDisplayDevice(PVBOXDISPLAYCONTEXT pCtx,
535 UINT Id, DWORD Width, DWORD Height, DWORD BitsPerPixel,
536 BOOL fEnabled, LONG dwNewPosX, LONG dwNewPosY, bool fChangeOrigin,
537 BOOL fExtDispSup)
538{
539 BOOL fDispAlreadyEnabled = false; /* check whether the monitor with ID is already enabled. */
540 BOOL fModeReset = ( Width == 0 && Height == 0 && BitsPerPixel == 0
541 && dwNewPosX == 0 && dwNewPosY == 0 && !fChangeOrigin);
542 DWORD dmFields = 0;
543 VBOXDISPLAY_DRIVER_TYPE enmDriverType = getVBoxDisplayDriverType(pCtx);
544
545 LogFlowFunc(("[%d] %dx%d at %d,%d fChangeOrigin %d fEnabled %d fExtDisSup %d\n",
546 Id, Width, Height, dwNewPosX, dwNewPosY, fChangeOrigin, fEnabled, fExtDispSup));
547
548 if (!pCtx->fAnyX)
549 Width &= 0xFFF8;
550
551 VBoxDispIfCancelPendingResize(&pCtx->pEnv->dispIf);
552
553 DWORD NumDevices = VBoxDisplayGetCount();
554
555 if (NumDevices == 0 || Id >= NumDevices)
556 {
557 LogFlowFunc(("ResizeDisplayDevice: Requested identifier %d is invalid. err %d\n", Id, GetLastError ()));
558 return FALSE;
559 }
560
561 LogFlowFunc(("ResizeDisplayDevice: Found total %d devices. err %d\n", NumDevices, GetLastError ()));
562
563 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca(sizeof (DISPLAY_DEVICE) * NumDevices);
564 DEVMODE *paDeviceModes = (DEVMODE *)alloca(sizeof (DEVMODE) * NumDevices);
565 RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices);
566 DWORD DevNum = 0;
567 DWORD DevPrimaryNum = 0;
568 DWORD dwStatus = VBoxDisplayGetConfig(NumDevices, &DevPrimaryNum, &DevNum, paDisplayDevices, paDeviceModes);
569 if (dwStatus != NO_ERROR)
570 {
571 LogFlowFunc(("ResizeDisplayDevice: VBoxGetDisplayConfig failed, %d\n", dwStatus));
572 return dwStatus;
573 }
574
575 if (NumDevices != DevNum)
576 LogFlowFunc(("ResizeDisplayDevice: NumDevices(%d) != DevNum(%d)\n", NumDevices, DevNum));
577
578 DWORD i;
579 for (i = 0; i < DevNum; ++i)
580 {
581 if (fExtDispSup)
582 {
583 LogRel(("Extended Display Support.\n"));
584 LogFlowFunc(("[%d] %dx%dx%d at %d,%d, dmFields 0x%x\n",
585 i,
586 paDeviceModes[i].dmPelsWidth,
587 paDeviceModes[i].dmPelsHeight,
588 paDeviceModes[i].dmBitsPerPel,
589 paDeviceModes[i].dmPosition.x,
590 paDeviceModes[i].dmPosition.y,
591 paDeviceModes[i].dmFields));
592 }
593 else
594 {
595 LogRel(("NO Ext Display Support \n"));
596 }
597
598 paRects[i].left = paDeviceModes[i].dmPosition.x;
599 paRects[i].top = paDeviceModes[i].dmPosition.y;
600 paRects[i].right = paDeviceModes[i].dmPosition.x + paDeviceModes[i].dmPelsWidth;
601 paRects[i].bottom = paDeviceModes[i].dmPosition.y + paDeviceModes[i].dmPelsHeight;
602 }
603
604 /* Keep a record if the display with ID is already active or not. */
605 if (paDisplayDevices[Id].StateFlags & DISPLAY_DEVICE_ACTIVE)
606 {
607 LogRel(("Display with ID=%d already enabled\n", Id));
608 fDispAlreadyEnabled = TRUE;
609 }
610
611 /* Width, height equal to 0 means that this value must be not changed.
612 * Update input parameters if necessary.
613 * Note: BitsPerPixel is taken into account later, when new rectangles
614 * are assigned to displays.
615 */
616 if (Width == 0)
617 Width = paRects[Id].right - paRects[Id].left;
618 else
619 dmFields |= DM_PELSWIDTH;
620
621 if (Height == 0)
622 Height = paRects[Id].bottom - paRects[Id].top;
623 else
624 dmFields |= DM_PELSHEIGHT;
625
626 if (BitsPerPixel == 0)
627 BitsPerPixel = paDeviceModes[Id].dmBitsPerPel;
628 else
629 dmFields |= DM_BITSPERPEL;
630
631 if (!fChangeOrigin)
632 {
633 /* Use existing position. */
634 dwNewPosX = paRects[Id].left;
635 dwNewPosY = paRects[Id].top;
636 LogFlowFunc(("existing dwNewPosX %d, dwNewPosY %d\n", dwNewPosX, dwNewPosY));
637 }
638
639 /* Always update the position.
640 * It is either explicitly requested or must be set to the existing position.
641 */
642 dmFields |= DM_POSITION;
643
644 /* Check whether a mode reset or a change is requested.
645 * Rectangle position is recalculated only if fEnabled is 1.
646 * For non extended supported modes (old Host VMs), fEnabled
647 * is always 1.
648 */
649 /* Handled the case where previouseresolution of secondary monitor
650 * was for eg. 1024*768*32 and monitor was in disabled state.
651 * User gives the command
652 * setvideomode 1024 768 32 1 yes.
653 * Now in this case the resolution request is same as previous one but
654 * monitor is going from disabled to enabled state so the below condition
655 * shour return false
656 * The below condition will only return true , if no mode reset has
657 * been requested AND fEnabled is 1 and fDispAlreadyEnabled is also 1 AND
658 * all rect conditions are true. Thus in this case nothing has to be done.
659 */
660 if ( !fModeReset
661 && (!fEnabled == !fDispAlreadyEnabled)
662 && paRects[Id].left == dwNewPosX
663 && paRects[Id].top == dwNewPosY
664 && paRects[Id].right - paRects[Id].left == (LONG)Width
665 && paRects[Id].bottom - paRects[Id].top == (LONG)Height
666 && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
667 {
668 LogRel(("Already at desired resolution. No Change.\n"));
669 return FALSE;
670 }
671
672 hlpResizeRect(paRects, NumDevices, DevPrimaryNum, Id,
673 fEnabled ? Width : 0, fEnabled ? Height : 0, dwNewPosX, dwNewPosY);
674
675 for (i = 0; i < NumDevices; i++)
676 {
677 LogFlowFunc(("ResizeDisplayDevice: [%d]: %d,%d %dx%d\n",
678 i, paRects[i].left, paRects[i].top,
679 paRects[i].right - paRects[i].left,
680 paRects[i].bottom - paRects[i].top));
681 }
682
683 /* Assign the new rectangles to displays. */
684 for (i = 0; i < NumDevices; i++)
685 {
686 paDeviceModes[i].dmPosition.x = paRects[i].left;
687 paDeviceModes[i].dmPosition.y = paRects[i].top;
688 paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
689 paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
690
691 if (i == Id)
692 paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
693
694 if (enmDriverType >= VBOXDISPLAY_DRIVER_TYPE_WDDM)
695 {
696 paDeviceModes[i].dmFields |= dmFields;
697
698 /* On Vista one must specify DM_BITSPERPEL.
699 * Note that the current mode dmBitsPerPel is already in the DEVMODE structure.
700 */
701 if (!(paDeviceModes[i].dmFields & DM_BITSPERPEL))
702 {
703 LogFlowFunc(("no DM_BITSPERPEL\n"));
704 paDeviceModes[i].dmFields |= DM_BITSPERPEL;
705 paDeviceModes[i].dmBitsPerPel = 32;
706 }
707 }
708 else
709 {
710 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH | DM_BITSPERPEL;
711 }
712
713 LogFlowFunc(("ResizeDisplayDevice: Going to resize display %d to %dx%dx%d at %d,%d fields 0x%X\n",
714 i,
715 paDeviceModes[i].dmPelsWidth,
716 paDeviceModes[i].dmPelsHeight,
717 paDeviceModes[i].dmBitsPerPel,
718 paDeviceModes[i].dmPosition.x,
719 paDeviceModes[i].dmPosition.y,
720 paDeviceModes[i].dmFields));
721 }
722
723 if (enmDriverType == VBOXDISPLAY_DRIVER_TYPE_WDDM)
724 {
725 DWORD err = VBoxDispIfResizeModes(&pCtx->pEnv->dispIf, Id, fEnabled, fExtDispSup, paDisplayDevices, paDeviceModes, DevNum);
726
727 return (err == ERROR_RETRY);
728 }
729
730 /* The XPDM code path goes below.
731 * Re-requesting modes with EnumDisplaySettings forces Windows to again ask the miniport for its mode table.
732 */
733 for (i = 0; i < NumDevices; i++)
734 {
735 DEVMODE tempDevMode;
736 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
737 tempDevMode.dmSize = sizeof(DEVMODE);
738 EnumDisplaySettings((LPSTR)paDisplayDevices[i].DeviceName, 0xffffff, &tempDevMode);
739 LogFlowFunc(("ResizeDisplayDevice: EnumDisplaySettings last error %d\n", GetLastError ()));
740 }
741
742 /* Assign the new rectangles to displays. */
743 for (i = 0; i < NumDevices; i++)
744 {
745 LONG status = pCtx->pfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
746 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
747 LogFlowFunc(("ResizeDisplayDevice: ChangeDisplaySettingsEx position status %d, err %d\n", status, GetLastError()));
748 NOREF(status);
749 }
750
751 LogFlowFunc(("Enable And Resize Device. Id = %d, Width=%d Height=%d, dwNewPosX = %d, dwNewPosY = %d fEnabled=%d & fExtDispSupport = %d \n",
752 Id, Width, Height, dwNewPosX, dwNewPosY, fEnabled, fExtDispSup));
753 dwStatus = EnableAndResizeDispDev(paDeviceModes, paDisplayDevices, DevNum, Id, Width, Height, BitsPerPixel,
754 dwNewPosX, dwNewPosY, fEnabled, fExtDispSup);
755 if (dwStatus == DISP_CHANGE_SUCCESSFUL || dwStatus == DISP_CHANGE_BADMODE)
756 {
757 /* Successfully set new video mode or our driver can not set
758 * the requested mode. Stop trying.
759 */
760 return FALSE;
761 }
762 /* Retry the request. */
763 return TRUE;
764}
765
766static void doResize(PVBOXDISPLAYCONTEXT pCtx,
767 uint32_t iDisplay,
768 uint32_t cx,
769 uint32_t cy,
770 uint32_t cBits,
771 bool fEnabled,
772 uint32_t cxOrigin,
773 uint32_t cyOrigin,
774 bool fChangeOrigin)
775{
776 for (;;)
777 {
778 VBOXDISPLAY_DRIVER_TYPE enmDriverType = getVBoxDisplayDriverType(pCtx);
779 if (enmDriverType == VBOXDISPLAY_DRIVER_TYPE_UNKNOWN)
780 {
781 LogFlowFunc(("vboxDisplayDriver is not active\n"));
782 break;
783 }
784
785 if (pCtx->pfnChangeDisplaySettingsEx != 0)
786 {
787 LogFlowFunc(("Detected W2K or later\n"));
788 if (!ResizeDisplayDevice(pCtx,
789 iDisplay,
790 cx,
791 cy,
792 cBits,
793 fEnabled,
794 cxOrigin,
795 cyOrigin,
796 fChangeOrigin,
797 true /*fExtDispSup*/ ))
798 {
799 LogFlowFunc(("ResizeDipspalyDevice return 0\n"));
800 break;
801 }
802
803 }
804 else
805 {
806 LogFlowFunc(("Detected NT\n"));
807 ResizeDisplayDeviceNT4(cx, cy, cBits);
808 break;
809 }
810
811 /* Retry the change a bit later. */
812 RTThreadSleep(1000);
813 }
814}
815
816static BOOL DisplayChangeRequestHandler(PVBOXDISPLAYCONTEXT pCtx)
817{
818 VMMDevDisplayDef aDisplays[64];
819 uint32_t cDisplays = RT_ELEMENTS(aDisplays);
820 int rc = VINF_SUCCESS;
821
822 /* Multidisplay resize is still implemented only for Win7 and newer guests. */
823 if (pCtx->pEnv->dispIf.enmMode >= VBOXDISPIF_MODE_WDDM_W7 &&
824 RT_SUCCESS(rc = VbglR3GetDisplayChangeRequestMulti(cDisplays, &cDisplays, &aDisplays[0], true /* fAck */)))
825 {
826 uint32_t i;
827
828 LogRel(("Got multi resize request %d displays\n", cDisplays));
829
830 for (i = 0; i < cDisplays; ++i)
831 {
832 LogRel(("[%d]: %d 0x%02X %d,%d %dx%d %d\n",
833 i, aDisplays[i].idDisplay,
834 aDisplays[i].fDisplayFlags,
835 aDisplays[i].xOrigin,
836 aDisplays[i].yOrigin,
837 aDisplays[i].cx,
838 aDisplays[i].cy,
839 aDisplays[i].cBitsPerPixel));
840 }
841
842 return VBoxDispIfResizeDisplayWin7(&pCtx->pEnv->dispIf, cDisplays, &aDisplays[0]);
843 }
844
845 /* Fall back to the single monitor resize request. */
846
847 /*
848 * We got at least one event. (bird: What does that mean actually? The driver wakes us up immediately upon
849 * receiving the event. Or are we refering to mouse & display? In the latter case it's misleading.)
850 *
851 * Read the requested resolution and try to set it until success.
852 * New events will not be seen but a new resolution will be read in
853 * this poll loop.
854 *
855 * Note! The interface we're using here was added in VBox 4.2.4. As of 2017-08-16, this
856 * version has been unsupported for a long time and we therefore don't bother
857 * implementing fallbacks using VMMDevDisplayChangeRequest2 and VMMDevDisplayChangeRequest.
858 */
859 uint32_t cx = 0;
860 uint32_t cy = 0;
861 uint32_t cBits = 0;
862 uint32_t iDisplay = 0;
863 uint32_t cxOrigin = 0;
864 uint32_t cyOrigin = 0;
865 bool fChangeOrigin = false;
866 bool fEnabled = false;
867 rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &iDisplay, &cxOrigin, &cyOrigin, &fEnabled, &fChangeOrigin,
868 true /*fAck*/);
869 if (RT_SUCCESS(rc))
870 {
871 /* Try to set the requested video mode. Repeat until it is successful or is rejected by the driver. */
872 LogFlowFunc(("DisplayChangeReqEx parameters iDisplay=%d x cx=%d x cy=%d x cBits=%d x SecondayMonEnb=%d x NewOriginX=%d x NewOriginY=%d x ChangeOrigin=%d\n",
873 iDisplay, cx, cy, cBits, fEnabled, cxOrigin, cyOrigin, fChangeOrigin));
874
875 doResize(pCtx,
876 iDisplay,
877 cx,
878 cy,
879 cBits,
880 fEnabled,
881 cxOrigin,
882 cyOrigin,
883 fChangeOrigin);
884 }
885
886 return rc;
887}
888
889/**
890 * Thread function to wait for and process display change requests.
891 */
892static DECLCALLBACK(int) VBoxDisplayWorker(void *pvInstance, bool volatile *pfShutdown)
893{
894 AssertPtr(pvInstance);
895 PVBOXDISPLAYCONTEXT pCtx = (PVBOXDISPLAYCONTEXT)pvInstance;
896 AssertPtr(pCtx->pEnv);
897 LogFlowFunc(("pvInstance=%p\n", pvInstance));
898
899 /*
900 * Tell the control thread that it can continue
901 * spawning services.
902 */
903 RTThreadUserSignal(RTThreadSelf());
904
905 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED, 0 /*fNot*/);
906 if (RT_FAILURE(rc))
907 {
908 LogFlowFunc(("VbglR3CtlFilterMask(mask,0): %Rrc\n", rc));
909 return rc;
910 }
911
912 PostMessage(g_hwndToolWindow, WM_VBOX_GRAPHICS_SUPPORTED, 0, 0);
913
914 VBoxDispIfResizeStarted(&pCtx->pEnv->dispIf);
915
916 for (;;)
917 {
918 /*
919 * Wait for a display change event, checking for shutdown both before and after.
920 */
921 if (*pfShutdown)
922 {
923 rc = VINF_SUCCESS;
924 break;
925 }
926
927 uint32_t fEvents = 0;
928 rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED, 1000 /*ms*/, &fEvents);
929
930 if (*pfShutdown)
931 {
932 rc = VINF_SUCCESS;
933 break;
934 }
935
936 if (RT_SUCCESS(rc))
937 {
938 if (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST)
939 DisplayChangeRequestHandler(pCtx);
940
941 if (fEvents & VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED)
942 hlpReloadCursor();
943 }
944 else
945 {
946 // Checking once a second whether or not WM_DISPLAYCHANGED happened.
947 if (ASMAtomicXchgU32(&g_fGuestDisplaysChanged, 0))
948 {
949 // XPDM driver has VBoxDispDrvNotify to receive such a notifications
950 if (pCtx->pEnv->dispIf.enmMode >= VBOXDISPIF_MODE_WDDM)
951 {
952 VBOXDISPIFESCAPE EscapeHdr = { 0 };
953 EscapeHdr.escapeCode = VBOXESC_GUEST_DISPLAYCHANGED;
954
955 DWORD err = VBoxDispIfEscapeInOut(&pCtx->pEnv->dispIf, &EscapeHdr, 0);
956 LogFlowFunc(("VBoxDispIfEscapeInOut returned %d\n", err)); NOREF(err);
957 }
958 }
959
960 /* sleep a bit to not eat too much CPU in case the above call always fails */
961 if (rc != VERR_TIMEOUT)
962 RTThreadSleep(10);
963 }
964 }
965
966 /*
967 * Remove event filter and graphics capability report.
968 */
969 int rc2 = VbglR3CtlFilterMask(0 /*fOr*/, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED /*fNot*/);
970 if (RT_FAILURE(rc2))
971 LogFlowFunc(("VbglR3CtlFilterMask failed: %Rrc\n", rc2));
972 PostMessage(g_hwndToolWindow, WM_VBOX_GRAPHICS_UNSUPPORTED, 0, 0);
973
974 LogFlowFuncLeaveRC(rc);
975 return rc;
976}
977
978/**
979 * The service description.
980 */
981VBOXSERVICEDESC g_SvcDescDisplay =
982{
983 /* pszName. */
984 "display",
985 /* pszDescription. */
986 "Display Notifications",
987 /* methods */
988 VBoxDisplayInit,
989 VBoxDisplayWorker,
990 NULL /* pfnStop */,
991 VBoxDisplayDestroy
992};
993
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette