VirtualBox

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

Last change on this file since 69500 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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

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