VirtualBox

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

Last change on this file since 44528 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.0 KB
Line 
1/** @file
2 * VBoxTray - Display Settings Interface abstraction for XPDM & WDDM
3 */
4
5/*
6 * Copyright (C) 2006-2012 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 "VBoxTray.h"
18
19#include <iprt/log.h>
20#include <iprt/err.h>
21#include <iprt/assert.h>
22
23#include <malloc.h>
24
25#ifdef VBOX_WITH_WDDM
26#include <iprt/asm.h>
27#endif
28
29/* display driver interface abstraction for XPDM & WDDM
30 * with WDDM we can not use ExtEscape to communicate with our driver
31 * because we do not have XPDM display driver any more, i.e. escape requests are handled by cdd
32 * that knows nothing about us */
33DWORD VBoxDispIfInit(PVBOXDISPIF pIf)
34{
35 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
36 return NO_ERROR;
37}
38
39#ifdef VBOX_WITH_WDDM
40static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf);
41static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf);
42#endif
43
44DWORD VBoxDispIfTerm(PVBOXDISPIF pIf)
45{
46#ifdef VBOX_WITH_WDDM
47 if (pIf->enmMode == VBOXDISPIF_MODE_WDDM)
48 {
49 vboxDispIfWddmTerm(pIf);
50 }
51#endif
52
53 pIf->enmMode = VBOXDISPIF_MODE_UNKNOWN;
54 return NO_ERROR;
55}
56
57static DWORD vboxDispIfEscapeXPDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, int iDirection)
58{
59 HDC hdc = GetDC(HWND_DESKTOP);
60 VOID *pvData = cbData ? VBOXDISPIFESCAPE_DATA(pEscape, VOID) : NULL;
61 int iRet = ExtEscape(hdc, pEscape->escapeCode,
62 iDirection >= 0 ? cbData : 0,
63 iDirection >= 0 ? (LPSTR)pvData : NULL,
64 iDirection <= 0 ? cbData : 0,
65 iDirection <= 0 ? (LPSTR)pvData : NULL);
66 ReleaseDC(HWND_DESKTOP, hdc);
67 if (iRet > 0)
68 return VINF_SUCCESS;
69 else if (iRet == 0)
70 return ERROR_NOT_SUPPORTED;
71 /* else */
72 return ERROR_GEN_FAILURE;
73}
74
75#ifdef VBOX_WITH_WDDM
76static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf)
77{
78 DWORD err = NO_ERROR;
79 OSVERSIONINFO OSinfo;
80 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
81 GetVersionEx (&OSinfo);
82 bool bSupported = true;
83
84 if (OSinfo.dwMajorVersion >= 6)
85 {
86 Log((__FUNCTION__": this is vista and up\n"));
87 HMODULE hUser = GetModuleHandle("USER32");
88 if (hUser)
89 {
90 *(uintptr_t *)&pIf->modeData.wddm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
91 Log((__FUNCTION__": VBoxDisplayInit: pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.wddm.pfnChangeDisplaySettingsEx));
92 bSupported &= !!(pIf->modeData.wddm.pfnChangeDisplaySettingsEx);
93
94 *(uintptr_t *)&pIf->modeData.wddm.pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA");
95 Log((__FUNCTION__": VBoxDisplayInit: pfnEnumDisplayDevices = %p\n", pIf->modeData.wddm.pfnEnumDisplayDevices));
96 bSupported &= !!(pIf->modeData.wddm.pfnEnumDisplayDevices);
97
98 /* this is vista and up */
99 HMODULE hGdi32 = GetModuleHandle("gdi32");
100 if (hGdi32 != NULL)
101 {
102 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromHdc");
103 Log((__FUNCTION__"pfnD3DKMTOpenAdapterFromHdc = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc));
104 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc);
105
106 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName = (PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromGdiDisplayName");
107 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName));
108 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName);
109
110 pIf->modeData.wddm.pfnD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter");
111 Log((__FUNCTION__": pfnD3DKMTCloseAdapter = %p\n", pIf->modeData.wddm.pfnD3DKMTCloseAdapter));
112 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
113
114 pIf->modeData.wddm.pfnD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(hGdi32, "D3DKMTEscape");
115 Log((__FUNCTION__": pfnD3DKMTEscape = %p\n", pIf->modeData.wddm.pfnD3DKMTEscape));
116 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
117
118 pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn = (PFND3DKMT_INVALIDATEACTIVEVIDPN)GetProcAddress(hGdi32, "D3DKMTInvalidateActiveVidPn");
119 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn = %p\n", pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn));
120 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn);
121
122 pIf->modeData.wddm.pfnD3DKMTPollDisplayChildren = (PFND3DKMT_POLLDISPLAYCHILDREN)GetProcAddress(hGdi32, "D3DKMTPollDisplayChildren");
123 Log((__FUNCTION__": pfnD3DKMTPollDisplayChildren = %p\n", pIf->modeData.wddm.pfnD3DKMTPollDisplayChildren));
124 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTPollDisplayChildren);
125
126 if (!bSupported)
127 {
128 Log((__FUNCTION__": one of pfnD3DKMT function pointers failed to initialize\n"));
129 err = ERROR_NOT_SUPPORTED;
130 }
131 }
132 else
133 {
134 Log((__FUNCTION__": GetModuleHandle(gdi32) failed, err(%d)\n", GetLastError()));
135 err = ERROR_NOT_SUPPORTED;
136 }
137
138 }
139 else
140 {
141 Log((__FUNCTION__": GetModuleHandle(USER32) failed, err(%d)\n", GetLastError()));
142 err = ERROR_NOT_SUPPORTED;
143 }
144 }
145 else
146 {
147 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n"));
148 err = ERROR_NOT_SUPPORTED;
149 }
150
151 if (err == ERROR_SUCCESS)
152 {
153 err = vboxDispIfWddmInit(pIf);
154 }
155
156 return err;
157}
158
159static DWORD vboxDispIfWDDMAdpHdcCreate(int iDisplay, HDC *phDc, DISPLAY_DEVICE *pDev)
160{
161 DWORD winEr = ERROR_INVALID_STATE;
162 memset(pDev, 0, sizeof (*pDev));
163 pDev->cb = sizeof (*pDev);
164
165 for (int i = 0; ; ++i)
166 {
167 if (EnumDisplayDevices(NULL, /* LPCTSTR lpDevice */ i, /* DWORD iDevNum */
168 pDev, 0 /* DWORD dwFlags*/))
169 {
170 if (i == iDisplay || (iDisplay < 0 && pDev->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
171 {
172 HDC hDc = CreateDC(NULL, pDev->DeviceName, NULL, NULL);
173 if (hDc)
174 {
175 *phDc = hDc;
176 return NO_ERROR;
177 }
178 else
179 {
180 winEr = GetLastError();
181 Log(("CreateDC failed %d", winEr));
182 break;
183 }
184 }
185 Log(("display data no match display(%d): i(%d), flags(%d)", iDisplay, i, pDev->StateFlags));
186 }
187 else
188 {
189 winEr = GetLastError();
190 Log(("EnumDisplayDevices failed %d", winEr));
191 break;
192 }
193 }
194
195 Log(("vboxDispIfWDDMAdpHdcCreate failure branch %d", winEr));
196 return winEr;
197}
198
199
200typedef DECLCALLBACK(BOOLEAN) FNVBOXDISPIFWDDM_ADAPTEROP(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext);
201typedef FNVBOXDISPIFWDDM_ADAPTEROP *PFNVBOXDISPIFWDDM_ADAPTEROP;
202static DWORD vboxDispIfWDDMAdapterOp(PCVBOXDISPIF pIf, int iDisplay, PFNVBOXDISPIFWDDM_ADAPTEROP pfnOp, PVOID pContext)
203{
204 D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0};
205 DISPLAY_DEVICE DDev;
206 DWORD err = vboxDispIfWDDMAdpHdcCreate(iDisplay, &OpenAdapterData.hDc, &DDev);
207 Assert(err == NO_ERROR);
208 if (err == NO_ERROR)
209 {
210 NTSTATUS Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData);
211 Assert(!Status);
212 if (!Status)
213 {
214 BOOLEAN bCloseAdapter = pfnOp(pIf, OpenAdapterData.hAdapter, &DDev, pContext);
215
216 if (bCloseAdapter)
217 {
218 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
219 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
220 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
221 if (Status)
222 {
223 Log((__FUNCTION__": pfnD3DKMTCloseAdapter failed, Status (0x%x)\n", Status));
224 }
225 }
226 }
227 else
228 {
229 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName failed, Status (0x%x)\n", Status));
230 err = ERROR_GEN_FAILURE;
231 }
232
233 DeleteDC(OpenAdapterData.hDc);
234 }
235 else
236 Log((__FUNCTION__": vboxDispIfWDDMAdpHdcCreate failed, winEr (%d)\n", err));
237
238 return err;
239}
240
241typedef struct
242{
243 NTSTATUS Status;
244 PVBOXDISPIFESCAPE pEscape;
245 int cbData;
246 D3DDDI_ESCAPEFLAGS EscapeFlags;
247} VBOXDISPIFWDDM_ESCAPEOP_CONTEXT, *PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT;
248
249DECLCALLBACK(BOOLEAN) vboxDispIfEscapeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
250{
251 PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT)pContext;
252
253 D3DKMT_ESCAPE EscapeData = {0};
254 EscapeData.hAdapter = hAdapter;
255 //EscapeData.hDevice = NULL;
256 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
257 EscapeData.Flags = pCtx->EscapeFlags;
258 EscapeData.pPrivateDriverData = pCtx->pEscape;
259 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(pCtx->cbData);
260 //EscapeData.hContext = NULL;
261
262 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTEscape(&EscapeData);
263
264 return TRUE;
265}
266
267static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess)
268{
269 VBOXDISPIFWDDM_ESCAPEOP_CONTEXT Ctx = {0};
270 Ctx.pEscape = pEscape;
271 Ctx.cbData = cbData;
272 if (fHwAccess)
273 Ctx.EscapeFlags.HardwareAccess = 1;
274 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1 /* iDisplay, -1 means primary */, vboxDispIfEscapeWDDMOp, &Ctx);
275 if (err == NO_ERROR)
276 {
277 if (!Ctx.Status)
278 err = NO_ERROR;
279 else
280 {
281 if (Ctx.Status == 0xC00000BBL) /* not supported */
282 err = ERROR_NOT_SUPPORTED;
283 else
284 err = ERROR_GEN_FAILURE;
285 Log((__FUNCTION__": pfnD3DKMTEscape failed, Status (0x%x)\n", Ctx.Status));
286 }
287 }
288 else
289 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
290
291 return err;
292}
293
294typedef struct
295{
296 NTSTATUS Status;
297 VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO Info;
298} VBOXDISPIFWDDM_RESIZEOP_CONTEXT, *PVBOXDISPIFWDDM_RESIZEOP_CONTEXT;
299
300DECLCALLBACK(BOOLEAN) vboxDispIfResizeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
301{
302 PVBOXDISPIFWDDM_RESIZEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_RESIZEOP_CONTEXT)pContext;
303 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
304 uint32_t cbData = VBOXWDDM_RECOMMENDVIDPN_SIZE(1);
305 PVBOXWDDM_RECOMMENDVIDPN pData = (PVBOXWDDM_RECOMMENDVIDPN)malloc(cbData);
306 if (pData)
307 {
308 memset(pData, 0, cbData);
309 pData->cScreenInfos = 1;
310 memcpy(&pData->aScreenInfos[0], &pCtx->Info, sizeof (VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO));
311
312 IAVidPnData.hAdapter = hAdapter;
313 IAVidPnData.pPrivateDriverData = pData;
314 IAVidPnData.PrivateDriverDataSize = cbData;
315
316 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
317 Assert(!pCtx->Status);
318 if (pCtx->Status)
319 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", pCtx->Status));
320
321 free(pData);
322 }
323 else
324 {
325 Log((__FUNCTION__": malloc failed\n"));
326 pCtx->Status = -1;
327 }
328
329 return TRUE;
330}
331
332static DWORD vboxDispIfResizeWDDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
333{
334 VBOXDISPIFWDDM_RESIZEOP_CONTEXT Ctx = {0};
335 Ctx.Info.Id = Id;
336 Ctx.Info.Width = Width;
337 Ctx.Info.Height = Height;
338 Ctx.Info.BitsPerPixel = BitsPerPixel;
339 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1, /* (int)Id - always say -1 to use primary display since the display does not really matter here */
340 vboxDispIfResizeWDDMOp, &Ctx);
341 if (err == NO_ERROR)
342 {
343 if (!Ctx.Status)
344 err = NO_ERROR;
345 else
346 {
347 if (Ctx.Status == 0xC00000BBL) /* not supported */
348 err = ERROR_NOT_SUPPORTED;
349 else
350 err = ERROR_GEN_FAILURE;
351 Log((__FUNCTION__": vboxDispIfResizeWDDMOp failed, Status (0x%x)\n", Ctx.Status));
352 }
353 }
354 else
355 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
356
357 return err;
358}
359#endif
360
361DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
362{
363 switch (pIf->enmMode)
364 {
365 case VBOXDISPIF_MODE_XPDM_NT4:
366 case VBOXDISPIF_MODE_XPDM:
367 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData, 1);
368#ifdef VBOX_WITH_WDDM
369 case VBOXDISPIF_MODE_WDDM:
370 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
371#endif
372 default:
373 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
374 return ERROR_INVALID_PARAMETER;
375 }
376}
377
378DWORD VBoxDispIfEscapeInOut(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
379{
380 switch (pIf->enmMode)
381 {
382 case VBOXDISPIF_MODE_XPDM_NT4:
383 case VBOXDISPIF_MODE_XPDM:
384 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData, 0);
385#ifdef VBOX_WITH_WDDM
386 case VBOXDISPIF_MODE_WDDM:
387 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
388#endif
389 default:
390 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
391 return ERROR_INVALID_PARAMETER;
392 }
393}
394
395static DWORD vboxDispIfResizeXPDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
396{
397 return ERROR_NOT_SUPPORTED;
398}
399
400DWORD VBoxDispIfResize(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
401{
402 switch (pIf->enmMode)
403 {
404 case VBOXDISPIF_MODE_XPDM_NT4:
405 return ERROR_NOT_SUPPORTED;
406 case VBOXDISPIF_MODE_XPDM:
407 return vboxDispIfResizeXPDM(pIf, Id, Width, Height, BitsPerPixel);
408#ifdef VBOX_WITH_WDDM
409 case VBOXDISPIF_MODE_WDDM:
410 return vboxDispIfResizeWDDM(pIf, Id, Width, Height, BitsPerPixel);
411#endif
412 default:
413 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
414 return ERROR_INVALID_PARAMETER;
415 }
416}
417
418
419#ifdef VBOX_WITH_WDDM
420typedef struct VBOXDISPIF_WDDM_INTERNAL
421{
422 PCVBOXDISPIF pIf;
423} VBOXDISPIF_WDDM_INTERNAL, *PVBOXDISPIF_WDDM_INTERNAL;
424
425static VBOXDISPIF_WDDM_INTERNAL g_VBoxDispIfWddm;
426
427static BOOL vboxDispIfWddmValidateResize(DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
428{
429 DISPLAY_DEVICE DisplayDevice;
430 int i = 0;
431 UINT cMatched = 0;
432 DEVMODE CurDevMode, RegDevMode;
433 for (int i = 0; ; ++i)
434 {
435 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
436 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
437
438 if (!EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
439 break;
440
441 Log(("VBoxTray: vboxDispIfValidateResize: [%d(%d)] %s\n", i, cMatched, DisplayDevice.DeviceName));
442
443 BOOL bFetchDevice = FALSE;
444
445 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
446 {
447 Log(("VBoxTray: vboxDispIfValidateResize: Found primary device. err %d\n", GetLastError ()));
448 bFetchDevice = TRUE;
449 }
450 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
451 {
452
453 Log(("VBoxTray: vboxDispIfValidateResize: Found secondary device. err %d\n", GetLastError ()));
454 bFetchDevice = TRUE;
455 }
456
457 if (bFetchDevice)
458 {
459 if (cMatched >= cDevModes)
460 {
461 Log(("VBoxTray: vboxDispIfValidateResize: %d >= %d\n", cDevModes, cMatched));
462 return FALSE;
463 }
464
465 /* First try to get the video mode stored in registry (ENUM_REGISTRY_SETTINGS).
466 * A secondary display could be not active at the moment and would not have
467 * a current video mode (ENUM_CURRENT_SETTINGS).
468 */
469 ZeroMemory(&RegDevMode, sizeof(RegDevMode));
470 RegDevMode.dmSize = sizeof(DEVMODE);
471 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
472 ENUM_REGISTRY_SETTINGS, &RegDevMode))
473 {
474 Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings error %d\n", GetLastError ()));
475 return FALSE;
476 }
477
478 /* with Win8 WDDM Display-only driver, it seems like sometimes we get an auto-resize setting being stored in registry, although current settings do not match */
479 ZeroMemory(&CurDevMode, sizeof(CurDevMode));
480 CurDevMode.dmSize = sizeof(CurDevMode);
481 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
482 ENUM_CURRENT_SETTINGS, &CurDevMode))
483 {
484 /* ENUM_CURRENT_SETTINGS returns FALSE when the display is not active:
485 * for example a disabled secondary display */
486 Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings(ENUM_CURRENT_SETTINGS) error %d\n", GetLastError ()));
487 return FALSE;
488 }
489
490 /* No ENUM_REGISTRY_SETTINGS yet. Seen on Vista after installation.
491 * Get the current video mode then.
492 */
493 if ( RegDevMode.dmPelsWidth != 0
494 && RegDevMode.dmPelsHeight == 0)
495 {
496 if (CurDevMode.dmBitsPerPel != RegDevMode.dmBitsPerPel
497 || CurDevMode.dmPelsWidth != RegDevMode.dmPelsWidth
498 || CurDevMode.dmPelsHeight != RegDevMode.dmPelsHeight
499 || CurDevMode.dmPosition.x != RegDevMode.dmPosition.x
500 || CurDevMode.dmPosition.y != RegDevMode.dmPosition.y)
501 {
502 Log(("VBoxTray: vboxDispIfValidateResize: current settings do not match registry settings, trating as no-match"));
503 return FALSE;
504 }
505 }
506
507 UINT j = 0;
508 for (; j < cDevModes; ++j)
509 {
510 if (!strncmp(DisplayDevice.DeviceName, paDisplayDevices[j].DeviceName, RT_ELEMENTS(CurDevMode.dmDeviceName)))
511 {
512 if (paDeviceModes[j].dmBitsPerPel != CurDevMode.dmBitsPerPel
513 || (paDeviceModes[j].dmPelsWidth & 0xfff8) != (CurDevMode.dmPelsWidth & 0xfff8)
514 || (paDeviceModes[j].dmPelsHeight & 0xfff8) != (CurDevMode.dmPelsHeight & 0xfff8)
515 || (paDeviceModes[j].dmPosition.x & 0xfff8) != (CurDevMode.dmPosition.x & 0xfff8)
516 || (paDeviceModes[j].dmPosition.y & 0xfff8) != (CurDevMode.dmPosition.y & 0xfff8)
517 || (paDisplayDevices[j].StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
518 {
519 return FALSE;
520 }
521 break;
522 }
523 }
524
525 if (j == cDevModes)
526 return FALSE;
527
528 ++cMatched;
529 }
530 }
531
532 return cMatched == cDevModes;
533}
534
535static DWORD vboxDispIfWddmValidateFixResize(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
536{
537 if (vboxDispIfWddmValidateResize(paDisplayDevices, paDeviceModes, cDevModes))
538 return NO_ERROR;
539
540 DWORD winEr;
541 LONG status = DISP_CHANGE_SUCCESSFUL;
542
543 /* now try to resize in a "regular" way */
544 /* Assign the new rectangles to displays. */
545 for (UINT i = 0; i < cDevModes; i++)
546 {
547 /* On Vista one must specify DM_BITSPERPEL.
548 * Note that the current mode dmBitsPerPel is already in the DEVMODE structure.
549 */
550 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH | DM_BITSPERPEL;
551
552 Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n",
553 pIf->modeData.wddm.pfnChangeDisplaySettingsEx,
554 paDeviceModes[i].dmPelsWidth,
555 paDeviceModes[i].dmPelsHeight,
556 paDeviceModes[i].dmBitsPerPel,
557 paDeviceModes[i].dmPosition.x,
558 paDeviceModes[i].dmPosition.y));
559
560 /* the miniport might have been adjusted the display mode stuff,
561 * adjust the paDeviceModes[i] by picking the closest available one */
562// DEVMODE AdjustedMode = paDeviceModes[i];
563// vboxDispIfAdjustMode(&paDisplayDevices[i], &AdjustedMode);
564
565 LONG tmpStatus = pIf->modeData.wddm.pfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
566 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
567 Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettingsEx position status %d, err %d\n", tmpStatus, GetLastError ()));
568
569 if (tmpStatus != DISP_CHANGE_SUCCESSFUL)
570 {
571 status = tmpStatus;
572 }
573 }
574
575 /* A second call to ChangeDisplaySettings updates the monitor. */
576 LONG tmpStatus = pIf->modeData.wddm.pfnChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
577 Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettings update status %d\n", status));
578 if (tmpStatus == DISP_CHANGE_SUCCESSFUL)
579 {
580 if (status == DISP_CHANGE_SUCCESSFUL)
581 {
582 return NO_ERROR;
583 }
584 tmpStatus = status;
585 }
586
587 winEr = ERROR_GEN_FAILURE;
588 return winEr;
589}
590
591static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf)
592{
593 memset(&g_VBoxDispIfWddm, 0, sizeof (g_VBoxDispIfWddm));
594 g_VBoxDispIfWddm.pIf = pIf;
595 return ERROR_SUCCESS;
596}
597
598static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf)
599{
600 memset(&g_VBoxDispIfWddm, 0, sizeof (g_VBoxDispIfWddm));
601}
602
603typedef struct VBOXDISPIF_REINITMODES_OP
604{
605 VBOXDISPIFESCAPE_REINITVIDEOMODESBYMASK EscData;
606} VBOXDISPIF_REINITMODES_OP, *PVBOXDISPIF_REINITMODES_OP;
607
608static DECLCALLBACK(BOOLEAN) vboxDispIfReninitModesWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
609{
610 PVBOXDISPIF_REINITMODES_OP pData = (PVBOXDISPIF_REINITMODES_OP)pContext;
611 VBOXDISPIFWDDM_ESCAPEOP_CONTEXT Ctx = {0};
612 Ctx.pEscape = &pData->EscData.EscapeHdr;
613 Ctx.cbData = sizeof (pData->EscData) - sizeof (pData->EscData.EscapeHdr);
614// Ctx.EscapeFlags.HardwareAccess = 0;
615 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1 /* iDisplay, -1 means primary */, vboxDispIfEscapeWDDMOp, &Ctx);
616 if (err == NO_ERROR)
617 {
618 if (!Ctx.Status)
619 err = NO_ERROR;
620 else
621 {
622 if (Ctx.Status == 0xC00000BBL) /* not supported */
623 err = ERROR_NOT_SUPPORTED;
624 else
625 err = ERROR_GEN_FAILURE;
626 Log((__FUNCTION__": pfnD3DKMTEscape failed, Status (0x%x)\n", Ctx.Status));
627 }
628 }
629 else
630 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
631
632 D3DKMT_POLLDISPLAYCHILDREN PollData = {0};
633 PollData.hAdapter = hAdapter;
634 PollData.NonDestructiveOnly = 1;
635 NTSTATUS Status = pIf->modeData.wddm.pfnD3DKMTPollDisplayChildren(&PollData);
636 if (Status != 0)
637 {
638 Log((__FUNCTION__": pfnD3DKMTPollDisplayChildren failed, Status (0x%x)\n", Status));
639 }
640 return TRUE;
641}
642
643static DWORD vboxDispIfReninitModesWDDM(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange)
644{
645 VBOXDISPIF_REINITMODES_OP OpData = {0};
646 OpData.EscData.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK;
647 if (fReconnectDisplaysOnChange)
648 OpData.EscData.EscapeHdr.u32CmdSpecific = VBOXWDDM_REINITVIDEOMODESBYMASK_F_RECONNECT_DISPLAYS_ON_CHANGE;
649
650 memcpy(OpData.EscData.ScreenMask, pScreenIdMask, sizeof (OpData.EscData.ScreenMask));
651
652 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1 /* iDisplay, -1 means primary */, vboxDispIfReninitModesWDDMOp, &OpData);
653 return err;
654}
655
656static DWORD vboxDispIfAdjustMode(DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode)
657{
658 DEVMODE CurMode;
659 DEVMODE BestMatchMode;
660 DWORD i = 0;
661 int64_t diffWH = INT64_MAX;
662 int diffBpp = INT32_MAX;
663 for (; ; ++i)
664 {
665 CurMode.dmSize = sizeof (CurMode);
666 CurMode.dmDriverExtra = 0;
667
668 if (!EnumDisplaySettings(pDisplayDevice->DeviceName, i, &CurMode))
669 break;
670
671 if (CurMode.dmPelsWidth == pDeviceMode->dmPelsWidth
672 && CurMode.dmPelsHeight == pDeviceMode->dmPelsHeight
673 && CurMode.dmBitsPerPel == pDeviceMode->dmBitsPerPel)
674 {
675 Log(("Exact match found"));
676 *pDeviceMode = CurMode;
677 return NO_ERROR;
678 }
679
680 int diffCurrW = RT_ABS((int)(CurMode.dmPelsWidth - pDeviceMode->dmPelsWidth));
681 int diffCurrH = RT_ABS((int)(CurMode.dmPelsHeight - pDeviceMode->dmPelsHeight));
682 int diffCurrBpp = RT_ABS((int)(CurMode.dmBitsPerPel - pDeviceMode->dmBitsPerPel)
683 - 1 /* <- to make higher bpp take precedence over lower ones */
684 );
685
686 int64_t diffCurrHW = (int64_t)diffCurrW*diffCurrW + (int64_t)diffCurrH*diffCurrH;
687
688 if (i == 0
689 || diffCurrHW < diffWH
690 || (diffCurrHW == diffWH && diffCurrBpp < diffBpp))
691 {
692 /* first run */
693 BestMatchMode = CurMode;
694 diffWH = diffCurrHW;
695 diffBpp = diffCurrBpp;
696 continue;
697 }
698 }
699
700 if (i == 0)
701 {
702 Log(("No modes found!"));
703 return NO_ERROR;
704 }
705
706 *pDeviceMode = BestMatchMode;
707 return NO_ERROR;
708}
709
710static DWORD vboxDispIfAdjustModeValues(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode)
711{
712 VBOXDISPIFESCAPE_ADJUSTVIDEOMODES Data = {0};
713 Data.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK;
714 Data.EscapeHdr.u32CmdSpecific = 1;
715 Data.aScreenInfos[0].Mode.Id =
716 Data.aScreenInfos[0].Mode.Width = pDeviceMode->dmPelsWidth;
717 Data.aScreenInfos[0].Mode.Height = pDeviceMode->dmPelsHeight;
718 Data.aScreenInfos[0].Mode.BitsPerPixel = pDeviceMode->dmBitsPerPel;
719 DWORD err = vboxDispIfEscapeWDDM(pIf, &Data.EscapeHdr, sizeof (Data) - sizeof (Data.EscapeHdr), TRUE);
720 if (err != NO_ERROR)
721 {
722 Log((__FUNCTION__": VBoxDispIfEscape failed with err (%d)\n", err));
723 }
724 return err;
725}
726
727DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
728{
729 UINT cbVidPnInfo = VBOXWDDM_RECOMMENDVIDPN_SIZE(cDevModes);
730 PVBOXWDDM_RECOMMENDVIDPN pVidPnInfo = (PVBOXWDDM_RECOMMENDVIDPN)alloca(cbVidPnInfo);
731 pVidPnInfo->cScreenInfos = cDevModes;
732 D3DKMT_HANDLE hAdapter = NULL;
733 NTSTATUS Status;
734 DWORD winEr = NO_ERROR;
735 UINT i = 0;
736
737 for (; i < cDevModes; i++)
738 {
739 PVBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO pInfo = &pVidPnInfo->aScreenInfos[i];
740 D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0};
741 OpenAdapterData.hDc = CreateDC(NULL, paDisplayDevices[i].DeviceName, NULL, NULL);
742 if (!OpenAdapterData.hDc)
743 {
744 winEr = GetLastError();
745 Log(("WARNING: Failed to get dc for display device %s, winEr %d\n", paDisplayDevices[i].DeviceName, winEr));
746 break;
747 }
748
749 Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData);
750 Assert(!Status);
751 if (Status)
752 {
753 winEr = ERROR_GEN_FAILURE;
754 Log(("WARNING: Failed to open adapter from dc, Status 0x%x\n", Status));
755 break;
756 }
757
758 pInfo->Id = OpenAdapterData.VidPnSourceId;
759 pInfo->Width = paDeviceModes[i].dmPelsWidth;
760 pInfo->Height = paDeviceModes[i].dmPelsHeight;
761 pInfo->BitsPerPixel = paDeviceModes[i].dmBitsPerPel;
762
763 if (!hAdapter)
764 {
765 hAdapter = OpenAdapterData.hAdapter;
766 }
767 else
768 {
769 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
770 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
771 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
772 if (Status)
773 Log(("WARNING: Failed to close adapter, Status 0x%x\n", Status));
774 }
775 }
776
777 BOOL fAbleToInvalidateVidPn = FALSE;
778
779 if (winEr == NO_ERROR)
780 {
781 Assert(hAdapter);
782
783 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
784 IAVidPnData.hAdapter = hAdapter;
785 IAVidPnData.pPrivateDriverData = pVidPnInfo;
786 IAVidPnData.PrivateDriverDataSize = cbVidPnInfo;
787
788 DWORD winEr = NO_ERROR;
789 Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
790 Assert(!Status);
791 if (Status)
792 {
793 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", Status));
794 winEr = ERROR_GEN_FAILURE;
795 }
796 else
797 {
798 fAbleToInvalidateVidPn = TRUE;
799 }
800 }
801
802 if (hAdapter)
803 {
804 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
805 ClosaAdapterData.hAdapter = hAdapter;
806 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
807 if (Status)
808 Log(("WARNING: Failed to close adapter[2], Status 0x%x\n", Status));
809 }
810
811// for (i = 0; i < cDevModes; i++)
812// {
813// vboxDispIfAdjustMode(&paDisplayDevices[i], &paDeviceModes[i]);
814// }
815
816 if (fAbleToInvalidateVidPn)
817 {
818 Log(("Invalidating VidPn Worked!\n"));
819 winEr = vboxDispIfWddmValidateFixResize(pIf, paDisplayDevices, paDeviceModes, cDevModes);
820 }
821 else
822 {
823 Log(("Falling back to monitor mode reinit\n"));
824 /* fallback impl needed for display-only driver
825 * since D3DKMTInvalidateActiveVidPn is not available for WDDM > 1.0:
826 * make the driver invalidate VidPn,
827 * which is done by emulating a monitor re-plug currently */
828 uint8_t ScreenMask[VBOXWDDM_SCREENMASK_SIZE] = {0};
829 ASMBitSet(ScreenMask, iChangedMode);
830 vboxDispIfReninitModesWDDM(pIf, ScreenMask, TRUE);
831
832 winEr = vboxDispIfWddmValidateFixResize(pIf, paDisplayDevices, paDeviceModes, cDevModes);
833
834 Assert(winEr == NO_ERROR);
835 }
836
837 return winEr;
838}
839#endif /* VBOX_WITH_WDDM */
840
841DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
842{
843 switch (pIf->enmMode)
844 {
845 case VBOXDISPIF_MODE_XPDM_NT4:
846 return ERROR_NOT_SUPPORTED;
847 case VBOXDISPIF_MODE_XPDM:
848 return ERROR_NOT_SUPPORTED;
849#ifdef VBOX_WITH_WDDM
850 case VBOXDISPIF_MODE_WDDM:
851 return vboxDispIfResizeModesWDDM(pIf, iChangedMode, paDisplayDevices, paDeviceModes, cDevModes);
852#endif
853 default:
854 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
855 return ERROR_INVALID_PARAMETER;
856 }
857}
858
859DWORD VBoxDispIfReninitModes(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange)
860{
861 switch (pIf->enmMode)
862 {
863 case VBOXDISPIF_MODE_XPDM_NT4:
864 return ERROR_NOT_SUPPORTED;
865 case VBOXDISPIF_MODE_XPDM:
866 return ERROR_NOT_SUPPORTED;
867#ifdef VBOX_WITH_WDDM
868 case VBOXDISPIF_MODE_WDDM:
869 return vboxDispIfReninitModesWDDM(pIf, pScreenIdMask, fReconnectDisplaysOnChange);
870#endif
871 default:
872 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
873 return ERROR_INVALID_PARAMETER;
874 }
875}
876
877static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
878{
879 return NO_ERROR;
880}
881
882static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
883{
884 DWORD err = NO_ERROR;
885 AssertBreakpoint();
886 OSVERSIONINFO OSinfo;
887 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
888 GetVersionEx (&OSinfo);
889 if (OSinfo.dwMajorVersion >= 5)
890 {
891 HMODULE hUser = GetModuleHandle("USER32");
892 if (NULL != hUser)
893 {
894 bool bSupported = true;
895 *(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
896 Log((__FUNCTION__": pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
897 bSupported &= !!(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
898
899 if (!bSupported)
900 {
901 Log((__FUNCTION__": pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
902 err = ERROR_NOT_SUPPORTED;
903 }
904 }
905 else
906 {
907 Log((__FUNCTION__": failed to get USER32 handle, err (%d)\n", GetLastError()));
908 err = ERROR_NOT_SUPPORTED;
909 }
910 }
911 else
912 {
913 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
914 err = ERROR_NOT_SUPPORTED;
915 }
916
917 return err;
918}
919
920DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
921{
922 /* @todo: may need to addd synchronization in case we want to change modes dynamically
923 * i.e. currently the mode is supposed to be initialized once on service initialization */
924 if (penmOldMode)
925 *penmOldMode = pIf->enmMode;
926
927 if (enmMode == pIf->enmMode)
928 return NO_ERROR;
929
930#ifdef VBOX_WITH_WDDM
931 if (pIf->enmMode == VBOXDISPIF_MODE_WDDM)
932 {
933 vboxDispIfWddmTerm(pIf);
934 }
935#endif
936
937 DWORD err = NO_ERROR;
938 switch (enmMode)
939 {
940 case VBOXDISPIF_MODE_XPDM_NT4:
941 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
942 err = vboxDispIfSwitchToXPDM_NT4(pIf);
943 if (err == NO_ERROR)
944 {
945 Log((__FUNCTION__": successfully switched to XPDM_NT4 mode\n"));
946 pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
947 }
948 else
949 Log((__FUNCTION__": failed to switch to XPDM_NT4 mode, err (%d)\n", err));
950 break;
951 case VBOXDISPIF_MODE_XPDM:
952 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM\n"));
953 err = vboxDispIfSwitchToXPDM(pIf);
954 if (err == NO_ERROR)
955 {
956 Log((__FUNCTION__": successfully switched to XPDM mode\n"));
957 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
958 }
959 else
960 Log((__FUNCTION__": failed to switch to XPDM mode, err (%d)\n", err));
961 break;
962#ifdef VBOX_WITH_WDDM
963 case VBOXDISPIF_MODE_WDDM:
964 {
965 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_WDDM\n"));
966 err = vboxDispIfSwitchToWDDM(pIf);
967 if (err == NO_ERROR)
968 {
969 Log((__FUNCTION__": successfully switched to WDDM mode\n"));
970 pIf->enmMode = VBOXDISPIF_MODE_WDDM;
971 }
972 else
973 Log((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err));
974 break;
975 }
976#endif
977 default:
978 err = ERROR_INVALID_PARAMETER;
979 break;
980 }
981 return err;
982}
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