VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 97.7 KB
Line 
1/* $Id: VBoxDispIf.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VBoxTray - Display Settings Interface abstraction for XPDM & WDDM
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.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#define _WIN32_WINNT 0x0601
23#include "VBoxTray.h"
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 /* SetDisplayConfig assumes the top-left corner of a primary display at (0, 0) position */
1784 const VMMDevDisplayDef* pDispDefPrimary = NULL;
1785
1786 for (i = 0; i < cDispDef; ++i)
1787 {
1788 pDispDef = &paDispDef[i];
1789
1790 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_PRIMARY)
1791 {
1792 pDispDefPrimary = pDispDef;
1793 break;
1794 }
1795 }
1796
1797 VBOXDISPIF_OP Op;
1798 DWORD winEr = vboxDispIfOpBegin(pIf, &Op);
1799 if (winEr != ERROR_SUCCESS)
1800 {
1801 WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x", winEr));
1802 return (winEr == ERROR_SUCCESS);
1803 }
1804
1805 for (i = 0; i < cDispDef; ++i)
1806 {
1807 pDispDef = &paDispDef[i];
1808
1809 if (RT_BOOL(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1810 continue;
1811
1812 if ( RT_BOOL(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CX)
1813 && RT_BOOL(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CY))
1814 {
1815 RTRECTSIZE Size;
1816 Size.cx = pDispDef->cx;
1817 Size.cy = pDispDef->cy;
1818
1819 winEr = vboxDispIfUpdateModesWDDM(&Op, pDispDef->idDisplay, &Size);
1820 if (winEr != ERROR_SUCCESS)
1821 break;
1822 }
1823 }
1824
1825 vboxDispIfOpEnd(&Op);
1826
1827 if (winEr != ERROR_SUCCESS)
1828 return (winEr == ERROR_SUCCESS);
1829
1830 VBOXDISPIF_WDDM_DISPCFG DispCfg;
1831 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
1832 if (winEr != ERROR_SUCCESS)
1833 {
1834 WARN(("VBoxTray: vboxDispIfWddmDcCreate failed winEr 0x%x", winEr));
1835 return (winEr == ERROR_SUCCESS);
1836 }
1837
1838 for (i = 0; i < cDispDef; ++i)
1839 {
1840 pDispDef = &paDispDef[i];
1841
1842 /* Modify the path which the same source and target ids. */
1843 int const iPath = vboxDispIfWddmDcSearchPath(&DispCfg, pDispDef->idDisplay, pDispDef->idDisplay);
1844 if (iPath < 0)
1845 {
1846 WARN(("VBoxTray:(WDDM) Unexpected iPath(%d) between src(%d) and tgt(%d)\n", iPath, pDispDef->idDisplay, pDispDef->idDisplay));
1847 continue;
1848 }
1849
1850 /* If the source is used by another active path, then deactivate the path. */
1851 int const iActiveSrcPath = vboxDispIfWddmDcSearchActiveSourcePath(&DispCfg, pDispDef->idDisplay);
1852 if (iActiveSrcPath >= 0 && iActiveSrcPath != iPath)
1853 DispCfg.pPathInfoArray[iActiveSrcPath].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
1854
1855 DISPLAYCONFIG_PATH_INFO *pPathInfo = &DispCfg.pPathInfoArray[iPath];
1856
1857 if (!(pDispDef->fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
1858 {
1859 DISPLAYCONFIG_SOURCE_MODE *pSrcMode;
1860 DISPLAYCONFIG_TARGET_MODE *pTgtMode;
1861
1862 if (pPathInfo->flags & DISPLAYCONFIG_PATH_ACTIVE)
1863 {
1864 UINT iSrcMode = pPathInfo->sourceInfo.modeInfoIdx;
1865 UINT iTgtMode = pPathInfo->targetInfo.modeInfoIdx;
1866
1867 if (iSrcMode >= DispCfg.cModeInfoArray || iTgtMode >= DispCfg.cModeInfoArray)
1868 {
1869 WARN(("VBoxTray:(WDDM) Unexpected iSrcMode(%d) and/or iTgtMode(%d)\n", iSrcMode, iTgtMode));
1870 continue;
1871 }
1872
1873 pSrcMode = &DispCfg.pModeInfoArray[iSrcMode].sourceMode;
1874 pTgtMode = &DispCfg.pModeInfoArray[iTgtMode].targetMode;
1875
1876 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CX)
1877 {
1878 pSrcMode->width =
1879 pTgtMode->targetVideoSignalInfo.activeSize.cx =
1880 pTgtMode->targetVideoSignalInfo.totalSize.cx = pDispDef->cx;
1881 }
1882
1883 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CY)
1884 {
1885 pSrcMode->height =
1886 pTgtMode->targetVideoSignalInfo.activeSize.cy =
1887 pTgtMode->targetVideoSignalInfo.totalSize.cy = pDispDef->cy;
1888 }
1889
1890 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_ORIGIN)
1891 {
1892 pSrcMode->position.x = pDispDef->xOrigin - (pDispDefPrimary ? pDispDefPrimary->xOrigin : 0);
1893 pSrcMode->position.y = pDispDef->yOrigin - (pDispDefPrimary ? pDispDefPrimary->yOrigin : 0);
1894 }
1895
1896 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_BPP)
1897 {
1898 switch (pDispDef->cBitsPerPixel)
1899 {
1900 case 32:
1901 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1902 break;
1903 case 24:
1904 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
1905 break;
1906 case 16:
1907 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
1908 break;
1909 case 8:
1910 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
1911 break;
1912 default:
1913 WARN(("VBoxTray: (WDDM) invalid bpp %d, using 32bpp instead\n", pDispDef->cBitsPerPixel));
1914 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1915 break;
1916 }
1917 }
1918 }
1919 else
1920 {
1921 /* "The source and target modes for each source and target identifiers can only appear
1922 * in the modeInfoArray array once."
1923 * Try to find the source mode.
1924 */
1925 DISPLAYCONFIG_MODE_INFO *pSrcModeInfo = NULL;
1926 int iSrcModeInfo = -1;
1927 for (UINT j = 0; j < DispCfg.cModeInfoArray; ++j)
1928 {
1929 if ( DispCfg.pModeInfoArray[j].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE
1930 && DispCfg.pModeInfoArray[j].id == pDispDef->idDisplay)
1931 {
1932 pSrcModeInfo = &DispCfg.pModeInfoArray[j];
1933 iSrcModeInfo = (int)j;
1934 break;
1935 }
1936 }
1937
1938 if (pSrcModeInfo == NULL)
1939 {
1940 /* No mode yet. Add the new mode to the ModeInfo array. */
1941 DISPLAYCONFIG_MODE_INFO *paModeInfo = (DISPLAYCONFIG_MODE_INFO *)realloc(DispCfg.pModeInfoArray, (DispCfg.cModeInfoArray + 1) * sizeof(DISPLAYCONFIG_MODE_INFO));
1942 if (!paModeInfo)
1943 {
1944 WARN(("VBoxTray:(WDDM) Unable to re-allocate DispCfg.pModeInfoArray\n"));
1945 continue;
1946 }
1947
1948 DispCfg.pModeInfoArray = paModeInfo;
1949 DispCfg.cModeInfoArray += 1;
1950
1951 iSrcModeInfo = DispCfg.cModeInfoArray - 1;
1952 pSrcModeInfo = &DispCfg.pModeInfoArray[iSrcModeInfo];
1953 RT_ZERO(*pSrcModeInfo);
1954
1955 pSrcModeInfo->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
1956 pSrcModeInfo->id = pDispDef->idDisplay;
1957 pSrcModeInfo->adapterId = DispCfg.pModeInfoArray[0].adapterId;
1958 }
1959
1960 /* Update the source mode information. */
1961 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CX)
1962 {
1963 pSrcModeInfo->sourceMode.width = pDispDef->cx;
1964 }
1965
1966 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_CY)
1967 {
1968 pSrcModeInfo->sourceMode.height = pDispDef->cy;
1969 }
1970
1971 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_BPP)
1972 {
1973 switch (pDispDef->cBitsPerPixel)
1974 {
1975 case 32:
1976 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1977 break;
1978 case 24:
1979 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
1980 break;
1981 case 16:
1982 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
1983 break;
1984 case 8:
1985 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
1986 break;
1987 default:
1988 WARN(("VBoxTray: (WDDM) invalid bpp %d, using 32bpp instead\n", pDispDef->cBitsPerPixel));
1989 pSrcModeInfo->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
1990 break;
1991 }
1992 }
1993
1994 if (pDispDef->fDisplayFlags & VMMDEV_DISPLAY_ORIGIN)
1995 {
1996 pSrcModeInfo->sourceMode.position.x = pDispDef->xOrigin - (pDispDefPrimary ? pDispDefPrimary->xOrigin : 0);
1997 pSrcModeInfo->sourceMode.position.y = pDispDef->yOrigin - (pDispDefPrimary ? pDispDefPrimary->yOrigin : 0);
1998 }
1999
2000 /* Configure the path information. */
2001 Assert(pPathInfo->sourceInfo.id == pDispDef->idDisplay);
2002 pPathInfo->sourceInfo.modeInfoIdx = iSrcModeInfo;
2003
2004 Assert(pPathInfo->targetInfo.id == pDispDef->idDisplay);
2005 /* "If the index value is DISPLAYCONFIG_PATH_MODE_IDX_INVALID ..., this indicates
2006 * the mode information is not being specified. It is valid for the path plus source mode ...
2007 * information to be specified for a given path."
2008 */
2009 pPathInfo->targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
2010 pPathInfo->targetInfo.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15;
2011 pPathInfo->targetInfo.rotation = DISPLAYCONFIG_ROTATION_IDENTITY;
2012 pPathInfo->targetInfo.scaling = DISPLAYCONFIG_SCALING_PREFERRED;
2013 /* "A refresh rate with both the numerator and denominator set to zero indicates that
2014 * the caller does not specify a refresh rate and the operating system should use
2015 * the most optimal refresh rate available. For this case, in a call to the SetDisplayConfig
2016 * function, the caller must set the scanLineOrdering member to the
2017 * DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED value; otherwise, SetDisplayConfig fails."
2018 *
2019 * If a refresh rate is set to a value, then the resize will fail if miniport driver
2020 * does not support VSync, i.e. with display-only driver on Win8+ (@bugref{8440}).
2021 */
2022 pPathInfo->targetInfo.refreshRate.Numerator = 0;
2023 pPathInfo->targetInfo.refreshRate.Denominator = 0;
2024 pPathInfo->targetInfo.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
2025 /* Make sure that "The output can be forced on this target even if a monitor is not detected." */
2026 pPathInfo->targetInfo.targetAvailable = TRUE;
2027 pPathInfo->targetInfo.statusFlags = DISPLAYCONFIG_TARGET_FORCIBLE;
2028 }
2029
2030 pPathInfo->flags |= DISPLAYCONFIG_PATH_ACTIVE;
2031 }
2032 else
2033 {
2034 pPathInfo->flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
2035 }
2036 }
2037
2038 UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
2039 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
2040 if (winEr != ERROR_SUCCESS)
2041 {
2042 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to VALIDATE winEr %d.\n", winEr));
2043 vboxDispIfWddmDcLogRel(&DispCfg, fSetFlags);
2044 fSetFlags |= SDC_ALLOW_CHANGES;
2045 }
2046
2047 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
2048 if (winEr != ERROR_SUCCESS)
2049 {
2050 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to SET, winEr %d.\n", winEr));
2051
2052 vboxDispIfWddmDcSettingsInvalidateModeIndeces(&DispCfg);
2053 winEr = vboxDispIfWddmDcSet(&DispCfg, SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES | SDC_APPLY);
2054 if (winEr != ERROR_SUCCESS)
2055 {
2056 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to APPLY TOPOLOGY ONLY, winEr %d.\n", winEr));
2057 winEr = vboxDispIfWddmDcSet(&DispCfg, SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_APPLY);
2058 if (winEr != ERROR_SUCCESS)
2059 {
2060 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to APPLY ANY TOPOLOGY, winEr %d.\n", winEr));
2061 }
2062 }
2063 }
2064
2065 vboxDispIfWddmDcTerm(&DispCfg);
2066
2067 return (winEr == ERROR_SUCCESS);
2068}
2069
2070static DWORD vboxDispIfWddmResizeDisplay2(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT devModes)
2071{
2072 RT_NOREF(pIf, paDeviceModes);
2073 VBOXDISPIF_WDDM_DISPCFG DispCfg;
2074 DWORD winEr = ERROR_SUCCESS;
2075 UINT idx;
2076 int iPath;
2077
2078 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
2079
2080 if (winEr != ERROR_SUCCESS)
2081 {
2082 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate\n"));
2083 return winEr;
2084 }
2085
2086 for (idx = 0; idx < devModes; idx++)
2087 {
2088 DEVMODE *pDeviceMode = &paDeviceModes[idx];
2089
2090 if (paDisplayDevices[idx].StateFlags & DISPLAY_DEVICE_ACTIVE)
2091 {
2092 DISPLAYCONFIG_PATH_INFO *pPathInfo;
2093
2094 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, idx, idx);
2095
2096 if (iPath < 0)
2097 {
2098 WARN(("VBoxTray:(WDDM) Unexpected iPath(%d) between src(%d) and tgt(%d)\n", iPath, idx, idx));
2099 continue;
2100 }
2101
2102 pPathInfo = &DispCfg.pPathInfoArray[iPath];
2103
2104 if (pPathInfo->flags & DISPLAYCONFIG_PATH_ACTIVE)
2105 {
2106 UINT iSrcMode, iTgtMode;
2107 DISPLAYCONFIG_SOURCE_MODE *pSrcMode;
2108 DISPLAYCONFIG_TARGET_MODE *pTgtMode;
2109
2110 iSrcMode = pPathInfo->sourceInfo.modeInfoIdx;
2111 iTgtMode = pPathInfo->targetInfo.modeInfoIdx;
2112
2113 if (iSrcMode >= DispCfg.cModeInfoArray || iTgtMode >= DispCfg.cModeInfoArray)
2114 {
2115 WARN(("VBoxTray:(WDDM) Unexpected iSrcMode(%d) and/or iTgtMode(%d)\n", iSrcMode, iTgtMode));
2116 continue;
2117 }
2118
2119 pSrcMode = &DispCfg.pModeInfoArray[iSrcMode].sourceMode;
2120 pTgtMode = &DispCfg.pModeInfoArray[iTgtMode].targetMode;
2121
2122 if (pDeviceMode->dmFields & DM_PELSWIDTH)
2123 {
2124 pSrcMode->width = pDeviceMode->dmPelsWidth;
2125 pTgtMode->targetVideoSignalInfo.activeSize.cx = pDeviceMode->dmPelsWidth;
2126 pTgtMode->targetVideoSignalInfo.totalSize.cx = pDeviceMode->dmPelsWidth;
2127 }
2128
2129 if (pDeviceMode->dmFields & DM_PELSHEIGHT)
2130 {
2131 pSrcMode->height = pDeviceMode->dmPelsHeight;
2132 pTgtMode->targetVideoSignalInfo.activeSize.cy = pDeviceMode->dmPelsHeight;
2133 pTgtMode->targetVideoSignalInfo.totalSize.cy = pDeviceMode->dmPelsHeight;
2134 }
2135
2136 if (pDeviceMode->dmFields & DM_POSITION)
2137 {
2138 pSrcMode->position.x = pDeviceMode->dmPosition.x;
2139 pSrcMode->position.y = pDeviceMode->dmPosition.y;
2140 }
2141
2142 if (pDeviceMode->dmFields & DM_BITSPERPEL)
2143 {
2144 switch (pDeviceMode->dmBitsPerPel)
2145 {
2146 case 32:
2147 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
2148 break;
2149 case 24:
2150 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
2151 break;
2152 case 16:
2153 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
2154 break;
2155 case 8:
2156 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
2157 break;
2158 default:
2159 LogRel(("VBoxTray: (WDDM) invalid bpp %d, using 32bpp instead\n", pDeviceMode->dmBitsPerPel));
2160 pSrcMode->pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
2161 break;
2162 }
2163 }
2164 }
2165 else
2166 {
2167 DISPLAYCONFIG_MODE_INFO *pModeInfo, *pModeInfoNew;
2168
2169 pModeInfo = (DISPLAYCONFIG_MODE_INFO *)realloc(DispCfg.pModeInfoArray, (DispCfg.cModeInfoArray + 2) * sizeof(DISPLAYCONFIG_MODE_INFO));
2170
2171 if (!pModeInfo)
2172 {
2173 WARN(("VBoxTray:(WDDM) Unable to re-allocate DispCfg.pModeInfoArray\n"));
2174 continue;
2175 }
2176
2177 DispCfg.pModeInfoArray = pModeInfo;
2178
2179 *pPathInfo = DispCfg.pPathInfoArray[0];
2180 pPathInfo->sourceInfo.id = idx;
2181 pPathInfo->targetInfo.id = idx;
2182
2183 pModeInfoNew = &pModeInfo[DispCfg.cModeInfoArray];
2184
2185 pModeInfoNew->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE;
2186 pModeInfoNew->id = idx;
2187 pModeInfoNew->adapterId = pModeInfo[0].adapterId;
2188 pModeInfoNew->sourceMode.width = pDeviceMode->dmPelsWidth;
2189 pModeInfoNew->sourceMode.height = pDeviceMode->dmPelsHeight;
2190 pModeInfoNew->sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
2191 pModeInfoNew->sourceMode.position.x = pDeviceMode->dmPosition.x;
2192 pModeInfoNew->sourceMode.position.y = pDeviceMode->dmPosition.y;
2193 pPathInfo->sourceInfo.modeInfoIdx = DispCfg.cModeInfoArray;
2194
2195 pModeInfoNew++;
2196 pModeInfoNew->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_TARGET;
2197 pModeInfoNew->id = idx;
2198 pModeInfoNew->adapterId = pModeInfo[0].adapterId;
2199 pModeInfoNew->targetMode = pModeInfo[0].targetMode;
2200 pModeInfoNew->targetMode.targetVideoSignalInfo.activeSize.cx = pDeviceMode->dmPelsWidth;
2201 pModeInfoNew->targetMode.targetVideoSignalInfo.totalSize.cx = pDeviceMode->dmPelsWidth;
2202 pModeInfoNew->targetMode.targetVideoSignalInfo.activeSize.cy = pDeviceMode->dmPelsHeight;
2203 pModeInfoNew->targetMode.targetVideoSignalInfo.totalSize.cy = pDeviceMode->dmPelsHeight;
2204 pPathInfo->targetInfo.modeInfoIdx = DispCfg.cModeInfoArray + 1;
2205
2206 DispCfg.cModeInfoArray += 2;
2207 }
2208 }
2209 else
2210 {
2211 iPath = vboxDispIfWddmDcSearchActivePath(&DispCfg, idx, idx);
2212
2213 if (iPath >= 0)
2214 {
2215 DispCfg.pPathInfoArray[idx].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
2216 }
2217 }
2218 }
2219
2220 UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
2221 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
2222 if (winEr != ERROR_SUCCESS)
2223 {
2224 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2225 fSetFlags |= SDC_ALLOW_CHANGES;
2226 }
2227
2228 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
2229 if (winEr != ERROR_SUCCESS)
2230 {
2231 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2232 }
2233
2234 vboxDispIfWddmDcTerm(&DispCfg);
2235
2236 return winEr;
2237}
2238
2239static DWORD vboxDispIfWddmResizeDisplay(PCVBOXDISPIF const pIf, UINT Id, BOOL fEnable, DISPLAY_DEVICE *paDisplayDevices,
2240 DEVMODE *paDeviceModes, UINT devModes)
2241{
2242 RT_NOREF(paDisplayDevices, devModes);
2243 VBOXDISPIF_WDDM_DISPCFG DispCfg;
2244 DWORD winEr;
2245 int iPath;
2246
2247 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
2248 if (winEr != ERROR_SUCCESS)
2249 {
2250 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate\n"));
2251 return winEr;
2252 }
2253
2254 iPath = vboxDispIfWddmDcSearchActivePath(&DispCfg, Id, Id);
2255
2256 if (iPath < 0)
2257 {
2258 vboxDispIfWddmDcTerm(&DispCfg);
2259
2260 if (!fEnable)
2261 {
2262 /* nothing to be done here, just leave */
2263 return ERROR_SUCCESS;
2264 }
2265
2266 winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable);
2267 if (winEr != ERROR_SUCCESS)
2268 {
2269 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr));
2270 return winEr;
2271 }
2272
2273 winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
2274 if (winEr != ERROR_SUCCESS)
2275 {
2276 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
2277 return winEr;
2278 }
2279
2280 iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id);
2281 if (iPath < 0)
2282 {
2283 WARN(("VBoxTray: (WDDM) path (%d) is still disabled, going to retry winEr %d\n", winEr));
2284 vboxDispIfWddmDcTerm(&DispCfg);
2285 return ERROR_RETRY;
2286 }
2287 }
2288
2289 Assert(iPath >= 0);
2290
2291 if (!fEnable)
2292 {
2293 /* need to disable it, and we are done */
2294 vboxDispIfWddmDcTerm(&DispCfg);
2295
2296 winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable);
2297 if (winEr != ERROR_SUCCESS)
2298 {
2299 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr));
2300 return winEr;
2301 }
2302
2303 return winEr;
2304 }
2305
2306 Assert(fEnable);
2307
2308 winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, &paDeviceModes[Id], FALSE, fEnable);
2309 if (winEr != ERROR_SUCCESS)
2310 {
2311 WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsUpdate\n"));
2312 vboxDispIfWddmDcTerm(&DispCfg);
2313 return winEr;
2314 }
2315
2316 UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
2317 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
2318 if (winEr != ERROR_SUCCESS)
2319 {
2320 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2321 fSetFlags |= SDC_ALLOW_CHANGES;
2322 }
2323
2324 winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
2325 if (winEr != ERROR_SUCCESS)
2326 {
2327 WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
2328 }
2329
2330 vboxDispIfWddmDcTerm(&DispCfg);
2331
2332 return winEr;
2333}
2334
2335#endif /* VBOX_WITH_WDDM */
2336
2337DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
2338{
2339 switch (pIf->enmMode)
2340 {
2341 case VBOXDISPIF_MODE_XPDM_NT4:
2342 return ERROR_NOT_SUPPORTED;
2343 case VBOXDISPIF_MODE_XPDM:
2344 return ERROR_NOT_SUPPORTED;
2345#ifdef VBOX_WITH_WDDM
2346 case VBOXDISPIF_MODE_WDDM:
2347 case VBOXDISPIF_MODE_WDDM_W7:
2348 return vboxDispIfResizeModesWDDM(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
2349#endif
2350 default:
2351 WARN(("unknown mode (%d)\n", pIf->enmMode));
2352 return ERROR_INVALID_PARAMETER;
2353 }
2354}
2355
2356DWORD VBoxDispIfCancelPendingResize(PCVBOXDISPIF const pIf)
2357{
2358 switch (pIf->enmMode)
2359 {
2360 case VBOXDISPIF_MODE_XPDM_NT4:
2361 return NO_ERROR;
2362 case VBOXDISPIF_MODE_XPDM:
2363 return NO_ERROR;
2364#ifdef VBOX_WITH_WDDM
2365 case VBOXDISPIF_MODE_WDDM:
2366 case VBOXDISPIF_MODE_WDDM_W7:
2367 return vboxDispIfCancelPendingResizeWDDM(pIf);
2368#endif
2369 default:
2370 WARN(("unknown mode (%d)\n", pIf->enmMode));
2371 return ERROR_INVALID_PARAMETER;
2372 }
2373}
2374
2375static DWORD vboxDispIfConfigureTargetsWDDM(VBOXDISPIF_OP *pOp, uint32_t *pcConnected)
2376{
2377 VBOXDISPIFESCAPE EscapeHdr = {0};
2378 EscapeHdr.escapeCode = VBOXESC_CONFIGURETARGETS;
2379 EscapeHdr.u32CmdSpecific = 0;
2380
2381 D3DKMT_ESCAPE EscapeData = {0};
2382 EscapeData.hAdapter = pOp->Adapter.hAdapter;
2383#ifdef VBOX_DISPIF_WITH_OPCONTEXT
2384 /* win8.1 does not allow context-based escapes for display-only mode */
2385 EscapeData.hDevice = pOp->Device.hDevice;
2386 EscapeData.hContext = pOp->Context.hContext;
2387#endif
2388 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
2389 EscapeData.Flags.HardwareAccess = 1;
2390 EscapeData.pPrivateDriverData = &EscapeHdr;
2391 EscapeData.PrivateDriverDataSize = sizeof (EscapeHdr);
2392
2393 NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
2394 if (NT_SUCCESS(Status))
2395 {
2396 if (pcConnected)
2397 *pcConnected = EscapeHdr.u32CmdSpecific;
2398 return NO_ERROR;
2399 }
2400 WARN(("VBoxTray: pfnD3DKMTEscape VBOXESC_CONFIGURETARGETS failed Status 0x%x\n", Status));
2401 return Status;
2402}
2403
2404static DWORD vboxDispIfResizeStartedWDDMOp(VBOXDISPIF_OP *pOp)
2405{
2406 DWORD NumDevices = VBoxDisplayGetCount();
2407 if (NumDevices == 0)
2408 {
2409 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: Zero devices found\n"));
2410 return ERROR_GEN_FAILURE;
2411 }
2412
2413 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
2414 DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
2415 DWORD DevNum = 0;
2416 DWORD DevPrimaryNum = 0;
2417
2418 DWORD winEr = VBoxDisplayGetConfig(NumDevices, &DevPrimaryNum, &DevNum, paDisplayDevices, paDeviceModes);
2419 if (winEr != NO_ERROR)
2420 {
2421 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed, %d\n", winEr));
2422 return winEr;
2423 }
2424
2425 if (NumDevices != DevNum)
2426 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != DevNum(%d)\n", NumDevices, DevNum));
2427
2428
2429 uint32_t cConnected = 0;
2430 winEr = vboxDispIfConfigureTargetsWDDM(pOp, &cConnected);
2431 if (winEr != NO_ERROR)
2432 {
2433 WARN(("VBoxTray: vboxDispIfConfigureTargetsWDDM failed winEr 0x%x\n", winEr));
2434 return winEr;
2435 }
2436
2437 if (!cConnected)
2438 {
2439 Log(("VBoxTray: all targets already connected, nothing to do\n"));
2440 return NO_ERROR;
2441 }
2442
2443 winEr = vboxDispIfWaitDisplayDataInited(pOp);
2444 if (winEr != NO_ERROR)
2445 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: vboxDispIfWaitDisplayDataInited failed winEr 0x%x\n", winEr));
2446
2447 DWORD NewNumDevices = VBoxDisplayGetCount();
2448 if (NewNumDevices == 0)
2449 {
2450 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: Zero devices found\n"));
2451 return ERROR_GEN_FAILURE;
2452 }
2453
2454 if (NewNumDevices != NumDevices)
2455 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != NewNumDevices(%d)\n", NumDevices, NewNumDevices));
2456
2457 DISPLAY_DEVICE *paNewDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NewNumDevices);
2458 DEVMODE *paNewDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NewNumDevices);
2459 DWORD NewDevNum = 0;
2460 DWORD NewDevPrimaryNum = 0;
2461
2462 winEr = VBoxDisplayGetConfig(NewNumDevices, &NewDevPrimaryNum, &NewDevNum, paNewDisplayDevices, paNewDeviceModes);
2463 if (winEr != NO_ERROR)
2464 {
2465 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed for new devices, %d\n", winEr));
2466 return winEr;
2467 }
2468
2469 if (NewNumDevices != NewDevNum)
2470 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NewNumDevices(%d) != NewDevNum(%d)\n", NewNumDevices, NewDevNum));
2471
2472 DWORD minDevNum = RT_MIN(DevNum, NewDevNum);
2473 UINT *pIds = (UINT*)alloca (sizeof (UINT) * minDevNum);
2474 UINT cIds = 0;
2475 for (DWORD i = 0; i < minDevNum; ++i)
2476 {
2477 if ((paNewDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE)
2478 && !(paDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE))
2479 {
2480 pIds[cIds] = i;
2481 ++cIds;
2482 }
2483 }
2484
2485 if (!cIds)
2486 {
2487 /* this is something we would not regularly expect */
2488 WARN(("VBoxTray: all targets already have proper config, nothing to do\n"));
2489 return NO_ERROR;
2490 }
2491
2492 if (pOp->pIf->enmMode > VBOXDISPIF_MODE_WDDM)
2493 {
2494 winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pOp->pIf, cIds, pIds, FALSE);
2495 if (winEr != NO_ERROR)
2496 WARN(("VBoxTray: vboxDispIfWddmEnableDisplaysTryingTopology failed to record current settings, %d, ignoring\n", winEr));
2497 }
2498 else
2499 {
2500 for (DWORD i = 0; i < cIds; ++i)
2501 {
2502 winEr = vboxDispIfWddmResizeDisplayVista(paNewDeviceModes, paNewDisplayDevices, NewDevNum, i, FALSE, TRUE);
2503 if (winEr != NO_ERROR)
2504 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: vboxDispIfWddmResizeDisplayVista failed winEr 0x%x\n", winEr));
2505 }
2506 }
2507
2508 return winEr;
2509}
2510
2511
2512static DWORD vboxDispIfResizeStartedWDDM(PCVBOXDISPIF const pIf)
2513{
2514 VBOXDISPIF_OP Op;
2515
2516 DWORD winEr = vboxDispIfOpBegin(pIf, &Op);
2517 if (winEr != NO_ERROR)
2518 {
2519 WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x\n", winEr));
2520 return winEr;
2521 }
2522
2523 winEr = vboxDispIfResizeStartedWDDMOp(&Op);
2524 if (winEr != NO_ERROR)
2525 {
2526 WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp failed winEr 0x%x\n", winEr));
2527 }
2528
2529 vboxDispIfOpEnd(&Op);
2530
2531 return winEr;
2532}
2533
2534DWORD VBoxDispIfResizeStarted(PCVBOXDISPIF const pIf)
2535{
2536 switch (pIf->enmMode)
2537 {
2538 case VBOXDISPIF_MODE_XPDM_NT4:
2539 return NO_ERROR;
2540 case VBOXDISPIF_MODE_XPDM:
2541 return NO_ERROR;
2542#ifdef VBOX_WITH_WDDM
2543 case VBOXDISPIF_MODE_WDDM:
2544 case VBOXDISPIF_MODE_WDDM_W7:
2545 return vboxDispIfResizeStartedWDDM(pIf);
2546#endif
2547 default:
2548 WARN(("unknown mode (%d)\n", pIf->enmMode));
2549 return ERROR_INVALID_PARAMETER;
2550 }
2551}
2552
2553static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
2554{
2555 RT_NOREF(pIf);
2556 return NO_ERROR;
2557}
2558
2559static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
2560{
2561 DWORD err = NO_ERROR;
2562
2563 uint64_t const uNtVersion = RTSystemGetNtVersion();
2564 if (uNtVersion >= RTSYSTEM_MAKE_NT_VERSION(5, 0, 0))
2565 {
2566 HMODULE hUser = GetModuleHandle("user32.dll");
2567 if (NULL != hUser)
2568 {
2569 *(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
2570 LogFunc(("pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
2571 bool const fSupported = RT_BOOL(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
2572 if (!fSupported)
2573 {
2574 WARN(("pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
2575 err = ERROR_NOT_SUPPORTED;
2576 }
2577 }
2578 else
2579 {
2580 WARN(("failed to get USER32 handle, err (%d)\n", GetLastError()));
2581 err = ERROR_NOT_SUPPORTED;
2582 }
2583 }
2584 else
2585 {
2586 WARN(("can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
2587 err = ERROR_NOT_SUPPORTED;
2588 }
2589
2590 return err;
2591}
2592
2593DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
2594{
2595 /** @todo may need to addd synchronization in case we want to change modes dynamically
2596 * i.e. currently the mode is supposed to be initialized once on service initialization */
2597 if (penmOldMode)
2598 *penmOldMode = pIf->enmMode;
2599
2600 if (enmMode == pIf->enmMode)
2601 return NO_ERROR;
2602
2603#ifdef VBOX_WITH_WDDM
2604 if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM)
2605 {
2606 vboxDispIfWddmTerm(pIf);
2607
2608 vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks);
2609 }
2610#endif
2611
2612 DWORD err = NO_ERROR;
2613 switch (enmMode)
2614 {
2615 case VBOXDISPIF_MODE_XPDM_NT4:
2616 LogFunc(("request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
2617 err = vboxDispIfSwitchToXPDM_NT4(pIf);
2618 if (err == NO_ERROR)
2619 {
2620 LogFunc(("successfully switched to XPDM_NT4 mode\n"));
2621 pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
2622 }
2623 else
2624 WARN(("failed to switch to XPDM_NT4 mode, err (%d)\n", err));
2625 break;
2626 case VBOXDISPIF_MODE_XPDM:
2627 LogFunc(("request to switch to VBOXDISPIF_MODE_XPDM\n"));
2628 err = vboxDispIfSwitchToXPDM(pIf);
2629 if (err == NO_ERROR)
2630 {
2631 LogFunc(("successfully switched to XPDM mode\n"));
2632 pIf->enmMode = VBOXDISPIF_MODE_XPDM;
2633 }
2634 else
2635 WARN(("failed to switch to XPDM mode, err (%d)\n", err));
2636 break;
2637#ifdef VBOX_WITH_WDDM
2638 case VBOXDISPIF_MODE_WDDM:
2639 {
2640 LogFunc(("request to switch to VBOXDISPIF_MODE_WDDM\n"));
2641 err = vboxDispIfSwitchToWDDM(pIf);
2642 if (err == NO_ERROR)
2643 {
2644 LogFunc(("successfully switched to WDDM mode\n"));
2645 pIf->enmMode = VBOXDISPIF_MODE_WDDM;
2646 }
2647 else
2648 WARN(("failed to switch to WDDM mode, err (%d)\n", err));
2649 break;
2650 }
2651 case VBOXDISPIF_MODE_WDDM_W7:
2652 {
2653 LogFunc(("request to switch to VBOXDISPIF_MODE_WDDM_W7\n"));
2654 err = vboxDispIfSwitchToWDDM_W7(pIf);
2655 if (err == NO_ERROR)
2656 {
2657 LogFunc(("successfully switched to WDDM mode\n"));
2658 pIf->enmMode = VBOXDISPIF_MODE_WDDM_W7;
2659 }
2660 else
2661 WARN(("failed to switch to WDDM mode, err (%d)\n", err));
2662 break;
2663 }
2664#endif
2665 default:
2666 err = ERROR_INVALID_PARAMETER;
2667 break;
2668 }
2669 return err;
2670}
2671
2672static DWORD vboxDispIfSeamlessCreateWDDM(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent)
2673{
2674 RT_NOREF(hEvent);
2675 HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &pSeamless->modeData.wddm.Adapter);
2676 if (SUCCEEDED(hr))
2677 {
2678#ifndef VBOX_DISPIF_WITH_OPCONTEXT
2679 return ERROR_SUCCESS;
2680#else
2681 hr = vboxDispKmtCreateDevice(&pSeamless->modeData.wddm.Adapter, &pSeamless->modeData.wddm.Device);
2682 if (SUCCEEDED(hr))
2683 {
2684 hr = vboxDispKmtCreateContext(&pSeamless->modeData.wddm.Device, &pSeamless->modeData.wddm.Context, VBOXWDDM_CONTEXT_TYPE_CUSTOM_DISPIF_SEAMLESS,
2685 hEvent, 0ULL);
2686 if (SUCCEEDED(hr))
2687 return ERROR_SUCCESS;
2688 WARN(("VBoxTray: vboxDispKmtCreateContext failed hr 0x%x", hr));
2689
2690 vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device);
2691 }
2692 else
2693 WARN(("VBoxTray: vboxDispKmtCreateDevice failed hr 0x%x", hr));
2694
2695 vboxDispKmtCloseAdapter(&pSeamless->modeData.wddm.Adapter);
2696#endif /* VBOX_DISPIF_WITH_OPCONTEXT */
2697 }
2698
2699 return hr;
2700}
2701
2702static DWORD vboxDispIfSeamlessTermWDDM(VBOXDISPIF_SEAMLESS *pSeamless)
2703{
2704#ifdef VBOX_DISPIF_WITH_OPCONTEXT
2705 vboxDispKmtDestroyContext(&pSeamless->modeData.wddm.Context);
2706 vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device);
2707#endif
2708 vboxDispKmtCloseAdapter(&pSeamless->modeData.wddm.Adapter);
2709
2710 return NO_ERROR;
2711}
2712
2713static DWORD vboxDispIfSeamlessSubmitWDDM(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData)
2714{
2715 D3DKMT_ESCAPE EscapeData = {0};
2716 EscapeData.hAdapter = pSeamless->modeData.wddm.Adapter.hAdapter;
2717#ifdef VBOX_DISPIF_WITH_OPCONTEXT
2718 EscapeData.hDevice = pSeamless->modeData.wddm.Device.hDevice;
2719 EscapeData.hContext = pSeamless->modeData.wddm.Context.hContext;
2720#endif
2721 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
2722 /*EscapeData.Flags.HardwareAccess = 1;*/
2723 EscapeData.pPrivateDriverData = pData;
2724 EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(cbData);
2725
2726 NTSTATUS Status = pSeamless->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
2727 if (NT_SUCCESS(Status))
2728 return ERROR_SUCCESS;
2729
2730 WARN(("VBoxTray: pfnD3DKMTEscape Seamless failed Status 0x%x\n", Status));
2731 return Status;
2732}
2733
2734DWORD VBoxDispIfSeamlessCreate(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent)
2735{
2736 memset(pSeamless, 0, sizeof (*pSeamless));
2737 pSeamless->pIf = pIf;
2738
2739 switch (pIf->enmMode)
2740 {
2741 case VBOXDISPIF_MODE_XPDM_NT4:
2742 case VBOXDISPIF_MODE_XPDM:
2743 return NO_ERROR;
2744#ifdef VBOX_WITH_WDDM
2745 case VBOXDISPIF_MODE_WDDM:
2746 case VBOXDISPIF_MODE_WDDM_W7:
2747 return vboxDispIfSeamlessCreateWDDM(pIf, pSeamless, hEvent);
2748#endif
2749 default:
2750 break;
2751 }
2752
2753 WARN(("VBoxTray: VBoxDispIfSeamlessCreate: invalid mode %d\n", pIf->enmMode));
2754 return ERROR_INVALID_PARAMETER;
2755}
2756
2757DWORD VBoxDispIfSeamlessTerm(VBOXDISPIF_SEAMLESS *pSeamless)
2758{
2759 PCVBOXDISPIF const pIf = pSeamless->pIf;
2760 DWORD winEr;
2761 switch (pIf->enmMode)
2762 {
2763 case VBOXDISPIF_MODE_XPDM_NT4:
2764 case VBOXDISPIF_MODE_XPDM:
2765 winEr = NO_ERROR;
2766 break;
2767#ifdef VBOX_WITH_WDDM
2768 case VBOXDISPIF_MODE_WDDM:
2769 case VBOXDISPIF_MODE_WDDM_W7:
2770 winEr = vboxDispIfSeamlessTermWDDM(pSeamless);
2771 break;
2772#endif
2773 default:
2774 WARN(("VBoxTray: VBoxDispIfSeamlessTerm: invalid mode %d\n", pIf->enmMode));
2775 winEr = ERROR_INVALID_PARAMETER;
2776 break;
2777 }
2778
2779 if (winEr == NO_ERROR)
2780 memset(pSeamless, 0, sizeof (*pSeamless));
2781
2782 return winEr;
2783}
2784
2785DWORD VBoxDispIfSeamlessSubmit(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData)
2786{
2787 PCVBOXDISPIF const pIf = pSeamless->pIf;
2788
2789 if (pData->escapeCode != VBOXESC_SETVISIBLEREGION)
2790 {
2791 WARN(("VBoxTray: invalid escape code for Seamless submit %d\n", pData->escapeCode));
2792 return ERROR_INVALID_PARAMETER;
2793 }
2794
2795 switch (pIf->enmMode)
2796 {
2797 case VBOXDISPIF_MODE_XPDM_NT4:
2798 case VBOXDISPIF_MODE_XPDM:
2799 return VBoxDispIfEscape(pIf, pData, cbData);
2800#ifdef VBOX_WITH_WDDM
2801 case VBOXDISPIF_MODE_WDDM:
2802 case VBOXDISPIF_MODE_WDDM_W7:
2803 return vboxDispIfSeamlessSubmitWDDM(pSeamless, pData, cbData);
2804#endif
2805 default:
2806 WARN(("VBoxTray: VBoxDispIfSeamlessSubmit: invalid mode %d\n", pIf->enmMode));
2807 return ERROR_INVALID_PARAMETER;
2808 }
2809}
2810
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