VirtualBox

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

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

wddm: autoresize, etc. fixes, more to follow..

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.1 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#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)
58{
59 HDC hdc = GetDC(HWND_DESKTOP);
60 VOID *pvData = cbData ? VBOXDISPIFESCAPE_DATA(pEscape, VOID) : NULL;
61 int iRet = ExtEscape(hdc, pEscape->escapeCode, cbData, (LPCSTR)pvData, 0, NULL);
62 ReleaseDC(HWND_DESKTOP, hdc);
63 if (iRet > 0)
64 return VINF_SUCCESS;
65 else if (iRet == 0)
66 return ERROR_NOT_SUPPORTED;
67 /* else */
68 return ERROR_GEN_FAILURE;
69}
70
71#ifdef VBOX_WITH_WDDM
72static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf)
73{
74 DWORD err = NO_ERROR;
75 OSVERSIONINFO OSinfo;
76 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
77 GetVersionEx (&OSinfo);
78 bool bSupported = true;
79
80 if (OSinfo.dwMajorVersion >= 6)
81 {
82 Log((__FUNCTION__": this is vista and up\n"));
83 HMODULE hUser = GetModuleHandle("USER32");
84 if (hUser)
85 {
86 *(uintptr_t *)&pIf->modeData.wddm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
87 Log((__FUNCTION__": VBoxDisplayInit: pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.wddm.pfnChangeDisplaySettingsEx));
88 bSupported &= !!(pIf->modeData.wddm.pfnChangeDisplaySettingsEx);
89
90 *(uintptr_t *)&pIf->modeData.wddm.pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA");
91 Log((__FUNCTION__": VBoxDisplayInit: pfnEnumDisplayDevices = %p\n", pIf->modeData.wddm.pfnEnumDisplayDevices));
92 bSupported &= !!(pIf->modeData.wddm.pfnEnumDisplayDevices);
93
94 /* this is vista and up */
95 HMODULE hGdi32 = GetModuleHandle("gdi32");
96 if (hGdi32 != NULL)
97 {
98 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromHdc");
99 Log((__FUNCTION__"pfnD3DKMTOpenAdapterFromHdc = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc));
100 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc);
101
102 pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName = (PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromGdiDisplayName");
103 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName));
104 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName);
105
106 pIf->modeData.wddm.pfnD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter");
107 Log((__FUNCTION__": pfnD3DKMTCloseAdapter = %p\n", pIf->modeData.wddm.pfnD3DKMTCloseAdapter));
108 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
109
110 pIf->modeData.wddm.pfnD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(hGdi32, "D3DKMTEscape");
111 Log((__FUNCTION__": pfnD3DKMTEscape = %p\n", pIf->modeData.wddm.pfnD3DKMTEscape));
112 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter);
113
114 pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn = (PFND3DKMT_INVALIDATEACTIVEVIDPN)GetProcAddress(hGdi32, "D3DKMTInvalidateActiveVidPn");
115 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn = %p\n", pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn));
116 bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn);
117
118 if (!bSupported)
119 {
120 Log((__FUNCTION__": one of pfnD3DKMT function pointers failed to initialize\n"));
121 err = ERROR_NOT_SUPPORTED;
122 }
123 }
124 else
125 {
126 Log((__FUNCTION__": GetModuleHandle(gdi32) failed, err(%d)\n", GetLastError()));
127 err = ERROR_NOT_SUPPORTED;
128 }
129
130 }
131 else
132 {
133 Log((__FUNCTION__": GetModuleHandle(USER32) failed, err(%d)\n", GetLastError()));
134 err = ERROR_NOT_SUPPORTED;
135 }
136 }
137 else
138 {
139 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n"));
140 err = ERROR_NOT_SUPPORTED;
141 }
142
143 if (err == ERROR_SUCCESS)
144 {
145 err = vboxDispIfWddmInit(pIf);
146 }
147
148 return err;
149}
150
151static DWORD vboxDispIfWDDMAdpHdcCreate(int iDisplay, HDC *phDc, DISPLAY_DEVICE *pDev)
152{
153 DWORD winEr = ERROR_INVALID_STATE;
154 memset(pDev, 0, sizeof (*pDev));
155 pDev->cb = sizeof (*pDev);
156
157 for (int i = 0; ; ++i)
158 {
159 if (EnumDisplayDevices(NULL, /* LPCTSTR lpDevice */ i, /* DWORD iDevNum */
160 pDev, 0 /* DWORD dwFlags*/))
161 {
162 if (i == iDisplay || (iDisplay < 0 && pDev->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
163 {
164 HDC hDc = CreateDC(NULL, pDev->DeviceName, NULL, NULL);
165 if (hDc)
166 {
167 *phDc = hDc;
168 return NO_ERROR;
169 }
170 else
171 {
172 winEr = GetLastError();
173 Assert(0);
174 break;
175 }
176 }
177 }
178 else
179 {
180 winEr = GetLastError();
181 Assert(0);
182 break;
183 }
184 }
185
186 return winEr;
187}
188
189
190typedef DECLCALLBACK(BOOLEAN) FNVBOXDISPIFWDDM_ADAPTEROP(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext);
191typedef FNVBOXDISPIFWDDM_ADAPTEROP *PFNVBOXDISPIFWDDM_ADAPTEROP;
192static DWORD vboxDispIfWDDMAdapterOp(PCVBOXDISPIF pIf, int iDisplay, PFNVBOXDISPIFWDDM_ADAPTEROP pfnOp, PVOID pContext)
193{
194 D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0};
195 DISPLAY_DEVICE DDev;
196 DWORD err = vboxDispIfWDDMAdpHdcCreate(iDisplay, &OpenAdapterData.hDc, &DDev);
197 Assert(err == NO_ERROR);
198 if (err == NO_ERROR)
199 {
200 NTSTATUS Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData);
201 Assert(!Status);
202 if (!Status)
203 {
204 BOOLEAN bCloseAdapter = pfnOp(pIf, OpenAdapterData.hAdapter, &DDev, pContext);
205
206 if (bCloseAdapter)
207 {
208 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
209 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
210 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
211 if (Status)
212 {
213 Log((__FUNCTION__": pfnD3DKMTCloseAdapter failed, Status (0x%x)\n", Status));
214 }
215 }
216 }
217 else
218 {
219 Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName failed, Status (0x%x)\n", Status));
220 err = ERROR_GEN_FAILURE;
221 }
222
223 DeleteDC(OpenAdapterData.hDc);
224 }
225
226 return err;
227}
228
229typedef struct
230{
231 NTSTATUS Status;
232 PVBOXDISPIFESCAPE pEscape;
233 int cbData;
234 D3DDDI_ESCAPEFLAGS EscapeFlags;
235} VBOXDISPIFWDDM_ESCAPEOP_CONTEXT, *PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT;
236
237DECLCALLBACK(BOOLEAN) vboxDispIfEscapeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
238{
239 PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT)pContext;
240
241 D3DKMT_ESCAPE EscapeData = {0};
242 EscapeData.hAdapter = hAdapter;
243 //EscapeData.hDevice = NULL;
244 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
245 EscapeData.Flags = pCtx->EscapeFlags;
246 EscapeData.pPrivateDriverData = pCtx->pEscape;
247 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(pCtx->cbData);
248 //EscapeData.hContext = NULL;
249
250 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTEscape(&EscapeData);
251
252 return TRUE;
253}
254
255static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess)
256{
257 VBOXDISPIFWDDM_ESCAPEOP_CONTEXT Ctx = {0};
258 Ctx.pEscape = pEscape;
259 Ctx.cbData = cbData;
260 if (fHwAccess)
261 Ctx.EscapeFlags.HardwareAccess = 1;
262 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1 /* iDisplay, -1 means primary */, vboxDispIfEscapeWDDMOp, &Ctx);
263 if (err == NO_ERROR)
264 {
265 if (!Ctx.Status)
266 err = NO_ERROR;
267 else
268 {
269 if (Ctx.Status == 0xC00000BBL) /* not supported */
270 err = ERROR_NOT_SUPPORTED;
271 else
272 err = ERROR_GEN_FAILURE;
273 Log((__FUNCTION__": pfnD3DKMTEscape failed, Status (0x%x)\n", Ctx.Status));
274 }
275 }
276 else
277 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
278
279 return err;
280}
281
282typedef struct
283{
284 NTSTATUS Status;
285 VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO Info;
286} VBOXDISPIFWDDM_RESIZEOP_CONTEXT, *PVBOXDISPIFWDDM_RESIZEOP_CONTEXT;
287
288DECLCALLBACK(BOOLEAN) vboxDispIfResizeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext)
289{
290 PVBOXDISPIFWDDM_RESIZEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_RESIZEOP_CONTEXT)pContext;
291 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
292 uint32_t cbData = VBOXWDDM_RECOMMENDVIDPN_SIZE(1);
293 PVBOXWDDM_RECOMMENDVIDPN pData = (PVBOXWDDM_RECOMMENDVIDPN)malloc(cbData);
294 if (pData)
295 {
296 memset(pData, 0, cbData);
297 pData->cScreenInfos = 1;
298 memcpy(&pData->aScreenInfos[0], &pCtx->Info, sizeof (VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO));
299
300 IAVidPnData.hAdapter = hAdapter;
301 IAVidPnData.pPrivateDriverData = pData;
302 IAVidPnData.PrivateDriverDataSize = cbData;
303
304 pCtx->Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
305 Assert(!pCtx->Status);
306 if (pCtx->Status)
307 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", pCtx->Status));
308
309 free(pData);
310 }
311 else
312 {
313 Log((__FUNCTION__": malloc failed\n"));
314 pCtx->Status = -1;
315 }
316
317 return TRUE;
318}
319
320static DWORD vboxDispIfResizeWDDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
321{
322 VBOXDISPIFWDDM_RESIZEOP_CONTEXT Ctx = {0};
323 Ctx.Info.Id = Id;
324 Ctx.Info.Width = Width;
325 Ctx.Info.Height = Height;
326 Ctx.Info.BitsPerPixel = BitsPerPixel;
327 DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1, /* (int)Id - always say -1 to use primary display since the display does not really matter here */
328 vboxDispIfResizeWDDMOp, &Ctx);
329 if (err == NO_ERROR)
330 {
331 if (!Ctx.Status)
332 err = NO_ERROR;
333 else
334 {
335 if (Ctx.Status == 0xC00000BBL) /* not supported */
336 err = ERROR_NOT_SUPPORTED;
337 else
338 err = ERROR_GEN_FAILURE;
339 Log((__FUNCTION__": vboxDispIfResizeWDDMOp failed, Status (0x%x)\n", Ctx.Status));
340 }
341 }
342 else
343 Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err));
344
345 return err;
346}
347#endif
348
349DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
350{
351 switch (pIf->enmMode)
352 {
353 case VBOXDISPIF_MODE_XPDM_NT4:
354 case VBOXDISPIF_MODE_XPDM:
355 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData);
356#ifdef VBOX_WITH_WDDM
357 case VBOXDISPIF_MODE_WDDM:
358 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
359#endif
360 default:
361 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
362 return ERROR_INVALID_PARAMETER;
363 }
364}
365
366static DWORD vboxDispIfResizeXPDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
367{
368 return ERROR_NOT_SUPPORTED;
369}
370
371DWORD VBoxDispIfResize(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
372{
373 switch (pIf->enmMode)
374 {
375 case VBOXDISPIF_MODE_XPDM_NT4:
376 return ERROR_NOT_SUPPORTED;
377 case VBOXDISPIF_MODE_XPDM:
378 return vboxDispIfResizeXPDM(pIf, Id, Width, Height, BitsPerPixel);
379#ifdef VBOX_WITH_WDDM
380 case VBOXDISPIF_MODE_WDDM:
381 return vboxDispIfResizeWDDM(pIf, Id, Width, Height, BitsPerPixel);
382#endif
383 default:
384 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
385 return ERROR_INVALID_PARAMETER;
386 }
387}
388
389
390#ifdef VBOX_WITH_WDDM
391/**/
392typedef DECLCALLBACK(VOID) FNVBOXSCREENMONRUNNER_CB(void *pvCb);
393typedef FNVBOXSCREENMONRUNNER_CB *PFNVBOXSCREENMONRUNNER_CB;
394
395typedef struct VBOXSCREENMON
396{
397 HANDLE hThread;
398 DWORD idThread;
399 HANDLE hEvent;
400 HWND hWnd;
401 PFNVBOXSCREENMONRUNNER_CB pfnCb;
402 void *pvCb;
403} VBOXSCREENMON, *PVBOXSCREENMON;
404
405
406static VBOXSCREENMON g_VBoxScreenMon;
407
408
409#define VBOX_E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
410#define VBOX_E_NOT_SUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
411
412
413static void vboxScreenMonOnChange()
414{
415 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
416 pMon->pfnCb(pMon->pvCb);
417}
418
419static LRESULT CALLBACK vboxScreenMonWndProc(HWND hwnd,
420 UINT uMsg,
421 WPARAM wParam,
422 LPARAM lParam
423)
424{
425 switch(uMsg)
426 {
427 case WM_DISPLAYCHANGE:
428 {
429 vboxScreenMonOnChange();
430 }
431 case WM_CLOSE:
432 Log((__FUNCTION__": got WM_CLOSE for hwnd(0x%x)", hwnd));
433 return 0;
434 case WM_DESTROY:
435 Log((__FUNCTION__": got WM_DESTROY for hwnd(0x%x)", hwnd));
436 return 0;
437 case WM_NCHITTEST:
438 Log((__FUNCTION__": got WM_NCHITTEST for hwnd(0x%x)\n", hwnd));
439 return HTNOWHERE;
440 }
441
442 return DefWindowProc(hwnd, uMsg, wParam, lParam);
443}
444
445#define VBOXSCREENMONWND_NAME "VboxScreenMonWnd"
446
447static HRESULT vboxScreenMonWndCreate(HWND *phWnd)
448{
449 HRESULT hr = S_OK;
450 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
451 /* Register the Window Class. */
452 WNDCLASS wc;
453 if (!GetClassInfo(hInstance, VBOXSCREENMONWND_NAME, &wc))
454 {
455 wc.style = 0;//CS_OWNDC;
456 wc.lpfnWndProc = vboxScreenMonWndProc;
457 wc.cbClsExtra = 0;
458 wc.cbWndExtra = 0;
459 wc.hInstance = hInstance;
460 wc.hIcon = NULL;
461 wc.hCursor = NULL;
462 wc.hbrBackground = NULL;
463 wc.lpszMenuName = NULL;
464 wc.lpszClassName = VBOXSCREENMONWND_NAME;
465 if (!RegisterClass(&wc))
466 {
467 DWORD winErr = GetLastError();
468 Log((__FUNCTION__": RegisterClass failed, winErr(%d)\n", winErr));
469 hr = E_FAIL;
470 }
471 }
472
473 if (hr == S_OK)
474 {
475 HWND hWnd = CreateWindowEx (WS_EX_TOOLWINDOW,
476 VBOXSCREENMONWND_NAME, VBOXSCREENMONWND_NAME,
477 WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED,
478 -100, -100,
479 10, 10,
480 NULL, //GetDesktopWindow() /* hWndParent */,
481 NULL /* hMenu */,
482 hInstance,
483 NULL /* lpParam */);
484 Assert(hWnd);
485 if (hWnd)
486 {
487 *phWnd = hWnd;
488 }
489 else
490 {
491 DWORD winErr = GetLastError();
492 Log((__FUNCTION__": CreateWindowEx failed, winErr(%d)\n", winErr));
493 hr = E_FAIL;
494 }
495 }
496
497 return hr;
498}
499
500static HRESULT vboxScreenMonWndDestroy(HWND hWnd)
501{
502 BOOL bResult = DestroyWindow(hWnd);
503 if (bResult)
504 return S_OK;
505
506 DWORD winErr = GetLastError();
507 Log((__FUNCTION__": DestroyWindow failed, winErr(%d) for hWnd(0x%x)\n", winErr, hWnd));
508 Assert(0);
509
510 return HRESULT_FROM_WIN32(winErr);
511}
512
513static HRESULT vboxScreenMonWndInit()
514{
515 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
516 return vboxScreenMonWndCreate(&pMon->hWnd);
517}
518
519HRESULT vboxScreenMonWndTerm()
520{
521 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
522 HRESULT tmpHr = vboxScreenMonWndDestroy(pMon->hWnd);
523 Assert(tmpHr == S_OK);
524
525 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
526 UnregisterClass(VBOXSCREENMONWND_NAME, hInstance);
527
528 return S_OK;
529}
530
531#define WM_VBOXSCREENMON_INIT_QUIT (WM_APP+2)
532
533HRESULT vboxScreenMonRun()
534{
535 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
536 MSG Msg;
537
538 HRESULT hr = S_FALSE;
539
540 PeekMessage(&Msg,
541 NULL /* HWND hWnd */,
542 WM_USER /* UINT wMsgFilterMin */,
543 WM_USER /* UINT wMsgFilterMax */,
544 PM_NOREMOVE);
545
546 BOOL bCheck = TRUE;
547
548 do
549 {
550 if (bCheck)
551 {
552 vboxScreenMonOnChange();
553
554 bCheck = FALSE;
555 }
556
557 BOOL bResult = GetMessage(&Msg,
558 0 /*HWND hWnd*/,
559 0 /*UINT wMsgFilterMin*/,
560 0 /*UINT wMsgFilterMax*/
561 );
562
563 if(!bResult) /* WM_QUIT was posted */
564 {
565 hr = S_FALSE;
566 break;
567 }
568
569 if(bResult == -1) /* error occurred */
570 {
571 DWORD winEr = GetLastError();
572 hr = HRESULT_FROM_WIN32(winEr);
573 Assert(0);
574 /* just ensure we never return success in this case */
575 Assert(hr != S_OK);
576 Assert(hr != S_FALSE);
577 if (hr == S_OK || hr == S_FALSE)
578 hr = E_FAIL;
579 break;
580 }
581
582 switch (Msg.message)
583 {
584 case WM_VBOXSCREENMON_INIT_QUIT:
585 case WM_CLOSE:
586 {
587 PostQuitMessage(0);
588 break;
589 }
590 case WM_DISPLAYCHANGE:
591 bCheck = TRUE;
592 default:
593 TranslateMessage(&Msg);
594 DispatchMessage(&Msg);
595 break;
596 }
597 } while (1);
598 return 0;
599}
600
601static DWORD WINAPI vboxScreenMonRunnerThread(void *pvUser)
602{
603 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
604
605 BOOL bRc = SetEvent(pMon->hEvent);
606 if (!bRc)
607 {
608 DWORD winErr = GetLastError();
609 Log((__FUNCTION__": SetEvent failed, winErr = (%d)", winErr));
610 HRESULT tmpHr = HRESULT_FROM_WIN32(winErr);
611 Assert(0);
612 Assert(tmpHr != S_OK);
613 }
614
615 HRESULT hr = vboxScreenMonWndInit();
616 Assert(hr == S_OK);
617 if (hr == S_OK)
618 {
619 hr = vboxScreenMonRun();
620 Assert(hr == S_OK);
621
622 vboxScreenMonWndTerm();
623 }
624
625 return 0;
626}
627
628HRESULT VBoxScreenMonInit(PFNVBOXSCREENMONRUNNER_CB pfnCb, void *pvCb)
629{
630 HRESULT hr = E_FAIL;
631 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
632 memset(pMon, 0, sizeof (*pMon));
633
634 pMon->pfnCb = pfnCb;
635 pMon->pvCb = pvCb;
636
637 pMon->hEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes*/
638 TRUE, /* BOOL bManualReset*/
639 FALSE, /* BOOL bInitialState */
640 NULL /* LPCTSTR lpName */
641 );
642 if (pMon->hEvent)
643 {
644 pMon->hThread = CreateThread(NULL /* LPSECURITY_ATTRIBUTES lpThreadAttributes */,
645 0 /* SIZE_T dwStackSize */,
646 vboxScreenMonRunnerThread,
647 pMon,
648 0 /* DWORD dwCreationFlags */,
649 &pMon->idThread);
650 if (pMon->hThread)
651 {
652 DWORD dwResult = WaitForSingleObject(pMon->hEvent, INFINITE);
653 if (dwResult == WAIT_OBJECT_0)
654 return S_OK;
655 else
656 {
657 Log(("WaitForSingleObject failed!"));
658 hr = E_FAIL;
659 }
660 }
661 else
662 {
663 DWORD winErr = GetLastError();
664 Log((__FUNCTION__": CreateThread failed, winErr = (%d)", winErr));
665 hr = HRESULT_FROM_WIN32(winErr);
666 Assert(0);
667 Assert(hr != S_OK);
668 }
669 CloseHandle(pMon->hEvent);
670 }
671 else
672 {
673 DWORD winErr = GetLastError();
674 Log((__FUNCTION__": CreateEvent failed, winErr = (%d)", winErr));
675 hr = HRESULT_FROM_WIN32(winErr);
676 Assert(0);
677 Assert(hr != S_OK);
678 }
679
680 return hr;
681}
682
683VOID VBoxScreenMonTerm()
684{
685 HRESULT hr;
686 PVBOXSCREENMON pMon = &g_VBoxScreenMon;
687 if (!pMon->hThread)
688 return;
689
690 BOOL bResult = PostThreadMessage(pMon->idThread, WM_VBOXSCREENMON_INIT_QUIT, 0, 0);
691 DWORD winErr;
692 if (bResult
693 || (winErr = GetLastError()) == ERROR_INVALID_THREAD_ID) /* <- could be that the thread is terminated */
694 {
695 DWORD dwErr = WaitForSingleObject(pMon->hThread, INFINITE);
696 if (dwErr == WAIT_OBJECT_0)
697 {
698 hr = S_OK;
699 }
700 else
701 {
702 winErr = GetLastError();
703 hr = HRESULT_FROM_WIN32(winErr);
704 Assert(0);
705 }
706 }
707 else
708 {
709 hr = HRESULT_FROM_WIN32(winErr);
710 Assert(0);
711 }
712
713 CloseHandle(pMon->hThread);
714 pMon->hThread = 0;
715 CloseHandle(pMon->hEvent);
716 pMon->hThread = 0;
717}
718/**/
719
720typedef struct VBOXDISPIF_WDDM_INTERNAL
721{
722 PCVBOXDISPIF pIf;
723
724 HANDLE hResizeEvent;
725} VBOXDISPIF_WDDM_INTERNAL, *PVBOXDISPIF_WDDM_INTERNAL;
726
727static VBOXDISPIF_WDDM_INTERNAL g_VBoxDispIfWddm;
728
729static BOOL vboxDispIfWddmValidateResize(DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
730{
731 DISPLAY_DEVICE DisplayDevice;
732 int i = 0;
733 UINT cMatched = 0;
734 DEVMODE DeviceMode;
735 for (int i = 0; ; ++i)
736 {
737 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
738 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
739
740 if (!EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
741 break;
742
743 Log(("VBoxTray: vboxDispIfValidateResize: [%d(%d)] %s\n", i, cMatched, DisplayDevice.DeviceName));
744
745 BOOL bFetchDevice = FALSE;
746
747 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
748 {
749 Log(("VBoxTray: vboxDispIfValidateResize: Found primary device. err %d\n", GetLastError ()));
750 bFetchDevice = TRUE;
751 }
752 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
753 {
754
755 Log(("VBoxTray: vboxDispIfValidateResize: Found secondary device. err %d\n", GetLastError ()));
756 bFetchDevice = TRUE;
757 }
758
759 if (bFetchDevice)
760 {
761 if (cMatched >= cDevModes)
762 {
763 Log(("VBoxTray: vboxDispIfValidateResize: %d >= %d\n", cDevModes, cMatched));
764 return FALSE;
765 }
766
767 /* First try to get the video mode stored in registry (ENUM_REGISTRY_SETTINGS).
768 * A secondary display could be not active at the moment and would not have
769 * a current video mode (ENUM_CURRENT_SETTINGS).
770 */
771 ZeroMemory(&DeviceMode, sizeof(DeviceMode));
772 DeviceMode.dmSize = sizeof(DEVMODE);
773 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
774 ENUM_REGISTRY_SETTINGS, &DeviceMode))
775 {
776 Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings error %d\n", GetLastError ()));
777 return FALSE;
778 }
779
780 if ( DeviceMode.dmPelsWidth == 0
781 || DeviceMode.dmPelsHeight == 0)
782 {
783 /* No ENUM_REGISTRY_SETTINGS yet. Seen on Vista after installation.
784 * Get the current video mode then.
785 */
786 ZeroMemory(&DeviceMode, sizeof(DeviceMode));
787 DeviceMode.dmSize = sizeof(DeviceMode);
788 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
789 ENUM_CURRENT_SETTINGS, &DeviceMode))
790 {
791 /* ENUM_CURRENT_SETTINGS returns FALSE when the display is not active:
792 * for example a disabled secondary display */
793 Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings(ENUM_CURRENT_SETTINGS) error %d\n", GetLastError ()));
794 return FALSE;
795 }
796 }
797
798 UINT j = 0;
799 for (; j < cDevModes; ++j)
800 {
801 if (!strncmp(DisplayDevice.DeviceName, paDisplayDevices[j].DeviceName, RT_ELEMENTS(DeviceMode.dmDeviceName)))
802 {
803 if (paDeviceModes[j].dmBitsPerPel != DeviceMode.dmBitsPerPel
804 || (paDeviceModes[j].dmPelsWidth & 0xfff8) != (DeviceMode.dmPelsWidth & 0xfff8)
805 || (paDeviceModes[j].dmPelsHeight & 0xfff8) != (DeviceMode.dmPelsHeight & 0xfff8)
806 || (paDeviceModes[j].dmPosition.x & 0xfff8) != (DeviceMode.dmPosition.x & 0xfff8)
807 || (paDeviceModes[j].dmPosition.y & 0xfff8) != (DeviceMode.dmPosition.y & 0xfff8)
808 || (paDisplayDevices[j].StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
809 {
810 return FALSE;
811 }
812 break;
813 }
814 }
815
816 if (j == cDevModes)
817 return FALSE;
818
819 ++cMatched;
820 }
821 }
822
823 return cMatched == cDevModes;
824}
825
826static DWORD vboxDispIfWddmValidateFixResize(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
827{
828 if (vboxDispIfWddmValidateResize(paDisplayDevices, paDeviceModes, cDevModes))
829 return NO_ERROR;
830
831 DWORD winEr;
832 LONG status = DISP_CHANGE_SUCCESSFUL;
833
834 /* now try to resize in a "regular" way */
835 /* Assign the new rectangles to displays. */
836 for (UINT i = 0; i < cDevModes; i++)
837 {
838 /* On Vista one must specify DM_BITSPERPEL.
839 * Note that the current mode dmBitsPerPel is already in the DEVMODE structure.
840 */
841 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH | DM_BITSPERPEL;
842
843 Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n",
844 pIf->modeData.wddm.pfnChangeDisplaySettingsEx,
845 paDeviceModes[i].dmPelsWidth,
846 paDeviceModes[i].dmPelsHeight,
847 paDeviceModes[i].dmBitsPerPel,
848 paDeviceModes[i].dmPosition.x,
849 paDeviceModes[i].dmPosition.y));
850
851 /* the miniport might have been adjusted the display mode stuff,
852 * adjust the paDeviceModes[i] by picking the closest available one */
853// DEVMODE AdjustedMode = paDeviceModes[i];
854// vboxDispIfAdjustMode(&paDisplayDevices[i], &AdjustedMode);
855
856 LONG tmpStatus = pIf->modeData.wddm.pfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
857 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
858 Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettingsEx position status %d, err %d\n", tmpStatus, GetLastError ()));
859
860 if (tmpStatus != DISP_CHANGE_SUCCESSFUL)
861 {
862 status = tmpStatus;
863 }
864 }
865
866 /* A second call to ChangeDisplaySettings updates the monitor. */
867 LONG tmpStatus = pIf->modeData.wddm.pfnChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
868 Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettings update status %d\n", status));
869 if (tmpStatus == DISP_CHANGE_SUCCESSFUL)
870 {
871 if (status == DISP_CHANGE_SUCCESSFUL)
872 {
873 return NO_ERROR;
874 }
875 tmpStatus = status;
876 }
877
878 if (tmpStatus == DISP_CHANGE_BADMODE)
879 {
880 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
881 winEr = ERROR_RETRY;
882 }
883 else
884 {
885 winEr = ERROR_GEN_FAILURE;
886 }
887 return winEr;
888}
889
890static DECLCALLBACK(VOID) vboxDispIfWddmScreenMonCb(void *pvCb)
891{
892 PVBOXDISPIF_WDDM_INTERNAL pData = (PVBOXDISPIF_WDDM_INTERNAL)pvCb;
893
894 SetEvent(pData->hResizeEvent);
895}
896
897static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf)
898{
899 memset(&g_VBoxDispIfWddm, 0, sizeof (g_VBoxDispIfWddm));
900 g_VBoxDispIfWddm.pIf = pIf;
901 g_VBoxDispIfWddm.hResizeEvent = CreateEvent(NULL,
902 FALSE, /* BOOL bManualReset */
903 FALSE, /* BOOL bInitialState */
904 NULL /* LPCTSTR lpName */
905 );
906 if (g_VBoxDispIfWddm.hResizeEvent)
907 {
908 HRESULT hr = VBoxScreenMonInit(vboxDispIfWddmScreenMonCb, &g_VBoxDispIfWddm);
909 if (SUCCEEDED(hr))
910 {
911 /* ensure event is reset */
912 WaitForSingleObject(g_VBoxDispIfWddm.hResizeEvent, 0);
913 return ERROR_SUCCESS;
914 }
915 CloseHandle(g_VBoxDispIfWddm.hResizeEvent);
916 }
917 return ERROR_GEN_FAILURE;
918}
919
920static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf)
921{
922 VBoxScreenMonTerm();
923 CloseHandle(g_VBoxDispIfWddm.hResizeEvent);
924 memset(&g_VBoxDispIfWddm, 0, sizeof (g_VBoxDispIfWddm));
925}
926
927static DWORD vboxDispIfReninitModesWDDM(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange)
928{
929 VBOXDISPIFESCAPE_REINITVIDEOMODESBYMASK Data = {0};
930 Data.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK;
931 if (fReconnectDisplaysOnChange)
932 Data.EscapeHdr.u32CmdSpecific = VBOXWDDM_REINITVIDEOMODESBYMASK_F_RECONNECT_DISPLAYS_ON_CHANGE;
933
934 memcpy(Data.ScreenMask, pScreenIdMask, sizeof (Data.ScreenMask));
935
936 DWORD err = vboxDispIfEscapeWDDM(pIf, &Data.EscapeHdr, sizeof (Data) - sizeof (Data.EscapeHdr), fReconnectDisplaysOnChange ? FALSE /* hw access must be false here,
937 * otherwise the miniport driver would fail
938 * request to prevent a deadlock */
939 : TRUE);
940 if (err != NO_ERROR)
941 {
942 Log((__FUNCTION__": VBoxDispIfEscape failed with err (%d)\n", err));
943 }
944 return err;
945}
946
947static DWORD vboxDispIfAdjustMode(DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode)
948{
949 DEVMODE CurMode;
950 DEVMODE BestMatchMode;
951 DWORD i = 0;
952 int64_t diffWH = INT64_MAX;
953 int diffBpp = INT32_MAX;
954 for (; ; ++i)
955 {
956 CurMode.dmSize = sizeof (CurMode);
957 CurMode.dmDriverExtra = 0;
958
959 if (!EnumDisplaySettings(pDisplayDevice->DeviceName, i, &CurMode))
960 break;
961
962 if (CurMode.dmPelsWidth == pDeviceMode->dmPelsWidth
963 && CurMode.dmPelsHeight == pDeviceMode->dmPelsHeight
964 && CurMode.dmBitsPerPel == pDeviceMode->dmBitsPerPel)
965 {
966 Log(("Exact match found"));
967 *pDeviceMode = CurMode;
968 return NO_ERROR;
969 }
970
971 int diffCurrW = RT_ABS((int)(CurMode.dmPelsWidth - pDeviceMode->dmPelsWidth));
972 int diffCurrH = RT_ABS((int)(CurMode.dmPelsHeight - pDeviceMode->dmPelsHeight));
973 int diffCurrBpp = RT_ABS((int)(CurMode.dmBitsPerPel - pDeviceMode->dmBitsPerPel)
974 - 1 /* <- to make higher bpp take precedence over lower ones */
975 );
976
977 int64_t diffCurrHW = (int64_t)diffCurrW*diffCurrW + (int64_t)diffCurrH*diffCurrH;
978
979 if (i == 0
980 || diffCurrHW < diffWH
981 || (diffCurrHW == diffWH && diffCurrBpp < diffBpp))
982 {
983 /* first run */
984 BestMatchMode = CurMode;
985 diffWH = diffCurrHW;
986 diffBpp = diffCurrBpp;
987 continue;
988 }
989 }
990
991 if (i == 0)
992 {
993 Log(("No modes found!"));
994 return NO_ERROR;
995 }
996
997 *pDeviceMode = BestMatchMode;
998 return NO_ERROR;
999}
1000
1001static DWORD vboxDispIfAdjustModeValues(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode)
1002{
1003 VBOXDISPIFESCAPE_ADJUSTVIDEOMODES Data = {0};
1004 Data.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK;
1005 Data.EscapeHdr.u32CmdSpecific = 1;
1006 Data.aScreenInfos[0].Mode.Id =
1007 Data.aScreenInfos[0].Mode.Width = pDeviceMode->dmPelsWidth;
1008 Data.aScreenInfos[0].Mode.Height = pDeviceMode->dmPelsHeight;
1009 Data.aScreenInfos[0].Mode.BitsPerPixel = pDeviceMode->dmBitsPerPel;
1010 DWORD err = vboxDispIfEscapeWDDM(pIf, &Data.EscapeHdr, sizeof (Data) - sizeof (Data.EscapeHdr), TRUE);
1011 if (err != NO_ERROR)
1012 {
1013 Log((__FUNCTION__": VBoxDispIfEscape failed with err (%d)\n", err));
1014 }
1015 return err;
1016}
1017
1018DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
1019{
1020 UINT cbVidPnInfo = VBOXWDDM_RECOMMENDVIDPN_SIZE(cDevModes);
1021 PVBOXWDDM_RECOMMENDVIDPN pVidPnInfo = (PVBOXWDDM_RECOMMENDVIDPN)alloca(cbVidPnInfo);
1022 pVidPnInfo->cScreenInfos = cDevModes;
1023 D3DKMT_HANDLE hAdapter = NULL;
1024 NTSTATUS Status;
1025 DWORD winEr = NO_ERROR;
1026 UINT i = 0;
1027
1028 for (; i < cDevModes; i++)
1029 {
1030 PVBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO pInfo = &pVidPnInfo->aScreenInfos[i];
1031 D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0};
1032 OpenAdapterData.hDc = CreateDC(NULL, paDisplayDevices[i].DeviceName, NULL, NULL);
1033 if (!OpenAdapterData.hDc)
1034 {
1035 winEr = GetLastError();
1036 Assert(0);
1037 break;
1038 }
1039
1040 Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData);
1041 Assert(!Status);
1042 if (Status)
1043 {
1044 winEr = ERROR_GEN_FAILURE;
1045 Assert(0);
1046 break;
1047 }
1048
1049 pInfo->Id = OpenAdapterData.VidPnSourceId;
1050 pInfo->Width = paDeviceModes[i].dmPelsWidth;
1051 pInfo->Height = paDeviceModes[i].dmPelsHeight;
1052 pInfo->BitsPerPixel = paDeviceModes[i].dmBitsPerPel;
1053
1054 if (!hAdapter)
1055 {
1056 hAdapter = OpenAdapterData.hAdapter;
1057 }
1058 else
1059 {
1060 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
1061 ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter;
1062 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
1063 Assert(!Status);
1064 }
1065 }
1066
1067 BOOL fAbleToInvalidateVidPn = FALSE;
1068
1069 if (winEr == NO_ERROR)
1070 {
1071 Assert(hAdapter);
1072
1073 D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0};
1074 IAVidPnData.hAdapter = hAdapter;
1075 IAVidPnData.pPrivateDriverData = pVidPnInfo;
1076 IAVidPnData.PrivateDriverDataSize = cbVidPnInfo;
1077
1078 DWORD winEr = NO_ERROR;
1079 Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData);
1080 Assert(!Status);
1081 if (Status)
1082 {
1083 Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", Status));
1084 winEr = ERROR_GEN_FAILURE;
1085 }
1086 else
1087 {
1088 fAbleToInvalidateVidPn = TRUE;
1089 }
1090 }
1091
1092 if (hAdapter)
1093 {
1094 D3DKMT_CLOSEADAPTER ClosaAdapterData = {0};
1095 ClosaAdapterData.hAdapter = hAdapter;
1096 Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData);
1097 Assert(!Status);
1098 }
1099
1100// for (i = 0; i < cDevModes; i++)
1101// {
1102// vboxDispIfAdjustMode(&paDisplayDevices[i], &paDeviceModes[i]);
1103// }
1104
1105 if (fAbleToInvalidateVidPn)
1106 {
1107 winEr = vboxDispIfWddmValidateFixResize(pIf, paDisplayDevices, paDeviceModes, cDevModes);
1108 }
1109 else
1110 {
1111 /* fallback impl needed for display-only driver
1112 * since D3DKMTInvalidateActiveVidPn is not available for WDDM > 1.0:
1113 * make the driver invalidate VidPn,
1114 * which is done by emulating a monitor re-plug currently */
1115 /* ensure event is reset */
1116 WaitForSingleObject(g_VBoxDispIfWddm.hResizeEvent, 0);
1117
1118 uint8_t ScreenMask[VBOXWDDM_SCREENMASK_SIZE] = {0};
1119 ASMBitSet(ScreenMask, iChangedMode);
1120 vboxDispIfReninitModesWDDM(pIf, ScreenMask, TRUE);
1121
1122 for (UINT i = 0; i < 4; ++i)
1123 {
1124 WaitForSingleObject(g_VBoxDispIfWddm.hResizeEvent, 500);
1125 winEr = vboxDispIfWddmValidateFixResize(pIf, paDisplayDevices, paDeviceModes, cDevModes);
1126 if (winEr == NO_ERROR)
1127 break;
1128 }
1129
1130 Assert(winEr == NO_ERROR);
1131 }
1132
1133 return winEr;
1134}
1135#endif /* VBOX_WITH_WDDM */
1136
1137DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
1138{
1139 switch (pIf->enmMode)
1140 {
1141 case VBOXDISPIF_MODE_XPDM_NT4:
1142 return ERROR_NOT_SUPPORTED;
1143 case VBOXDISPIF_MODE_XPDM:
1144 return ERROR_NOT_SUPPORTED;
1145#ifdef VBOX_WITH_WDDM
1146 case VBOXDISPIF_MODE_WDDM:
1147 return vboxDispIfResizeModesWDDM(pIf, iChangedMode, paDisplayDevices, paDeviceModes, cDevModes);
1148#endif
1149 default:
1150 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
1151 return ERROR_INVALID_PARAMETER;
1152 }
1153}
1154
1155DWORD VBoxDispIfReninitModes(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange)
1156{
1157 switch (pIf->enmMode)
1158 {
1159 case VBOXDISPIF_MODE_XPDM_NT4:
1160 return ERROR_NOT_SUPPORTED;
1161 case VBOXDISPIF_MODE_XPDM:
1162 return ERROR_NOT_SUPPORTED;
1163#ifdef VBOX_WITH_WDDM
1164 case VBOXDISPIF_MODE_WDDM:
1165 return vboxDispIfReninitModesWDDM(pIf, pScreenIdMask, fReconnectDisplaysOnChange);
1166#endif
1167 default:
1168 Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode));
1169 return ERROR_INVALID_PARAMETER;
1170 }
1171}
1172
1173static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
1174{
1175 return NO_ERROR;
1176}
1177
1178static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
1179{
1180 DWORD err = NO_ERROR;
1181 AssertBreakpoint();
1182 OSVERSIONINFO OSinfo;
1183 OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
1184 GetVersionEx (&OSinfo);
1185 if (OSinfo.dwMajorVersion >= 5)
1186 {
1187 HMODULE hUser = GetModuleHandle("USER32");
1188 if (NULL != hUser)
1189 {
1190 bool bSupported = true;
1191 *(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
1192 Log((__FUNCTION__": pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
1193 bSupported &= !!(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
1194
1195 if (!bSupported)
1196 {
1197 Log((__FUNCTION__": pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
1198 err = ERROR_NOT_SUPPORTED;
1199 }
1200 }
1201 else
1202 {
1203 Log((__FUNCTION__": failed to get USER32 handle, err (%d)\n", GetLastError()));
1204 err = ERROR_NOT_SUPPORTED;
1205 }
1206 }
1207 else
1208 {
1209 Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
1210 err = ERROR_NOT_SUPPORTED;
1211 }
1212
1213 return err;
1214}
1215
1216DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
1217{
1218 /* @todo: may need to addd synchronization in case we want to change modes dynamically
1219 * i.e. currently the mode is supposed to be initialized once on service initialization */
1220 if (penmOldMode)
1221 *penmOldMode = pIf->enmMode;
1222
1223 if (enmMode == pIf->enmMode)
1224 return NO_ERROR;
1225
1226#ifdef VBOX_WITH_WDDM
1227 if (pIf->enmMode == VBOXDISPIF_MODE_WDDM)
1228 {
1229 vboxDispIfWddmTerm(pIf);
1230 }
1231#endif
1232
1233 DWORD err = NO_ERROR;
1234 switch (enmMode)
1235 {
1236 case VBOXDISPIF_MODE_XPDM_NT4:
1237 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
1238 err = vboxDispIfSwitchToXPDM_NT4(pIf);
1239 if (err == NO_ERROR)
1240 {
1241 Log((__FUNCTION__": successfully switched to XPDM_NT4 mode\n"));
1242 pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
1243 }
1244 else
1245 Log((__FUNCTION__": failed to switch to XPDM_NT4 mode, err (%d)\n", err));
1246 break;
1247 case VBOXDISPIF_MODE_XPDM:
1248 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM\n"));
1249 err = vboxDispIfSwitchToXPDM(pIf);
1250 if (err == NO_ERROR)
1251 {
1252 Log((__FUNCTION__": successfully switched to XPDM mode\n"));
1253 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
1254 }
1255 else
1256 Log((__FUNCTION__": failed to switch to XPDM mode, err (%d)\n", err));
1257 break;
1258#ifdef VBOX_WITH_WDDM
1259 case VBOXDISPIF_MODE_WDDM:
1260 {
1261 Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_WDDM\n"));
1262 err = vboxDispIfSwitchToWDDM(pIf);
1263 if (err == NO_ERROR)
1264 {
1265 Log((__FUNCTION__": successfully switched to WDDM mode\n"));
1266 pIf->enmMode = VBOXDISPIF_MODE_WDDM;
1267 }
1268 else
1269 Log((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err));
1270 break;
1271 }
1272#endif
1273 default:
1274 err = ERROR_INVALID_PARAMETER;
1275 break;
1276 }
1277 return err;
1278}
Note: See TracBrowser for help on using the repository browser.

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