VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use