VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp@ 42108

Last change on this file since 42108 was 42108, checked in by vboxsync, 13 years ago

wddm/vboxtray: autoresize working for display-only driver

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/** @file
2 * VBoxTray - Display Settings Interface abstraction for XPDM & WDDM
3 */
4
5/*
6 * Copyright (C) 2006-2010 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#include "VBoxDispIf.h"
18
19#include <iprt/log.h>
20#include <iprt/err.h>
21#include <iprt/assert.h>
22
23#include <malloc.h>
24
25/* display driver interface abstraction for XPDM & WDDM
26 * with WDDM we can not use ExtEscape to communicate with our driver
27 * because we do not have XPDM display driver any more, i.e. escape requests are handled by cdd
28 * that knows nothing about us */
29DWORD VBoxDispIfInit(PVBOXDISPIF pIf)
30{
31 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
32 return NO_ERROR;
33}
34
35DWORD VBoxDispIfTerm(PVBOXDISPIF pIf)
36{
37 pIf->enmMode = VBOXDISPIF_MODE_UNKNOWN;
38 return NO_ERROR;
39}
40
41static DWORD vboxDispIfEscapeXPDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
42{
43 HDC hdc = GetDC(HWND_DESKTOP);
44 VOID *pvData = cbData ? VBOXDISPIFESCAPE_DATA(pEscape, VOID) : NULL;
45 int iRet = ExtEscape(hdc, pEscape->escapeCode, cbData, (LPCSTR)pvData, 0, NULL);
46 ReleaseDC(HWND_DESKTOP, hdc);
47 if (iRet > 0)
48 return VINF_SUCCESS;
49 else if (iRet == 0)
50 return ERROR_NOT_SUPPORTED;
51 /* else */
52 return ERROR_GEN_FAILURE;
53}
54
55#ifdef VBOX_WITH_WDDM
56static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf)
57{
58 DWORD err = NO_ERROR;
59 OSVERSIONINFO OSinfo;
60 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
61 GetVersionEx (&OSinfo);
62 bool bSupported = true;
63
64 if (OSinfo.dwMajorVersion >= 6)
65 {
66 Log((__FUNCTION__": this is vista and up\n"));
67 HMODULE hUser = GetModuleHandle("USER32");
68 if (hUser)
69 {
70 *(uintptr_t *)&pIf->modeData.wddm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
71 Log((__FUNCTION__": VBoxDisplayInit: pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.wddm.pfnChangeDisplaySettingsEx));
72 bSupported &= !!(pIf->modeData.wddm.pfnChangeDisplaySettingsEx);
73
74 *(uintptr_t *)&pIf->modeData.wddm.pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA");
75 Log((__FUNCTION__": VBoxDisplayInit: pfnEnumDisplayDevices = %p\n", pIf->modeData.wddm.pfnEnumDisplayDevices));
76 bSupported &= !!(pIf->modeData.wddm.pfnEnumDisplayDevices);
77
78 /* this is vista and up */
79 HMODULE hGdi32 = GetModuleHandle("gdi32");
80 if (hGdi32 != NULL)
81 {
82 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromHdc");
83 Log((__FUNCTION__"pfnD3DKMTOpenAdapterFromHdc = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc));
84 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc);
85
86 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName = (PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromGdiDisplayName");
87 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName));
88 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName);
89
90 pIf->modeData.wddm.pfnD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter");
91 Log((__FUNCTION__": pfnD3DKMTCloseAdapter = %p\n", pIf->modeData.wddm.pfnD3DKMTCloseAdapter));
92 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
93
94 pIf->modeData.wddm.pfnD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(hGdi32, "D3DKMTEscape");
95 Log((__FUNCTION__": pfnD3DKMTEscape = %p\n", pIf->modeData.wddm.pfnD3DKMTEscape));
96 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
97
98 pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn = (PFND3DKMT_INVALIDATEACTIVEVIDPN)GetProcAddress(hGdi32, "D3DKMTInvalidateActiveVidPn");
99 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn = %p\n", pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn));
100 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn);
101
102 if (!bSupported)
103 {
104 Log((__FUNCTION__": one of pfnD3DKMT function pointers failed to initialize\n"));
105 err = ERROR_NOT_SUPPORTED;
106 }
107 }
108 else
109 {
110 Log((__FUNCTION__": GetModuleHandle(gdi32) failed, err(%d)\n", GetLastError()));
111 err = ERROR_NOT_SUPPORTED;
112 }
113
114 }
115 else
116 {
117 Log((__FUNCTION__": GetModuleHandle(USER32) failed, err(%d)\n", GetLastError()));
118 err = ERROR_NOT_SUPPORTED;
119 }
120 }
121 else
122 {
123 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n"));
124 err = ERROR_NOT_SUPPORTED;
125 }
126
127 return err;
128}
129
130static DWORD vboxDispIfWDDMAdpHdcCreate(int iDisplay, HDC *phDc, DISPLAY_DEVICE *pDev)
131{
132 DWORD winEr = ERROR_INVALID_STATE;
133 memset(pDev, 0, sizeof (*pDev));
134 pDev->cb = sizeof (*pDev);
135
136 for (int i = 0; ; ++i)
137 {
138 if (EnumDisplayDevices(NULL, /* LPCTSTR lpDevice */ i, /* DWORD iDevNum */
139 pDev, 0 /* DWORD dwFlags*/))
140 {
141 if (i == iDisplay || (iDisplay < 0 && pDev->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
142 {
143 HDC hDc = CreateDC(NULL, pDev->DeviceName, NULL, NULL);
144 if (hDc)
145 {
146 *phDc = hDc;
147 return NO_ERROR;
148 }
149 else
150 {
151 winEr = GetLastError();
152 Assert(0);
153 break;
154 }
155 }
156 }
157 else
158 {
159 winEr = GetLastError();
160 Assert(0);
161 break;
162 }
163 }
164
165 return winEr;
166}
167
168
169typedef DECLCALLBACK(BOOLEAN) FNVBOXDISPIFWDDM_ADAPTEROP(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext);
170typedef FNVBOXDISPIFWDDM_ADAPTEROP *PFNVBOXDISPIFWDDM_ADAPTEROP;
171static DWORD vboxDispIfWDDMAdapterOp(PCVBOXDISPIF pIf, int iDisplay, PFNVBOXDISPIFWDDM_ADAPTEROP pfnOp, PVOID pContext)
172{
173 D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0};
174 DISPLAY_DEVICE DDev;
175 DWORD err = vboxDispIfWDDMAdpHdcCreate(iDisplay, &OpenAdapterData.hDc, &DDev);
176 Assert(err == NO_ERROR);
177 if (err == NO_ERROR)
178 {
179 NTSTATUS Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData);
180 Assert(!Status);
181 if (!Status)
182 {
183 BOOLEAN bCloseAdapter = pfnOp(pIf, OpenAdapterData.hAdapter, &DDev, pContext);
184
185 if (bCloseAdapter)
186 {
187 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
188 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
189 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
190 if (Status)
191 {
192 Log((__FUNCTION__": pfnD3DKMTCloseAdapter failed, Status (0x%x)\n", Status));
193 }
194 }
195 }
196 else
197 {
198 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName failed, Status (0x%x)\n", Status));
199 err = ERROR_GEN_FAILURE;
200 }
201
202 DeleteDC(OpenAdapterData.hDc);
203 }
204
205 return err;
206}
207
208typedef struct
209{
210 NTSTATUS Status;
211 PVBOXDISPIFESCAPE pEscape;
212 int cbData;
213 D3DDDI_ESCAPEFLAGS EscapeFlags;
214} VBOXDISPIFWDDM_ESCAPEOP_CONTEXT, *PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT;
215
216DECLCALLBACK(BOOLEAN) vboxDispIfEscapeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
217{
218 PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT)pContext;
219
220 D3DKMT_ESCAPE EscapeData = {0};
221 EscapeData.hAdapter = hAdapter;
222 //EscapeData.hDevice = NULL;
223 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
224 EscapeData.Flags = pCtx->EscapeFlags;
225 EscapeData.pPrivateDriverData = pCtx->pEscape;
226 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(pCtx->cbData);
227 //EscapeData.hContext = NULL;
228
229 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTEscape(&EscapeData);
230
231 return TRUE;
232}
233
234static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess)
235{
236 VBOXDISPIFWDDM_ESCAPEOP_CONTEXT Ctx = {0};
237 Ctx.pEscape = pEscape;
238 Ctx.cbData = cbData;
239 if (fHwAccess)
240 Ctx.EscapeFlags.HardwareAccess = 1;
241 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1 /* iDisplay, -1 means primary */, vboxDispIfEscapeWDDMOp, &Ctx);
242 if (err == NO_ERROR)
243 {
244 if (!Ctx.Status)
245 err = NO_ERROR;
246 else
247 {
248 if (Ctx.Status == 0xC00000BBL) /* not supported */
249 err = ERROR_NOT_SUPPORTED;
250 else
251 err = ERROR_GEN_FAILURE;
252 Log((__FUNCTION__": pfnD3DKMTEscape failed, Status (0x%x)\n", Ctx.Status));
253 }
254 }
255 else
256 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
257
258 return err;
259}
260
261typedef struct
262{
263 NTSTATUS Status;
264 VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO Info;
265} VBOXDISPIFWDDM_RESIZEOP_CONTEXT, *PVBOXDISPIFWDDM_RESIZEOP_CONTEXT;
266
267DECLCALLBACK(BOOLEAN) vboxDispIfResizeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
268{
269 PVBOXDISPIFWDDM_RESIZEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_RESIZEOP_CONTEXT)pContext;
270 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
271 uint32_t cbData = VBOXWDDM_RECOMMENDVIDPN_SIZE(1);
272 PVBOXWDDM_RECOMMENDVIDPN pData = (PVBOXWDDM_RECOMMENDVIDPN)malloc(cbData);
273 if (pData)
274 {
275 memset(pData, 0, cbData);
276 pData->cScreenInfos = 1;
277 memcpy(&pData->aScreenInfos[0], &pCtx->Info, sizeof (VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO));
278
279 IAVidPnData.hAdapter = hAdapter;
280 IAVidPnData.pPrivateDriverData = pData;
281 IAVidPnData.PrivateDriverDataSize = cbData;
282
283 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
284 Assert(!pCtx->Status);
285 if (pCtx->Status)
286 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", pCtx->Status));
287
288 free(pData);
289 }
290 else
291 {
292 Log((__FUNCTION__": malloc failed\n"));
293 pCtx->Status = -1;
294 }
295
296 return TRUE;
297}
298
299static DWORD vboxDispIfResizeWDDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
300{
301 VBOXDISPIFWDDM_RESIZEOP_CONTEXT Ctx = {0};
302 Ctx.Info.Id = Id;
303 Ctx.Info.Width = Width;
304 Ctx.Info.Height = Height;
305 Ctx.Info.BitsPerPixel = BitsPerPixel;
306 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1, /* (int)Id - always say -1 to use primary display since the display does not really matter here */
307 vboxDispIfResizeWDDMOp, &Ctx);
308 if (err == NO_ERROR)
309 {
310 if (!Ctx.Status)
311 err = NO_ERROR;
312 else
313 {
314 if (Ctx.Status == 0xC00000BBL) /* not supported */
315 err = ERROR_NOT_SUPPORTED;
316 else
317 err = ERROR_GEN_FAILURE;
318 Log((__FUNCTION__": vboxDispIfResizeWDDMOp failed, Status (0x%x)\n", Ctx.Status));
319 }
320 }
321 else
322 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
323
324 return err;
325}
326#endif
327
328DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
329{
330 switch (pIf->enmMode)
331 {
332 case VBOXDISPIF_MODE_XPDM_NT4:
333 case VBOXDISPIF_MODE_XPDM:
334 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData);
335#ifdef VBOX_WITH_WDDM
336 case VBOXDISPIF_MODE_WDDM:
337 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
338#endif
339 default:
340 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
341 return ERROR_INVALID_PARAMETER;
342 }
343}
344
345static DWORD vboxDispIfResizeXPDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
346{
347 return ERROR_NOT_SUPPORTED;
348}
349
350DWORD VBoxDispIfResize(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
351{
352 switch (pIf->enmMode)
353 {
354 case VBOXDISPIF_MODE_XPDM_NT4:
355 return ERROR_NOT_SUPPORTED;
356 case VBOXDISPIF_MODE_XPDM:
357 return vboxDispIfResizeXPDM(pIf, Id, Width, Height, BitsPerPixel);
358#ifdef VBOX_WITH_WDDM
359 case VBOXDISPIF_MODE_WDDM:
360 return vboxDispIfResizeWDDM(pIf, Id, Width, Height, BitsPerPixel);
361#endif
362 default:
363 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
364 return ERROR_INVALID_PARAMETER;
365 }
366}
367
368static BOOL vboxDispIfValidateResize(DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
369{
370 DISPLAY_DEVICE DisplayDevice;
371 int i = 0;
372 UINT cMatched = 0;
373 DEVMODE DeviceMode;
374 for (int i = 0; ; ++i)
375 {
376 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
377 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
378
379 if (!EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
380 break;
381
382 Log(("VBoxTray: vboxDispIfValidateResize: [%d(%d)] %s\n", i, cMatched, DisplayDevice.DeviceName));
383
384 BOOL bFetchDevice = FALSE;
385
386 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
387 {
388 Log(("VBoxTray: vboxDispIfValidateResize: Found primary device. err %d\n", GetLastError ()));
389 bFetchDevice = TRUE;
390 }
391 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
392 {
393
394 Log(("VBoxTray: vboxDispIfValidateResize: Found secondary device. err %d\n", GetLastError ()));
395 bFetchDevice = TRUE;
396 }
397
398 if (bFetchDevice)
399 {
400 if (cMatched >= cDevModes)
401 {
402 Log(("VBoxTray: vboxDispIfValidateResize: %d >= %d\n", cDevModes, cMatched));
403 return FALSE;
404 }
405
406 /* First try to get the video mode stored in registry (ENUM_REGISTRY_SETTINGS).
407 * A secondary display could be not active at the moment and would not have
408 * a current video mode (ENUM_CURRENT_SETTINGS).
409 */
410 ZeroMemory(&DeviceMode, sizeof(DeviceMode));
411 DeviceMode.dmSize = sizeof(DEVMODE);
412 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
413 ENUM_REGISTRY_SETTINGS, &DeviceMode))
414 {
415 Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings error %d\n", GetLastError ()));
416 return FALSE;
417 }
418
419 if ( DeviceMode.dmPelsWidth == 0
420 || DeviceMode.dmPelsHeight == 0)
421 {
422 /* No ENUM_REGISTRY_SETTINGS yet. Seen on Vista after installation.
423 * Get the current video mode then.
424 */
425 ZeroMemory(&DeviceMode, sizeof(DeviceMode));
426 DeviceMode.dmSize = sizeof(DeviceMode);
427 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
428 ENUM_CURRENT_SETTINGS, &DeviceMode))
429 {
430 /* ENUM_CURRENT_SETTINGS returns FALSE when the display is not active:
431 * for example a disabled secondary display */
432 Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings(ENUM_CURRENT_SETTINGS) error %d\n", GetLastError ()));
433 return FALSE;
434 }
435 }
436
437 UINT j = 0;
438 for (; j < cDevModes; ++j)
439 {
440 if (!strncmp(DisplayDevice.DeviceName, paDisplayDevices[j].DeviceName, RT_ELEMENTS(DeviceMode.dmDeviceName)))
441 {
442 if (paDeviceModes[j].dmBitsPerPel != DeviceMode.dmBitsPerPel
443 || (paDeviceModes[j].dmPelsWidth & 0xfff8) != (DeviceMode.dmPelsWidth & 0xfff8)
444 || (paDeviceModes[j].dmPelsHeight & 0xfff8) != (DeviceMode.dmPelsHeight & 0xfff8)
445 || (paDeviceModes[j].dmPosition.x & 0xfff8) != (DeviceMode.dmPosition.x & 0xfff8)
446 || (paDeviceModes[j].dmPosition.y & 0xfff8) != (DeviceMode.dmPosition.y & 0xfff8)
447 || (paDisplayDevices[j].StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
448 {
449 return FALSE;
450 }
451 break;
452 }
453 }
454
455 if (j == cDevModes)
456 return FALSE;
457
458 ++cMatched;
459 }
460 }
461
462 return cMatched == cDevModes;
463}
464
465#ifdef VBOX_WITH_WDDM
466static DWORD vboxDispIfReinitVideoModes(PCVBOXDISPIF const pIf)
467{
468 VBOXDISPIFESCAPE escape = {0};
469 escape.escapeCode = VBOXESC_REINITVIDEOMODES;
470 DWORD err = vboxDispIfEscapeWDDM(pIf, &escape, 0, FALSE /* hw access must be false here,
471 * otherwise the miniport driver would fail
472 * request to prevent a deadlock */);
473 if (err != NO_ERROR)
474 {
475 Log((__FUNCTION__": VBoxDispIfEscape failed with err (%d)\n", err));
476 }
477 return err;
478}
479
480static DWORD vboxDispIfAdjustMode(DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode)
481{
482 DEVMODE CurMode;
483 DEVMODE BestMatchMode;
484 DWORD i = 0;
485 int64_t diffWH = INT64_MAX;
486 int diffBpp = INT32_MAX;
487 for (; ; ++i)
488 {
489 CurMode.dmSize = sizeof (CurMode);
490 CurMode.dmDriverExtra = 0;
491
492 if (!EnumDisplaySettings(pDisplayDevice->DeviceName, i, &CurMode))
493 break;
494
495 if (CurMode.dmPelsWidth == pDeviceMode->dmPelsWidth
496 && CurMode.dmPelsHeight == pDeviceMode->dmPelsHeight
497 && CurMode.dmBitsPerPel == pDeviceMode->dmBitsPerPel)
498 {
499 Log(("Exact match found"));
500 *pDeviceMode = CurMode;
501 return NO_ERROR;
502 }
503
504 int diffCurrW = RT_ABS((int)(CurMode.dmPelsWidth - pDeviceMode->dmPelsWidth));
505 int diffCurrH = RT_ABS((int)(CurMode.dmPelsHeight - pDeviceMode->dmPelsHeight));
506 int diffCurrBpp = RT_ABS((int)(CurMode.dmBitsPerPel - pDeviceMode->dmBitsPerPel)
507 - 1 /* <- to make higher bpp take precedence over lower ones */
508 );
509
510 int64_t diffCurrHW = (int64_t)diffCurrW*diffCurrW + (int64_t)diffCurrH*diffCurrH;
511
512 if (i == 0
513 || diffCurrHW < diffWH
514 || (diffCurrHW == diffWH && diffCurrBpp < diffBpp))
515 {
516 /* first run */
517 BestMatchMode = CurMode;
518 diffWH = diffCurrHW;
519 diffBpp = diffCurrBpp;
520 continue;
521 }
522 }
523
524 if (i == 0)
525 {
526 Log(("No modes found!"));
527 return NO_ERROR;
528 }
529
530 *pDeviceMode = BestMatchMode;
531 return NO_ERROR;
532}
533
534DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
535{
536 UINT cbVidPnInfo = VBOXWDDM_RECOMMENDVIDPN_SIZE(cDevModes);
537 PVBOXWDDM_RECOMMENDVIDPN pVidPnInfo = (PVBOXWDDM_RECOMMENDVIDPN)alloca(cbVidPnInfo);
538 pVidPnInfo->cScreenInfos = cDevModes;
539 D3DKMT_HANDLE hAdapter = NULL;
540 NTSTATUS Status;
541 DWORD winEr = NO_ERROR;
542 UINT i = 0;
543
544 for (; i < cDevModes; i++)
545 {
546 PVBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO pInfo = &pVidPnInfo->aScreenInfos[i];
547 D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0};
548 OpenAdapterData.hDc = CreateDC(NULL, paDisplayDevices[i].DeviceName, NULL, NULL);
549 if (!OpenAdapterData.hDc)
550 {
551 winEr = GetLastError();
552 Assert(0);
553 break;
554 }
555
556 Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData);
557 Assert(!Status);
558 if (Status)
559 {
560 winEr = ERROR_GEN_FAILURE;
561 Assert(0);
562 break;
563 }
564
565 pInfo->Id = OpenAdapterData.VidPnSourceId;
566 pInfo->Width = paDeviceModes[i].dmPelsWidth;
567 pInfo->Height = paDeviceModes[i].dmPelsHeight;
568 pInfo->BitsPerPixel = paDeviceModes[i].dmBitsPerPel;
569
570 if (!hAdapter)
571 {
572 hAdapter = OpenAdapterData.hAdapter;
573 }
574 else
575 {
576 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
577 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
578 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
579 Assert(!Status);
580 }
581 }
582
583 BOOL fAbleToInvalidateVidPn = FALSE;
584
585 if (winEr == NO_ERROR)
586 {
587 Assert(hAdapter);
588
589 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
590 IAVidPnData.hAdapter = hAdapter;
591 IAVidPnData.pPrivateDriverData = pVidPnInfo;
592 IAVidPnData.PrivateDriverDataSize = cbVidPnInfo;
593
594 DWORD winEr = NO_ERROR;
595 Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
596 Assert(!Status);
597 if (Status)
598 {
599 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", Status));
600 winEr = ERROR_GEN_FAILURE;
601 }
602 else
603 {
604 fAbleToInvalidateVidPn = TRUE;
605 }
606 }
607
608 if (hAdapter)
609 {
610 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
611 ClosaAdapterData.hAdapter = hAdapter;
612 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
613 Assert(!Status);
614 }
615
616 if (!fAbleToInvalidateVidPn)
617 {
618 /* fallback impl: make the driver invalidate VidPn,
619 * which is done by emulating a monitor re-plug currently */
620 vboxDispIfReinitVideoModes(pIf);
621
622 /* sleep 2 seconds: dirty hack to wait for the new monitor info to be picked up,
623 * @todo: implement it properly by monitoring monitor device arrival/removal */
624 Sleep(2 * 1000);
625 }
626
627 /* ignore any prev errors and just check if resize is OK */
628 if (!vboxDispIfValidateResize(paDisplayDevices, paDeviceModes, cDevModes))
629 {
630 /* now try to resize in a "regular" way */
631 /* Assign the new rectangles to displays. */
632 for (i = 0; i < cDevModes; i++)
633 {
634 /* On Vista one must specify DM_BITSPERPEL.
635 * Note that the current mode dmBitsPerPel is already in the DEVMODE structure.
636 */
637 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH | DM_BITSPERPEL;
638
639 Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n",
640 pIf->modeData.wddm.pfnChangeDisplaySettingsEx,
641 paDeviceModes[i].dmPelsWidth,
642 paDeviceModes[i].dmPelsHeight,
643 paDeviceModes[i].dmBitsPerPel,
644 paDeviceModes[i].dmPosition.x,
645 paDeviceModes[i].dmPosition.y));
646
647 /* the miniport might have been adjusted the display mode stuff,
648 * adjust the paDeviceModes[i] by picking the closest available one */
649 DEVMODE AdjustedMode = paDeviceModes[i];
650 vboxDispIfAdjustMode(&paDisplayDevices[i], &AdjustedMode);
651
652 LONG status = pIf->modeData.wddm.pfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
653 &AdjustedMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
654 Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettingsEx position status %d, err %d\n", status, GetLastError ()));
655 }
656
657 /* A second call to ChangeDisplaySettings updates the monitor. */
658 LONG status = pIf->modeData.wddm.pfnChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
659 Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettings update status %d\n", status));
660 if (status == DISP_CHANGE_SUCCESSFUL)
661 {
662 winEr = NO_ERROR;
663 }
664 else if (status == DISP_CHANGE_BADMODE)
665 {
666 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
667 winEr = ERROR_RETRY;
668 }
669 else
670 {
671 winEr = ERROR_GEN_FAILURE;
672 }
673 }
674 else
675 {
676 winEr = NO_ERROR;
677 }
678
679 return winEr;
680}
681#endif /* VBOX_WITH_WDDM */
682
683DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
684{
685 switch (pIf->enmMode)
686 {
687 case VBOXDISPIF_MODE_XPDM_NT4:
688 return ERROR_NOT_SUPPORTED;
689 case VBOXDISPIF_MODE_XPDM:
690 return ERROR_NOT_SUPPORTED;
691#ifdef VBOX_WITH_WDDM
692 case VBOXDISPIF_MODE_WDDM:
693 return vboxDispIfResizeModesWDDM(pIf, paDisplayDevices, paDeviceModes, cDevModes);
694#endif
695 default:
696 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
697 return ERROR_INVALID_PARAMETER;
698 }
699}
700
701static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
702{
703 return NO_ERROR;
704}
705
706static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
707{
708 DWORD err = NO_ERROR;
709 AssertBreakpoint();
710 OSVERSIONINFO OSinfo;
711 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
712 GetVersionEx (&OSinfo);
713 if (OSinfo.dwMajorVersion >= 5)
714 {
715 HMODULE hUser = GetModuleHandle("USER32");
716 if (NULL != hUser)
717 {
718 bool bSupported = true;
719 *(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
720 Log((__FUNCTION__": pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
721 bSupported &= !!(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
722
723 if (!bSupported)
724 {
725 Log((__FUNCTION__": pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
726 err = ERROR_NOT_SUPPORTED;
727 }
728 }
729 else
730 {
731 Log((__FUNCTION__": failed to get USER32 handle, err (%d)\n", GetLastError()));
732 err = ERROR_NOT_SUPPORTED;
733 }
734 }
735 else
736 {
737 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
738 err = ERROR_NOT_SUPPORTED;
739 }
740
741 return err;
742}
743
744DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
745{
746 /* @todo: may need to addd synchronization in case we want to change modes dynamically
747 * i.e. currently the mode is supposed to be initialized once on service initialization */
748 if (penmOldMode)
749 *penmOldMode = pIf->enmMode;
750
751 if (enmMode == pIf->enmMode)
752 return VINF_ALREADY_INITIALIZED;
753
754 DWORD err = NO_ERROR;
755 switch (enmMode)
756 {
757 case VBOXDISPIF_MODE_XPDM_NT4:
758 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
759 err = vboxDispIfSwitchToXPDM_NT4(pIf);
760 if (err == NO_ERROR)
761 {
762 Log((__FUNCTION__": successfully switched to XPDM_NT4 mode\n"));
763 pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
764 }
765 else
766 Log((__FUNCTION__": failed to switch to XPDM_NT4 mode, err (%d)\n", err));
767 break;
768 case VBOXDISPIF_MODE_XPDM:
769 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM\n"));
770 err = vboxDispIfSwitchToXPDM(pIf);
771 if (err == NO_ERROR)
772 {
773 Log((__FUNCTION__": successfully switched to XPDM mode\n"));
774 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
775 }
776 else
777 Log((__FUNCTION__": failed to switch to XPDM mode, err (%d)\n", err));
778 break;
779#ifdef VBOX_WITH_WDDM
780 case VBOXDISPIF_MODE_WDDM:
781 {
782 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_WDDM\n"));
783 err = vboxDispIfSwitchToWDDM(pIf);
784 if (err == NO_ERROR)
785 {
786 Log((__FUNCTION__": successfully switched to WDDM mode\n"));
787 pIf->enmMode = VBOXDISPIF_MODE_WDDM;
788 }
789 else
790 Log((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err));
791 break;
792 }
793#endif
794 default:
795 err = ERROR_INVALID_PARAMETER;
796 break;
797 }
798 return err;
799}
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