VBoxDispIf.cpp revision a29027bb2378172c7c88610ad5311548b66f96f6
/** @file
* VBoxDispIf.cpp - Display Settings Interface abstraction for XPDM & WDDM.
*/
/*
* Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "VBoxTray.h"
#include "VBoxDisplay.h"
#include <malloc.h>
#define _WIN32_WINNT 0x0601
#include <iprt/log.h>
#include <iprt/err.h>
#include <iprt/assert.h>
#ifdef VBOX_WITH_WDDM
#include <iprt/asm.h>
#endif
#ifndef NT_SUCCESS
# define NT_SUCCESS(_Status) ((_Status) >= 0)
#endif
typedef struct VBOXDISPIF_OP
{
PCVBOXDISPIF pIf;
VBOXDISPKMT_ADAPTER Adapter;
VBOXDISPKMT_DEVICE Device;
VBOXDISPKMT_CONTEXT Context;
} VBOXDISPIF_OP;
DWORD EnableAndResizeDispDev(DEVMODE *paDeviceModes, DISPLAY_DEVICE *paDisplayDevices, DWORD totalDispNum, UINT Id, DWORD aWidth, DWORD aHeight,
DWORD aBitsPerPixel, DWORD aPosX, DWORD aPosY, BOOL fEnabled, BOOL fExtDispSup);
static DWORD vboxDispIfWddmResizeDisplay(PCVBOXDISPIF const pIf, UINT Id, BOOL fEnable, DISPLAY_DEVICE * paDisplayDevices, DEVMODE *paDeviceMode, UINT devModes);
static DWORD vboxDispIfResizePerform(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes);
static DWORD vboxDispIfWddmEnableDisplaysTryingTopology(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnable);
static DWORD vboxDispIfResizeStartedWDDMOp(VBOXDISPIF_OP *pOp);
/* APIs specific to win7 and above WDDM architecture. Not available for Vista WDDM.
* This is the reason they have not been put in the VBOXDISPIF struct in VBoxDispIf.h
*/
typedef struct _VBOXDISPLAYWDDMAPICONTEXT
{
LONG (WINAPI * pfnSetDisplayConfig)(UINT numPathArrayElements,DISPLAYCONFIG_PATH_INFO *pathArray,UINT numModeInfoArrayElements,
DISPLAYCONFIG_MODE_INFO *modeInfoArray, UINT Flags);
LONG (WINAPI * pfnQueryDisplayConfig)(UINT Flags,UINT *pNumPathArrayElements, DISPLAYCONFIG_PATH_INFO *pPathInfoArray,
UINT *pNumModeInfoArrayElements, DISPLAYCONFIG_MODE_INFO *pModeInfoArray,
DISPLAYCONFIG_TOPOLOGY_ID *pCurrentTopologyId);
LONG (WINAPI * pfnGetDisplayConfigBufferSizes)(UINT Flags, UINT *pNumPathArrayElements, UINT *pNumModeInfoArrayElements);
} _VBOXDISPLAYWDDMAPICONTEXT;
static _VBOXDISPLAYWDDMAPICONTEXT gCtx = {0};
typedef struct VBOXDISPIF_WDDM_DISPCFG
{
UINT32 cPathInfoArray;
DISPLAYCONFIG_PATH_INFO *pPathInfoArray;
UINT32 cModeInfoArray;
DISPLAYCONFIG_MODE_INFO *pModeInfoArray;
} VBOXDISPIF_WDDM_DISPCFG;
static DWORD vboxDispIfWddmDcCreate(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT32 fFlags)
{
UINT32 cPathInfoArray = 0;
UINT32 cModeInfoArray = 0;
DISPLAYCONFIG_PATH_INFO *pPathInfoArray;
DISPLAYCONFIG_MODE_INFO *pModeInfoArray;
DWORD winEr = gCtx.pfnGetDisplayConfigBufferSizes(fFlags, &cPathInfoArray, &cModeInfoArray);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed calling GetDisplayConfigBufferSizes, dwErr=%ld\n", winEr));
return winEr;
}
/** @todo r=andy Why using malloc() instead of RTMemAlloc*? */
pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(cPathInfoArray * sizeof(DISPLAYCONFIG_PATH_INFO));
if (!pPathInfoArray)
{
LogFlowFunc(("Allocating pPathInfoArray failed\n"));
return ERROR_OUTOFMEMORY;
}
pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(cModeInfoArray * sizeof(DISPLAYCONFIG_MODE_INFO));
if (!pModeInfoArray)
{
LogFlowFunc(("Allocating pModeInfoArray failed\n"));
free(pPathInfoArray);
return ERROR_OUTOFMEMORY;
}
winEr = gCtx.pfnQueryDisplayConfig(fFlags, &cPathInfoArray, pPathInfoArray, &cModeInfoArray, pModeInfoArray, NULL);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed calling QueryDisplayConfig, dwErr=%ld\n", winEr));
free(pPathInfoArray);
free(pModeInfoArray);
return winEr;
}
pCfg->cPathInfoArray = cPathInfoArray;
pCfg->pPathInfoArray = pPathInfoArray;
pCfg->cModeInfoArray = cModeInfoArray;
pCfg->pModeInfoArray = pModeInfoArray;
return ERROR_SUCCESS;
}
static DWORD vboxDispIfWddmDcClone(VBOXDISPIF_WDDM_DISPCFG *pCfg, VBOXDISPIF_WDDM_DISPCFG *pCfgDst)
{
memset(pCfgDst, 0, sizeof (*pCfgDst));
/** @todo r=andy Why using malloc() instead of RTMemAlloc*? */
if (pCfg->cPathInfoArray)
{
pCfgDst->pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(pCfg->cPathInfoArray * sizeof (DISPLAYCONFIG_PATH_INFO));
if (!pCfgDst->pPathInfoArray)
{
LogFlowFunc(("malloc failed!\n"));
return ERROR_OUTOFMEMORY;
}
memcpy(pCfgDst->pPathInfoArray, pCfg->pPathInfoArray, pCfg->cPathInfoArray * sizeof (DISPLAYCONFIG_PATH_INFO));
pCfgDst->cPathInfoArray = pCfg->cPathInfoArray;
}
if (pCfg->cModeInfoArray)
{
pCfgDst->pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(pCfg->cModeInfoArray * sizeof (DISPLAYCONFIG_MODE_INFO));
if (!pCfgDst->pModeInfoArray)
{
LogFlowFunc(("malloc failed!\n"));
if (pCfgDst->pPathInfoArray)
{
free(pCfgDst->pPathInfoArray);
pCfgDst->pPathInfoArray = NULL;
}
return ERROR_OUTOFMEMORY;
}
memcpy(pCfgDst->pModeInfoArray, pCfg->pModeInfoArray, pCfg->cModeInfoArray * sizeof (DISPLAYCONFIG_MODE_INFO));
pCfgDst->cModeInfoArray = pCfg->cModeInfoArray;
}
return ERROR_SUCCESS;
}
static VOID vboxDispIfWddmDcTerm(VBOXDISPIF_WDDM_DISPCFG *pCfg)
{
if (pCfg->pPathInfoArray)
free(pCfg->pPathInfoArray);
if (pCfg->pModeInfoArray)
free(pCfg->pModeInfoArray);
/* sanity */
memset(pCfg, 0, sizeof (*pCfg));
}
static UINT32 g_cVBoxDispIfWddmDisplays = 0;
static DWORD vboxDispIfWddmDcQueryNumDisplays(UINT32 *pcDisplays)
{
if (!g_cVBoxDispIfWddmDisplays)
{
VBOXDISPIF_WDDM_DISPCFG DispCfg;
*pcDisplays = 0;
DWORD winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("vboxDispIfWddmDcCreate failed winEr %ld\n", winEr));
return winEr;
}
int cDisplays = -1;
for (UINT iter = 0; iter < DispCfg.cPathInfoArray; ++iter)
{
if (cDisplays < (int)(DispCfg.pPathInfoArray[iter].sourceInfo.id))
cDisplays = (int)(DispCfg.pPathInfoArray[iter].sourceInfo.id);
}
cDisplays++;
g_cVBoxDispIfWddmDisplays = cDisplays;
Assert(g_cVBoxDispIfWddmDisplays);
vboxDispIfWddmDcTerm(&DispCfg);
}
*pcDisplays = g_cVBoxDispIfWddmDisplays;
return ERROR_SUCCESS;
}
static int vboxDispIfWddmDcSearchPath(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT srcId, UINT trgId)
{
for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
{
if ((srcId == ~0UL || pCfg->pPathInfoArray[iter].sourceInfo.id == srcId)
&& (trgId == ~0UL || pCfg->pPathInfoArray[iter].targetInfo.id == trgId))
{
return (int)iter;
}
}
return -1;
}
static int vboxDispIfWddmDcSearchActivePath(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT srcId, UINT trgId)
{
int idx = vboxDispIfWddmDcSearchPath(pCfg, srcId, trgId);
if (idx < 0)
return idx;
if (!(pCfg->pPathInfoArray[idx].flags & DISPLAYCONFIG_PATH_ACTIVE))
return -1;
return idx;
}
static VOID vboxDispIfWddmDcSettingsInvalidateModeIndex(VBOXDISPIF_WDDM_DISPCFG *pCfg, int idx)
{
pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
pCfg->pPathInfoArray[idx].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
}
static VOID vboxDispIfWddmDcSettingsInvalidateModeIndeces(VBOXDISPIF_WDDM_DISPCFG *pCfg)
{
for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
{
vboxDispIfWddmDcSettingsInvalidateModeIndex(pCfg, (int)iter);
}
if (pCfg->pModeInfoArray)
{
free(pCfg->pModeInfoArray);
pCfg->pModeInfoArray = NULL;
}
pCfg->cModeInfoArray = 0;
}
static DWORD vboxDispIfWddmDcSettingsModeAdd(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT *pIdx)
{
UINT32 cModeInfoArray = pCfg->cModeInfoArray + 1;
DISPLAYCONFIG_MODE_INFO *pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(cModeInfoArray * sizeof (DISPLAYCONFIG_MODE_INFO));
if (!pModeInfoArray)
{
LogFlowFunc(("malloc failed!\n"));
return ERROR_OUTOFMEMORY;
}
memcpy (pModeInfoArray, pCfg->pModeInfoArray, pCfg->cModeInfoArray * sizeof(DISPLAYCONFIG_MODE_INFO));
memset(&pModeInfoArray[cModeInfoArray-1], 0, sizeof (pModeInfoArray[0]));
free(pCfg->pModeInfoArray);
*pIdx = cModeInfoArray-1;
pCfg->pModeInfoArray = pModeInfoArray;
pCfg->cModeInfoArray = cModeInfoArray;
return ERROR_SUCCESS;
}
static DWORD vboxDispIfWddmDcSettingsUpdate(VBOXDISPIF_WDDM_DISPCFG *pCfg, int idx, DEVMODE *pDeviceMode, BOOL fInvalidateSrcMode, BOOL fEnable)
{
UINT Id = pCfg->pPathInfoArray[idx].sourceInfo.id;
if (fInvalidateSrcMode)
pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
else if (pDeviceMode)
{
UINT iSrcMode = pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx;
if (iSrcMode == DISPLAYCONFIG_PATH_MODE_IDX_INVALID)
{
LogFlowFunc(("no source mode index specified"));
DWORD winEr = vboxDispIfWddmDcSettingsModeAdd(pCfg, &iSrcMode);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("vboxDispIfWddmDcSettingsModeAdd Failed winEr %d\n", winEr));
return winEr;
}
pCfg->pPathInfoArray[idx].sourceInfo.modeInfoIdx = iSrcMode;
}
for (int i = 0; i < (int)pCfg->cPathInfoArray; ++i)
{
if (i == idx)
continue;
if (pCfg->pPathInfoArray[i].sourceInfo.modeInfoIdx == iSrcMode)
{
/* this is something we're not expecting/supporting */
LogFlowFunc(("multiple paths have the same mode index"));
return ERROR_NOT_SUPPORTED;
}
}
if (pDeviceMode->dmFields & DM_PELSWIDTH)
pCfg->pModeInfoArray[iSrcMode].sourceMode.width = pDeviceMode->dmPelsWidth;
if (pDeviceMode->dmFields & DM_PELSHEIGHT)
pCfg->pModeInfoArray[iSrcMode].sourceMode.height = pDeviceMode->dmPelsHeight;
if (pDeviceMode->dmFields & DM_POSITION)
{
pCfg->pModeInfoArray[iSrcMode].sourceMode.position.x = pDeviceMode->dmPosition.x;
pCfg->pModeInfoArray[iSrcMode].sourceMode.position.y = pDeviceMode->dmPosition.y;
}
if (pDeviceMode->dmFields & DM_BITSPERPEL)
{
switch (pDeviceMode->dmBitsPerPel)
{
case 32:
pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
break;
case 24:
pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_24BPP;
break;
case 16:
pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_16BPP;
break;
case 8:
pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_8BPP;
break;
default:
LogRel(("invalid bpp %d, using 32\n", pDeviceMode->dmBitsPerPel));
pCfg->pModeInfoArray[iSrcMode].sourceMode.pixelFormat = DISPLAYCONFIG_PIXELFORMAT_32BPP;
break;
}
}
}
pCfg->pPathInfoArray[idx].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
if (fEnable)
pCfg->pPathInfoArray[idx].flags |= DISPLAYCONFIG_PATH_ACTIVE;
else
pCfg->pPathInfoArray[idx].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
return ERROR_SUCCESS;
}
static DWORD vboxDispIfWddmDcSet(VBOXDISPIF_WDDM_DISPCFG *pCfg, UINT fFlags)
{
DWORD winEr = gCtx.pfnSetDisplayConfig(pCfg->cPathInfoArray, pCfg->pPathInfoArray, pCfg->cModeInfoArray, pCfg->pModeInfoArray, fFlags);
if (winEr != ERROR_SUCCESS)
LogFlowFunc(("pfnSetDisplayConfig Failed for Flags 0x%x\n", fFlags));
return winEr;
}
static BOOL vboxDispIfWddmDcSettingsAdjustSupportedPaths(VBOXDISPIF_WDDM_DISPCFG *pCfg)
{
BOOL fAdjusted = FALSE;
for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
{
if (pCfg->pPathInfoArray[iter].sourceInfo.id == pCfg->pPathInfoArray[iter].targetInfo.id)
continue;
if (!(pCfg->pPathInfoArray[iter].flags & DISPLAYCONFIG_PATH_ACTIVE))
continue;
pCfg->pPathInfoArray[iter].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
fAdjusted = TRUE;
}
return fAdjusted;
}
static void vboxDispIfWddmDcSettingsAttachDisbledToPrimary(VBOXDISPIF_WDDM_DISPCFG *pCfg)
{
for (UINT iter = 0; iter < pCfg->cPathInfoArray; ++iter)
{
if ((pCfg->pPathInfoArray[iter].flags & DISPLAYCONFIG_PATH_ACTIVE))
continue;
pCfg->pPathInfoArray[iter].sourceInfo.id = 0;
pCfg->pPathInfoArray[iter].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
pCfg->pPathInfoArray[iter].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
}
}
static DWORD vboxDispIfWddmDcSettingsIncludeAllTargets(VBOXDISPIF_WDDM_DISPCFG *pCfg)
{
UINT32 cDisplays = 0;
VBOXDISPIF_WDDM_DISPCFG AllCfg;
BOOL fAllCfgInited = FALSE;
DWORD winEr = vboxDispIfWddmDcQueryNumDisplays(&cDisplays);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("vboxDispIfWddmDcQueryNumDisplays Failed winEr %d\n", winEr));
return winEr;
}
DISPLAYCONFIG_PATH_INFO *pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(cDisplays * sizeof(DISPLAYCONFIG_PATH_INFO));
if (!pPathInfoArray)
{
LogFlowFunc(("malloc failed\n"));
return ERROR_OUTOFMEMORY;
}
for (UINT i = 0; i < cDisplays; ++i)
{
int idx = vboxDispIfWddmDcSearchPath(pCfg, i, i);
if (idx < 0)
{
idx = vboxDispIfWddmDcSearchPath(pCfg, -1, i);
if (idx >= 0)
{
LogFlowFunc(("different source and target paare enabled, this is something we would not expect\n"));
}
}
if (idx >= 0)
pPathInfoArray[i] = pCfg->pPathInfoArray[idx];
else
{
if (!fAllCfgInited)
{
winEr = vboxDispIfWddmDcCreate(&AllCfg, QDC_ALL_PATHS);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("vboxDispIfWddmDcCreate Failed winEr %d\n", winEr));
free(pPathInfoArray);
return winEr;
}
fAllCfgInited = TRUE;
}
idx = vboxDispIfWddmDcSearchPath(&AllCfg, i, i);
if (idx < 0)
{
LogFlowFunc(("%d %d path not supported\n", i, i));
idx = vboxDispIfWddmDcSearchPath(pCfg, -1, i);
if (idx < 0)
{
LogFlowFunc(("%d %d path not supported\n", -1, i));
}
}
if (idx >= 0)
{
pPathInfoArray[i] = AllCfg.pPathInfoArray[idx];
if (pPathInfoArray[i].flags & DISPLAYCONFIG_PATH_ACTIVE)
{
LogFlowFunc(("disabled path %d %d is marked active\n",
pPathInfoArray[i].sourceInfo.id, pPathInfoArray[i].targetInfo.id));
pPathInfoArray[i].flags &= ~DISPLAYCONFIG_PATH_ACTIVE;
}
Assert(pPathInfoArray[i].sourceInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID);
Assert(pPathInfoArray[i].sourceInfo.statusFlags == 0);
Assert(pPathInfoArray[i].targetInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID);
Assert(pPathInfoArray[i].targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15);
Assert(pPathInfoArray[i].targetInfo.rotation == DISPLAYCONFIG_ROTATION_IDENTITY);
Assert(pPathInfoArray[i].targetInfo.scaling == DISPLAYCONFIG_SCALING_PREFERRED);
Assert(pPathInfoArray[i].targetInfo.refreshRate.Numerator == 0);
Assert(pPathInfoArray[i].targetInfo.refreshRate.Denominator == 0);
Assert(pPathInfoArray[i].targetInfo.scanLineOrdering == DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED);
Assert(pPathInfoArray[i].targetInfo.targetAvailable == TRUE);
Assert(pPathInfoArray[i].targetInfo.statusFlags == DISPLAYCONFIG_TARGET_FORCIBLE);
Assert(pPathInfoArray[i].flags == 0);
}
else
{
pPathInfoArray[i].sourceInfo.adapterId = pCfg->pPathInfoArray[0].sourceInfo.adapterId;
pPathInfoArray[i].sourceInfo.id = i;
pPathInfoArray[i].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
pPathInfoArray[i].sourceInfo.statusFlags = 0;
pPathInfoArray[i].targetInfo.adapterId = pPathInfoArray[i].sourceInfo.adapterId;
pPathInfoArray[i].targetInfo.id = i;
pPathInfoArray[i].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
pPathInfoArray[i].targetInfo.outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15;
pPathInfoArray[i].targetInfo.rotation = DISPLAYCONFIG_ROTATION_IDENTITY;
pPathInfoArray[i].targetInfo.scaling = DISPLAYCONFIG_SCALING_PREFERRED;
pPathInfoArray[i].targetInfo.refreshRate.Numerator = 0;
pPathInfoArray[i].targetInfo.refreshRate.Denominator = 0;
pPathInfoArray[i].targetInfo.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED;
pPathInfoArray[i].targetInfo.targetAvailable = TRUE;
pPathInfoArray[i].targetInfo.statusFlags = DISPLAYCONFIG_TARGET_FORCIBLE;
pPathInfoArray[i].flags = 0;
}
}
}
free(pCfg->pPathInfoArray);
pCfg->pPathInfoArray = pPathInfoArray;
pCfg->cPathInfoArray = cDisplays;
if (fAllCfgInited)
vboxDispIfWddmDcTerm(&AllCfg);
return ERROR_SUCCESS;
}
static DWORD vboxDispIfOpBegin(PCVBOXDISPIF pIf, VBOXDISPIF_OP *pOp)
{
pOp->pIf = pIf;
HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &pOp->Adapter);
if (SUCCEEDED(hr))
{
hr = vboxDispKmtCreateDevice(&pOp->Adapter, &pOp->Device);
if (SUCCEEDED(hr))
{
hr = vboxDispKmtCreateContext(&pOp->Device, &pOp->Context, VBOXWDDM_CONTEXT_TYPE_CUSTOM_DISPIF_RESIZE,
0, 0, NULL, 0ULL);
if (SUCCEEDED(hr))
return ERROR_SUCCESS;
else
LogFlowFunc(("vboxDispKmtCreateContext failed hr 0x%x", hr));
vboxDispKmtDestroyDevice(&pOp->Device);
}
else
LogFlowFunc(("vboxDispKmtCreateDevice failed hr 0x%x", hr));
vboxDispKmtCloseAdapter(&pOp->Adapter);
}
return hr;
}
static VOID vboxDispIfOpEnd(VBOXDISPIF_OP *pOp)
{
vboxDispKmtDestroyContext(&pOp->Context);
vboxDispKmtDestroyDevice(&pOp->Device);
vboxDispKmtCloseAdapter(&pOp->Adapter);
}
/* display driver interface abstraction for XPDM & WDDM
* with WDDM we can not use ExtEscape to communicate with our driver
* because we do not have XPDM display driver any more, i.e. escape requests are handled by cdd
* that knows nothing about us */
DWORD VBoxDispIfInit(PVBOXDISPIF pIf)
{
pIf->enmMode = VBOXDISPIF_MODE_XPDM;
return NO_ERROR;
}
#ifdef VBOX_WITH_WDDM
static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf);
static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf);
#endif
DWORD VBoxDispIfTerm(PVBOXDISPIF pIf)
{
#ifdef VBOX_WITH_WDDM
if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM)
{
vboxDispIfWddmTerm(pIf);
vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks);
}
#endif
pIf->enmMode = VBOXDISPIF_MODE_UNKNOWN;
return NO_ERROR;
}
static DWORD vboxDispIfEscapeXPDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, int iDirection)
{
HDC hdc = GetDC(HWND_DESKTOP);
VOID *pvData = cbData ? VBOXDISPIFESCAPE_DATA(pEscape, VOID) : NULL;
int iRet = ExtEscape(hdc, pEscape->escapeCode,
iDirection >= 0 ? cbData : 0,
iDirection >= 0 ? (LPSTR)pvData : NULL,
iDirection <= 0 ? cbData : 0,
iDirection <= 0 ? (LPSTR)pvData : NULL);
ReleaseDC(HWND_DESKTOP, hdc);
if (iRet > 0)
return VINF_SUCCESS;
else if (iRet == 0)
return ERROR_NOT_SUPPORTED;
/* else */
return ERROR_GEN_FAILURE;
}
#ifdef VBOX_WITH_WDDM
static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf)
{
DWORD err = NO_ERROR;
OSVERSIONINFO OSinfo;
OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
GetVersionEx (&OSinfo);
bool bSupported = true;
if (OSinfo.dwMajorVersion >= 6)
{
LogFlowFunc(("this is vista and up\n"));
HMODULE hUser = GetModuleHandle("user32.dll");
if (hUser)
{
*(uintptr_t *)&pIf->modeData.wddm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
LogFlowFunc(("pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.wddm.pfnChangeDisplaySettingsEx));
bSupported &= !!(pIf->modeData.wddm.pfnChangeDisplaySettingsEx);
*(uintptr_t *)&pIf->modeData.wddm.pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA");
LogFlowFunc(("pfnEnumDisplayDevices = %p\n", pIf->modeData.wddm.pfnEnumDisplayDevices));
bSupported &= !!(pIf->modeData.wddm.pfnEnumDisplayDevices);
/* for win 7 and above */
if (OSinfo.dwMinorVersion >= 1)
{
*(uintptr_t *)&gCtx.pfnSetDisplayConfig = (uintptr_t)GetProcAddress(hUser, "SetDisplayConfig");
LogFlowFunc(("pfnSetDisplayConfig = %p\n", gCtx.pfnSetDisplayConfig));
bSupported &= !!(gCtx.pfnSetDisplayConfig);
*(uintptr_t *)&gCtx.pfnQueryDisplayConfig = (uintptr_t)GetProcAddress(hUser, "QueryDisplayConfig");
LogFlowFunc(("pfnQueryDisplayConfig = %p\n", gCtx.pfnQueryDisplayConfig));
bSupported &= !!(gCtx.pfnQueryDisplayConfig);
*(uintptr_t *)&gCtx.pfnGetDisplayConfigBufferSizes = (uintptr_t)GetProcAddress(hUser, "GetDisplayConfigBufferSizes");
LogFlowFunc(("pfnGetDisplayConfigBufferSizes = %p\n", gCtx.pfnGetDisplayConfigBufferSizes));
bSupported &= !!(gCtx.pfnGetDisplayConfigBufferSizes);
}
/* this is vista and up */
HRESULT hr = vboxDispKmtCallbacksInit(&pIf->modeData.wddm.KmtCallbacks);
if (FAILED(hr))
{
LogFlowFunc(("vboxDispKmtCallbacksInit failed hr 0x%x\n", hr));
err = hr;
}
}
else
{
LogFlowFunc(("GetModuleHandle(USER32) failed, err(%d)\n", GetLastError()));
err = ERROR_NOT_SUPPORTED;
}
}
else
{
LogFlowFunc(("Could not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n"));
err = ERROR_NOT_SUPPORTED;
}
if (err == ERROR_SUCCESS)
{
err = vboxDispIfWddmInit(pIf);
}
return err;
}
static DWORD vboxDispIfSwitchToWDDM_W7(PVBOXDISPIF pIf)
{
return vboxDispIfSwitchToWDDM(pIf);
}
static DWORD vboxDispIfWDDMAdpHdcCreate(int iDisplay, HDC *phDc, DISPLAY_DEVICE *pDev)
{
DWORD winEr = ERROR_INVALID_STATE;
memset(pDev, 0, sizeof (*pDev));
pDev->cb = sizeof (*pDev);
for (int i = 0; ; ++i)
{
if (EnumDisplayDevices(NULL, /* LPCTSTR lpDevice */ i, /* DWORD iDevNum */
pDev, 0 /* DWORD dwFlags*/))
{
if (i == iDisplay || (iDisplay < 0 && pDev->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
{
HDC hDc = CreateDC(NULL, pDev->DeviceName, NULL, NULL);
if (hDc)
{
*phDc = hDc;
return NO_ERROR;
}
else
{
winEr = GetLastError();
LogFlowFunc(("CreateDC failed %d", winEr));
break;
}
}
LogFlowFunc(("display data no match display(%d): i(%d), flags(%d)", iDisplay, i, pDev->StateFlags));
}
else
{
winEr = GetLastError();
LogFlowFunc(("EnumDisplayDevices failed %d", winEr));
break;
}
}
LogFlowFunc(("vboxDispIfWDDMAdpHdcCreate failure branch %d", winEr));
return winEr;
}
static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess)
{
DWORD winEr = ERROR_SUCCESS;
VBOXDISPKMT_ADAPTER Adapter;
HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &Adapter);
if (!SUCCEEDED(hr))
{
LogFlowFunc(("vboxDispKmtOpenAdapter failed hr 0x%x\n", hr));
return hr;
}
D3DKMT_ESCAPE EscapeData = {0};
EscapeData.hAdapter = Adapter.hAdapter;
//EscapeData.hDevice = NULL;
EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
if (fHwAccess)
EscapeData.Flags.HardwareAccess = 1;
EscapeData.pPrivateDriverData = pEscape;
EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(cbData);
//EscapeData.hContext = NULL;
NTSTATUS Status = pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
if (NT_SUCCESS(Status))
winEr = ERROR_SUCCESS;
else
{
LogFlowFunc(("pfnD3DKMTEscape failed Status 0x%x\n", Status));
winEr = ERROR_GEN_FAILURE;
}
vboxDispKmtCloseAdapter(&Adapter);
return winEr;
}
#endif
DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
{
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
case VBOXDISPIF_MODE_XPDM:
return vboxDispIfEscapeXPDM(pIf, pEscape, cbData, 1);
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
#endif
default:
LogFlowFunc(("unknown mode (%d)\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}
DWORD VBoxDispIfEscapeInOut(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, int cbData)
{
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
case VBOXDISPIF_MODE_XPDM:
return vboxDispIfEscapeXPDM(pIf, pEscape, cbData, 0);
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfEscapeWDDM(pIf, pEscape, cbData, TRUE /* BOOL fHwAccess */);
#endif
default:
LogFlowFunc(("unknown mode (%d)\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}
#ifdef VBOX_WITH_WDDM
#define VBOXRR_TIMER_ID 1234
typedef struct VBOXRR
{
HANDLE hThread;
DWORD idThread;
HANDLE hEvent;
HWND hWnd;
CRITICAL_SECTION CritSect;
UINT_PTR idTimer;
PCVBOXDISPIF pIf;
UINT iChangedMode;
BOOL fEnable;
BOOL fExtDispSup;
DISPLAY_DEVICE *paDisplayDevices;
DEVMODE *paDeviceModes;
UINT cDevModes;
} VBOXRR, *PVBOXRR;
static VBOXRR g_VBoxRr = {0};
#define VBOX_E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
#define VBOX_E_NOT_SUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
static void vboxRrRetryStopLocked()
{
PVBOXRR pMon = &g_VBoxRr;
if (pMon->pIf)
{
if (pMon->paDisplayDevices)
{
free(pMon->paDisplayDevices);
pMon->paDisplayDevices = NULL;
}
if (pMon->paDeviceModes)
{
free(pMon->paDeviceModes);
pMon->paDeviceModes = NULL;
}
if (pMon->idTimer)
{
KillTimer(pMon->hWnd, pMon->idTimer);
pMon->idTimer = 0;
}
pMon->cDevModes = 0;
pMon->pIf = NULL;
}
}
static void VBoxRrRetryStop()
{
PVBOXRR pMon = &g_VBoxRr;
EnterCriticalSection(&pMon->CritSect);
vboxRrRetryStopLocked();
LeaveCriticalSection(&pMon->CritSect);
}
//static DWORD vboxDispIfWddmValidateFixResize(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes);
static void vboxRrRetryReschedule()
{
}
static void VBoxRrRetrySchedule(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
{
PVBOXRR pMon = &g_VBoxRr;
EnterCriticalSection(&pMon->CritSect);
vboxRrRetryStopLocked();
pMon->pIf = pIf;
pMon->iChangedMode = iChangedMode;
pMon->fEnable = fEnable;
pMon->fExtDispSup = fExtDispSup;
/** @todo r=andy Why using malloc() instead of RTMemAlloc*? */
if (cDevModes)
{
pMon->paDisplayDevices = (DISPLAY_DEVICE*)malloc(sizeof (*paDisplayDevices) * cDevModes);
Assert(pMon->paDisplayDevices);
if (!pMon->paDisplayDevices)
{
LogFlowFunc(("malloc failed"));
vboxRrRetryStopLocked();
LeaveCriticalSection(&pMon->CritSect);
return;
}
memcpy(pMon->paDisplayDevices, paDisplayDevices, sizeof (*paDisplayDevices) * cDevModes);
pMon->paDeviceModes = (DEVMODE*)malloc(sizeof (*paDeviceModes) * cDevModes);
Assert(pMon->paDeviceModes);
if (!pMon->paDeviceModes)
{
LogFlowFunc(("malloc failed"));
vboxRrRetryStopLocked();
LeaveCriticalSection(&pMon->CritSect);
return;
}
memcpy(pMon->paDeviceModes, paDeviceModes, sizeof (*paDeviceModes) * cDevModes);
}
pMon->cDevModes = cDevModes;
pMon->idTimer = SetTimer(pMon->hWnd, VBOXRR_TIMER_ID, 1000, (TIMERPROC)NULL);
Assert(pMon->idTimer);
if (!pMon->idTimer)
{
LogFlowFunc(("SetTimer failed, err %ld\n", GetLastError()));
vboxRrRetryStopLocked();
}
LeaveCriticalSection(&pMon->CritSect);
}
static void vboxRrRetryPerform()
{
PVBOXRR pMon = &g_VBoxRr;
EnterCriticalSection(&pMon->CritSect);
if (pMon->pIf)
{
DWORD dwErr = vboxDispIfResizePerform(pMon->pIf, pMon->iChangedMode, pMon->fEnable, pMon->fExtDispSup, pMon->paDisplayDevices, pMon->paDeviceModes, pMon->cDevModes);
if (ERROR_RETRY != dwErr)
VBoxRrRetryStop();
else
vboxRrRetryReschedule();
}
LeaveCriticalSection(&pMon->CritSect);
}
static LRESULT CALLBACK vboxRrWndProc(HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch(uMsg)
{
case WM_DISPLAYCHANGE:
{
LogFlowFunc(("WM_DISPLAYCHANGE\n"));
VBoxRrRetryStop();
return 0;
}
case WM_TIMER:
{
if (wParam == VBOXRR_TIMER_ID)
{
LogFlowFunc(("VBOXRR_TIMER_ID\n"));
vboxRrRetryPerform();
return 0;
}
break;
}
case WM_CLOSE:
LogFlowFunc(("got WM_CLOSE for hwnd(0x%x)", hwnd));
return 0;
case WM_DESTROY:
LogFlowFunc(("got WM_DESTROY for hwnd(0x%x)", hwnd));
return 0;
case WM_NCHITTEST:
LogFlowFunc(("got WM_NCHITTEST for hwnd(0x%x)\n", hwnd));
return HTNOWHERE;
default:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
#define VBOXRRWND_NAME "VBoxRrWnd"
static HRESULT vboxRrWndCreate(HWND *phWnd)
{
HRESULT hr = S_OK;
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
/* Register the Window Class. */
WNDCLASS wc;
if (!GetClassInfo(hInstance, VBOXRRWND_NAME, &wc))
{
wc.style = 0;//CS_OWNDC;
wc.lpfnWndProc = vboxRrWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = VBOXRRWND_NAME;
if (!RegisterClass(&wc))
{
DWORD winErr = GetLastError();
LogFlowFunc(("RegisterClass failed, winErr(%d)\n", winErr));
hr = E_FAIL;
}
}
if (hr == S_OK)
{
HWND hWnd = CreateWindowEx (WS_EX_TOOLWINDOW,
VBOXRRWND_NAME, VBOXRRWND_NAME,
WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED,
-100, -100,
10, 10,
NULL, //GetDesktopWindow() /* hWndParent */,
NULL /* hMenu */,
hInstance,
NULL /* lpParam */);
Assert(hWnd);
if (hWnd)
{
*phWnd = hWnd;
}
else
{
DWORD winErr = GetLastError();
LogFlowFunc(("CreateWindowEx failed, winErr(%d)\n", winErr));
hr = E_FAIL;
}
}
return hr;
}
static HRESULT vboxRrWndDestroy(HWND hWnd)
{
BOOL bResult = DestroyWindow(hWnd);
if (bResult)
return S_OK;
DWORD winErr = GetLastError();
LogFlowFunc(("DestroyWindow failed, winErr(%d) for hWnd(0x%x)\n", winErr, hWnd));
return HRESULT_FROM_WIN32(winErr);
}
static HRESULT vboxRrWndInit()
{
PVBOXRR pMon = &g_VBoxRr;
return vboxRrWndCreate(&pMon->hWnd);
}
HRESULT vboxRrWndTerm()
{
PVBOXRR pMon = &g_VBoxRr;
HRESULT tmpHr = vboxRrWndDestroy(pMon->hWnd);
Assert(tmpHr == S_OK);
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
UnregisterClass(VBOXRRWND_NAME, hInstance);
return S_OK;
}
#define WM_VBOXRR_INIT_QUIT (WM_APP+2)
HRESULT vboxRrRun()
{
PVBOXRR pMon = &g_VBoxRr;
MSG Msg;
HRESULT hr = S_FALSE;
PeekMessage(&Msg,
NULL /* HWND hWnd */,
WM_USER /* UINT wMsgFilterMin */,
WM_USER /* UINT wMsgFilterMax */,
PM_NOREMOVE);
do
{
BOOL bResult = GetMessage(&Msg,
0 /*HWND hWnd*/,
0 /*UINT wMsgFilterMin*/,
0 /*UINT wMsgFilterMax*/
);
if(!bResult) /* WM_QUIT was posted */
{
hr = S_FALSE;
LogFlowFunc(("GetMessage returned FALSE\n"));
VBoxRrRetryStop();
break;
}
if(bResult == -1) /* error occurred */
{
DWORD winEr = GetLastError();
hr = HRESULT_FROM_WIN32(winEr);
/* just ensure we never return success in this case */
Assert(hr != S_OK);
Assert(hr != S_FALSE);
if (hr == S_OK || hr == S_FALSE)
hr = E_FAIL;
LogFlowFunc(("GetMessage returned -1, err %d\n", winEr));
VBoxRrRetryStop();
break;
}
switch (Msg.message)
{
case WM_VBOXRR_INIT_QUIT:
case WM_CLOSE:
{
LogFlowFunc(("closing Rr %d\n", Msg.message));
VBoxRrRetryStop();
PostQuitMessage(0);
break;
}
default:
TranslateMessage(&Msg);
DispatchMessage(&Msg);
break;
}
} while (1);
return 0;
}
static DWORD WINAPI vboxRrRunnerThread(void *pvUser)
{
PVBOXRR pMon = &g_VBoxRr;
BOOL bRc = SetEvent(pMon->hEvent);
if (!bRc)
{
DWORD winErr = GetLastError();
LogFlowFunc(("SetEvent failed, winErr = (%d)", winErr));
HRESULT tmpHr = HRESULT_FROM_WIN32(winErr);
Assert(tmpHr != S_OK);
}
HRESULT hr = vboxRrWndInit();
Assert(hr == S_OK);
if (hr == S_OK)
{
hr = vboxRrRun();
Assert(hr == S_OK);
vboxRrWndTerm();
}
return 0;
}
HRESULT VBoxRrInit()
{
HRESULT hr = E_FAIL;
PVBOXRR pMon = &g_VBoxRr;
memset(pMon, 0, sizeof (*pMon));
InitializeCriticalSection(&pMon->CritSect);
pMon->hEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes*/
TRUE, /* BOOL bManualReset*/
FALSE, /* BOOL bInitialState */
NULL /* LPCTSTR lpName */
);
if (pMon->hEvent)
{
pMon->hThread = CreateThread(NULL /* LPSECURITY_ATTRIBUTES lpThreadAttributes */,
0 /* SIZE_T dwStackSize */,
vboxRrRunnerThread,
pMon,
0 /* DWORD dwCreationFlags */,
&pMon->idThread);
if (pMon->hThread)
{
DWORD dwResult = WaitForSingleObject(pMon->hEvent, INFINITE);
if (dwResult == WAIT_OBJECT_0)
return S_OK;
else
{
LogFlowFunc(("WaitForSingleObject failed"));
hr = E_FAIL;
}
}
else
{
DWORD winErr = GetLastError();
LogFlowFunc(("CreateThread failed, winErr = (%d)", winErr));
hr = HRESULT_FROM_WIN32(winErr);
Assert(hr != S_OK);
}
CloseHandle(pMon->hEvent);
}
else
{
DWORD winErr = GetLastError();
LogFlowFunc(("CreateEvent failed, winErr = (%d)", winErr));
hr = HRESULT_FROM_WIN32(winErr);
Assert(hr != S_OK);
}
DeleteCriticalSection(&pMon->CritSect);
return hr;
}
VOID VBoxRrTerm()
{
HRESULT hr;
PVBOXRR pMon = &g_VBoxRr;
if (!pMon->hThread)
return;
BOOL bResult = PostThreadMessage(pMon->idThread, WM_VBOXRR_INIT_QUIT, 0, 0);
DWORD winErr;
if (bResult
|| (winErr = GetLastError()) == ERROR_INVALID_THREAD_ID) /* <- could be that the thread is terminated */
{
DWORD dwErr = WaitForSingleObject(pMon->hThread, INFINITE);
if (dwErr == WAIT_OBJECT_0)
{
hr = S_OK;
}
else
{
winErr = GetLastError();
hr = HRESULT_FROM_WIN32(winErr);
}
}
else
{
hr = HRESULT_FROM_WIN32(winErr);
}
DeleteCriticalSection(&pMon->CritSect);
CloseHandle(pMon->hThread);
pMon->hThread = 0;
CloseHandle(pMon->hEvent);
pMon->hThread = 0;
}
static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf)
{
HRESULT hr = VBoxRrInit();
if (SUCCEEDED(hr))
{
return ERROR_SUCCESS;
}
LogFlowFunc(("VBoxRrInit failed hr 0x%x\n", hr));
return hr;
}
static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf)
{
VBoxRrTerm();
}
static DWORD vboxDispIfQueryDisplayConnection(VBOXDISPIF_OP *pOp, UINT32 iDisplay, BOOL *pfConnected)
{
if (pOp->pIf->enmMode == VBOXDISPIF_MODE_WDDM)
{
/* @todo: do we need ti impl it? */
*pfConnected = TRUE;
return ERROR_SUCCESS;
}
*pfConnected = FALSE;
VBOXDISPIF_WDDM_DISPCFG DispCfg;
DWORD winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
return winEr;
}
int idx = vboxDispIfWddmDcSearchPath(&DispCfg, iDisplay, iDisplay);
*pfConnected = (idx >= 0);
vboxDispIfWddmDcTerm(&DispCfg);
return ERROR_SUCCESS;
}
static DWORD vboxDispIfWaitDisplayDataInited(VBOXDISPIF_OP *pOp, const uint8_t *pu8DisplayMask)
{
DWORD winEr = ERROR_SUCCESS;
do
{
Sleep(100);
D3DKMT_POLLDISPLAYCHILDREN PollData = {0};
PollData.hAdapter = pOp->Adapter.hAdapter;
PollData.NonDestructiveOnly = 1;
NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTPollDisplayChildren(&PollData);
if (Status != 0)
{
LogFlowFunc(("pfnD3DKMTPollDisplayChildren failed, Status (0x%x)\n", Status));
continue;
}
BOOL fFound = FALSE;
#if 0
for (UINT i = 0; i < VBOXWDDM_SCREENMASK_SIZE; ++i)
{
if (pu8DisplayMask && !ASMBitTest(pu8DisplayMask, i))
continue;
BOOL fConnected = FALSE;
winEr = vboxDispIfQueryDisplayConnection(pOp, i, &fConnected);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfQueryDisplayConnection winEr %d\n", winEr));
return winEr;
}
if (!fConnected)
{
LogFlowFunc(("Display %d not connected, not expected\n", i));
fFound = TRUE;
break;
}
}
#endif
if (!fFound)
break;
} while (1);
return winEr;
}
static DWORD vboxDispIfReninitModesWDDM(VBOXDISPIF_OP *pOp, const uint8_t *pScreenIdMask)
{
DWORD winEr = ERROR_SUCCESS;
VBOXDISPIFESCAPE_REINITVIDEOMODESBYMASK EscData = {0};
EscData.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK;
memcpy(EscData.ScreenMask, pScreenIdMask, sizeof (EscData.ScreenMask));
D3DKMT_ESCAPE EscapeData = {0};
EscapeData.hAdapter = pOp->Adapter.hAdapter;
#ifdef VBOX_DISPIF_WITH_OPCONTEXT
/* win8.1 does not allow context-based escapes for display-only mode */
EscapeData.hDevice = pOp->Device.hDevice;
EscapeData.hContext = pOp->Context.hContext;
#endif
EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
EscapeData.Flags.HardwareAccess = 1;
EscapeData.pPrivateDriverData = &EscData;
EscapeData.PrivateDriverDataSize = sizeof (EscData);
NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
if (NT_SUCCESS(Status))
winEr = ERROR_SUCCESS;
else
{
LogFlowFunc(("pfnD3DKMTEscape VBOXESC_REINITVIDEOMODESBYMASK failed Status 0x%x\n", Status));
winEr = ERROR_GEN_FAILURE;
}
winEr = vboxDispIfWaitDisplayDataInited(pOp, pScreenIdMask);
if (winEr != NO_ERROR)
LogFlowFunc(("Failed vboxDispIfWaitDisplayDataInited winEr %d\n", winEr));
return winEr;
}
DWORD vboxDispIfCancelPendingResizeWDDM(PCVBOXDISPIF const pIf)
{
LogFlowFunc(("cancelling pending resize\n"));
VBoxRrRetryStop();
return NO_ERROR;
}
static DWORD vboxDispIfWddmResizeDisplayVista(DEVMODE *paDeviceModes, DISPLAY_DEVICE *paDisplayDevices, DWORD cDevModes, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup)
{
/* Without this, Windows will not ask the miniport for its
* mode table but uses an internal cache instead.
*/
for (DWORD i = 0; i < cDevModes; i++)
{
DEVMODE tempDevMode;
ZeroMemory (&tempDevMode, sizeof (tempDevMode));
tempDevMode.dmSize = sizeof(DEVMODE);
EnumDisplaySettings((LPSTR)paDisplayDevices[i].DeviceName, 0xffffff, &tempDevMode);
LogFlowFunc(("ResizeDisplayDevice: EnumDisplaySettings last error %d\n", GetLastError ()));
}
DWORD winEr = EnableAndResizeDispDev(paDeviceModes, paDisplayDevices, cDevModes, iChangedMode, paDeviceModes[iChangedMode].dmPelsWidth, paDeviceModes[iChangedMode].dmPelsHeight,
paDeviceModes[iChangedMode].dmBitsPerPel, paDeviceModes[iChangedMode].dmPosition.x, paDeviceModes[iChangedMode].dmPosition.y, fEnable, fExtDispSup);
if (winEr != NO_ERROR)
LogFlowFunc(("Failed EnableAndResizeDispDev winEr %d\n", winEr));
return winEr;
}
static DWORD vboxDispIfResizePerform(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
{
DWORD winEr;
if (pIf->enmMode > VBOXDISPIF_MODE_WDDM)
{
winEr = vboxDispIfWddmResizeDisplay(pIf, iChangedMode, fEnable, paDisplayDevices, paDeviceModes, cDevModes);
if (winEr != NO_ERROR)
LogFlowFunc(("Failed vboxDispIfWddmResizeDisplay winEr %d\n", winEr));
}
else
{
winEr = vboxDispIfWddmResizeDisplayVista(paDeviceModes, paDisplayDevices, cDevModes, iChangedMode, fEnable, fExtDispSup);
if (winEr != NO_ERROR)
LogFlowFunc(("Failed vboxDispIfWddmResizeDisplayVista winEr %d\n", winEr));
}
return winEr;
}
DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
{
UINT cbVidPnInfo = VBOXWDDM_RECOMMENDVIDPN_SIZE(cDevModes);
PVBOXWDDM_RECOMMENDVIDPN pVidPnInfo = (PVBOXWDDM_RECOMMENDVIDPN)alloca(cbVidPnInfo);
pVidPnInfo->cScreenInfos = cDevModes;
D3DKMT_HANDLE hAdapter = NULL;
DWORD winEr = NO_ERROR;
UINT i = 0;
LogFlowFunc(("vboxDispIfResizeModesWDDM\n"));
VBoxRrRetryStop();
VBOXDISPIF_OP Op;
winEr = vboxDispIfOpBegin(pIf, &Op);
if (winEr != NO_ERROR)
{
LogFlowFunc(("vboxDispIfOpBegin failed winEr 0x%x", winEr));
return winEr;
}
// if (fEnable)
{
uint8_t ScreenMask[VBOXWDDM_SCREENMASK_SIZE] = {0};
ASMBitSet(ScreenMask, iChangedMode);
vboxDispIfReninitModesWDDM(&Op, ScreenMask);
}
winEr = vboxDispIfResizePerform(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
if (winEr == ERROR_RETRY)
{
VBoxRrRetrySchedule(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
/* just pretend everything is fine so far */
winEr = NO_ERROR;
}
vboxDispIfOpEnd(&Op);
return winEr;
}
static DWORD vboxDispIfWddmEnableDisplays(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnabled, BOOL fSetTopology, DEVMODE *pDeviceMode)
{
VBOXDISPIF_WDDM_DISPCFG DispCfg;
DWORD winEr;
int iPath;
winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
return winEr;
}
UINT cChangeIds = 0;
UINT *pChangeIds = (UINT*)alloca(cIds * sizeof (*pChangeIds));
if (!pChangeIds)
{
LogFlowFunc(("Failed to alloc change ids\n"));
winEr = ERROR_OUTOFMEMORY;
goto done;
}
for (UINT i = 0; i < cIds; ++i)
{
UINT Id = pIds[i];
bool fIsDup = false;
for (UINT j = 0; j < cChangeIds; ++j)
{
if (pChangeIds[j] == Id)
{
fIsDup = true;
break;
}
}
if (fIsDup)
continue;
iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id);
if (!((iPath >= 0) && (DispCfg.pPathInfoArray[iPath].flags & DISPLAYCONFIG_PATH_ACTIVE)) != !fEnabled)
{
pChangeIds[cChangeIds] = Id;
++cChangeIds;
}
}
if (cChangeIds == 0)
{
LogFlowFunc(("vboxDispIfWddmEnableDisplay: settings are up to date\n"));
winEr = ERROR_SUCCESS;
goto done;
}
/* we want to set primary for every disabled for non-topoly mode only */
winEr = vboxDispIfWddmDcSettingsIncludeAllTargets(&DispCfg);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcSettingsIncludeAllTargets winEr %d\n", winEr));
return winEr;
}
if (fSetTopology)
vboxDispIfWddmDcSettingsInvalidateModeIndeces(&DispCfg);
for (UINT i = 0; i < cChangeIds; ++i)
{
UINT Id = pChangeIds[i];
/* re-query paths */
iPath = vboxDispIfWddmDcSearchPath(&DispCfg, -1, Id);
if (iPath < 0)
{
LogFlowFunc(("path index not found while it should"));
winEr = ERROR_GEN_FAILURE;
goto done;
}
winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, pDeviceMode, !fEnabled || fSetTopology, fEnabled);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcSettingsUpdate winEr %d\n", winEr));
goto done;
}
}
if (!fSetTopology)
vboxDispIfWddmDcSettingsAttachDisbledToPrimary(&DispCfg);
#if 0
/* ensure the zero-index (primary) screen is enabled */
iPath = vboxDispIfWddmDcSearchPath(&DispCfg, 0, 0);
if (iPath < 0)
{
LogFlowFunc(("path index not found while it should"));
winEr = ERROR_GEN_FAILURE;
goto done;
}
winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, /* just re-use device node here*/ pDeviceMode, fSetTopology, TRUE);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcSettingsUpdate winEr %d\n", winEr));
goto done;
}
#endif
UINT fSetFlags = !fSetTopology ? (SDC_USE_SUPPLIED_DISPLAY_CONFIG) : (SDC_ALLOW_PATH_ORDER_CHANGES | SDC_TOPOLOGY_SUPPLIED);
winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
if (winEr != ERROR_SUCCESS)
{
if (!fSetTopology)
{
LogFlowFunc(("vboxDispIfWddmDcSet validation failed winEr, trying with changes %d\n", winEr));
fSetFlags |= SDC_ALLOW_CHANGES;
}
else
{
LogFlowFunc(("vboxDispIfWddmDcSet topology validation failed winEr %d\n", winEr));
goto done;
}
}
if (!fSetTopology)
fSetFlags |= SDC_SAVE_TO_DATABASE;
winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_APPLY);
if (winEr != ERROR_SUCCESS)
LogFlowFunc(("vboxDispIfWddmDcSet apply failed winEr %d\n", winEr));
done:
vboxDispIfWddmDcTerm(&DispCfg);
return winEr;
}
static DWORD vboxDispIfWddmEnableDisplaysTryingTopology(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnable)
{
DWORD winEr = vboxDispIfWddmEnableDisplays(pIf, cIds, pIds, fEnable, FALSE, NULL);
if (winEr != ERROR_SUCCESS)
{
if (fEnable)
LogFlowFunc(("Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr));
else
LogFlowFunc(("Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr));
winEr = vboxDispIfWddmEnableDisplays(pIf, cIds, pIds, fEnable, TRUE, NULL);
if (winEr != ERROR_SUCCESS)
LogFlowFunc(("Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr));
}
return winEr;
}
static DWORD vboxDispIfWddmResizeDisplay(PCVBOXDISPIF const pIf, UINT Id, BOOL fEnable, DISPLAY_DEVICE * paDisplayDevices, DEVMODE *paDeviceMode, UINT devModes)
{
VBOXDISPIF_WDDM_DISPCFG DispCfg;
DWORD winEr;
int iPath;
winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcCreate\n"));
return winEr;
}
iPath = vboxDispIfWddmDcSearchActivePath(&DispCfg, Id, Id);
if (iPath < 0)
{
vboxDispIfWddmDcTerm(&DispCfg);
if (!fEnable)
{
/* nothing to be done here, just leave */
return ERROR_SUCCESS;
}
winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr));
return winEr;
}
winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcCreate winEr %d\n", winEr));
return winEr;
}
iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id);
if (iPath < 0)
{
LogFlowFunc(("path (%d) is still disabled, going to retry winEr %d\n", winEr));
vboxDispIfWddmDcTerm(&DispCfg);
return ERROR_RETRY;
}
}
Assert(iPath >= 0);
if (!fEnable)
{
/* need to disable it, and we are done */
vboxDispIfWddmDcTerm(&DispCfg);
winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr));
return winEr;
}
return winEr;
}
Assert(fEnable);
winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, &paDeviceMode[Id], FALSE, fEnable);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("Failed vboxDispIfWddmDcSettingsUpdate\n"));
vboxDispIfWddmDcTerm(&DispCfg);
return winEr;
}
UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG;
winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
fSetFlags |= SDC_ALLOW_CHANGES;
}
winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY);
if (winEr != ERROR_SUCCESS)
{
LogFlowFunc(("pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr));
}
vboxDispIfWddmDcTerm(&DispCfg);
return winEr;
}
#endif /* VBOX_WITH_WDDM */
DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes)
{
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
return ERROR_NOT_SUPPORTED;
case VBOXDISPIF_MODE_XPDM:
return ERROR_NOT_SUPPORTED;
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfResizeModesWDDM(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes);
#endif
default:
LogFlowFunc(("unknown mode (%d)\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}
DWORD VBoxDispIfCancelPendingResize(PCVBOXDISPIF const pIf)
{
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
return NO_ERROR;
case VBOXDISPIF_MODE_XPDM:
return NO_ERROR;
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfCancelPendingResizeWDDM(pIf);
#endif
default:
LogFlowFunc(("unknown mode (%d)\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}
static DWORD vboxDispIfConfigureTargetsWDDM(VBOXDISPIF_OP *pOp, uint32_t *pcConnected)
{
VBOXDISPIFESCAPE EscapeHdr = {0};
EscapeHdr.escapeCode = VBOXESC_CONFIGURETARGETS;
EscapeHdr.u32CmdSpecific = 0;
D3DKMT_ESCAPE EscapeData = {0};
EscapeData.hAdapter = pOp->Adapter.hAdapter;
#ifdef VBOX_DISPIF_WITH_OPCONTEXT
/* win8.1 does not allow context-based escapes for display-only mode */
EscapeData.hDevice = pOp->Device.hDevice;
EscapeData.hContext = pOp->Context.hContext;
#endif
EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
EscapeData.Flags.HardwareAccess = 1;
EscapeData.pPrivateDriverData = &EscapeHdr;
EscapeData.PrivateDriverDataSize = sizeof (EscapeHdr);
NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
if (NT_SUCCESS(Status))
{
if (pcConnected)
*pcConnected = EscapeHdr.u32CmdSpecific;
return NO_ERROR;
}
LogFlowFunc(("pfnD3DKMTEscape VBOXESC_CONFIGURETARGETS failed Status 0x%x\n", Status));
return Status;
}
static DWORD vboxDispIfResizeStartedWDDMOp(VBOXDISPIF_OP *pOp)
{
DWORD NumDevices = VBoxGetDisplayConfigCount();
if (NumDevices == 0)
{
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: Zero devices found\n"));
return ERROR_GEN_FAILURE;
}
DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
DWORD DevNum = 0;
DWORD DevPrimaryNum = 0;
DWORD winEr = VBoxGetDisplayConfig(NumDevices, &DevPrimaryNum, &DevNum, paDisplayDevices, paDeviceModes);
if (winEr != NO_ERROR)
{
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed, %d\n", winEr));
return winEr;
}
if (NumDevices != DevNum)
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != DevNum(%d)\n", NumDevices, DevNum));
uint32_t cConnected = 0;
winEr = vboxDispIfConfigureTargetsWDDM(pOp, &cConnected);
if (winEr != NO_ERROR)
{
LogFlowFunc(("vboxDispIfConfigureTargetsWDDM failed winEr 0x%x\n", winEr));
return winEr;
}
if (!cConnected)
{
LogFlowFunc(("all targets already connected, nothing to do\n"));
return NO_ERROR;
}
winEr = vboxDispIfWaitDisplayDataInited(pOp, NULL);
if (winEr != NO_ERROR)
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: vboxDispIfWaitDisplayDataInited failed winEr 0x%x\n", winEr));
DWORD NewNumDevices = VBoxGetDisplayConfigCount();
if (NewNumDevices == 0)
{
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: Zero devices found\n"));
return ERROR_GEN_FAILURE;
}
if (NewNumDevices != NumDevices)
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != NewNumDevices(%d)\n", NumDevices, NewNumDevices));
DISPLAY_DEVICE *paNewDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NewNumDevices);
DEVMODE *paNewDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NewNumDevices);
DWORD NewDevNum = 0;
DWORD NewDevPrimaryNum = 0;
winEr = VBoxGetDisplayConfig(NewNumDevices, &NewDevPrimaryNum, &NewDevNum, paNewDisplayDevices, paNewDeviceModes);
if (winEr != NO_ERROR)
{
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed for new devices, %d\n", winEr));
return winEr;
}
if (NewNumDevices != NewDevNum)
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: NewNumDevices(%d) != NewDevNum(%d)\n", NewNumDevices, NewDevNum));
DWORD minDevNum = RT_MIN(DevNum, NewDevNum);
UINT *pIds = (UINT*)alloca (sizeof (UINT) * minDevNum);
UINT cIds = 0;
for (DWORD i = 0; i < minDevNum; ++i)
{
if ((paNewDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE)
&& !(paDisplayDevices[i].StateFlags & DISPLAY_DEVICE_ACTIVE))
{
pIds[cIds] = i;
++cIds;
}
}
if (!cIds)
{
/* this is something we would not regularly expect */
LogFlowFunc(("all targets already have proper config, nothing to do\n"));
return NO_ERROR;
}
if (pOp->pIf->enmMode > VBOXDISPIF_MODE_WDDM)
{
winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pOp->pIf, cIds, pIds, FALSE);
if (winEr != NO_ERROR)
LogFlowFunc(("vboxDispIfWddmEnableDisplaysTryingTopology failed to record current settings, %d, ignoring\n", winEr));
}
else
{
for (DWORD i = 0; i < cIds; ++i)
{
winEr = vboxDispIfWddmResizeDisplayVista(paNewDeviceModes, paNewDisplayDevices, NewDevNum, i, FALSE, TRUE);
if (winEr != NO_ERROR)
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp: vboxDispIfWddmResizeDisplayVista failed winEr 0x%x\n", winEr));
}
}
return winEr;
}
static DWORD vboxDispIfResizeStartedWDDM(PCVBOXDISPIF const pIf)
{
VBOXDISPIF_OP Op;
DWORD winEr = vboxDispIfOpBegin(pIf, &Op);
if (winEr != NO_ERROR)
{
LogFlowFunc(("vboxDispIfOpBegin failed winEr 0x%x\n", winEr));
return winEr;
}
winEr = vboxDispIfResizeStartedWDDMOp(&Op);
if (winEr != NO_ERROR)
{
LogFlowFunc(("vboxDispIfResizeStartedWDDMOp failed winEr 0x%x\n", winEr));
}
vboxDispIfOpEnd(&Op);
return winEr;
}
DWORD VBoxDispIfResizeStarted(PCVBOXDISPIF const pIf)
{
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
return NO_ERROR;
case VBOXDISPIF_MODE_XPDM:
return NO_ERROR;
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfResizeStartedWDDM(pIf);
#endif
default:
LogFlowFunc(("unknown mode (%d)\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}
static DWORD vboxDispIfSwitchToXPDM_NT4(PVBOXDISPIF pIf)
{
return NO_ERROR;
}
static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf)
{
DWORD err = NO_ERROR;
AssertBreakpoint();
OSVERSIONINFO OSinfo;
OSinfo.dwOSVersionInfoSize = sizeof (OSinfo);
GetVersionEx (&OSinfo);
if (OSinfo.dwMajorVersion >= 5)
{
HMODULE hUser = GetModuleHandle("user32.dll");
if (NULL != hUser)
{
bool bSupported = true;
*(uintptr_t *)&pIf->modeData.xpdm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
LogFlowFunc(("pfnChangeDisplaySettingsEx = %p\n", pIf->modeData.xpdm.pfnChangeDisplaySettingsEx));
bSupported &= !!(pIf->modeData.xpdm.pfnChangeDisplaySettingsEx);
if (!bSupported)
{
LogFlowFunc(("pfnChangeDisplaySettingsEx function pointer failed to initialize\n"));
err = ERROR_NOT_SUPPORTED;
}
}
else
{
LogFlowFunc(("failed to get USER32 handle, err (%d)\n", GetLastError()));
err = ERROR_NOT_SUPPORTED;
}
}
else
{
LogFlowFunc(("can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n"));
err = ERROR_NOT_SUPPORTED;
}
return err;
}
DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_MODE *penmOldMode)
{
/* @todo: may need to addd synchronization in case we want to change modes dynamically
* i.e. currently the mode is supposed to be initialized once on service initialization */
if (penmOldMode)
*penmOldMode = pIf->enmMode;
if (enmMode == pIf->enmMode)
return NO_ERROR;
#ifdef VBOX_WITH_WDDM
if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM)
{
vboxDispIfWddmTerm(pIf);
vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks);
}
#endif
DWORD err = NO_ERROR;
switch (enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
LogFlowFunc(("request to switch to VBOXDISPIF_MODE_XPDM_NT4\n"));
err = vboxDispIfSwitchToXPDM_NT4(pIf);
if (err == NO_ERROR)
{
LogFlowFunc(("successfully switched to XPDM_NT4 mode\n"));
pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4;
}
else
LogFlowFunc(("failed to switch to XPDM_NT4 mode, err (%d)\n", err));
break;
case VBOXDISPIF_MODE_XPDM:
LogFlowFunc(("request to switch to VBOXDISPIF_MODE_XPDM\n"));
err = vboxDispIfSwitchToXPDM(pIf);
if (err == NO_ERROR)
{
LogFlowFunc(("successfully switched to XPDM mode\n"));
pIf->enmMode = VBOXDISPIF_MODE_XPDM;
}
else
LogFlowFunc(("failed to switch to XPDM mode, err (%d)\n", err));
break;
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
{
LogFlowFunc(("request to switch to VBOXDISPIF_MODE_WDDM\n"));
err = vboxDispIfSwitchToWDDM(pIf);
if (err == NO_ERROR)
{
LogFlowFunc(("successfully switched to WDDM mode\n"));
pIf->enmMode = VBOXDISPIF_MODE_WDDM;
}
else
LogFlowFunc(("failed to switch to WDDM mode, err (%d)\n", err));
break;
}
case VBOXDISPIF_MODE_WDDM_W7:
{
LogFlowFunc(("request to switch to VBOXDISPIF_MODE_WDDM_W7\n"));
err = vboxDispIfSwitchToWDDM_W7(pIf);
if (err == NO_ERROR)
{
LogFlowFunc(("successfully switched to WDDM mode\n"));
pIf->enmMode = VBOXDISPIF_MODE_WDDM_W7;
}
else
LogFlowFunc(("failed to switch to WDDM mode, err (%d)\n", err));
break;
}
#endif
default:
err = ERROR_INVALID_PARAMETER;
break;
}
return err;
}
static DWORD vboxDispIfSeamlesCreateWDDM(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent)
{
HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &pSeamless->modeData.wddm.Adapter);
if (SUCCEEDED(hr))
{
#ifdef VBOX_DISPIF_WITH_OPCONTEXT
hr = vboxDispKmtCreateDevice(&pSeamless->modeData.wddm.Adapter, &pSeamless->modeData.wddm.Device);
if (SUCCEEDED(hr))
{
hr = vboxDispKmtCreateContext(&pSeamless->modeData.wddm.Device, &pSeamless->modeData.wddm.Context, VBOXWDDM_CONTEXT_TYPE_CUSTOM_DISPIF_SEAMLESS,
0, 0, hEvent, 0ULL);
if (SUCCEEDED(hr))
#endif
return ERROR_SUCCESS;
#ifdef VBOX_DISPIF_WITH_OPCONTEXT
else
LogFlowFunc(("vboxDispKmtCreateContext failed hr 0x%x", hr));
vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device);
}
else
LogFlowFunc(("vboxDispKmtCreateDevice failed hr 0x%x", hr));
vboxDispKmtCloseAdapter(&pSeamless->modeData.wddm.Adapter);
#endif
}
return hr;
}
static DWORD vboxDispIfSeamlesTermWDDM(VBOXDISPIF_SEAMLESS *pSeamless)
{
#ifdef VBOX_DISPIF_WITH_OPCONTEXT
vboxDispKmtDestroyContext(&pSeamless->modeData.wddm.Context);
vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device);
#endif
vboxDispKmtCloseAdapter(&pSeamless->modeData.wddm.Adapter);
return NO_ERROR;
}
static DWORD vboxDispIfSeamlesSubmitWDDM(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData)
{
D3DKMT_ESCAPE EscapeData = {0};
EscapeData.hAdapter = pSeamless->modeData.wddm.Adapter.hAdapter;
#ifdef VBOX_DISPIF_WITH_OPCONTEXT
EscapeData.hDevice = pSeamless->modeData.wddm.Device.hDevice;
EscapeData.hContext = pSeamless->modeData.wddm.Context.hContext;
#endif
EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
/*EscapeData.Flags.HardwareAccess = 1;*/
EscapeData.pPrivateDriverData = pData;
EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(cbData);
NTSTATUS Status = pSeamless->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData);
if (NT_SUCCESS(Status))
return ERROR_SUCCESS;
LogFlowFunc(("pfnD3DKMTEscape Seamless failed Status 0x%x\n", Status));
return Status;
}
DWORD VBoxDispIfSeamlesCreate(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent)
{
memset(pSeamless, 0, sizeof (*pSeamless));
pSeamless->pIf = pIf;
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
case VBOXDISPIF_MODE_XPDM:
return NO_ERROR;
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfSeamlesCreateWDDM(pIf, pSeamless, hEvent);
#endif
default:
LogFlowFunc(("VBoxDispIfSeamlesCreate: invalid mode %d\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}
DWORD VBoxDispIfSeamlesTerm(VBOXDISPIF_SEAMLESS *pSeamless)
{
PCVBOXDISPIF const pIf = pSeamless->pIf;
DWORD winEr;
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
case VBOXDISPIF_MODE_XPDM:
winEr = NO_ERROR;
break;
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
winEr = vboxDispIfSeamlesTermWDDM(pSeamless);
break;
#endif
default:
LogFlowFunc(("VBoxDispIfSeamlesTerm: invalid mode %d\n", pIf->enmMode));
winEr = ERROR_INVALID_PARAMETER;
break;
}
if (winEr == NO_ERROR)
memset(pSeamless, 0, sizeof (*pSeamless));
return winEr;
}
DWORD VBoxDispIfSeamlesSubmit(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData)
{
PCVBOXDISPIF const pIf = pSeamless->pIf;
if (pData->escapeCode != VBOXESC_SETVISIBLEREGION)
{
LogFlowFunc(("invalid escape code for Seamless submit %d\n", pData->escapeCode));
return ERROR_INVALID_PARAMETER;
}
switch (pIf->enmMode)
{
case VBOXDISPIF_MODE_XPDM_NT4:
case VBOXDISPIF_MODE_XPDM:
return VBoxDispIfEscape(pIf, pData, cbData);
#ifdef VBOX_WITH_WDDM
case VBOXDISPIF_MODE_WDDM:
case VBOXDISPIF_MODE_WDDM_W7:
return vboxDispIfSeamlesSubmitWDDM(pSeamless, pData, cbData);
#endif
default:
LogFlowFunc(("VBoxDispIfSeamlesSubmit: invalid mode %d\n", pIf->enmMode));
return ERROR_INVALID_PARAMETER;
}
}