VirtualBox

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

最後變更 在這個檔案從95868是 95868,由 vboxsync 提交於 3 年 前

VBoxTray/VBoxDisIf.cpp+VBoxSeamless.cpp: Replaced malloc, realloc and free with the IPRT equivalents (RTMemAlloc, RTMemRealloc, RTMemFree) - someone clearly didn't get the memo about using IPRT instead of CRT. bugref:10261

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

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