VirtualBox

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

Last change on this file since 85569 was 85121, checked in by vboxsync, 5 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 97.2 KB
Line 
1/* $Id: VBoxDispIf.cpp 85121 2020-07-08 19:33:26Z vboxsync $ */
2/** @file
3 * VBoxTray - Display Settings Interface abstraction for XPDM & WDDM
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "VBoxTray.h"
23#define _WIN32_WINNT 0x0601
24#include <iprt/log.h>
25#include <iprt/errcore.h>
26#include <iprt/assert.h>
27#include <iprt/system.h>
28
29#include <malloc.h>
30
31
32/*********************************************************************************************************************************
33* Defined Constants And Macros *
34*********************************************************************************************************************************/
35#ifdef DEBUG
36# define WARN(_m) do { \
37 AssertFailed(); \
38 LogRelFunc(_m); \
39 } while (0)
40#else
41# define WARN(_m) do { \
42 LogRelFunc(_m); \
43 } while (0)
44#endif
45
46#ifdef VBOX_WITH_WDDM
47#include <iprt/asm.h>
48#endif
49
50#include "VBoxDisplay.h"
51
52#ifndef NT_SUCCESS
53# define NT_SUCCESS(_Status) ((_Status) >= 0)
54#endif
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60typedef struct VBOXDISPIF_OP
61{
62 PCVBOXDISPIF pIf;
63 VBOXDISPKMT_ADAPTER Adapter;
64 VBOXDISPKMT_DEVICE Device;
65 VBOXDISPKMT_CONTEXT Context;
66} VBOXDISPIF_OP;
67
68/*
69 * APIs specific to Win7 and above WDDM architecture. Not available for Vista WDDM.
70 * This is the reason they have not been put in the VBOXDISPIF struct in VBoxDispIf.h.
71 */
72typedef struct _VBOXDISPLAYWDDMAPICONTEXT
73{
74 DECLCALLBACKMEMBER_EX(LONG, WINAPI, pfnSetDisplayConfig,(UINT numPathArrayElements, DISPLAYCONFIG_PATH_INFO *pathArray,
75 UINT numModeInfoArrayElements,
76 DISPLAYCONFIG_MODE_INFO *modeInfoArray, UINT Flags));
77 DECLCALLBACKMEMBER_EX(LONG, WINAPI, pfnQueryDisplayConfig,(UINT Flags, UINT *pNumPathArrayElements,
78 DISPLAYCONFIG_PATH_INFO *pPathInfoArray,
79 UINT *pNumModeInfoArrayElements,
80 DISPLAYCONFIG_MODE_INFO *pModeInfoArray,
81 DISPLAYCONFIG_TOPOLOGY_ID *pCurrentTopologyId));
82 DECLCALLBACKMEMBER_EX(LONG, WINAPI, pfnGetDisplayConfigBufferSizes,(UINT Flags, UINT *pNumPathArrayElements,
83 UINT *pNumModeInfoArrayElements));
84} _VBOXDISPLAYWDDMAPICONTEXT;
85
86static _VBOXDISPLAYWDDMAPICONTEXT gCtx = {0};
87
88typedef struct VBOXDISPIF_WDDM_DISPCFG
89{
90 UINT32 cPathInfoArray;
91 DISPLAYCONFIG_PATH_INFO *pPathInfoArray;
92 UINT32 cModeInfoArray;
93 DISPLAYCONFIG_MODE_INFO *pModeInfoArray;
94} VBOXDISPIF_WDDM_DISPCFG;
95
96
97/*********************************************************************************************************************************
98* Internal Functions *
99*********************************************************************************************************************************/
100static DWORD vboxDispIfWddmResizeDisplay(PCVBOXDISPIF const pIf, UINT Id, BOOL fEnable, DISPLAY_DEVICE * paDisplayDevices,
101 DEVMODE *paDeviceModes, UINT devModes);
102
103static DWORD vboxDispIfWddmResizeDisplay2(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT devModes);
104
105static DWORD vboxDispIfResizePerform(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup,
106 DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes);
107static DWORD vboxDispIfWddmEnableDisplaysTryingTopology(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnable);
108static DWORD vboxDispIfResizeStartedWDDMOp(VBOXDISPIF_OP *pOp);
109
110static void vboxDispIfWddmDcLogRel(VBOXDISPIF_WDDM_DISPCFG const *pCfg, UINT fFlags)
111{
112 LogRel(("Display config: Flags = 0x%08X\n", fFlags));
113
114 LogRel(("PATH_INFO[%d]:\n", pCfg->cPathInfoArray));
115 for (uint32_t i = 0; i < pCfg->cPathInfoArray; ++i)
116 {
117 DISPLAYCONFIG_PATH_INFO *p = &pCfg->pPathInfoArray[i];
118
119 LogRel(("%d: flags 0x%08x\n", i, p->flags));
120
121 LogRel((" sourceInfo: adapterId 0x%08x:%08x, id %u, modeIdx %d, statusFlags 0x%08x\n",
122 p->sourceInfo.adapterId.HighPart, p->sourceInfo.adapterId.LowPart,
123 p->sourceInfo.id, p->sourceInfo.modeInfoIdx, p->sourceInfo.statusFlags));
124
125 LogRel((" targetInfo: adapterId 0x%08x:%08x, id %u, modeIdx %d,\n"
126 " ot %d, r %d, s %d, rr %d/%d, so %d, ta %d, statusFlags 0x%08x\n",
127 p->targetInfo.adapterId.HighPart, p->targetInfo.adapterId.LowPart,
128 p->targetInfo.id, p->targetInfo.modeInfoIdx,
129 p->targetInfo.outputTechnology,
130 p->targetInfo.rotation,
131 p->targetInfo.scaling,
132 p->targetInfo.refreshRate.Numerator, p->targetInfo.refreshRate.Denominator,
133 p->targetInfo.scanLineOrdering,
134 p->targetInfo.targetAvailable,
135 p->targetInfo.statusFlags
136 ));
137 }
138
139 LogRel(("MODE_INFO[%d]:\n", pCfg->cModeInfoArray));
140 for (uint32_t i = 0; i < pCfg->cModeInfoArray; ++i)
141 {
142 DISPLAYCONFIG_MODE_INFO *p = &pCfg->pModeInfoArray[i];
143
144 LogRel(("%d: adapterId 0x%08x:%08x, id %u\n",
145 i, p->adapterId.HighPart, p->adapterId.LowPart, p->id));
146
147 if (p->infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE)
148 {
149 LogRel((" src %ux%u, fmt %d, @%dx%d\n",
150 p->sourceMode.width, p->sourceMode.height, p->sourceMode.pixelFormat,
151 p->sourceMode.position.x, p->sourceMode.position.y));
152 }
153 else if (p->infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET)
154 {
155 LogRel((" tgt pr 0x%RX64, hSyncFreq %d/%d, vSyncFreq %d/%d, active %ux%u, total %ux%u, std %d, so %d\n",
156 p->targetMode.targetVideoSignalInfo.pixelRate,
157 p->targetMode.targetVideoSignalInfo.hSyncFreq.Numerator, p->targetMode.targetVideoSignalInfo.hSyncFreq.Denominator,
158 p->targetMode.targetVideoSignalInfo.vSyncFreq.Numerator, p->targetMode.targetVideoSignalInfo.vSyncFreq.Denominator,
159 p->targetMode.targetVideoSignalInfo.activeSize.cx, p->targetMode.targetVideoSignalInfo.activeSize.cy,
160 p->targetMode.targetVideoSignalInfo.totalSize.cx, p->targetMode.targetVideoSignalInfo.totalSize.cy,
161 p->targetMode.targetVideoSignalInfo.videoStandard,
162 p->targetMode.targetVideoSignalInfo.scanLineOrdering));
163 }
164 else
165 {
166 LogRel((" Invalid infoType %u(0x%08x)\n", p->infoType, p->infoType));
167 }
168 }
169}
170
171static DWORD vboxDispIfWddmDcCreate(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT32 fFlags)
172{
173 UINT32 cPathInfoArray = 0;
174 UINT32 cModeInfoArray = 0;
175 DISPLAYCONFIG_PATH_INFO *pPathInfoArray;
176 DISPLAYCONFIG_MODE_INFO *pModeInfoArray;
177 DWORD winEr = gCtx.pfnGetDisplayConfigBufferSizes(fFlags, &cPathInfoArray, &cModeInfoArray);
178 if (winEr != ERROR_SUCCESS)
179 {
180 WARN(("VBoxTray: (WDDM) Failed GetDisplayConfigBufferSizes\n"));
181 return winEr;
182 }
183
184 pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(cPathInfoArray * sizeof(DISPLAYCONFIG_PATH_INFO));
185 if (!pPathInfoArray)
186 {
187 WARN(("VBoxTray: (WDDM) malloc failed!\n"));
188 return ERROR_OUTOFMEMORY;
189 }
190 pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(cModeInfoArray * sizeof(DISPLAYCONFIG_MODE_INFO));
191 if (!pModeInfoArray)
192 {
193 WARN(("VBoxTray: (WDDM) malloc failed!\n"));
194 free(pPathInfoArray);
195 return ERROR_OUTOFMEMORY;
196 }
197
198 winEr = gCtx.pfnQueryDisplayConfig(fFlags, &cPathInfoArray, pPathInfoArray, &cModeInfoArray, pModeInfoArray, NULL);
199 if (winEr != ERROR_SUCCESS)
200 {
201 WARN(("VBoxTray: (WDDM) Failed QueryDisplayConfig\n"));
202 free(pPathInfoArray);
203 free(pModeInfoArray);
204 return winEr;
205 }
206
207 pCfg->cPathInfoArray = cPathInfoArray;
208 pCfg->pPathInfoArray = pPathInfoArray;
209 pCfg->cModeInfoArray = cModeInfoArray;
210 pCfg->pModeInfoArray = pModeInfoArray;
211 return ERROR_SUCCESS;
212}
213
214static DWORD vboxDispIfWddmDcClone(VBOXDISPIF_WDDM_DISPCFG *pCfg, VBOXDISPIF_WDDM_DISPCFG *pCfgDst)
215{
216 memset(pCfgDst, 0, sizeof (*pCfgDst));
217
218 if (pCfg->cPathInfoArray)
219 {
220 pCfgDst->pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(pCfg->cPathInfoArray * sizeof (DISPLAYCONFIG_PATH_INFO));
221 if (!pCfgDst->pPathInfoArray)
222 {
223 WARN(("VBoxTray: (WDDM) malloc failed!\n"));
224 return ERROR_OUTOFMEMORY;
225 }
226
227 memcpy(pCfgDst->pPathInfoArray, pCfg->pPathInfoArray, pCfg->cPathInfoArray * sizeof (DISPLAYCONFIG_PATH_INFO));
228
229 pCfgDst->cPathInfoArray = pCfg->cPathInfoArray;
230 }
231
232 if (pCfg->cModeInfoArray)
233 {
234 pCfgDst->pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(pCfg->cModeInfoArray * sizeof (DISPLAYCONFIG_MODE_INFO));
235 if (!pCfgDst->pModeInfoArray)
236 {
237 WARN(("VBoxTray: (WDDM) malloc failed!\n"));
238 if (pCfgDst->pPathInfoArray)
239 {
240 free(pCfgDst->pPathInfoArray);
241 pCfgDst->pPathInfoArray = NULL;
242 }
243 return ERROR_OUTOFMEMORY;
244 }
245
246 memcpy(pCfgDst->pModeInfoArray, pCfg->pModeInfoArray, pCfg->cModeInfoArray * sizeof (DISPLAYCONFIG_MODE_INFO));
247
248 pCfgDst->cModeInfoArray = pCfg->cModeInfoArray;
249 }
250
251 return ERROR_SUCCESS;
252}
253
254
255static VOID vboxDispIfWddmDcTerm(VBOXDISPIF_WDDM_DISPCFG *pCfg)
256{
257 if (pCfg->pPathInfoArray)
258 free(pCfg->pPathInfoArray);
259 if (pCfg->pModeInfoArray)
260 free(pCfg->pModeInfoArray);
261 /* sanity */
262 memset(pCfg, 0, sizeof (*pCfg));
263}
264
265static UINT32 g_cVBoxDispIfWddmDisplays = 0;
266static DWORD vboxDispIfWddmDcQueryNumDisplays(UINT32 *pcDisplays)
267{
268 if (!g_cVBoxDispIfWddmDisplays)
269 {
270 VBOXDISPIF_WDDM_DISPCFG DispCfg;
271 *pcDisplays = 0;
272 DWORD winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
273 if (winEr != ERROR_SUCCESS)
274 {
275 WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcCreate Failed winEr %d\n", winEr));
276 return winEr;
277 }
278
279 int cDisplays = -1;
280
281 for (UINT iter = 0; iter < DispCfg.cPathInfoArray; ++iter)
282 {
283 if (cDisplays < (int)(DispCfg.pPathInfoArray[iter].sourceInfo.id))
284 cDisplays = (int)(DispCfg.pPathInfoArray[iter].sourceInfo.id);
285 }
286
287 cDisplays++;
288
289 g_cVBoxDispIfWddmDisplays = cDisplays;
290 Assert(g_cVBoxDispIfWddmDisplays);
291
292 vboxDispIfWddmDcTerm(&DispCfg);
293 }
294
295 *pcDisplays = g_cVBoxDispIfWddmDisplays;
296 return ERROR_SUCCESS;
297}
298
299#define VBOX_WDDM_DC_SEARCH_PATH_ANY (~(UINT)0)
300static int vboxDispIfWddmDcSearchPath(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT srcId, UINT trgId)
301{
302 for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
303 {
304 if ( (srcId == VBOX_WDDM_DC_SEARCH_PATH_ANY || pCfg->pPathInfoArray[iter].sourceInfo.id == srcId)
305 && (trgId == VBOX_WDDM_DC_SEARCH_PATH_ANY || pCfg->pPathInfoArray[iter].targetInfo.id == trgId))
306 {
307 return (int)iter;
308 }
309 }
310 return -1;
311}
312
313static int vboxDispIfWddmDcSearchActiveSourcePath(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT srcId)
314{
315 for (UINT i = 0; i < pCfg->cPathInfoArray; ++i)
316 {
317 if ( pCfg->pPathInfoArray[i].sourceInfo.id == srcId
318 && RT_BOOL(pCfg->pPathInfoArray[i].flags & DISPLAYCONFIG_PATH_ACTIVE))
319 {
320 return (int)i;
321 }
322 }
323 return -1;
324}
325
326static int vboxDispIfWddmDcSearchActivePath(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT srcId, UINT trgId)
327{
328 int idx = vboxDispIfWddmDcSearchPath(pCfg, srcId, trgId);
329 if (idx < 0)
330 return idx;
331
332 if (!(pCfg->pPathInfoArray[idx].flags & DISPLAYCONFIG_PATH_ACTIVE))
333 return -1;
334
335 return idx;
336}
337
338static VOID vboxDispIfWddmDcSettingsInvalidateModeIndex(VBOXDISPIF_WDDM_DISPCFG *pCfg, int idx)
339{
340 pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
341 pCfg->pPathInfoArray[idx].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
342}
343
344static VOID vboxDispIfWddmDcSettingsInvalidateModeIndeces(VBOXDISPIF_WDDM_DISPCFG *pCfg)
345{
346 for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
347 {
348 vboxDispIfWddmDcSettingsInvalidateModeIndex(pCfg, (int)iter);
349 }
350
351 if (pCfg->pModeInfoArray)
352 {
353 free(pCfg->pModeInfoArray);
354 pCfg->pModeInfoArray = NULL;
355 }
356 pCfg->cModeInfoArray = 0;
357}
358
359static DWORD vboxDispIfWddmDcSettingsModeAdd(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT *pIdx)
360{
361 UINT32 cModeInfoArray = pCfg->cModeInfoArray + 1;
362 DISPLAYCONFIG_MODE_INFO *pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(cModeInfoArray * sizeof (DISPLAYCONFIG_MODE_INFO));
363 if (!pModeInfoArray)
364 {
365 WARN(("VBoxTray: (WDDM) malloc failed!\n"));
366 return ERROR_OUTOFMEMORY;
367 }
368
369 memcpy (pModeInfoArray, pCfg->pModeInfoArray, pCfg->cModeInfoArray * sizeof(DISPLAYCONFIG_MODE_INFO));
370 memset(&pModeInfoArray[cModeInfoArray-1], 0, sizeof (pModeInfoArray[0]));
371 free(pCfg->pModeInfoArray);
372 *pIdx = cModeInfoArray-1;
373 pCfg->pModeInfoArray = pModeInfoArray;
374 pCfg->cModeInfoArray = cModeInfoArray;
375 return ERROR_SUCCESS;
376}
377
378static DWORD vboxDispIfWddmDcSettingsUpdate(VBOXDISPIF_WDDM_DISPCFG *pCfg, int idx, DEVMODE *pDeviceMode, BOOL fInvalidateSrcMode, BOOL fEnable)
379{
380 if (fInvalidateSrcMode)
381 pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
382 else if (pDeviceMode)
383 {
384 UINT iSrcMode = pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx;
385 if (iSrcMode == DISPLAYCONFIG_PATH_MODE_IDX_INVALID)
386 {
387
388 WARN(("VBoxTray: (WDDM) no source mode index specified"));
389 DWORD winEr = vboxDispIfWddmDcSettingsModeAdd(pCfg, &iSrcMode);
390 if (winEr != ERROR_SUCCESS)
391 {
392 WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcSettingsModeAdd Failed winEr %d\n", winEr));
393 return winEr;
394 }
395 pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx = iSrcMode;
396 }
397
398 for (int i = 0; i < (int)pCfg->cPathInfoArray; ++i)
399 {
400 if (i == idx)
401 continue;
402
403 if (pCfg->pPathInfoArray[i].sourceInfo.modeInfoIdx == iSrcMode)
404 {
405 /* this is something we're not expecting/supporting */
406 WARN(("VBoxTray: (WDDM) multiple paths have the same mode index"));
407 return ERROR_NOT_SUPPORTED;
408 }
409 }
410
411 if (pDeviceMode->dmFields & DM_PELSWIDTH)
412 pCfg->pModeInfoArray[iSrcMode].sourceMode.width = pDeviceMode->dmPelsWidth;
413 if (pDeviceMode->dmFields & DM_PELSHEIGHT)
414 pCfg->pModeInfoArray[iSrcMode].sourceMode.height = pDeviceMode->dmPelsHeight;
415 if (pDeviceMode->dmFields & DM_POSITION)
416 {
417 LogFlowFunc(("DM_POSITION %d,%d -> %d,%d\n",
418 pCfg->pModeInfoArray[iSrcMode].sourceMode.position.x,
419 pCfg->pModeInfoArray[iSrcMode].sourceMode.position.y,
420 pDeviceMode->dmPosition.x, pDeviceMode->dmPosition.y));
421 pCfg->pModeInfoArray[iSrcMode].sourceMode.position.x = pDeviceMode->dmPosition.x;
422 pCfg->pModeInfoArray[iSrcMode].sourceMode.position.y = pDeviceMode->dmPosition.y;
423 }
424 if (pDeviceMode->dmFields & DM_BITSPERPEL)
425 {
426 switch (pDeviceMode->dmBitsPerPel)
427 {
428 case 32:
429 pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
430 break;
431 case 24:
432 pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
433 break;
434 case 16:
435 pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
436 break;
437 case 8:
438 pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
439 break;
440 default:
441 LogRel(("VBoxTray: (WDDM) invalid bpp %d, using 32\n", pDeviceMode->dmBitsPerPel));
442 pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
443 break;
444 }
445 }
446 }
447
448 pCfg->pPathInfoArray[idx].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
449
450 /* "A refresh rate with both the numerator and denominator set to zero indicates that
451 * the caller does not specify a refresh rate and the operating system should use
452 * the most optimal refresh rate available. For this case, in a call to the SetDisplayConfig
453 * function, the caller must set the scanLineOrdering member to the
454 * DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED value; otherwise, SetDisplayConfig fails."
455 *
456 * If a refresh rate is set to a value, then the resize will fail if miniport driver
457 * does not support VSync, i.e. with display-only driver on Win8+ (@bugref{8440}).
458 */
459 pCfg->pPathInfoArray[idx].targetInfo.refreshRate.Numerator = 0;
460 pCfg->pPathInfoArray[idx].targetInfo.refreshRate.Denominator = 0;
461 pCfg->pPathInfoArray[idx].targetInfo.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
462
463 /* Make sure that "The output can be forced on this target even if a monitor is not detected." */
464 pCfg->pPathInfoArray[idx].targetInfo.targetAvailable = TRUE;
465 pCfg->pPathInfoArray[idx].targetInfo.statusFlags |= DISPLAYCONFIG_TARGET_FORCIBLE;
466
467 if (fEnable)
468 pCfg->pPathInfoArray[idx].flags |= DISPLAYCONFIG_PATH_ACTIVE;
469 else
470 pCfg->pPathInfoArray[idx].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
471
472 return ERROR_SUCCESS;
473}
474
475static DWORD vboxDispIfWddmDcSet(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT fFlags)
476{
477 DWORD winEr = gCtx.pfnSetDisplayConfig(pCfg->cPathInfoArray, pCfg->pPathInfoArray, pCfg->cModeInfoArray, pCfg->pModeInfoArray, fFlags);
478 if (winEr != ERROR_SUCCESS)
479 Log(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed for Flags 0x%x\n", fFlags));
480 return winEr;
481}
482
483static BOOL vboxDispIfWddmDcSettingsAdjustSupportedPaths(VBOXDISPIF_WDDM_DISPCFG *pCfg)
484{
485 BOOL fAdjusted = FALSE;
486 for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
487 {
488 if (pCfg->pPathInfoArray[iter].sourceInfo.id == pCfg->pPathInfoArray[iter].targetInfo.id)
489 continue;
490
491 if (!(pCfg->pPathInfoArray[iter].flags & DISPLAYCONFIG_PATH_ACTIVE))
492 continue;
493
494 pCfg->pPathInfoArray[iter].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
495 fAdjusted = TRUE;
496 }
497
498 return fAdjusted;
499}
500
501static void vboxDispIfWddmDcSettingsAttachDisbledToPrimary(VBOXDISPIF_WDDM_DISPCFG *pCfg)
502{
503 for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
504 {
505 if ((pCfg->pPathInfoArray[iter].flags & DISPLAYCONFIG_PATH_ACTIVE))
506 continue;
507
508 pCfg->pPathInfoArray[iter].sourceInfo.id = 0;
509 pCfg->pPathInfoArray[iter].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
510 pCfg->pPathInfoArray[iter].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
511 }
512}
513
514static DWORD vboxDispIfWddmDcSettingsIncludeAllTargets(VBOXDISPIF_WDDM_DISPCFG *pCfg)
515{
516 UINT32 cDisplays = 0;
517 VBOXDISPIF_WDDM_DISPCFG AllCfg;
518 BOOL fAllCfgInited = FALSE;
519
520 DWORD winEr = vboxDispIfWddmDcQueryNumDisplays(&cDisplays);
521 if (winEr != ERROR_SUCCESS)
522 {
523 WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcQueryNumDisplays Failed winEr %d\n", winEr));
524 return winEr;
525 }
526
527 DISPLAYCONFIG_PATH_INFO *pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(cDisplays * sizeof(DISPLAYCONFIG_PATH_INFO));
528 if (!pPathInfoArray)
529 {
530 WARN(("malloc failed\n"));
531 return ERROR_OUTOFMEMORY;
532 }
533
534 for (UINT i = 0; i < cDisplays; ++i)
535 {
536 int idx = vboxDispIfWddmDcSearchPath(pCfg, i, i);
537 if (idx < 0)
538 {
539 idx = vboxDispIfWddmDcSearchPath(pCfg, VBOX_WDDM_DC_SEARCH_PATH_ANY, i);
540 if (idx >= 0)
541 {
542 WARN(("VBoxTray:(WDDM) different source and target paare enabled, this is something we would not expect\n"));
543 }
544 }
545
546 if (idx >= 0)
547 pPathInfoArray[i] = pCfg->pPathInfoArray[idx];
548 else
549 {
550 if (!fAllCfgInited)
551 {
552 winEr = vboxDispIfWddmDcCreate(&AllCfg, QDC_ALL_PATHS);
553 if (winEr != ERROR_SUCCESS)
554 {
555 WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcCreate Failed winEr %d\n", winEr));
556 free(pPathInfoArray);
557 return winEr;
558 }
559 fAllCfgInited = TRUE;
560 }
561
562 idx = vboxDispIfWddmDcSearchPath(&AllCfg, i, i);
563 if (idx < 0)
564 {
565 WARN(("VBoxTray:(WDDM) %d %d path not supported\n", i, i));
566 idx = vboxDispIfWddmDcSearchPath(pCfg, VBOX_WDDM_DC_SEARCH_PATH_ANY, i);
567 if (idx < 0)
568 {
569 WARN(("VBoxTray:(WDDM) %d %d path not supported\n", -1, i));
570 }
571 }
572
573 if (idx >= 0)
574 {
575 pPathInfoArray[i] = AllCfg.pPathInfoArray[idx];
576
577 if (pPathInfoArray[i].flags & DISPLAYCONFIG_PATH_ACTIVE)
578 {
579 WARN(("VBoxTray:(WDDM) disabled path %d %d is marked active\n",
580 pPathInfoArray[i].sourceInfo.id, pPathInfoArray[i].targetInfo.id));
581 pPathInfoArray[i].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
582 }
583
584 Assert(pPathInfoArray[i].sourceInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID);
585 Assert(pPathInfoArray[i].sourceInfo.statusFlags == 0);
586
587 Assert(pPathInfoArray[i].targetInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID);
588 Assert(pPathInfoArray[i].targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15);
589 Assert(pPathInfoArray[i].targetInfo.rotation == DISPLAYCONFIG_ROTATION_IDENTITY);
590 Assert(pPathInfoArray[i].targetInfo.scaling == DISPLAYCONFIG_SCALING_PREFERRED);
591 Assert(pPathInfoArray[i].targetInfo.refreshRate.Numerator == 0);
592 Assert(pPathInfoArray[i].targetInfo.refreshRate.Denominator == 0);
593 Assert(pPathInfoArray[i].targetInfo.scanLineOrdering == DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED);
594 Assert(pPathInfoArray[i].targetInfo.targetAvailable == TRUE);
595 Assert(pPathInfoArray[i].targetInfo.statusFlags == DISPLAYCONFIG_TARGET_FORCIBLE);
596
597 Assert(pPathInfoArray[i].flags == 0);
598 }
599 else
600 {
601 pPathInfoArray[i].sourceInfo.adapterId = pCfg->pPathInfoArray[0].sourceInfo.adapterId;
602 pPathInfoArray[i].sourceInfo.id = i;
603 pPathInfoArray[i].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
604 pPathInfoArray[i].sourceInfo.statusFlags = 0;
605
606 pPathInfoArray[i].targetInfo.adapterId = pPathInfoArray[i].sourceInfo.adapterId;
607 pPathInfoArray[i].targetInfo.id = i;
608 pPathInfoArray[i].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
609 pPathInfoArray[i].targetInfo.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15;
610 pPathInfoArray[i].targetInfo.rotation = DISPLAYCONFIG_ROTATION_IDENTITY;
611 pPathInfoArray[i].targetInfo.scaling = DISPLAYCONFIG_SCALING_PREFERRED;
612 pPathInfoArray[i].targetInfo.refreshRate.Numerator = 0;
613 pPathInfoArray[i].targetInfo.refreshRate.Denominator = 0;
614 pPathInfoArray[i].targetInfo.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
615 pPathInfoArray[i].targetInfo.targetAvailable = TRUE;
616 pPathInfoArray[i].targetInfo.statusFlags = DISPLAYCONFIG_TARGET_FORCIBLE;
617
618 pPathInfoArray[i].flags = 0;
619 }
620 }
621 }
622
623 free(pCfg->pPathInfoArray);
624 pCfg->pPathInfoArray = pPathInfoArray;
625 pCfg->cPathInfoArray = cDisplays;
626 if (fAllCfgInited)
627 vboxDispIfWddmDcTerm(&AllCfg);
628
629 return ERROR_SUCCESS;
630}
631
632static DWORD vboxDispIfOpBegin(PCVBOXDISPIF pIf, VBOXDISPIF_OP *pOp)
633{
634 pOp->pIf = pIf;
635
636 HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &pOp->Adapter);
637 if (SUCCEEDED(hr))
638 {
639 hr = vboxDispKmtCreateDevice(&pOp->Adapter, &pOp->Device);
640 if (SUCCEEDED(hr))
641 {
642 hr = vboxDispKmtCreateContext(&pOp->Device, &pOp->Context, VBOXWDDM_CONTEXT_TYPE_CUSTOM_DISPIF_RESIZE,
643 NULL, 0ULL);
644 if (SUCCEEDED(hr))
645 return ERROR_SUCCESS;
646 else
647 WARN(("VBoxTray: vboxDispKmtCreateContext failed hr 0x%x", hr));
648
649 vboxDispKmtDestroyDevice(&pOp->Device);
650 }
651 else
652 WARN(("VBoxTray: vboxDispKmtCreateDevice failed hr 0x%x", hr));
653
654 vboxDispKmtCloseAdapter(&pOp->Adapter);
655 }
656
657 return ERROR_NOT_SUPPORTED;
658}
659
660static VOID vboxDispIfOpEnd(VBOXDISPIF_OP *pOp)
661{
662 vboxDispKmtDestroyContext(&pOp->Context);
663 vboxDispKmtDestroyDevice(&pOp->Device);
664 vboxDispKmtCloseAdapter(&pOp->Adapter);
665}
666
667/* display driver interface abstraction for XPDM & WDDM
668 * with WDDM we can not use ExtEscape to communicate with our driver
669 * because we do not have XPDM display driver any more, i.e. escape requests are handled by cdd
670 * that knows nothing about us */
671DWORD VBoxDispIfInit(PVBOXDISPIF pDispIf)
672{
673 VBoxDispIfSwitchMode(pDispIf, VBOXDISPIF_MODE_XPDM, NULL);
674
675 return NO_ERROR;
676}
677
678#ifdef VBOX_WITH_WDDM
679static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf);
680static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf);
681#endif
682
683DWORD VBoxDispIfTerm(PVBOXDISPIF pIf)
684{
685#ifdef VBOX_WITH_WDDM
686 if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM)
687 {
688 vboxDispIfWddmTerm(pIf);
689
690 vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks);
691 }
692#endif
693
694 pIf->enmMode = VBOXDISPIF_MODE_UNKNOWN;
695 return NO_ERROR;
696}
697
698static DWORD vboxDispIfEscapeXPDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, int iDirection)
699{
700 RT_NOREF(pIf);
701 HDC hdc = GetDC(HWND_DESKTOP);
702 VOID *pvData = cbData ? VBOXDISPIFESCAPE_DATA(pEscape, VOID) : NULL;
703 int iRet = ExtEscape(hdc, pEscape->escapeCode,
704 iDirection >= 0 ? cbData : 0,
705 iDirection >= 0 ? (LPSTR)pvData : NULL,
706 iDirection <= 0 ? cbData : 0,
707 iDirection <= 0 ? (LPSTR)pvData : NULL);
708 ReleaseDC(HWND_DESKTOP, hdc);
709 if (iRet > 0)
710 return VINF_SUCCESS;
711 if (iRet == 0)
712 return ERROR_NOT_SUPPORTED;
713 /* else */
714 return ERROR_GEN_FAILURE;
715}
716
717#ifdef VBOX_WITH_WDDM
718static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf)
719{
720 DWORD err = NO_ERROR;
721
722 bool fSupported = true;
723
724 uint64_t const uNtVersion = RTSystemGetNtVersion();
725 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
726 {
727 LogFunc(("this is vista and up\n"));
728 HMODULE hUser = GetModuleHandle("user32.dll");
729 if (hUser)
730 {
731 *(uintptr_t *)&pIf->modeData.wddm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
732 LogFunc(("VBoxDisplayInit: pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.wddm.pfnChangeDisplaySettingsEx));
733 fSupported &= !!(pIf->modeData.wddm.pfnChangeDisplaySettingsEx);
734
735 *(uintptr_t *)&pIf->modeData.wddm.pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA");
736 LogFunc(("VBoxDisplayInit: pfnEnumDisplayDevices = %p\n", pIf->modeData.wddm.pfnEnumDisplayDevices));
737 fSupported &= !!(pIf->modeData.wddm.pfnEnumDisplayDevices);
738
739 /* for win 7 and above */
740 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(6, 1, 0))
741 {
742 *(uintptr_t *)&gCtx.pfnSetDisplayConfig = (uintptr_t)GetProcAddress(hUser, "SetDisplayConfig");
743 LogFunc(("VBoxDisplayInit: pfnSetDisplayConfig = %p\n", gCtx.pfnSetDisplayConfig));
744 fSupported &= !!(gCtx.pfnSetDisplayConfig);
745
746 *(uintptr_t *)&gCtx.pfnQueryDisplayConfig = (uintptr_t)GetProcAddress(hUser, "QueryDisplayConfig");
747 LogFunc(("VBoxDisplayInit: pfnQueryDisplayConfig = %p\n", gCtx.pfnQueryDisplayConfig));
748 fSupported &= !!(gCtx.pfnQueryDisplayConfig);
749
750 *(uintptr_t *)&gCtx.pfnGetDisplayConfigBufferSizes = (uintptr_t)GetProcAddress(hUser, "GetDisplayConfigBufferSizes");
751 LogFunc(("VBoxDisplayInit: pfnGetDisplayConfigBufferSizes = %p\n", gCtx.pfnGetDisplayConfigBufferSizes));
752 fSupported &= !!(gCtx.pfnGetDisplayConfigBufferSizes);
753 }
754
755 /* this is vista and up */
756 HRESULT hr = vboxDispKmtCallbacksInit(&pIf->modeData.wddm.KmtCallbacks);
757 if (FAILED(hr))
758 {
759 WARN(("VBoxTray: vboxDispKmtCallbacksInit failed hr 0x%x\n", hr));
760 err = hr;
761 }
762 }
763 else
764 {
765 WARN(("GetModuleHandle(USER32) failed, err(%d)\n", GetLastError()));
766 err = ERROR_NOT_SUPPORTED;
767 }
768 }
769 else
770 {
771 WARN(("can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n"));
772 err = ERROR_NOT_SUPPORTED;
773 }
774
775 if (err == ERROR_SUCCESS)
776 {
777 err = vboxDispIfWddmInit(pIf);
778 }
779
780 return err;
781}
782
783static DWORD vboxDispIfSwitchToWDDM_W7(PVBOXDISPIF pIf)
784{
785 return vboxDispIfSwitchToWDDM(pIf);
786}
787
788static DWORD vboxDispIfWDDMAdpHdcCreate(int iDisplay, HDC *phDc, DISPLAY_DEVICE *pDev)
789{
790 DWORD winEr = ERROR_INVALID_STATE;
791 memset(pDev, 0, sizeof (*pDev));
792 pDev->cb = sizeof (*pDev);
793
794 for (int i = 0; ; ++i)
795 {
796 if (EnumDisplayDevices(NULL, /* LPCTSTR lpDevice */ i, /* DWORD iDevNum */
797 pDev, 0 /* DWORD dwFlags*/))
798 {
799 if (i == iDisplay || (iDisplay < 0 && pDev->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
800 {
801 HDC hDc = CreateDC(NULL, pDev->DeviceName, NULL, NULL);
802 if (hDc)
803 {
804 *phDc = hDc;
805 return NO_ERROR;
806 }
807 else
808 {
809 winEr = GetLastError();
810 WARN(("CreateDC failed %d", winEr));
811 break;
812 }
813 }
814 Log(("display data no match display(%d): i(%d), flags(%d)", iDisplay, i, pDev->StateFlags));
815 }
816 else
817 {
818 winEr = GetLastError();
819 WARN(("EnumDisplayDevices failed %d", winEr));
820 break;
821 }
822 }
823
824 WARN(("vboxDispIfWDDMAdpHdcCreate failure branch %d", winEr));
825 return winEr;
826}
827
828static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess)
829{
830 DWORD winEr = ERROR_SUCCESS;
831 VBOXDISPKMT_ADAPTER Adapter;
832 HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &Adapter);
833 if (!SUCCEEDED(hr))
834 {
835 WARN(("VBoxTray: vboxDispKmtOpenAdapter failed hr 0x%x\n", hr));
836 return hr;
837 }
838
839 D3DKMT_ESCAPE EscapeData = {0};
840 EscapeData.hAdapter = Adapter.hAdapter;
841 //EscapeData.hDevice = NULL;
842 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
843 if (fHwAccess)
844 EscapeData.Flags.HardwareAccess = 1;
845 EscapeData.pPrivateDriverData = pEscape;
846 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(cbData);
847 //EscapeData.hContext = NULL;
848
849 NTSTATUS Status = pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
850 if (NT_SUCCESS(Status))
851 winEr = ERROR_SUCCESS;
852 else
853 {
854 WARN(("VBoxTray: pfnD3DKMTEscape(0x%08X) failed Status 0x%x\n", pEscape->escapeCode, Status));
855 winEr = ERROR_GEN_FAILURE;
856 }
857
858 vboxDispKmtCloseAdapter(&Adapter);
859
860 return winEr;
861}
862#endif
863
864DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
865{
866 switch (pIf->enmMode)
867 {
868 case VBOXDISPIF_MODE_XPDM_NT4:
869 case VBOXDISPIF_MODE_XPDM:
870 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData, 1);
871#ifdef VBOX_WITH_WDDM
872 case VBOXDISPIF_MODE_WDDM:
873 case VBOXDISPIF_MODE_WDDM_W7:
874 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
875#endif
876 default:
877 LogFunc(("unknown mode (%d)\n", pIf->enmMode));
878 return ERROR_INVALID_PARAMETER;
879 }
880}
881
882DWORD VBoxDispIfEscapeInOut(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
883{
884 switch (pIf->enmMode)
885 {
886 case VBOXDISPIF_MODE_XPDM_NT4:
887 case VBOXDISPIF_MODE_XPDM:
888 return vboxDispIfEscapeXPDM(pIf, pEscape, cbData, 0);
889#ifdef VBOX_WITH_WDDM
890 case VBOXDISPIF_MODE_WDDM:
891 case VBOXDISPIF_MODE_WDDM_W7:
892 return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
893#endif
894 default:
895 LogFunc(("unknown mode (%d)\n", pIf->enmMode));
896 return ERROR_INVALID_PARAMETER;
897 }
898}
899
900#ifdef VBOX_WITH_WDDM
901
902#define VBOXRR_TIMER_ID 1234
903
904typedef struct VBOXRR
905{
906 HANDLE hThread;
907 DWORD idThread;
908 HANDLE hEvent;
909 HWND hWnd;
910 CRITICAL_SECTION CritSect;
911 UINT_PTR idTimer;
912 PCVBOXDISPIF pIf;
913 UINT iChangedMode;
914 BOOL fEnable;
915 BOOL fExtDispSup;
916 DISPLAY_DEVICE *paDisplayDevices;
917 DEVMODE *paDeviceModes;
918 UINT cDevModes;
919} VBOXRR, *PVBOXRR;
920
921static VBOXRR g_VBoxRr = {0};
922
923#define VBOX_E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
924#define VBOX_E_NOT_SUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
925
926static void vboxRrRetryStopLocked()
927{
928 PVBOXRR pMon = &g_VBoxRr;
929 if (pMon->pIf)
930 {
931 if (pMon->paDisplayDevices)
932 {
933 free(pMon->paDisplayDevices);
934 pMon->paDisplayDevices = NULL;
935 }
936
937 if (pMon->paDeviceModes)
938 {
939 free(pMon->paDeviceModes);
940 pMon->paDeviceModes = NULL;
941 }
942
943 if (pMon->idTimer)
944 {
945 KillTimer(pMon->hWnd, pMon->idTimer);
946 pMon->idTimer = 0;
947 }
948
949 pMon->cDevModes = 0;
950 pMon->pIf = NULL;
951 }
952}
953
954static void VBoxRrRetryStop()
955{
956 PVBOXRR pMon = &g_VBoxRr;
957 EnterCriticalSection(&pMon->CritSect);
958 vboxRrRetryStopLocked();
959 LeaveCriticalSection(&pMon->CritSect);
960}
961
962//static DWORD vboxDispIfWddmValidateFixResize(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes);
963
964static void vboxRrRetryReschedule()
965{
966}
967
968static void VBoxRrRetrySchedule(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
969{
970 PVBOXRR pMon = &g_VBoxRr;
971 EnterCriticalSection(&pMon->CritSect);
972 vboxRrRetryStopLocked();
973
974 pMon->pIf = pIf;
975 pMon->iChangedMode = iChangedMode;
976 pMon->fEnable = fEnable;
977 pMon->fExtDispSup = fExtDispSup;
978
979 if (cDevModes)
980 {
981 pMon->paDisplayDevices = (DISPLAY_DEVICE*)malloc(sizeof (*paDisplayDevices) * cDevModes);
982 Assert(pMon->paDisplayDevices);
983 if (!pMon->paDisplayDevices)
984 {
985 Log(("malloc failed!"));
986 vboxRrRetryStopLocked();
987 LeaveCriticalSection(&pMon->CritSect);
988 return;
989 }
990 memcpy(pMon->paDisplayDevices, paDisplayDevices, sizeof (*paDisplayDevices) * cDevModes);
991
992 pMon->paDeviceModes = (DEVMODE*)malloc(sizeof (*paDeviceModes) * cDevModes);
993 Assert(pMon->paDeviceModes);
994 if (!pMon->paDeviceModes)
995 {
996 Log(("malloc failed!"));
997 vboxRrRetryStopLocked();
998 LeaveCriticalSection(&pMon->CritSect);
999 return;
1000 }
1001 memcpy(pMon->paDeviceModes, paDeviceModes, sizeof (*paDeviceModes) * cDevModes);
1002 }
1003 pMon->cDevModes = cDevModes;
1004
1005 pMon->idTimer = SetTimer(pMon->hWnd, VBOXRR_TIMER_ID, 1000, (TIMERPROC)NULL);
1006 Assert(pMon->idTimer);
1007 if (!pMon->idTimer)
1008 {
1009 WARN(("VBoxTray: SetTimer failed!, err %d\n", GetLastError()));
1010 vboxRrRetryStopLocked();
1011 }
1012
1013 LeaveCriticalSection(&pMon->CritSect);
1014}
1015
1016static void vboxRrRetryPerform()
1017{
1018 PVBOXRR pMon = &g_VBoxRr;
1019 EnterCriticalSection(&pMon->CritSect);
1020 if (pMon->pIf)
1021 {
1022 DWORD dwErr = vboxDispIfResizePerform(pMon->pIf, pMon->iChangedMode, pMon->fEnable, pMon->fExtDispSup, pMon->paDisplayDevices, pMon->paDeviceModes, pMon->cDevModes);
1023 if (ERROR_RETRY != dwErr)
1024 VBoxRrRetryStop();
1025 else
1026 vboxRrRetryReschedule();
1027 }
1028 LeaveCriticalSection(&pMon->CritSect);
1029}
1030
1031static LRESULT CALLBACK vboxRrWndProc(HWND hwnd,
1032 UINT uMsg,
1033 WPARAM wParam,
1034 LPARAM lParam
1035)
1036{
1037 switch(uMsg)
1038 {
1039 case WM_DISPLAYCHANGE:
1040 {
1041 Log(("VBoxTray: WM_DISPLAYCHANGE\n"));
1042 VBoxRrRetryStop();
1043 return 0;
1044 }
1045 case WM_TIMER:
1046 {
1047 if (wParam == VBOXRR_TIMER_ID)
1048 {
1049 Log(("VBoxTray: VBOXRR_TIMER_ID\n"));
1050 vboxRrRetryPerform();
1051 return 0;
1052 }
1053 break;
1054 }
1055 case WM_NCHITTEST:
1056 LogFunc(("got WM_NCHITTEST for hwnd(0x%x)\n", hwnd));
1057 return HTNOWHERE;
1058 default:
1059 break;
1060 }
1061
1062 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1063}
1064
1065#define VBOXRRWND_NAME "VBoxRrWnd"
1066
1067static HRESULT vboxRrWndCreate(HWND *phWnd)
1068{
1069 HRESULT hr = S_OK;
1070
1071 /** @todo r=andy Use VBOXSERVICEENV::hInstance. */
1072 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
1073
1074 /* Register the Window Class. */
1075 WNDCLASSEX wc = { 0 };
1076 wc.cbSize = sizeof(WNDCLASSEX);
1077
1078 if (!GetClassInfoEx(hInstance, VBOXRRWND_NAME, &wc))
1079 {
1080 wc.lpfnWndProc = vboxRrWndProc;
1081 wc.hInstance = hInstance;
1082 wc.lpszClassName = VBOXRRWND_NAME;
1083
1084 if (!RegisterClassEx(&wc))
1085 {
1086 WARN(("RegisterClass failed, winErr(%d)\n", GetLastError()));
1087 hr = E_FAIL;
1088 }
1089 }
1090
1091 if (hr == S_OK)
1092 {
1093 HWND hWnd = CreateWindowEx (WS_EX_TOOLWINDOW,
1094 VBOXRRWND_NAME, VBOXRRWND_NAME,
1095 WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED,
1096 -100, -100,
1097 10, 10,
1098 NULL, //GetDesktopWindow() /* hWndParent */,
1099 NULL /* hMenu */,
1100 hInstance,
1101 NULL /* lpParam */);
1102 Assert(hWnd);
1103 if (hWnd)
1104 {
1105 *phWnd = hWnd;
1106 }
1107 else
1108 {
1109 WARN(("CreateWindowEx failed, winErr(%d)\n", GetLastError()));
1110 hr = E_FAIL;
1111 }
1112 }
1113
1114 return hr;
1115}
1116
1117static HRESULT vboxRrWndDestroy(HWND hWnd)
1118{
1119 BOOL bResult = DestroyWindow(hWnd);
1120 if (bResult)
1121 return S_OK;
1122
1123 DWORD winErr = GetLastError();
1124 WARN(("DestroyWindow failed, winErr(%d) for hWnd(0x%x)\n", winErr, hWnd));
1125
1126 return HRESULT_FROM_WIN32(winErr);
1127}
1128
1129static HRESULT vboxRrWndInit()
1130{
1131 PVBOXRR pMon = &g_VBoxRr;
1132 return vboxRrWndCreate(&pMon->hWnd);
1133}
1134
1135HRESULT vboxRrWndTerm()
1136{
1137 PVBOXRR pMon = &g_VBoxRr;
1138 HRESULT hrTmp = vboxRrWndDestroy(pMon->hWnd);
1139 Assert(hrTmp == S_OK); NOREF(hrTmp);
1140
1141 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
1142 UnregisterClass(VBOXRRWND_NAME, hInstance);
1143
1144 return S_OK;
1145}
1146
1147#define WM_VBOXRR_INIT_QUIT (WM_APP+2)
1148
1149HRESULT vboxRrRun()
1150{
1151 PVBOXRR pMon = &g_VBoxRr;
1152 MSG Msg;
1153
1154 HRESULT hr = S_FALSE;
1155
1156 /* Create the thread message queue*/
1157 PeekMessage(&Msg,
1158 NULL /* HWND hWnd */,
1159 WM_USER /* UINT wMsgFilterMin */,
1160 WM_USER /* UINT wMsgFilterMax */,
1161 PM_NOREMOVE);
1162
1163 /*
1164 * Send signal that message queue is ready.
1165 * From this moment only the thread is ready to receive messages.
1166 */
1167 BOOL bRc = SetEvent(pMon->hEvent);
1168 if (!bRc)
1169 {
1170 DWORD winErr = GetLastError();
1171 WARN(("SetEvent failed, winErr = (%d)", winErr));
1172 HRESULT hrTmp = HRESULT_FROM_WIN32(winErr);
1173 Assert(hrTmp != S_OK); NOREF(hrTmp);
1174 }
1175
1176 do
1177 {
1178 BOOL bResult = GetMessage(&Msg,
1179 0 /*HWND hWnd*/,
1180 0 /*UINT wMsgFilterMin*/,
1181 0 /*UINT wMsgFilterMax*/
1182 );
1183
1184 if (bResult == -1) /* error occurred */
1185 {
1186 DWORD winEr = GetLastError();
1187 hr = HRESULT_FROM_WIN32(winEr);
1188 /* just ensure we never return success in this case */
1189 Assert(hr != S_OK);
1190 Assert(hr != S_FALSE);
1191 if (hr == S_OK || hr == S_FALSE)
1192 hr = E_FAIL;
1193 WARN(("VBoxTray: GetMessage returned -1, err %d\n", winEr));
1194 VBoxRrRetryStop();
1195 break;
1196 }
1197
1198 if(!bResult) /* WM_QUIT was posted */
1199 {
1200 hr = S_FALSE;
1201 Log(("VBoxTray: GetMessage returned FALSE\n"));
1202 VBoxRrRetryStop();
1203 break;
1204 }
1205
1206 switch (Msg.message)
1207 {
1208 case WM_VBOXRR_INIT_QUIT:
1209 case WM_CLOSE:
1210 {
1211 Log(("VBoxTray: closing Rr %d\n", Msg.message));
1212 VBoxRrRetryStop();
1213 PostQuitMessage(0);
1214 break;
1215 }
1216 default:
1217 TranslateMessage(&Msg);
1218 DispatchMessage(&Msg);
1219 break;
1220 }
1221 } while (1);
1222 return 0;
1223}
1224
1225/** @todo r=bird: Only the CRT uses CreateThread for creating threading!! */
1226static DWORD WINAPI vboxRrRunnerThread(void *pvUser) RT_NOTHROW_DEF
1227{
1228 RT_NOREF(pvUser);
1229 HRESULT hr = vboxRrWndInit();
1230 Assert(hr == S_OK);
1231 if (hr == S_OK)
1232 {
1233 hr = vboxRrRun();
1234 Assert(hr == S_OK);
1235
1236 vboxRrWndTerm();
1237 }
1238
1239 return 0;
1240}
1241
1242HRESULT VBoxRrInit()
1243{
1244 HRESULT hr = E_FAIL;
1245 PVBOXRR pMon = &g_VBoxRr;
1246 memset(pMon, 0, sizeof (*pMon));
1247
1248 InitializeCriticalSection(&pMon->CritSect);
1249
1250 pMon->hEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes*/
1251 TRUE, /* BOOL bManualReset*/
1252 FALSE, /* BOOL bInitialState */
1253 NULL /* LPCTSTR lpName */
1254 );
1255 if (pMon->hEvent)
1256 {
1257 /** @todo r=bird: What kind of stupid nonsense is this?!?
1258 * Only the CRT uses CreateThread for creating threading!!
1259 */
1260 pMon->hThread = CreateThread(NULL /* LPSECURITY_ATTRIBUTES lpThreadAttributes */,
1261 0 /* SIZE_T dwStackSize */,
1262 vboxRrRunnerThread,
1263 pMon,
1264 0 /* DWORD dwCreationFlags */,
1265 &pMon->idThread);
1266 if (pMon->hThread)
1267 {
1268 DWORD dwResult = WaitForSingleObject(pMon->hEvent, INFINITE);
1269 if (dwResult == WAIT_OBJECT_0)
1270 return S_OK;
1271 Log(("WaitForSingleObject failed!"));
1272 hr = E_FAIL;
1273 }
1274 else
1275 {
1276 DWORD winErr = GetLastError();
1277 WARN(("CreateThread failed, winErr = (%d)", winErr));
1278 hr = HRESULT_FROM_WIN32(winErr);
1279 Assert(hr != S_OK);
1280 }
1281 CloseHandle(pMon->hEvent);
1282 }
1283 else
1284 {
1285 DWORD winErr = GetLastError();
1286 WARN(("CreateEvent failed, winErr = (%d)", winErr));
1287 hr = HRESULT_FROM_WIN32(winErr);
1288 Assert(hr != S_OK);
1289 }
1290
1291 DeleteCriticalSection(&pMon->CritSect);
1292
1293 return hr;
1294}
1295
1296VOID VBoxRrTerm()
1297{
1298 HRESULT hr;
1299 PVBOXRR pMon = &g_VBoxRr;
1300 if (!pMon->hThread)
1301 return;
1302
1303 BOOL bResult = PostThreadMessage(pMon->idThread, WM_VBOXRR_INIT_QUIT, 0, 0);
1304 DWORD winErr;
1305 if (bResult
1306 || (winErr = GetLastError()) == ERROR_INVALID_THREAD_ID) /* <- could be that the thread is terminated */
1307 {
1308 DWORD dwErr = WaitForSingleObject(pMon->hThread, INFINITE);
1309 if (dwErr == WAIT_OBJECT_0)
1310 {
1311 hr = S_OK;
1312 }
1313 else
1314 {
1315 winErr = GetLastError();
1316 hr = HRESULT_FROM_WIN32(winErr);
1317 }
1318 }
1319 else
1320 {
1321 hr = HRESULT_FROM_WIN32(winErr);
1322 }
1323
1324 DeleteCriticalSection(&pMon->CritSect);
1325
1326 CloseHandle(pMon->hThread);
1327 pMon->hThread = 0;
1328 CloseHandle(pMon->hEvent);
1329 pMon->hThread = 0;
1330}
1331
1332static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf)
1333{
1334 RT_NOREF(pIf);
1335 HRESULT hr = VBoxRrInit();
1336 if (SUCCEEDED(hr))
1337 return ERROR_SUCCESS;
1338 WARN(("VBoxTray: VBoxRrInit failed hr 0x%x\n", hr));
1339 return hr;
1340}
1341
1342static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf)
1343{
1344 RT_NOREF(pIf);
1345 VBoxRrTerm();
1346}
1347
1348static DWORD vboxDispIfQueryDisplayConnection(VBOXDISPIF_OP *pOp, UINT32 iDisplay, BOOL *pfConnected)
1349{
1350 if (pOp->pIf->enmMode == VBOXDISPIF_MODE_WDDM)
1351 {
1352 /** @todo do we need ti impl it? */
1353 *pfConnected = TRUE;
1354 return ERROR_SUCCESS;
1355 }
1356
1357 *pfConnected = FALSE;
1358
1359 VBOXDISPIF_WDDM_DISPCFG DispCfg;
1360 DWORD winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
1361 if (winEr != ERROR_SUCCESS)
1362 {
1363 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
1364 return winEr;
1365 }
1366
1367 int idx = vboxDispIfWddmDcSearchPath(&DispCfg, iDisplay, iDisplay);
1368 *pfConnected = (idx >= 0);
1369
1370 vboxDispIfWddmDcTerm(&DispCfg);
1371
1372 return ERROR_SUCCESS;
1373}
1374
1375static DWORD vboxDispIfWaitDisplayDataInited(VBOXDISPIF_OP *pOp)
1376{
1377 DWORD winEr = ERROR_SUCCESS;
1378 do
1379 {
1380 Sleep(100);
1381
1382 D3DKMT_POLLDISPLAYCHILDREN PollData = {0};
1383 PollData.hAdapter = pOp->Adapter.hAdapter;
1384 PollData.NonDestructiveOnly = 1;
1385 NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTPollDisplayChildren(&PollData);
1386 if (Status != 0)
1387 {
1388 Log(("VBoxTray: (WDDM) pfnD3DKMTPollDisplayChildren failed, Status (0x%x)\n", Status));
1389 continue;
1390 }
1391
1392 BOOL fFound = FALSE;
1393#if 0
1394 for (UINT i = 0; i < VBOXWDDM_SCREENMASK_SIZE; ++i)
1395 {
1396 if (pu8DisplayMask && !ASMBitTest(pu8DisplayMask, i))
1397 continue;
1398
1399 BOOL fConnected = FALSE;
1400 winEr = vboxDispIfQueryDisplayConnection(pOp, i, &fConnected);
1401 if (winEr != ERROR_SUCCESS)
1402 {
1403 WARN(("VBoxTray: (WDDM) Failed vboxDispIfQueryDisplayConnection winEr %d\n", winEr));
1404 return winEr;
1405 }
1406
1407 if (!fConnected)
1408 {
1409 WARN(("VBoxTray: (WDDM) Display %d not connected, not expected\n", i));
1410 fFound = TRUE;
1411 break;
1412 }
1413 }
1414#endif
1415 if (!fFound)
1416 break;
1417 } while (1);
1418
1419 return winEr;
1420}
1421
1422static DWORD vboxDispIfUpdateModesWDDM(VBOXDISPIF_OP *pOp, uint32_t u32TargetId, const RTRECTSIZE *pSize)
1423{
1424 DWORD winEr = ERROR_SUCCESS;
1425 VBOXDISPIFESCAPE_UPDATEMODES EscData = {0};
1426 EscData.EscapeHdr.escapeCode = VBOXESC_UPDATEMODES;
1427 EscData.u32TargetId = u32TargetId;
1428 EscData.Size = *pSize;
1429
1430 D3DKMT_ESCAPE EscapeData = {0};
1431 EscapeData.hAdapter = pOp->Adapter.hAdapter;
1432#ifdef VBOX_DISPIF_WITH_OPCONTEXT
1433 /* win8.1 does not allow context-based escapes for display-only mode */
1434 EscapeData.hDevice = pOp->Device.hDevice;
1435 EscapeData.hContext = pOp->Context.hContext;
1436#endif
1437 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
1438 EscapeData.Flags.HardwareAccess = 1;
1439 EscapeData.pPrivateDriverData = &EscData;
1440 EscapeData.PrivateDriverDataSize = sizeof (EscData);
1441
1442 NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
1443 if (NT_SUCCESS(Status))
1444 winEr = ERROR_SUCCESS;
1445 else
1446 {
1447 WARN(("VBoxTray: pfnD3DKMTEscape VBOXESC_UPDATEMODES failed Status 0x%x\n", Status));
1448 winEr = ERROR_GEN_FAILURE;
1449 }
1450
1451#ifdef VBOX_WDDM_REPLUG_ON_MODE_CHANGE
1452 /* The code was disabled because VBOXESC_UPDATEMODES should not cause (un)plugging virtual displays. */
1453 winEr = vboxDispIfWaitDisplayDataInited(pOp);
1454 if (winEr != NO_ERROR)
1455 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWaitDisplayDataInited winEr %d\n", winEr));
1456#endif
1457
1458 return winEr;
1459}
1460
1461static DWORD vboxDispIfTargetConnectivityWDDM(VBOXDISPIF_OP *pOp, uint32_t u32TargetId, uint32_t fu32Connect)
1462{
1463 VBOXDISPIFESCAPE_TARGETCONNECTIVITY PrivateData;
1464 RT_ZERO(PrivateData);
1465 PrivateData.EscapeHdr.escapeCode = VBOXESC_TARGET_CONNECTIVITY;
1466 PrivateData.u32TargetId = u32TargetId;
1467 PrivateData.fu32Connect = fu32Connect;
1468
1469 D3DKMT_ESCAPE EscapeData;
1470 RT_ZERO(EscapeData);
1471 EscapeData.hAdapter = pOp->Adapter.hAdapter;
1472 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
1473 EscapeData.Flags.HardwareAccess = 1;
1474 EscapeData.pPrivateDriverData = &PrivateData;
1475 EscapeData.PrivateDriverDataSize = sizeof(PrivateData);
1476
1477 NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
1478 if (NT_SUCCESS(Status))
1479 return ERROR_SUCCESS;
1480
1481 WARN(("VBoxTray: pfnD3DKMTEscape VBOXESC_TARGETCONNECTIVITY failed Status 0x%x\n", Status));
1482 return ERROR_GEN_FAILURE;
1483}
1484
1485DWORD vboxDispIfCancelPendingResizeWDDM(PCVBOXDISPIF const pIf)
1486{
1487 RT_NOREF(pIf);
1488 Log(("VBoxTray: cancelling pending resize\n"));
1489 VBoxRrRetryStop();
1490 return NO_ERROR;
1491}
1492
1493static DWORD vboxDispIfWddmResizeDisplayVista(DEVMODE *paDeviceModes, DISPLAY_DEVICE *paDisplayDevices, DWORD cDevModes, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup)
1494{
1495 /* Without this, Windows will not ask the miniport for its
1496 * mode table but uses an internal cache instead.
1497 */
1498 for (DWORD i = 0; i < cDevModes; i++)
1499 {
1500 DEVMODE tempDevMode;
1501 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
1502 tempDevMode.dmSize = sizeof(DEVMODE);
1503 EnumDisplaySettings((LPSTR)paDisplayDevices[i].DeviceName, 0xffffff, &tempDevMode);
1504 Log(("VBoxTray: ResizeDisplayDevice: EnumDisplaySettings last error %d\n", GetLastError ()));
1505 }
1506
1507 DWORD winEr = EnableAndResizeDispDev(paDeviceModes, paDisplayDevices, cDevModes, iChangedMode, paDeviceModes[iChangedMode].dmPelsWidth, paDeviceModes[iChangedMode].dmPelsHeight,
1508 paDeviceModes[iChangedMode].dmBitsPerPel, paDeviceModes[iChangedMode].dmPosition.x, paDeviceModes[iChangedMode].dmPosition.y, fEnable, fExtDispSup);
1509 if (winEr != NO_ERROR)
1510 WARN(("VBoxTray: (WDDM) Failed EnableAndResizeDispDev winEr %d\n", winEr));
1511
1512 return winEr;
1513}
1514
1515static DWORD vboxDispIfResizePerform(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
1516{
1517 LogFunc((" ENTER"));
1518 DWORD winEr;
1519
1520 if (pIf->enmMode > VBOXDISPIF_MODE_WDDM)
1521 {
1522 if (fEnable)
1523 paDisplayDevices[iChangedMode].StateFlags |= DISPLAY_DEVICE_ACTIVE;
1524 else
1525 paDisplayDevices[iChangedMode].StateFlags &= ~DISPLAY_DEVICE_ACTIVE;
1526
1527 winEr = vboxDispIfWddmResizeDisplay2(pIf, paDisplayDevices, paDeviceModes, cDevModes);
1528
1529 if (winEr != NO_ERROR)
1530 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmResizeDisplay winEr %d\n", winEr));
1531 }
1532 else
1533 {
1534 winEr = vboxDispIfWddmResizeDisplayVista(paDeviceModes, paDisplayDevices, cDevModes, iChangedMode, fEnable, fExtDispSup);
1535 if (winEr != NO_ERROR)
1536 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmResizeDisplayVista winEr %d\n", winEr));
1537 }
1538
1539 LogFunc((" LEAVE"));
1540 return winEr;
1541}
1542
1543DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
1544{
1545 DWORD winEr = NO_ERROR;
1546
1547 Log(("VBoxTray: vboxDispIfResizeModesWDDM iChanged %d cDevModes %d fEnable %d fExtDispSup %d\n", iChangedMode, cDevModes, fEnable, fExtDispSup));
1548 VBoxRrRetryStop();
1549
1550 VBOXDISPIF_OP Op;
1551
1552 winEr = vboxDispIfOpBegin(pIf, &Op);
1553 if (winEr != NO_ERROR)
1554 {
1555 WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x", winEr));
1556 return winEr;
1557 }
1558
1559/* The pfnD3DKMTInvalidateActiveVidPn was deprecated since Win7 and causes deadlocks since Win10 TH2.
1560 Instead, the VidPn Manager can replace an old VidPn as soon as SetDisplayConfig or ChangeDisplaySettingsEx will try to set a new display mode.
1561 On Vista D3DKMTInvalidateActiveVidPn is still required. TBD: Get rid of it. */
1562 if (Op.pIf->enmMode < VBOXDISPIF_MODE_WDDM_W7)
1563 {
1564 D3DKMT_INVALIDATEACTIVEVIDPN ddiArgInvalidateVidPN;
1565 VBOXWDDM_RECOMMENDVIDPN vboxRecommendVidPN;
1566
1567 memset(&ddiArgInvalidateVidPN, 0, sizeof(ddiArgInvalidateVidPN));
1568 memset(&vboxRecommendVidPN, 0, sizeof(vboxRecommendVidPN));
1569
1570 uint32_t cElements = 0;
1571
1572 for (uint32_t i = 0; i < cDevModes; ++i)
1573 {
1574 if ((i == iChangedMode) ? fEnable : (paDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE))
1575 {
1576 vboxRecommendVidPN.aSources[cElements].Size.cx = paDeviceModes[i].dmPelsWidth;
1577 vboxRecommendVidPN.aSources[cElements].Size.cy = paDeviceModes[i].dmPelsHeight;
1578 vboxRecommendVidPN.aTargets[cElements].iSource = cElements;
1579 ++cElements;
1580 }
1581 else
1582 vboxRecommendVidPN.aTargets[cElements].iSource = -1;
1583 }
1584
1585 ddiArgInvalidateVidPN.hAdapter = Op.Adapter.hAdapter;
1586 ddiArgInvalidateVidPN.pPrivateDriverData = &vboxRecommendVidPN;
1587 ddiArgInvalidateVidPN.PrivateDriverDataSize = sizeof (vboxRecommendVidPN);
1588
1589 NTSTATUS Status;
1590 Status = Op.pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTInvalidateActiveVidPn(&ddiArgInvalidateVidPN);
1591 LogFunc(("D3DKMTInvalidateActiveVidPn returned %d)\n", Status));
1592 }
1593
1594 vboxDispIfTargetConnectivityWDDM(&Op, iChangedMode, fEnable? 1: 0);
1595
1596 /* Whether the current display is already or should be enabled. */
1597 BOOL fChangedEnable = fEnable || RT_BOOL(paDisplayDevices[iChangedMode].StateFlags & DISPLAY_DEVICE_ACTIVE);
1598
1599 if (fChangedEnable)
1600 {
1601 RTRECTSIZE Size;
1602
1603 Size.cx = paDeviceModes[iChangedMode].dmPelsWidth;
1604 Size.cy = paDeviceModes[iChangedMode].dmPelsHeight;
1605
1606 LogFunc(("Calling vboxDispIfUpdateModesWDDM to change target %d mode to (%d x %d)\n", iChangedMode, Size.cx, Size.cy));
1607 winEr = vboxDispIfUpdateModesWDDM(&Op, iChangedMode, &Size);
1608 }
1609
1610 winEr = vboxDispIfResizePerform(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
1611
1612 if (winEr == ERROR_RETRY)
1613 {
1614 VBoxRrRetrySchedule(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
1615
1616 winEr = NO_ERROR;
1617 }
1618
1619 vboxDispIfOpEnd(&Op);
1620
1621 return winEr;
1622}
1623
1624static DWORD vboxDispIfWddmEnableDisplays(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnabled, BOOL fSetTopology, DEVMODE *pDeviceMode)
1625{
1626 RT_NOREF(pIf);
1627 VBOXDISPIF_WDDM_DISPCFG DispCfg;
1628
1629 DWORD winEr;
1630 int iPath;
1631
1632 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
1633 if (winEr != ERROR_SUCCESS)
1634 {
1635 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
1636 return winEr;
1637 }
1638
1639 UINT cChangeIds = 0;
1640 UINT *pChangeIds = (UINT*)alloca(cIds * sizeof (*pChangeIds));
1641 if (!pChangeIds)
1642 {
1643 WARN(("VBoxTray: (WDDM) Failed to alloc change ids\n"));
1644 winEr = ERROR_OUTOFMEMORY;
1645 goto done;
1646 }
1647
1648 for (UINT i = 0; i < cIds; ++i)
1649 {
1650 UINT Id = pIds[i];
1651 bool fIsDup = false;
1652 for (UINT j = 0; j < cChangeIds; ++j)
1653 {
1654 if (pChangeIds[j] == Id)
1655 {
1656 fIsDup = true;
1657 break;
1658 }
1659 }
1660
1661 if (fIsDup)
1662 continue;
1663
1664 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id);
1665
1666 if (!((iPath >= 0) && (DispCfg.pPathInfoArray[iPath].flags & DISPLAYCONFIG_PATH_ACTIVE)) != !fEnabled)
1667 {
1668 pChangeIds[cChangeIds] = Id;
1669 ++cChangeIds;
1670 }
1671 }
1672
1673 if (cChangeIds == 0)
1674 {
1675 Log(("VBoxTray: (WDDM) vboxDispIfWddmEnableDisplay: settings are up to date\n"));
1676 winEr = ERROR_SUCCESS;
1677 goto done;
1678 }
1679
1680 /* we want to set primary for every disabled for non-topoly mode only */
1681 winEr = vboxDispIfWddmDcSettingsIncludeAllTargets(&DispCfg);
1682 if (winEr != ERROR_SUCCESS)
1683 {
1684 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsIncludeAllTargets winEr %d\n", winEr));
1685 return winEr;
1686 }
1687
1688 if (fSetTopology)
1689 vboxDispIfWddmDcSettingsInvalidateModeIndeces(&DispCfg);
1690
1691 for (UINT i = 0; i < cChangeIds; ++i)
1692 {
1693 UINT Id = pChangeIds[i];
1694 /* re-query paths */
1695 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, VBOX_WDDM_DC_SEARCH_PATH_ANY, Id);
1696 if (iPath < 0)
1697 {
1698 WARN(("VBoxTray: (WDDM) path index not found while it should"));
1699 winEr = ERROR_GEN_FAILURE;
1700 goto done;
1701 }
1702
1703 winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, pDeviceMode, !fEnabled || fSetTopology, fEnabled);
1704 if (winEr != ERROR_SUCCESS)
1705 {
1706 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsUpdate winEr %d\n", winEr));
1707 goto done;
1708 }
1709 }
1710
1711 if (!fSetTopology)
1712 vboxDispIfWddmDcSettingsAttachDisbledToPrimary(&DispCfg);
1713
1714#if 0
1715 /* ensure the zero-index (primary) screen is enabled */
1716 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, 0, 0);
1717 if (iPath < 0)
1718 {
1719 WARN(("VBoxTray: (WDDM) path index not found while it should"));
1720 winEr = ERROR_GEN_FAILURE;
1721 goto done;
1722 }
1723
1724 winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, /* just re-use device node here*/ pDeviceMode, fSetTopology, TRUE);
1725 if (winEr != ERROR_SUCCESS)
1726 {
1727 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsUpdate winEr %d\n", winEr));
1728 goto done;
1729 }
1730#endif
1731
1732 UINT fSetFlags = !fSetTopology ? (SDC_USE_SUPPLIED_DISPLAY_CONFIG) : (SDC_ALLOW_PATH_ORDER_CHANGES | SDC_TOPOLOGY_SUPPLIED);
1733 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
1734 if (winEr != ERROR_SUCCESS)
1735 {
1736 if (!fSetTopology)
1737 {
1738 WARN(("VBoxTray: (WDDM) vboxDispIfWddmDcSet validation failed winEr, trying with changes %d\n", winEr));
1739 fSetFlags |= SDC_ALLOW_CHANGES;
1740 }
1741 else
1742 {
1743 Log(("VBoxTray: (WDDM) vboxDispIfWddmDcSet topology validation failed winEr %d\n", winEr));
1744 goto done;
1745 }
1746 }
1747
1748 if (!fSetTopology)
1749 fSetFlags |= SDC_SAVE_TO_DATABASE;
1750
1751 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_APPLY);
1752 if (winEr != ERROR_SUCCESS)
1753 WARN(("VBoxTray: (WDDM) vboxDispIfWddmDcSet apply failed winEr %d\n", winEr));
1754
1755done:
1756 vboxDispIfWddmDcTerm(&DispCfg);
1757
1758 return winEr;
1759}
1760
1761static DWORD vboxDispIfWddmEnableDisplaysTryingTopology(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnable)
1762{
1763 DWORD winEr = vboxDispIfWddmEnableDisplays(pIf, cIds, pIds, fEnable, FALSE, NULL);
1764 if (winEr != ERROR_SUCCESS)
1765 {
1766 if (fEnable)
1767 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr));
1768 else
1769 Log(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr));
1770 winEr = vboxDispIfWddmEnableDisplays(pIf, cIds, pIds, fEnable, TRUE, NULL);
1771 if (winEr != ERROR_SUCCESS)
1772 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr));
1773 }
1774
1775 return winEr;
1776}
1777
1778BOOL VBoxDispIfResizeDisplayWin7(PCVBOXDISPIF const pIf, uint32_t cDispDef, const VMMDevDisplayDef *paDispDef)
1779{
1780 const VMMDevDisplayDef *pDispDef;
1781 uint32_t i;
1782
1783 VBOXDISPIF_OP Op;
1784 DWORD winEr = vboxDispIfOpBegin(pIf, &Op);
1785 if (winEr != ERROR_SUCCESS)
1786 {
1787 WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x", winEr));
1788 return (winEr == ERROR_SUCCESS);
1789 }
1790
1791 for (i = 0; i < cDispDef; ++i)
1792 {
1793 pDispDef = &paDispDef[i];
1794
1795 if (RT_BOOL(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1796 continue;
1797
1798 if ( RT_BOOL(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CX)
1799 && RT_BOOL(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CY))
1800 {
1801 RTRECTSIZE Size;
1802 Size.cx = pDispDef->cx;
1803 Size.cy = pDispDef->cy;
1804
1805 winEr = vboxDispIfUpdateModesWDDM(&Op, pDispDef->idDisplay, &Size);
1806 if (winEr != ERROR_SUCCESS)
1807 break;
1808 }
1809 }
1810
1811 vboxDispIfOpEnd(&Op);
1812
1813 if (winEr != ERROR_SUCCESS)
1814 return (winEr == ERROR_SUCCESS);
1815
1816 VBOXDISPIF_WDDM_DISPCFG DispCfg;
1817 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
1818 if (winEr != ERROR_SUCCESS)
1819 {
1820 WARN(("VBoxTray: vboxDispIfWddmDcCreate failed winEr 0x%x", winEr));
1821 return (winEr == ERROR_SUCCESS);
1822 }
1823
1824 for (i = 0; i < cDispDef; ++i)
1825 {
1826 pDispDef = &paDispDef[i];
1827
1828 /* Modify the path which the same source and target ids. */
1829 int const iPath = vboxDispIfWddmDcSearchPath(&DispCfg, pDispDef->idDisplay, pDispDef->idDisplay);
1830 if (iPath < 0)
1831 {
1832 WARN(("VBoxTray:(WDDM) Unexpected iPath(%d) between src(%d) and tgt(%d)\n", iPath, pDispDef->idDisplay, pDispDef->idDisplay));
1833 continue;
1834 }
1835
1836 /* If the source is used by another active path, then deactivate the path. */
1837 int const iActiveSrcPath = vboxDispIfWddmDcSearchActiveSourcePath(&DispCfg, pDispDef->idDisplay);
1838 if (iActiveSrcPath >= 0 && iActiveSrcPath != iPath)
1839 DispCfg.pPathInfoArray[iActiveSrcPath].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
1840
1841 DISPLAYCONFIG_PATH_INFO *pPathInfo = &DispCfg.pPathInfoArray[iPath];
1842
1843 if (!(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1844 {
1845 DISPLAYCONFIG_SOURCE_MODE *pSrcMode;
1846 DISPLAYCONFIG_TARGET_MODE *pTgtMode;
1847
1848 if (pPathInfo->flags & DISPLAYCONFIG_PATH_ACTIVE)
1849 {
1850 UINT iSrcMode = pPathInfo->sourceInfo.modeInfoIdx;
1851 UINT iTgtMode = pPathInfo->targetInfo.modeInfoIdx;
1852
1853 if (iSrcMode >= DispCfg.cModeInfoArray || iTgtMode >= DispCfg.cModeInfoArray)
1854 {
1855 WARN(("VBoxTray:(WDDM) Unexpected iSrcMode(%d) and/or iTgtMode(%d)\n", iSrcMode, iTgtMode));
1856 continue;
1857 }
1858
1859 pSrcMode = &DispCfg.pModeInfoArray[iSrcMode].sourceMode;
1860 pTgtMode = &DispCfg.pModeInfoArray[iTgtMode].targetMode;
1861
1862 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CX)
1863 {
1864 pSrcMode->width =
1865 pTgtMode->targetVideoSignalInfo.activeSize.cx =
1866 pTgtMode->targetVideoSignalInfo.totalSize.cx = pDispDef->cx;
1867 }
1868
1869 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CY)
1870 {
1871 pSrcMode->height =
1872 pTgtMode->targetVideoSignalInfo.activeSize.cy =
1873 pTgtMode->targetVideoSignalInfo.totalSize.cy = pDispDef->cy;
1874 }
1875
1876 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_ORIGIN)
1877 {
1878 pSrcMode->position.x = pDispDef->xOrigin;
1879 pSrcMode->position.y = pDispDef->yOrigin;
1880 }
1881
1882 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_BPP)
1883 {
1884 switch (pDispDef->cBitsPerPixel)
1885 {
1886 case 32:
1887 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1888 break;
1889 case 24:
1890 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
1891 break;
1892 case 16:
1893 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
1894 break;
1895 case 8:
1896 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
1897 break;
1898 default:
1899 WARN(("VBoxTray: (WDDM) invalid bpp %d, using 32bpp instead\n", pDispDef->cBitsPerPixel));
1900 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1901 break;
1902 }
1903 }
1904 }
1905 else
1906 {
1907 /* "The source and target modes for each source and target identifiers can only appear
1908 * in the modeInfoArray array once."
1909 * Try to find the source mode.
1910 */
1911 DISPLAYCONFIG_MODE_INFO *pSrcModeInfo = NULL;
1912 int iSrcModeInfo = -1;
1913 for (UINT j = 0; j < DispCfg.cModeInfoArray; ++j)
1914 {
1915 if ( DispCfg.pModeInfoArray[j].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE
1916 && DispCfg.pModeInfoArray[j].id == pDispDef->idDisplay)
1917 {
1918 pSrcModeInfo = &DispCfg.pModeInfoArray[j];
1919 iSrcModeInfo = (int)j;
1920 break;
1921 }
1922 }
1923
1924 if (pSrcModeInfo == NULL)
1925 {
1926 /* No mode yet. Add the new mode to the ModeInfo array. */
1927 DISPLAYCONFIG_MODE_INFO *paModeInfo = (DISPLAYCONFIG_MODE_INFO *)realloc(DispCfg.pModeInfoArray, (DispCfg.cModeInfoArray + 1) * sizeof(DISPLAYCONFIG_MODE_INFO));
1928 if (!paModeInfo)
1929 {
1930 WARN(("VBoxTray:(WDDM) Unable to re-allocate DispCfg.pModeInfoArray\n"));
1931 continue;
1932 }
1933
1934 DispCfg.pModeInfoArray = paModeInfo;
1935 DispCfg.cModeInfoArray += 1;
1936
1937 iSrcModeInfo = DispCfg.cModeInfoArray - 1;
1938 pSrcModeInfo = &DispCfg.pModeInfoArray[iSrcModeInfo];
1939 RT_ZERO(*pSrcModeInfo);
1940
1941 pSrcModeInfo->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
1942 pSrcModeInfo->id = pDispDef->idDisplay;
1943 pSrcModeInfo->adapterId = DispCfg.pModeInfoArray[0].adapterId;
1944 }
1945
1946 /* Update the source mode information. */
1947 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CX)
1948 {
1949 pSrcModeInfo->sourceMode.width = pDispDef->cx;
1950 }
1951
1952 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CY)
1953 {
1954 pSrcModeInfo->sourceMode.height = pDispDef->cy;
1955 }
1956
1957 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_BPP)
1958 {
1959 switch (pDispDef->cBitsPerPixel)
1960 {
1961 case 32:
1962 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1963 break;
1964 case 24:
1965 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
1966 break;
1967 case 16:
1968 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
1969 break;
1970 case 8:
1971 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
1972 break;
1973 default:
1974 WARN(("VBoxTray: (WDDM) invalid bpp %d, using 32bpp instead\n", pDispDef->cBitsPerPixel));
1975 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1976 break;
1977 }
1978 }
1979
1980 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_ORIGIN)
1981 {
1982 pSrcModeInfo->sourceMode.position.x = pDispDef->xOrigin;
1983 pSrcModeInfo->sourceMode.position.y = pDispDef->yOrigin;
1984 }
1985
1986 /* Configure the path information. */
1987 Assert(pPathInfo->sourceInfo.id == pDispDef->idDisplay);
1988 pPathInfo->sourceInfo.modeInfoIdx = iSrcModeInfo;
1989
1990 Assert(pPathInfo->targetInfo.id == pDispDef->idDisplay);
1991 /* "If the index value is DISPLAYCONFIG_PATH_MODE_IDX_INVALID ..., this indicates
1992 * the mode information is not being specified. It is valid for the path plus source mode ...
1993 * information to be specified for a given path."
1994 */
1995 pPathInfo->targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
1996 pPathInfo->targetInfo.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15;
1997 pPathInfo->targetInfo.rotation = DISPLAYCONFIG_ROTATION_IDENTITY;
1998 pPathInfo->targetInfo.scaling = DISPLAYCONFIG_SCALING_PREFERRED;
1999 /* "A refresh rate with both the numerator and denominator set to zero indicates that
2000 * the caller does not specify a refresh rate and the operating system should use
2001 * the most optimal refresh rate available. For this case, in a call to the SetDisplayConfig
2002 * function, the caller must set the scanLineOrdering member to the
2003 * DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED value; otherwise, SetDisplayConfig fails."
2004 *
2005 * If a refresh rate is set to a value, then the resize will fail if miniport driver
2006 * does not support VSync, i.e. with display-only driver on Win8+ (@bugref{8440}).
2007 */
2008 pPathInfo->targetInfo.refreshRate.Numerator = 0;
2009 pPathInfo->targetInfo.refreshRate.Denominator = 0;
2010 pPathInfo->targetInfo.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
2011 /* Make sure that "The output can be forced on this target even if a monitor is not detected." */
2012 pPathInfo->targetInfo.targetAvailable = TRUE;
2013 pPathInfo->targetInfo.statusFlags = DISPLAYCONFIG_TARGET_FORCIBLE;
2014 }
2015
2016 pPathInfo->flags |= DISPLAYCONFIG_PATH_ACTIVE;
2017 }
2018 else
2019 {
2020 pPathInfo->flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
2021 }
2022 }
2023
2024 UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
2025 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
2026 if (winEr != ERROR_SUCCESS)
2027 {
2028 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to VALIDATE winEr %d.\n", winEr));
2029 vboxDispIfWddmDcLogRel(&DispCfg, fSetFlags);
2030 fSetFlags |= SDC_ALLOW_CHANGES;
2031 }
2032
2033 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
2034 if (winEr != ERROR_SUCCESS)
2035 {
2036 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to SET, winEr %d.\n", winEr));
2037
2038 vboxDispIfWddmDcSettingsInvalidateModeIndeces(&DispCfg);
2039 winEr = vboxDispIfWddmDcSet(&DispCfg, SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES | SDC_APPLY);
2040 if (winEr != ERROR_SUCCESS)
2041 {
2042 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to APPLY TOPOLOGY ONLY, winEr %d.\n", winEr));
2043 winEr = vboxDispIfWddmDcSet(&DispCfg, SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_APPLY);
2044 if (winEr != ERROR_SUCCESS)
2045 {
2046 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to APPLY ANY TOPOLOGY, winEr %d.\n", winEr));
2047 }
2048 }
2049 }
2050
2051 vboxDispIfWddmDcTerm(&DispCfg);
2052
2053 return (winEr == ERROR_SUCCESS);
2054}
2055
2056static DWORD vboxDispIfWddmResizeDisplay2(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT devModes)
2057{
2058 RT_NOREF(pIf, paDeviceModes);
2059 VBOXDISPIF_WDDM_DISPCFG DispCfg;
2060 DWORD winEr = ERROR_SUCCESS;
2061 UINT idx;
2062 int iPath;
2063
2064 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
2065
2066 if (winEr != ERROR_SUCCESS)
2067 {
2068 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate\n"));
2069 return winEr;
2070 }
2071
2072 for (idx = 0; idx < devModes; idx++)
2073 {
2074 DEVMODE *pDeviceMode = &paDeviceModes[idx];
2075
2076 if (paDisplayDevices[idx].StateFlags & DISPLAY_DEVICE_ACTIVE)
2077 {
2078 DISPLAYCONFIG_PATH_INFO *pPathInfo;
2079
2080 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, idx, idx);
2081
2082 if (iPath < 0)
2083 {
2084 WARN(("VBoxTray:(WDDM) Unexpected iPath(%d) between src(%d) and tgt(%d)\n", iPath, idx, idx));
2085 continue;
2086 }
2087
2088 pPathInfo = &DispCfg.pPathInfoArray[iPath];
2089
2090 if (pPathInfo->flags & DISPLAYCONFIG_PATH_ACTIVE)
2091 {
2092 UINT iSrcMode, iTgtMode;
2093 DISPLAYCONFIG_SOURCE_MODE *pSrcMode;
2094 DISPLAYCONFIG_TARGET_MODE *pTgtMode;
2095
2096 iSrcMode = pPathInfo->sourceInfo.modeInfoIdx;
2097 iTgtMode = pPathInfo->targetInfo.modeInfoIdx;
2098
2099 if (iSrcMode >= DispCfg.cModeInfoArray || iTgtMode >= DispCfg.cModeInfoArray)
2100 {
2101 WARN(("VBoxTray:(WDDM) Unexpected iSrcMode(%d) and/or iTgtMode(%d)\n", iSrcMode, iTgtMode));
2102 continue;
2103 }
2104
2105 pSrcMode = &DispCfg.pModeInfoArray[iSrcMode].sourceMode;
2106 pTgtMode = &DispCfg.pModeInfoArray[iTgtMode].targetMode;
2107
2108 if (pDeviceMode->dmFields & DM_PELSWIDTH)
2109 {
2110 pSrcMode->width = pDeviceMode->dmPelsWidth;
2111 pTgtMode->targetVideoSignalInfo.activeSize.cx = pDeviceMode->dmPelsWidth;
2112 pTgtMode->targetVideoSignalInfo.totalSize.cx = pDeviceMode->dmPelsWidth;
2113 }
2114
2115 if (pDeviceMode->dmFields & DM_PELSHEIGHT)
2116 {
2117 pSrcMode->height = pDeviceMode->dmPelsHeight;
2118 pTgtMode->targetVideoSignalInfo.activeSize.cy = pDeviceMode->dmPelsHeight;
2119 pTgtMode->targetVideoSignalInfo.totalSize.cy = pDeviceMode->dmPelsHeight;
2120 }
2121
2122 if (pDeviceMode->dmFields & DM_POSITION)
2123 {
2124 pSrcMode->position.x = pDeviceMode->dmPosition.x;
2125 pSrcMode->position.y = pDeviceMode->dmPosition.y;
2126 }
2127
2128 if (pDeviceMode->dmFields & DM_BITSPERPEL)
2129 {
2130 switch (pDeviceMode->dmBitsPerPel)
2131 {
2132 case 32:
2133 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
2134 break;
2135 case 24:
2136 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
2137 break;
2138 case 16:
2139 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
2140 break;
2141 case 8:
2142 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
2143 break;
2144 default:
2145 LogRel(("VBoxTray: (WDDM) invalid bpp %d, using 32bpp instead\n", pDeviceMode->dmBitsPerPel));
2146 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
2147 break;
2148 }
2149 }
2150 }
2151 else
2152 {
2153 DISPLAYCONFIG_MODE_INFO *pModeInfo, *pModeInfoNew;
2154
2155 pModeInfo = (DISPLAYCONFIG_MODE_INFO *)realloc(DispCfg.pModeInfoArray, (DispCfg.cModeInfoArray + 2) * sizeof(DISPLAYCONFIG_MODE_INFO));
2156
2157 if (!pModeInfo)
2158 {
2159 WARN(("VBoxTray:(WDDM) Unable to re-allocate DispCfg.pModeInfoArray\n"));
2160 continue;
2161 }
2162
2163 DispCfg.pModeInfoArray = pModeInfo;
2164
2165 *pPathInfo = DispCfg.pPathInfoArray[0];
2166 pPathInfo->sourceInfo.id = idx;
2167 pPathInfo->targetInfo.id = idx;
2168
2169 pModeInfoNew = &pModeInfo[DispCfg.cModeInfoArray];
2170
2171 pModeInfoNew->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
2172 pModeInfoNew->id = idx;
2173 pModeInfoNew->adapterId = pModeInfo[0].adapterId;
2174 pModeInfoNew->sourceMode.width = pDeviceMode->dmPelsWidth;
2175 pModeInfoNew->sourceMode.height = pDeviceMode->dmPelsHeight;
2176 pModeInfoNew->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
2177 pModeInfoNew->sourceMode.position.x = pDeviceMode->dmPosition.x;
2178 pModeInfoNew->sourceMode.position.y = pDeviceMode->dmPosition.y;
2179 pPathInfo->sourceInfo.modeInfoIdx = DispCfg.cModeInfoArray;
2180
2181 pModeInfoNew++;
2182 pModeInfoNew->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
2183 pModeInfoNew->id = idx;
2184 pModeInfoNew->adapterId = pModeInfo[0].adapterId;
2185 pModeInfoNew->targetMode = pModeInfo[0].targetMode;
2186 pModeInfoNew->targetMode.targetVideoSignalInfo.activeSize.cx = pDeviceMode->dmPelsWidth;
2187 pModeInfoNew->targetMode.targetVideoSignalInfo.totalSize.cx = pDeviceMode->dmPelsWidth;
2188 pModeInfoNew->targetMode.targetVideoSignalInfo.activeSize.cy = pDeviceMode->dmPelsHeight;
2189 pModeInfoNew->targetMode.targetVideoSignalInfo.totalSize.cy = pDeviceMode->dmPelsHeight;
2190 pPathInfo->targetInfo.modeInfoIdx = DispCfg.cModeInfoArray + 1;
2191
2192 DispCfg.cModeInfoArray += 2;
2193 }
2194 }
2195 else
2196 {
2197 iPath = vboxDispIfWddmDcSearchActivePath(&DispCfg, idx, idx);
2198
2199 if (iPath >= 0)
2200 {
2201 DispCfg.pPathInfoArray[idx].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
2202 }
2203 }
2204 }
2205
2206 UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
2207 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
2208 if (winEr != ERROR_SUCCESS)
2209 {
2210 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2211 fSetFlags |= SDC_ALLOW_CHANGES;
2212 }
2213
2214 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
2215 if (winEr != ERROR_SUCCESS)
2216 {
2217 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2218 }
2219
2220 vboxDispIfWddmDcTerm(&DispCfg);
2221
2222 return winEr;
2223}
2224
2225static DWORD vboxDispIfWddmResizeDisplay(PCVBOXDISPIF const pIf, UINT Id, BOOL fEnable, DISPLAY_DEVICE *paDisplayDevices,
2226 DEVMODE *paDeviceModes, UINT devModes)
2227{
2228 RT_NOREF(paDisplayDevices, devModes);
2229 VBOXDISPIF_WDDM_DISPCFG DispCfg;
2230 DWORD winEr;
2231 int iPath;
2232
2233 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
2234 if (winEr != ERROR_SUCCESS)
2235 {
2236 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate\n"));
2237 return winEr;
2238 }
2239
2240 iPath = vboxDispIfWddmDcSearchActivePath(&DispCfg, Id, Id);
2241
2242 if (iPath < 0)
2243 {
2244 vboxDispIfWddmDcTerm(&DispCfg);
2245
2246 if (!fEnable)
2247 {
2248 /* nothing to be done here, just leave */
2249 return ERROR_SUCCESS;
2250 }
2251
2252 winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable);
2253 if (winEr != ERROR_SUCCESS)
2254 {
2255 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr));
2256 return winEr;
2257 }
2258
2259 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
2260 if (winEr != ERROR_SUCCESS)
2261 {
2262 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
2263 return winEr;
2264 }
2265
2266 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id);
2267 if (iPath < 0)
2268 {
2269 WARN(("VBoxTray: (WDDM) path (%d) is still disabled, going to retry winEr %d\n", winEr));
2270 vboxDispIfWddmDcTerm(&DispCfg);
2271 return ERROR_RETRY;
2272 }
2273 }
2274
2275 Assert(iPath >= 0);
2276
2277 if (!fEnable)
2278 {
2279 /* need to disable it, and we are done */
2280 vboxDispIfWddmDcTerm(&DispCfg);
2281
2282 winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable);
2283 if (winEr != ERROR_SUCCESS)
2284 {
2285 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr));
2286 return winEr;
2287 }
2288
2289 return winEr;
2290 }
2291
2292 Assert(fEnable);
2293
2294 winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, &paDeviceModes[Id], FALSE, fEnable);
2295 if (winEr != ERROR_SUCCESS)
2296 {
2297 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsUpdate\n"));
2298 vboxDispIfWddmDcTerm(&DispCfg);
2299 return winEr;
2300 }
2301
2302 UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
2303 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
2304 if (winEr != ERROR_SUCCESS)
2305 {
2306 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2307 fSetFlags |= SDC_ALLOW_CHANGES;
2308 }
2309
2310 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
2311 if (winEr != ERROR_SUCCESS)
2312 {
2313 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2314 }
2315
2316 vboxDispIfWddmDcTerm(&DispCfg);
2317
2318 return winEr;
2319}
2320
2321#endif /* VBOX_WITH_WDDM */
2322
2323DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
2324{
2325 switch (pIf->enmMode)
2326 {
2327 case VBOXDISPIF_MODE_XPDM_NT4:
2328 return ERROR_NOT_SUPPORTED;
2329 case VBOXDISPIF_MODE_XPDM:
2330 return ERROR_NOT_SUPPORTED;
2331#ifdef VBOX_WITH_WDDM
2332 case VBOXDISPIF_MODE_WDDM:
2333 case VBOXDISPIF_MODE_WDDM_W7:
2334 return vboxDispIfResizeModesWDDM(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
2335#endif
2336 default:
2337 WARN(("unknown mode (%d)\n", pIf->enmMode));
2338 return ERROR_INVALID_PARAMETER;
2339 }
2340}
2341
2342DWORD VBoxDispIfCancelPendingResize(PCVBOXDISPIF const pIf)
2343{
2344 switch (pIf->enmMode)
2345 {
2346 case VBOXDISPIF_MODE_XPDM_NT4:
2347 return NO_ERROR;
2348 case VBOXDISPIF_MODE_XPDM:
2349 return NO_ERROR;
2350#ifdef VBOX_WITH_WDDM
2351 case VBOXDISPIF_MODE_WDDM:
2352 case VBOXDISPIF_MODE_WDDM_W7:
2353 return vboxDispIfCancelPendingResizeWDDM(pIf);
2354#endif
2355 default:
2356 WARN(("unknown mode (%d)\n", pIf->enmMode));
2357 return ERROR_INVALID_PARAMETER;
2358 }
2359}
2360
2361static DWORD vboxDispIfConfigureTargetsWDDM(VBOXDISPIF_OP *pOp, uint32_t *pcConnected)
2362{
2363 VBOXDISPIFESCAPE EscapeHdr = {0};
2364 EscapeHdr.escapeCode = VBOXESC_CONFIGURETARGETS;
2365 EscapeHdr.u32CmdSpecific = 0;
2366
2367 D3DKMT_ESCAPE EscapeData = {0};
2368 EscapeData.hAdapter = pOp->Adapter.hAdapter;
2369#ifdef VBOX_DISPIF_WITH_OPCONTEXT
2370 /* win8.1 does not allow context-based escapes for display-only mode */
2371 EscapeData.hDevice = pOp->Device.hDevice;
2372 EscapeData.hContext = pOp->Context.hContext;
2373#endif
2374 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
2375 EscapeData.Flags.HardwareAccess = 1;
2376 EscapeData.pPrivateDriverData = &EscapeHdr;
2377 EscapeData.PrivateDriverDataSize = sizeof (EscapeHdr);
2378
2379 NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
2380 if (NT_SUCCESS(Status))
2381 {
2382 if (pcConnected)
2383 *pcConnected = EscapeHdr.u32CmdSpecific;
2384 return NO_ERROR;
2385 }
2386 WARN(("VBoxTray: pfnD3DKMTEscape VBOXESC_CONFIGURETARGETS failed Status 0x%x\n", Status));
2387 return Status;
2388}
2389
2390static DWORD vboxDispIfResizeStartedWDDMOp(VBOXDISPIF_OP *pOp)
2391{
2392 DWORD NumDevices = VBoxDisplayGetCount();
2393 if (NumDevices == 0)
2394 {
2395 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: Zero devices found\n"));
2396 return ERROR_GEN_FAILURE;
2397 }
2398
2399 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
2400 DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
2401 DWORD DevNum = 0;
2402 DWORD DevPrimaryNum = 0;
2403
2404 DWORD winEr = VBoxDisplayGetConfig(NumDevices, &DevPrimaryNum, &DevNum, paDisplayDevices, paDeviceModes);
2405 if (winEr != NO_ERROR)
2406 {
2407 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed, %d\n", winEr));
2408 return winEr;
2409 }
2410
2411 if (NumDevices != DevNum)
2412 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != DevNum(%d)\n", NumDevices, DevNum));
2413
2414
2415 uint32_t cConnected = 0;
2416 winEr = vboxDispIfConfigureTargetsWDDM(pOp, &cConnected);
2417 if (winEr != NO_ERROR)
2418 {
2419 WARN(("VBoxTray: vboxDispIfConfigureTargetsWDDM failed winEr 0x%x\n", winEr));
2420 return winEr;
2421 }
2422
2423 if (!cConnected)
2424 {
2425 Log(("VBoxTray: all targets already connected, nothing to do\n"));
2426 return NO_ERROR;
2427 }
2428
2429 winEr = vboxDispIfWaitDisplayDataInited(pOp);
2430 if (winEr != NO_ERROR)
2431 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: vboxDispIfWaitDisplayDataInited failed winEr 0x%x\n", winEr));
2432
2433 DWORD NewNumDevices = VBoxDisplayGetCount();
2434 if (NewNumDevices == 0)
2435 {
2436 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: Zero devices found\n"));
2437 return ERROR_GEN_FAILURE;
2438 }
2439
2440 if (NewNumDevices != NumDevices)
2441 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != NewNumDevices(%d)\n", NumDevices, NewNumDevices));
2442
2443 DISPLAY_DEVICE *paNewDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NewNumDevices);
2444 DEVMODE *paNewDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NewNumDevices);
2445 DWORD NewDevNum = 0;
2446 DWORD NewDevPrimaryNum = 0;
2447
2448 winEr = VBoxDisplayGetConfig(NewNumDevices, &NewDevPrimaryNum, &NewDevNum, paNewDisplayDevices, paNewDeviceModes);
2449 if (winEr != NO_ERROR)
2450 {
2451 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed for new devices, %d\n", winEr));
2452 return winEr;
2453 }
2454
2455 if (NewNumDevices != NewDevNum)
2456 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NewNumDevices(%d) != NewDevNum(%d)\n", NewNumDevices, NewDevNum));
2457
2458 DWORD minDevNum = RT_MIN(DevNum, NewDevNum);
2459 UINT *pIds = (UINT*)alloca (sizeof (UINT) * minDevNum);
2460 UINT cIds = 0;
2461 for (DWORD i = 0; i < minDevNum; ++i)
2462 {
2463 if ((paNewDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE)
2464 && !(paDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE))
2465 {
2466 pIds[cIds] = i;
2467 ++cIds;
2468 }
2469 }
2470
2471 if (!cIds)
2472 {
2473 /* this is something we would not regularly expect */
2474 WARN(("VBoxTray: all targets already have proper config, nothing to do\n"));
2475 return NO_ERROR;
2476 }
2477
2478 if (pOp->pIf->enmMode > VBOXDISPIF_MODE_WDDM)
2479 {
2480 winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pOp->pIf, cIds, pIds, FALSE);
2481 if (winEr != NO_ERROR)
2482 WARN(("VBoxTray: vboxDispIfWddmEnableDisplaysTryingTopology failed to record current settings, %d, ignoring\n", winEr));
2483 }
2484 else
2485 {
2486 for (DWORD i = 0; i < cIds; ++i)
2487 {
2488 winEr = vboxDispIfWddmResizeDisplayVista(paNewDeviceModes, paNewDisplayDevices, NewDevNum, i, FALSE, TRUE);
2489 if (winEr != NO_ERROR)
2490 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: vboxDispIfWddmResizeDisplayVista failed winEr 0x%x\n", winEr));
2491 }
2492 }
2493
2494 return winEr;
2495}
2496
2497
2498static DWORD vboxDispIfResizeStartedWDDM(PCVBOXDISPIF const pIf)
2499{
2500 VBOXDISPIF_OP Op;
2501
2502 DWORD winEr = vboxDispIfOpBegin(pIf, &Op);
2503 if (winEr != NO_ERROR)
2504 {
2505 WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x\n", winEr));
2506 return winEr;
2507 }
2508
2509 winEr = vboxDispIfResizeStartedWDDMOp(&Op);
2510 if (winEr != NO_ERROR)
2511 {
2512 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp failed winEr 0x%x\n", winEr));
2513 }
2514
2515 vboxDispIfOpEnd(&Op);
2516
2517 return winEr;
2518}
2519
2520DWORD VBoxDispIfResizeStarted(PCVBOXDISPIF const pIf)
2521{
2522 switch (pIf->enmMode)
2523 {
2524 case VBOXDISPIF_MODE_XPDM_NT4:
2525 return NO_ERROR;
2526 case VBOXDISPIF_MODE_XPDM:
2527 return NO_ERROR;
2528#ifdef VBOX_WITH_WDDM
2529 case VBOXDISPIF_MODE_WDDM:
2530 case VBOXDISPIF_MODE_WDDM_W7:
2531 return vboxDispIfResizeStartedWDDM(pIf);
2532#endif
2533 default:
2534 WARN(("unknown mode (%d)\n", pIf->enmMode));
2535 return ERROR_INVALID_PARAMETER;
2536 }
2537}
2538
2539static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
2540{
2541 RT_NOREF(pIf);
2542 return NO_ERROR;
2543}
2544
2545static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
2546{
2547 DWORD err = NO_ERROR;
2548
2549 uint64_t const uNtVersion = RTSystemGetNtVersion();
2550 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0))
2551 {
2552 HMODULE hUser = GetModuleHandle("user32.dll");
2553 if (NULL != hUser)
2554 {
2555 *(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
2556 LogFunc(("pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
2557 bool const fSupported = RT_BOOL(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
2558 if (!fSupported)
2559 {
2560 WARN(("pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
2561 err = ERROR_NOT_SUPPORTED;
2562 }
2563 }
2564 else
2565 {
2566 WARN(("failed to get USER32 handle, err (%d)\n", GetLastError()));
2567 err = ERROR_NOT_SUPPORTED;
2568 }
2569 }
2570 else
2571 {
2572 WARN(("can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
2573 err = ERROR_NOT_SUPPORTED;
2574 }
2575
2576 return err;
2577}
2578
2579DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
2580{
2581 /** @todo may need to addd synchronization in case we want to change modes dynamically
2582 * i.e. currently the mode is supposed to be initialized once on service initialization */
2583 if (penmOldMode)
2584 *penmOldMode = pIf->enmMode;
2585
2586 if (enmMode == pIf->enmMode)
2587 return NO_ERROR;
2588
2589#ifdef VBOX_WITH_WDDM
2590 if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM)
2591 {
2592 vboxDispIfWddmTerm(pIf);
2593
2594 vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks);
2595 }
2596#endif
2597
2598 DWORD err = NO_ERROR;
2599 switch (enmMode)
2600 {
2601 case VBOXDISPIF_MODE_XPDM_NT4:
2602 LogFunc(("request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
2603 err = vboxDispIfSwitchToXPDM_NT4(pIf);
2604 if (err == NO_ERROR)
2605 {
2606 LogFunc(("successfully switched to XPDM_NT4 mode\n"));
2607 pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
2608 }
2609 else
2610 WARN(("failed to switch to XPDM_NT4 mode, err (%d)\n", err));
2611 break;
2612 case VBOXDISPIF_MODE_XPDM:
2613 LogFunc(("request to switch to VBOXDISPIF_MODE_XPDM\n"));
2614 err = vboxDispIfSwitchToXPDM(pIf);
2615 if (err == NO_ERROR)
2616 {
2617 LogFunc(("successfully switched to XPDM mode\n"));
2618 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
2619 }
2620 else
2621 WARN(("failed to switch to XPDM mode, err (%d)\n", err));
2622 break;
2623#ifdef VBOX_WITH_WDDM
2624 case VBOXDISPIF_MODE_WDDM:
2625 {
2626 LogFunc(("request to switch to VBOXDISPIF_MODE_WDDM\n"));
2627 err = vboxDispIfSwitchToWDDM(pIf);
2628 if (err == NO_ERROR)
2629 {
2630 LogFunc(("successfully switched to WDDM mode\n"));
2631 pIf->enmMode = VBOXDISPIF_MODE_WDDM;
2632 }
2633 else
2634 WARN(("failed to switch to WDDM mode, err (%d)\n", err));
2635 break;
2636 }
2637 case VBOXDISPIF_MODE_WDDM_W7:
2638 {
2639 LogFunc(("request to switch to VBOXDISPIF_MODE_WDDM_W7\n"));
2640 err = vboxDispIfSwitchToWDDM_W7(pIf);
2641 if (err == NO_ERROR)
2642 {
2643 LogFunc(("successfully switched to WDDM mode\n"));
2644 pIf->enmMode = VBOXDISPIF_MODE_WDDM_W7;
2645 }
2646 else
2647 WARN(("failed to switch to WDDM mode, err (%d)\n", err));
2648 break;
2649 }
2650#endif
2651 default:
2652 err = ERROR_INVALID_PARAMETER;
2653 break;
2654 }
2655 return err;
2656}
2657
2658static DWORD vboxDispIfSeamlessCreateWDDM(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent)
2659{
2660 RT_NOREF(hEvent);
2661 HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &pSeamless->modeData.wddm.Adapter);
2662 if (SUCCEEDED(hr))
2663 {
2664#ifndef VBOX_DISPIF_WITH_OPCONTEXT
2665 return ERROR_SUCCESS;
2666#else
2667 hr = vboxDispKmtCreateDevice(&pSeamless->modeData.wddm.Adapter, &pSeamless->modeData.wddm.Device);
2668 if (SUCCEEDED(hr))
2669 {
2670 hr = vboxDispKmtCreateContext(&pSeamless->modeData.wddm.Device, &pSeamless->modeData.wddm.Context, VBOXWDDM_CONTEXT_TYPE_CUSTOM_DISPIF_SEAMLESS,
2671 hEvent, 0ULL);
2672 if (SUCCEEDED(hr))
2673 return ERROR_SUCCESS;
2674 WARN(("VBoxTray: vboxDispKmtCreateContext failed hr 0x%x", hr));
2675
2676 vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device);
2677 }
2678 else
2679 WARN(("VBoxTray: vboxDispKmtCreateDevice failed hr 0x%x", hr));
2680
2681 vboxDispKmtCloseAdapter(&pSeamless->modeData.wddm.Adapter);
2682#endif /* VBOX_DISPIF_WITH_OPCONTEXT */
2683 }
2684
2685 return hr;
2686}
2687
2688static DWORD vboxDispIfSeamlessTermWDDM(VBOXDISPIF_SEAMLESS *pSeamless)
2689{
2690#ifdef VBOX_DISPIF_WITH_OPCONTEXT
2691 vboxDispKmtDestroyContext(&pSeamless->modeData.wddm.Context);
2692 vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device);
2693#endif
2694 vboxDispKmtCloseAdapter(&pSeamless->modeData.wddm.Adapter);
2695
2696 return NO_ERROR;
2697}
2698
2699static DWORD vboxDispIfSeamlessSubmitWDDM(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData)
2700{
2701 D3DKMT_ESCAPE EscapeData = {0};
2702 EscapeData.hAdapter = pSeamless->modeData.wddm.Adapter.hAdapter;
2703#ifdef VBOX_DISPIF_WITH_OPCONTEXT
2704 EscapeData.hDevice = pSeamless->modeData.wddm.Device.hDevice;
2705 EscapeData.hContext = pSeamless->modeData.wddm.Context.hContext;
2706#endif
2707 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
2708 /*EscapeData.Flags.HardwareAccess = 1;*/
2709 EscapeData.pPrivateDriverData = pData;
2710 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(cbData);
2711
2712 NTSTATUS Status = pSeamless->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
2713 if (NT_SUCCESS(Status))
2714 return ERROR_SUCCESS;
2715
2716 WARN(("VBoxTray: pfnD3DKMTEscape Seamless failed Status 0x%x\n", Status));
2717 return Status;
2718}
2719
2720DWORD VBoxDispIfSeamlessCreate(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent)
2721{
2722 memset(pSeamless, 0, sizeof (*pSeamless));
2723 pSeamless->pIf = pIf;
2724
2725 switch (pIf->enmMode)
2726 {
2727 case VBOXDISPIF_MODE_XPDM_NT4:
2728 case VBOXDISPIF_MODE_XPDM:
2729 return NO_ERROR;
2730#ifdef VBOX_WITH_WDDM
2731 case VBOXDISPIF_MODE_WDDM:
2732 case VBOXDISPIF_MODE_WDDM_W7:
2733 return vboxDispIfSeamlessCreateWDDM(pIf, pSeamless, hEvent);
2734#endif
2735 default:
2736 break;
2737 }
2738
2739 WARN(("VBoxTray: VBoxDispIfSeamlessCreate: invalid mode %d\n", pIf->enmMode));
2740 return ERROR_INVALID_PARAMETER;
2741}
2742
2743DWORD VBoxDispIfSeamlessTerm(VBOXDISPIF_SEAMLESS *pSeamless)
2744{
2745 PCVBOXDISPIF const pIf = pSeamless->pIf;
2746 DWORD winEr;
2747 switch (pIf->enmMode)
2748 {
2749 case VBOXDISPIF_MODE_XPDM_NT4:
2750 case VBOXDISPIF_MODE_XPDM:
2751 winEr = NO_ERROR;
2752 break;
2753#ifdef VBOX_WITH_WDDM
2754 case VBOXDISPIF_MODE_WDDM:
2755 case VBOXDISPIF_MODE_WDDM_W7:
2756 winEr = vboxDispIfSeamlessTermWDDM(pSeamless);
2757 break;
2758#endif
2759 default:
2760 WARN(("VBoxTray: VBoxDispIfSeamlessTerm: invalid mode %d\n", pIf->enmMode));
2761 winEr = ERROR_INVALID_PARAMETER;
2762 break;
2763 }
2764
2765 if (winEr == NO_ERROR)
2766 memset(pSeamless, 0, sizeof (*pSeamless));
2767
2768 return winEr;
2769}
2770
2771DWORD VBoxDispIfSeamlessSubmit(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData)
2772{
2773 PCVBOXDISPIF const pIf = pSeamless->pIf;
2774
2775 if (pData->escapeCode != VBOXESC_SETVISIBLEREGION)
2776 {
2777 WARN(("VBoxTray: invalid escape code for Seamless submit %d\n", pData->escapeCode));
2778 return ERROR_INVALID_PARAMETER;
2779 }
2780
2781 switch (pIf->enmMode)
2782 {
2783 case VBOXDISPIF_MODE_XPDM_NT4:
2784 case VBOXDISPIF_MODE_XPDM:
2785 return VBoxDispIfEscape(pIf, pData, cbData);
2786#ifdef VBOX_WITH_WDDM
2787 case VBOXDISPIF_MODE_WDDM:
2788 case VBOXDISPIF_MODE_WDDM_W7:
2789 return vboxDispIfSeamlessSubmitWDDM(pSeamless, pData, cbData);
2790#endif
2791 default:
2792 WARN(("VBoxTray: VBoxDispIfSeamlessSubmit: invalid mode %d\n", pIf->enmMode));
2793 return ERROR_INVALID_PARAMETER;
2794 }
2795}
2796
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