VirtualBox

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

Last change on this file since 100541 was 99828, checked in by vboxsync, 19 months ago

*: A bunch of adjustments that allows using /permissive- with Visual C++ (qt 6.x necessity).

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

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