diff options
Diffstat (limited to 'src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp')
-rw-r--r-- | src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp | 2034 |
1 files changed, 1444 insertions, 590 deletions
diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp index 311b4626..422d2193 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.cpp @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -15,7 +15,7 @@ */ #include "VBoxTray.h" - +#define _WIN32_WINNT 0x0601 #include <iprt/log.h> #include <iprt/err.h> #include <iprt/assert.h> @@ -26,6 +26,517 @@ #include <iprt/asm.h> #endif +#include "VBoxDisplay.h" + +#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) + { + WARN(("VBoxTray: (WDDM) Failed GetDisplayConfigBufferSizes\n")); + return winEr; + } + + pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(cPathInfoArray * sizeof(DISPLAYCONFIG_PATH_INFO)); + if (!pPathInfoArray) + { + WARN(("VBoxTray: (WDDM) malloc failed!\n")); + return ERROR_OUTOFMEMORY; + } + pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(cModeInfoArray * sizeof(DISPLAYCONFIG_MODE_INFO)); + if (!pModeInfoArray) + { + WARN(("VBoxTray: (WDDM) malloc failed!\n")); + free(pPathInfoArray); + return ERROR_OUTOFMEMORY; + } + + winEr = gCtx.pfnQueryDisplayConfig(fFlags, &cPathInfoArray, pPathInfoArray, &cModeInfoArray, pModeInfoArray, NULL); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) Failed QueryDisplayConfig\n")); + 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)); + + if (pCfg->cPathInfoArray) + { + pCfgDst->pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(pCfg->cPathInfoArray * sizeof (DISPLAYCONFIG_PATH_INFO)); + if (!pCfgDst->pPathInfoArray) + { + WARN(("VBoxTray: (WDDM) 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) + { + WARN(("VBoxTray: (WDDM) 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) + { + WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcCreate Failed winEr %d\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) + { + WARN(("VBoxTray: (WDDM) 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) + { + + WARN(("VBoxTray: (WDDM) no source mode index specified")); + DWORD winEr = vboxDispIfWddmDcSettingsModeAdd(pCfg, &iSrcMode); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray:(WDDM) 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 */ + WARN(("VBoxTray: (WDDM) 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(("VBoxTray: (WDDM) 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) + Log(("VBoxTray:(WDDM) 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) + { + WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcQueryNumDisplays Failed winEr %d\n", winEr)); + return winEr; + } + + DISPLAYCONFIG_PATH_INFO *pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(cDisplays * sizeof(DISPLAYCONFIG_PATH_INFO)); + if (!pPathInfoArray) + { + WARN(("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) + { + WARN(("VBoxTray:(WDDM) 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) + { + WARN(("VBoxTray:(WDDM) vboxDispIfWddmDcCreate Failed winEr %d\n", winEr)); + free(pPathInfoArray); + return winEr; + } + fAllCfgInited = TRUE; + } + + idx = vboxDispIfWddmDcSearchPath(&AllCfg, i, i); + if (idx < 0) + { + WARN(("VBoxTray:(WDDM) %d %d path not supported\n", i, i)); + idx = vboxDispIfWddmDcSearchPath(pCfg, -1, i); + if (idx < 0) + { + WARN(("VBoxTray:(WDDM) %d %d path not supported\n", -1, i)); + } + } + + if (idx >= 0) + { + pPathInfoArray[i] = AllCfg.pPathInfoArray[idx]; + + if (pPathInfoArray[i].flags & DISPLAYCONFIG_PATH_ACTIVE) + { + WARN(("VBoxTray:(WDDM) 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 + WARN(("VBoxTray: vboxDispKmtCreateContext failed hr 0x%x", hr)); + + vboxDispKmtDestroyDevice(&pOp->Device); + } + else + WARN(("VBoxTray: 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 @@ -44,9 +555,11 @@ static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf); DWORD VBoxDispIfTerm(PVBOXDISPIF pIf) { #ifdef VBOX_WITH_WDDM - if (pIf->enmMode == VBOXDISPIF_MODE_WDDM) + if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM) { vboxDispIfWddmTerm(pIf); + + vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks); } #endif @@ -84,7 +597,7 @@ static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf) if (OSinfo.dwMajorVersion >= 6) { Log((__FUNCTION__": this is vista and up\n")); - HMODULE hUser = GetModuleHandle("USER32"); + HMODULE hUser = GetModuleHandle("user32.dll"); if (hUser) { *(uintptr_t *)&pIf->modeData.wddm.pfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA"); @@ -94,53 +607,39 @@ static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF pIf) *(uintptr_t *)&pIf->modeData.wddm.pfnEnumDisplayDevices = (uintptr_t)GetProcAddress(hUser, "EnumDisplayDevicesA"); Log((__FUNCTION__": VBoxDisplayInit: pfnEnumDisplayDevices = %p\n", pIf->modeData.wddm.pfnEnumDisplayDevices)); bSupported &= !!(pIf->modeData.wddm.pfnEnumDisplayDevices); - - /* this is vista and up */ - HMODULE hGdi32 = GetModuleHandle("gdi32"); - if (hGdi32 != NULL) + /* for win 7 and above */ + if (OSinfo.dwMinorVersion >= 1) { - pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromHdc"); - Log((__FUNCTION__"pfnD3DKMTOpenAdapterFromHdc = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc)); - bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc); - - pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName = (PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME)GetProcAddress(hGdi32, "D3DKMTOpenAdapterFromGdiDisplayName"); - Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName = %p\n", pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName)); - bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromGdiDisplayName); + *(uintptr_t *)&gCtx.pfnSetDisplayConfig = (uintptr_t)GetProcAddress(hUser, "SetDisplayConfig"); + Log((__FUNCTION__": VBoxDisplayInit: pfnSetDisplayConfig = %p\n", gCtx.pfnSetDisplayConfig)); + bSupported &= !!(gCtx.pfnSetDisplayConfig); - pIf->modeData.wddm.pfnD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(hGdi32, "D3DKMTCloseAdapter"); - Log((__FUNCTION__": pfnD3DKMTCloseAdapter = %p\n", pIf->modeData.wddm.pfnD3DKMTCloseAdapter)); - bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter); + *(uintptr_t *)&gCtx.pfnQueryDisplayConfig = (uintptr_t)GetProcAddress(hUser, "QueryDisplayConfig"); + Log((__FUNCTION__": VBoxDisplayInit: pfnQueryDisplayConfig = %p\n", gCtx.pfnQueryDisplayConfig)); + bSupported &= !!(gCtx.pfnQueryDisplayConfig); - pIf->modeData.wddm.pfnD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(hGdi32, "D3DKMTEscape"); - Log((__FUNCTION__": pfnD3DKMTEscape = %p\n", pIf->modeData.wddm.pfnD3DKMTEscape)); - bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTCloseAdapter); - - pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn = (PFND3DKMT_INVALIDATEACTIVEVIDPN)GetProcAddress(hGdi32, "D3DKMTInvalidateActiveVidPn"); - Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn = %p\n", pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn)); - bSupported &= !!(pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn); - - if (!bSupported) - { - Log((__FUNCTION__": one of pfnD3DKMT function pointers failed to initialize\n")); - err = ERROR_NOT_SUPPORTED; - } + *(uintptr_t *)&gCtx.pfnGetDisplayConfigBufferSizes = (uintptr_t)GetProcAddress(hUser, "GetDisplayConfigBufferSizes"); + Log((__FUNCTION__": VBoxDisplayInit: pfnGetDisplayConfigBufferSizes = %p\n", gCtx.pfnGetDisplayConfigBufferSizes)); + bSupported &= !!(gCtx.pfnGetDisplayConfigBufferSizes); } - else + + /* this is vista and up */ + HRESULT hr = vboxDispKmtCallbacksInit(&pIf->modeData.wddm.KmtCallbacks); + if (FAILED(hr)) { - Log((__FUNCTION__": GetModuleHandle(gdi32) failed, err(%d)\n", GetLastError())); - err = ERROR_NOT_SUPPORTED; + WARN(("VBoxTray: vboxDispKmtCallbacksInit failed hr 0x%x\n", hr)); + err = hr; } - } else { - Log((__FUNCTION__": GetModuleHandle(USER32) failed, err(%d)\n", GetLastError())); + WARN((__FUNCTION__": GetModuleHandle(USER32) failed, err(%d)\n", GetLastError())); err = ERROR_NOT_SUPPORTED; } } else { - Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n")); + WARN((__FUNCTION__": can not switch to VBOXDISPIF_MODE_WDDM, because os is not Vista or upper\n")); err = ERROR_NOT_SUPPORTED; } @@ -152,6 +651,11 @@ static DWORD vboxDispIfSwitchToWDDM(PVBOXDISPIF 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; @@ -174,179 +678,57 @@ static DWORD vboxDispIfWDDMAdpHdcCreate(int iDisplay, HDC *phDc, DISPLAY_DEVICE else { winEr = GetLastError(); - Assert(0); + WARN(("CreateDC failed %d", winEr)); break; } } + Log(("display data no match display(%d): i(%d), flags(%d)", iDisplay, i, pDev->StateFlags)); } else { winEr = GetLastError(); - Assert(0); + WARN(("EnumDisplayDevices failed %d", winEr)); break; } } + WARN(("vboxDispIfWDDMAdpHdcCreate failure branch %d", winEr)); return winEr; } - -typedef DECLCALLBACK(BOOLEAN) FNVBOXDISPIFWDDM_ADAPTEROP(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext); -typedef FNVBOXDISPIFWDDM_ADAPTEROP *PFNVBOXDISPIFWDDM_ADAPTEROP; -static DWORD vboxDispIfWDDMAdapterOp(PCVBOXDISPIF pIf, int iDisplay, PFNVBOXDISPIFWDDM_ADAPTEROP pfnOp, PVOID pContext) +static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess) { - D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0}; - DISPLAY_DEVICE DDev; - DWORD err = vboxDispIfWDDMAdpHdcCreate(iDisplay, &OpenAdapterData.hDc, &DDev); - Assert(err == NO_ERROR); - if (err == NO_ERROR) + DWORD winEr = ERROR_SUCCESS; + VBOXDISPKMT_ADAPTER Adapter; + HRESULT hr = vboxDispKmtOpenAdapter(&pIf->modeData.wddm.KmtCallbacks, &Adapter); + if (!SUCCEEDED(hr)) { - NTSTATUS Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData); - Assert(!Status); - if (!Status) - { - BOOLEAN bCloseAdapter = pfnOp(pIf, OpenAdapterData.hAdapter, &DDev, pContext); - - if (bCloseAdapter) - { - D3DKMT_CLOSEADAPTER ClosaAdapterData = {0}; - ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter; - Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData); - if (Status) - { - Log((__FUNCTION__": pfnD3DKMTCloseAdapter failed, Status (0x%x)\n", Status)); - } - } - } - else - { - Log((__FUNCTION__": pfnD3DKMTOpenAdapterFromGdiDisplayName failed, Status (0x%x)\n", Status)); - err = ERROR_GEN_FAILURE; - } - - DeleteDC(OpenAdapterData.hDc); + WARN(("VBoxTray: vboxDispKmtOpenAdapter failed hr 0x%x\n", hr)); + return hr; } - return err; -} - -typedef struct -{ - NTSTATUS Status; - PVBOXDISPIFESCAPE pEscape; - int cbData; - D3DDDI_ESCAPEFLAGS EscapeFlags; -} VBOXDISPIFWDDM_ESCAPEOP_CONTEXT, *PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT; - -DECLCALLBACK(BOOLEAN) vboxDispIfEscapeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext) -{ - PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_ESCAPEOP_CONTEXT)pContext; - D3DKMT_ESCAPE EscapeData = {0}; - EscapeData.hAdapter = hAdapter; + EscapeData.hAdapter = Adapter.hAdapter; //EscapeData.hDevice = NULL; EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE; - EscapeData.Flags = pCtx->EscapeFlags; - EscapeData.pPrivateDriverData = pCtx->pEscape; - EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(pCtx->cbData); - //EscapeData.hContext = NULL; - - pCtx->Status = pIf->modeData.wddm.pfnD3DKMTEscape(&EscapeData); - - return TRUE; -} - -static DWORD vboxDispIfEscapeWDDM(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData, BOOL fHwAccess) -{ - VBOXDISPIFWDDM_ESCAPEOP_CONTEXT Ctx = {0}; - Ctx.pEscape = pEscape; - Ctx.cbData = cbData; if (fHwAccess) - Ctx.EscapeFlags.HardwareAccess = 1; - DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1 /* iDisplay, -1 means primary */, vboxDispIfEscapeWDDMOp, &Ctx); - if (err == NO_ERROR) - { - if (!Ctx.Status) - err = NO_ERROR; - else - { - if (Ctx.Status == 0xC00000BBL) /* not supported */ - err = ERROR_NOT_SUPPORTED; - else - err = ERROR_GEN_FAILURE; - Log((__FUNCTION__": pfnD3DKMTEscape failed, Status (0x%x)\n", Ctx.Status)); - } - } - else - Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err)); - - return err; -} - -typedef struct -{ - NTSTATUS Status; - VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO Info; -} VBOXDISPIFWDDM_RESIZEOP_CONTEXT, *PVBOXDISPIFWDDM_RESIZEOP_CONTEXT; - -DECLCALLBACK(BOOLEAN) vboxDispIfResizeWDDMOp(PCVBOXDISPIF pIf, D3DKMT_HANDLE hAdapter, DISPLAY_DEVICE *pDev, PVOID pContext) -{ - PVBOXDISPIFWDDM_RESIZEOP_CONTEXT pCtx = (PVBOXDISPIFWDDM_RESIZEOP_CONTEXT)pContext; - D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0}; - uint32_t cbData = VBOXWDDM_RECOMMENDVIDPN_SIZE(1); - PVBOXWDDM_RECOMMENDVIDPN pData = (PVBOXWDDM_RECOMMENDVIDPN)malloc(cbData); - if (pData) - { - memset(pData, 0, cbData); - pData->cScreenInfos = 1; - memcpy(&pData->aScreenInfos[0], &pCtx->Info, sizeof (VBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO)); - - IAVidPnData.hAdapter = hAdapter; - IAVidPnData.pPrivateDriverData = pData; - IAVidPnData.PrivateDriverDataSize = cbData; - - pCtx->Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData); - Assert(!pCtx->Status); - if (pCtx->Status) - Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", pCtx->Status)); + EscapeData.Flags.HardwareAccess = 1; + EscapeData.pPrivateDriverData = pEscape; + EscapeData.PrivateDriverDataSize = VBOXDISPIFESCAPE_SIZE(cbData); + //EscapeData.hContext = NULL; - free(pData); - } + NTSTATUS Status = pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData); + if (NT_SUCCESS(Status)) + winEr = ERROR_SUCCESS; else { - Log((__FUNCTION__": malloc failed\n")); - pCtx->Status = -1; + WARN(("VBoxTray: pfnD3DKMTEscape failed Status 0x%x\n", Status)); + winEr = ERROR_GEN_FAILURE; } - return TRUE; -} + vboxDispKmtCloseAdapter(&Adapter); -static DWORD vboxDispIfResizeWDDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel) -{ - VBOXDISPIFWDDM_RESIZEOP_CONTEXT Ctx = {0}; - Ctx.Info.Id = Id; - Ctx.Info.Width = Width; - Ctx.Info.Height = Height; - Ctx.Info.BitsPerPixel = BitsPerPixel; - DWORD err = vboxDispIfWDDMAdapterOp(pIf, -1, /* (int)Id - always say -1 to use primary display since the display does not really matter here */ - vboxDispIfResizeWDDMOp, &Ctx); - if (err == NO_ERROR) - { - if (!Ctx.Status) - err = NO_ERROR; - else - { - if (Ctx.Status == 0xC00000BBL) /* not supported */ - err = ERROR_NOT_SUPPORTED; - else - err = ERROR_GEN_FAILURE; - Log((__FUNCTION__": vboxDispIfResizeWDDMOp failed, Status (0x%x)\n", Ctx.Status)); - } - } - else - Log((__FUNCTION__": vboxDispIfWDDMAdapterOp failed, err (%d)\n", err)); - - return err; + return winEr; } #endif @@ -359,6 +741,7 @@ DWORD VBoxDispIfEscape(PCVBOXDISPIF pIf, PVBOXDISPIFESCAPE pEscape, int cbData) 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: @@ -376,6 +759,7 @@ DWORD VBoxDispIfEscapeInOut(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, i 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: @@ -384,60 +768,138 @@ DWORD VBoxDispIfEscapeInOut(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, i } } -static DWORD vboxDispIfResizeXPDM(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel) +#ifdef VBOX_WITH_WDDM + +#define VBOXRR_TIMER_ID 1234 + +typedef struct VBOXRR { - return ERROR_NOT_SUPPORTED; -} + 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) -DWORD VBoxDispIfResize(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel) +static void vboxRrRetryStopLocked() { - switch (pIf->enmMode) + PVBOXRR pMon = &g_VBoxRr; + if (pMon->pIf) { - case VBOXDISPIF_MODE_XPDM_NT4: - return ERROR_NOT_SUPPORTED; - case VBOXDISPIF_MODE_XPDM: - return vboxDispIfResizeXPDM(pIf, Id, Width, Height, BitsPerPixel); -#ifdef VBOX_WITH_WDDM - case VBOXDISPIF_MODE_WDDM: - return vboxDispIfResizeWDDM(pIf, Id, Width, Height, BitsPerPixel); -#endif - default: - Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode)); - return ERROR_INVALID_PARAMETER; + 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); +} -#ifdef VBOX_WITH_WDDM -/**/ -typedef DECLCALLBACK(VOID) FNVBOXSCREENMONRUNNER_CB(void *pvCb); -typedef FNVBOXSCREENMONRUNNER_CB *PFNVBOXSCREENMONRUNNER_CB; +//static DWORD vboxDispIfWddmValidateFixResize(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes); -typedef struct VBOXSCREENMON +static void vboxRrRetryReschedule() { - HANDLE hThread; - DWORD idThread; - HANDLE hEvent; - HWND hWnd; - PFNVBOXSCREENMONRUNNER_CB pfnCb; - void *pvCb; -} VBOXSCREENMON, *PVBOXSCREENMON; +} +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(); -static VBOXSCREENMON g_VBoxScreenMon; + pMon->pIf = pIf; + pMon->iChangedMode = iChangedMode; + pMon->fEnable = fEnable; + pMon->fExtDispSup = fExtDispSup; + if (cDevModes) + { + pMon->paDisplayDevices = (DISPLAY_DEVICE*)malloc(sizeof (*paDisplayDevices) * cDevModes); + Assert(pMon->paDisplayDevices); + if (!pMon->paDisplayDevices) + { + Log(("malloc failed!")); + vboxRrRetryStopLocked(); + LeaveCriticalSection(&pMon->CritSect); + return; + } + memcpy(pMon->paDisplayDevices, paDisplayDevices, sizeof (*paDisplayDevices) * cDevModes); -#define VBOX_E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) -#define VBOX_E_NOT_SUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) + pMon->paDeviceModes = (DEVMODE*)malloc(sizeof (*paDeviceModes) * cDevModes); + Assert(pMon->paDeviceModes); + if (!pMon->paDeviceModes) + { + Log(("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) + { + WARN(("VBoxTray: SetTimer failed!, err %d\n", GetLastError())); + vboxRrRetryStopLocked(); + } -static void vboxScreenMonOnChange() + LeaveCriticalSection(&pMon->CritSect); +} + +static void vboxRrRetryPerform() { - PVBOXSCREENMON pMon = &g_VBoxScreenMon; - pMon->pfnCb(pMon->pvCb); + 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 vboxScreenMonWndProc(HWND hwnd, +static LRESULT CALLBACK vboxRrWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam @@ -447,7 +909,19 @@ static LRESULT CALLBACK vboxScreenMonWndProc(HWND hwnd, { case WM_DISPLAYCHANGE: { - vboxScreenMonOnChange(); + Log(("VBoxTray: WM_DISPLAYCHANGE\n")); + VBoxRrRetryStop(); + return 0; + } + case WM_TIMER: + { + if (wParam == VBOXRR_TIMER_ID) + { + Log(("VBoxTray: VBOXRR_TIMER_ID\n")); + vboxRrRetryPerform(); + return 0; + } + break; } case WM_CLOSE: Log((__FUNCTION__": got WM_CLOSE for hwnd(0x%x)", hwnd)); @@ -458,23 +932,25 @@ static LRESULT CALLBACK vboxScreenMonWndProc(HWND hwnd, case WM_NCHITTEST: Log((__FUNCTION__": got WM_NCHITTEST for hwnd(0x%x)\n", hwnd)); return HTNOWHERE; + default: + break; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } -#define VBOXSCREENMONWND_NAME "VboxScreenMonWnd" +#define VBOXRRWND_NAME "VBoxRrWnd" -static HRESULT vboxScreenMonWndCreate(HWND *phWnd) +static HRESULT vboxRrWndCreate(HWND *phWnd) { HRESULT hr = S_OK; HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); /* Register the Window Class. */ WNDCLASS wc; - if (!GetClassInfo(hInstance, VBOXSCREENMONWND_NAME, &wc)) + if (!GetClassInfo(hInstance, VBOXRRWND_NAME, &wc)) { wc.style = 0;//CS_OWNDC; - wc.lpfnWndProc = vboxScreenMonWndProc; + wc.lpfnWndProc = vboxRrWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; @@ -482,11 +958,11 @@ static HRESULT vboxScreenMonWndCreate(HWND *phWnd) wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; - wc.lpszClassName = VBOXSCREENMONWND_NAME; + wc.lpszClassName = VBOXRRWND_NAME; if (!RegisterClass(&wc)) { DWORD winErr = GetLastError(); - Log((__FUNCTION__": RegisterClass failed, winErr(%d)\n", winErr)); + WARN((__FUNCTION__": RegisterClass failed, winErr(%d)\n", winErr)); hr = E_FAIL; } } @@ -494,7 +970,7 @@ static HRESULT vboxScreenMonWndCreate(HWND *phWnd) if (hr == S_OK) { HWND hWnd = CreateWindowEx (WS_EX_TOOLWINDOW, - VBOXSCREENMONWND_NAME, VBOXSCREENMONWND_NAME, + VBOXRRWND_NAME, VBOXRRWND_NAME, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED, -100, -100, 10, 10, @@ -510,7 +986,7 @@ static HRESULT vboxScreenMonWndCreate(HWND *phWnd) else { DWORD winErr = GetLastError(); - Log((__FUNCTION__": CreateWindowEx failed, winErr(%d)\n", winErr)); + WARN((__FUNCTION__": CreateWindowEx failed, winErr(%d)\n", winErr)); hr = E_FAIL; } } @@ -518,42 +994,41 @@ static HRESULT vboxScreenMonWndCreate(HWND *phWnd) return hr; } -static HRESULT vboxScreenMonWndDestroy(HWND hWnd) +static HRESULT vboxRrWndDestroy(HWND hWnd) { BOOL bResult = DestroyWindow(hWnd); if (bResult) return S_OK; DWORD winErr = GetLastError(); - Log((__FUNCTION__": DestroyWindow failed, winErr(%d) for hWnd(0x%x)\n", winErr, hWnd)); - Assert(0); + WARN((__FUNCTION__": DestroyWindow failed, winErr(%d) for hWnd(0x%x)\n", winErr, hWnd)); return HRESULT_FROM_WIN32(winErr); } -static HRESULT vboxScreenMonWndInit() +static HRESULT vboxRrWndInit() { - PVBOXSCREENMON pMon = &g_VBoxScreenMon; - return vboxScreenMonWndCreate(&pMon->hWnd); + PVBOXRR pMon = &g_VBoxRr; + return vboxRrWndCreate(&pMon->hWnd); } -HRESULT vboxScreenMonWndTerm() +HRESULT vboxRrWndTerm() { - PVBOXSCREENMON pMon = &g_VBoxScreenMon; - HRESULT tmpHr = vboxScreenMonWndDestroy(pMon->hWnd); + PVBOXRR pMon = &g_VBoxRr; + HRESULT tmpHr = vboxRrWndDestroy(pMon->hWnd); Assert(tmpHr == S_OK); HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); - UnregisterClass(VBOXSCREENMONWND_NAME, hInstance); + UnregisterClass(VBOXRRWND_NAME, hInstance); return S_OK; } -#define WM_VBOXSCREENMON_INIT_QUIT (WM_APP+2) +#define WM_VBOXRR_INIT_QUIT (WM_APP+2) -HRESULT vboxScreenMonRun() +HRESULT vboxRrRun() { - PVBOXSCREENMON pMon = &g_VBoxScreenMon; + PVBOXRR pMon = &g_VBoxRr; MSG Msg; HRESULT hr = S_FALSE; @@ -564,17 +1039,8 @@ HRESULT vboxScreenMonRun() WM_USER /* UINT wMsgFilterMax */, PM_NOREMOVE); - BOOL bCheck = TRUE; - do { - if (bCheck) - { - vboxScreenMonOnChange(); - - bCheck = FALSE; - } - BOOL bResult = GetMessage(&Msg, 0 /*HWND hWnd*/, 0 /*UINT wMsgFilterMin*/, @@ -584,6 +1050,8 @@ HRESULT vboxScreenMonRun() if(!bResult) /* WM_QUIT was posted */ { hr = S_FALSE; + Log(("VBoxTray: GetMessage returned FALSE\n")); + VBoxRrRetryStop(); break; } @@ -591,25 +1059,26 @@ HRESULT vboxScreenMonRun() { DWORD winEr = GetLastError(); hr = HRESULT_FROM_WIN32(winEr); - Assert(0); /* 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; + WARN(("VBoxTray: GetMessage returned -1, err %d\n", winEr)); + VBoxRrRetryStop(); break; } switch (Msg.message) { - case WM_VBOXSCREENMON_INIT_QUIT: + case WM_VBOXRR_INIT_QUIT: case WM_CLOSE: { + Log(("VBoxTray: closing Rr %d\n", Msg.message)); + VBoxRrRetryStop(); PostQuitMessage(0); break; } - case WM_DISPLAYCHANGE: - bCheck = TRUE; default: TranslateMessage(&Msg); DispatchMessage(&Msg); @@ -619,41 +1088,39 @@ HRESULT vboxScreenMonRun() return 0; } -static DWORD WINAPI vboxScreenMonRunnerThread(void *pvUser) +static DWORD WINAPI vboxRrRunnerThread(void *pvUser) { - PVBOXSCREENMON pMon = &g_VBoxScreenMon; + PVBOXRR pMon = &g_VBoxRr; BOOL bRc = SetEvent(pMon->hEvent); if (!bRc) { DWORD winErr = GetLastError(); - Log((__FUNCTION__": SetEvent failed, winErr = (%d)", winErr)); + WARN((__FUNCTION__": SetEvent failed, winErr = (%d)", winErr)); HRESULT tmpHr = HRESULT_FROM_WIN32(winErr); - Assert(0); Assert(tmpHr != S_OK); } - HRESULT hr = vboxScreenMonWndInit(); + HRESULT hr = vboxRrWndInit(); Assert(hr == S_OK); if (hr == S_OK) { - hr = vboxScreenMonRun(); + hr = vboxRrRun(); Assert(hr == S_OK); - vboxScreenMonWndTerm(); + vboxRrWndTerm(); } return 0; } -HRESULT VBoxScreenMonInit(PFNVBOXSCREENMONRUNNER_CB pfnCb, void *pvCb) +HRESULT VBoxRrInit() { HRESULT hr = E_FAIL; - PVBOXSCREENMON pMon = &g_VBoxScreenMon; + PVBOXRR pMon = &g_VBoxRr; memset(pMon, 0, sizeof (*pMon)); - pMon->pfnCb = pfnCb; - pMon->pvCb = pvCb; + InitializeCriticalSection(&pMon->CritSect); pMon->hEvent = CreateEvent(NULL, /* LPSECURITY_ATTRIBUTES lpEventAttributes*/ TRUE, /* BOOL bManualReset*/ @@ -664,7 +1131,7 @@ HRESULT VBoxScreenMonInit(PFNVBOXSCREENMONRUNNER_CB pfnCb, void *pvCb) { pMon->hThread = CreateThread(NULL /* LPSECURITY_ATTRIBUTES lpThreadAttributes */, 0 /* SIZE_T dwStackSize */, - vboxScreenMonRunnerThread, + vboxRrRunnerThread, pMon, 0 /* DWORD dwCreationFlags */, &pMon->idThread); @@ -682,9 +1149,8 @@ HRESULT VBoxScreenMonInit(PFNVBOXSCREENMONRUNNER_CB pfnCb, void *pvCb) else { DWORD winErr = GetLastError(); - Log((__FUNCTION__": CreateThread failed, winErr = (%d)", winErr)); + WARN((__FUNCTION__": CreateThread failed, winErr = (%d)", winErr)); hr = HRESULT_FROM_WIN32(winErr); - Assert(0); Assert(hr != S_OK); } CloseHandle(pMon->hEvent); @@ -692,23 +1158,24 @@ HRESULT VBoxScreenMonInit(PFNVBOXSCREENMONRUNNER_CB pfnCb, void *pvCb) else { DWORD winErr = GetLastError(); - Log((__FUNCTION__": CreateEvent failed, winErr = (%d)", winErr)); + WARN((__FUNCTION__": CreateEvent failed, winErr = (%d)", winErr)); hr = HRESULT_FROM_WIN32(winErr); - Assert(0); Assert(hr != S_OK); } + DeleteCriticalSection(&pMon->CritSect); + return hr; } -VOID VBoxScreenMonTerm() +VOID VBoxRrTerm() { HRESULT hr; - PVBOXSCREENMON pMon = &g_VBoxScreenMon; + PVBOXRR pMon = &g_VBoxRr; if (!pMon->hThread) return; - BOOL bResult = PostThreadMessage(pMon->idThread, WM_VBOXSCREENMON_INIT_QUIT, 0, 0); + 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 */ @@ -722,432 +1189,487 @@ VOID VBoxScreenMonTerm() { winErr = GetLastError(); hr = HRESULT_FROM_WIN32(winErr); - Assert(0); } } else { hr = HRESULT_FROM_WIN32(winErr); - Assert(0); } + DeleteCriticalSection(&pMon->CritSect); + CloseHandle(pMon->hThread); pMon->hThread = 0; CloseHandle(pMon->hEvent); pMon->hThread = 0; } -/**/ -typedef struct VBOXDISPIF_WDDM_INTERNAL +static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf) { - PCVBOXDISPIF pIf; - - HANDLE hResizeEvent; -} VBOXDISPIF_WDDM_INTERNAL, *PVBOXDISPIF_WDDM_INTERNAL; + HRESULT hr = VBoxRrInit(); + if (SUCCEEDED(hr)) + { + return ERROR_SUCCESS; + } + WARN(("VBoxTray: VBoxRrInit failed hr 0x%x\n", hr)); + return hr; +} -static VBOXDISPIF_WDDM_INTERNAL g_VBoxDispIfWddm; +static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf) +{ + VBoxRrTerm(); +} -static BOOL vboxDispIfWddmValidateResize(DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes) +static DWORD vboxDispIfQueryDisplayConnection(VBOXDISPIF_OP *pOp, UINT32 iDisplay, BOOL *pfConnected) { - DISPLAY_DEVICE DisplayDevice; - int i = 0; - UINT cMatched = 0; - DEVMODE DeviceMode; - for (int i = 0; ; ++i) + if (pOp->pIf->enmMode == VBOXDISPIF_MODE_WDDM) { - ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE)); - DisplayDevice.cb = sizeof(DISPLAY_DEVICE); + /* @todo: do we need ti impl it? */ + *pfConnected = TRUE; + return ERROR_SUCCESS; + } - if (!EnumDisplayDevices (NULL, i, &DisplayDevice, 0)) - break; + *pfConnected = FALSE; - Log(("VBoxTray: vboxDispIfValidateResize: [%d(%d)] %s\n", i, cMatched, DisplayDevice.DeviceName)); + VBOXDISPIF_WDDM_DISPCFG DispCfg; + DWORD winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ALL_PATHS); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr)); + return winEr; + } - BOOL bFetchDevice = FALSE; + int idx = vboxDispIfWddmDcSearchPath(&DispCfg, iDisplay, iDisplay); + *pfConnected = (idx >= 0); - if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) - { - Log(("VBoxTray: vboxDispIfValidateResize: Found primary device. err %d\n", GetLastError ())); - bFetchDevice = TRUE; - } - else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) - { + vboxDispIfWddmDcTerm(&DispCfg); - Log(("VBoxTray: vboxDispIfValidateResize: Found secondary device. err %d\n", GetLastError ())); - bFetchDevice = TRUE; - } + return ERROR_SUCCESS; +} - if (bFetchDevice) +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) { - if (cMatched >= cDevModes) - { - Log(("VBoxTray: vboxDispIfValidateResize: %d >= %d\n", cDevModes, cMatched)); - return FALSE; - } + Log(("VBoxTray: (WDDM) pfnD3DKMTPollDisplayChildren failed, Status (0x%x)\n", Status)); + continue; + } - /* First try to get the video mode stored in registry (ENUM_REGISTRY_SETTINGS). - * A secondary display could be not active at the moment and would not have - * a current video mode (ENUM_CURRENT_SETTINGS). - */ - ZeroMemory(&DeviceMode, sizeof(DeviceMode)); - DeviceMode.dmSize = sizeof(DEVMODE); - if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName, - ENUM_REGISTRY_SETTINGS, &DeviceMode)) - { - Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings error %d\n", GetLastError ())); - return FALSE; - } + BOOL fFound = FALSE; +#if 0 + for (UINT i = 0; i < VBOXWDDM_SCREENMASK_SIZE; ++i) + { + if (pu8DisplayMask && !ASMBitTest(pu8DisplayMask, i)) + continue; - if ( DeviceMode.dmPelsWidth == 0 - || DeviceMode.dmPelsHeight == 0) + BOOL fConnected = FALSE; + winEr = vboxDispIfQueryDisplayConnection(pOp, i, &fConnected); + if (winEr != ERROR_SUCCESS) { - /* No ENUM_REGISTRY_SETTINGS yet. Seen on Vista after installation. - * Get the current video mode then. - */ - ZeroMemory(&DeviceMode, sizeof(DeviceMode)); - DeviceMode.dmSize = sizeof(DeviceMode); - if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName, - ENUM_CURRENT_SETTINGS, &DeviceMode)) - { - /* ENUM_CURRENT_SETTINGS returns FALSE when the display is not active: - * for example a disabled secondary display */ - Log(("VBoxTray: vboxDispIfValidateResize: EnumDisplaySettings(ENUM_CURRENT_SETTINGS) error %d\n", GetLastError ())); - return FALSE; - } + WARN(("VBoxTray: (WDDM) Failed vboxDispIfQueryDisplayConnection winEr %d\n", winEr)); + return winEr; } - UINT j = 0; - for (; j < cDevModes; ++j) + if (!fConnected) { - if (!strncmp(DisplayDevice.DeviceName, paDisplayDevices[j].DeviceName, RT_ELEMENTS(DeviceMode.dmDeviceName))) - { - if (paDeviceModes[j].dmBitsPerPel != DeviceMode.dmBitsPerPel - || (paDeviceModes[j].dmPelsWidth & 0xfff8) != (DeviceMode.dmPelsWidth & 0xfff8) - || (paDeviceModes[j].dmPelsHeight & 0xfff8) != (DeviceMode.dmPelsHeight & 0xfff8) - || (paDeviceModes[j].dmPosition.x & 0xfff8) != (DeviceMode.dmPosition.x & 0xfff8) - || (paDeviceModes[j].dmPosition.y & 0xfff8) != (DeviceMode.dmPosition.y & 0xfff8) - || (paDisplayDevices[j].StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) - { - return FALSE; - } - break; - } + WARN(("VBoxTray: (WDDM) Display %d not connected, not expected\n", i)); + fFound = TRUE; + break; } - - if (j == cDevModes) - return FALSE; - - ++cMatched; } - } +#endif + if (!fFound) + break; + } while (1); - return cMatched == cDevModes; + return winEr; } -static DWORD vboxDispIfWddmValidateFixResize(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes) +static DWORD vboxDispIfReninitModesWDDM(VBOXDISPIF_OP *pOp, const uint8_t *pScreenIdMask) { - if (vboxDispIfWddmValidateResize(paDisplayDevices, paDeviceModes, cDevModes)) - return NO_ERROR; + DWORD winEr = ERROR_SUCCESS; + VBOXDISPIFESCAPE_REINITVIDEOMODESBYMASK EscData = {0}; + EscData.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK; + memcpy(EscData.ScreenMask, pScreenIdMask, sizeof (EscData.ScreenMask)); - DWORD winEr; - LONG status = DISP_CHANGE_SUCCESSFUL; - - /* now try to resize in a "regular" way */ - /* Assign the new rectangles to displays. */ - for (UINT i = 0; i < cDevModes; i++) - { - /* On Vista one must specify DM_BITSPERPEL. - * Note that the current mode dmBitsPerPel is already in the DEVMODE structure. - */ - paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH | DM_BITSPERPEL; - - Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n", - pIf->modeData.wddm.pfnChangeDisplaySettingsEx, - paDeviceModes[i].dmPelsWidth, - paDeviceModes[i].dmPelsHeight, - paDeviceModes[i].dmBitsPerPel, - paDeviceModes[i].dmPosition.x, - paDeviceModes[i].dmPosition.y)); - - /* the miniport might have been adjusted the display mode stuff, - * adjust the paDeviceModes[i] by picking the closest available one */ -// DEVMODE AdjustedMode = paDeviceModes[i]; -// vboxDispIfAdjustMode(&paDisplayDevices[i], &AdjustedMode); - - LONG tmpStatus = pIf->modeData.wddm.pfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName, - &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL); - Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettingsEx position status %d, err %d\n", tmpStatus, GetLastError ())); - - if (tmpStatus != DISP_CHANGE_SUCCESSFUL) - { - status = tmpStatus; - } - } + 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); - /* A second call to ChangeDisplaySettings updates the monitor. */ - LONG tmpStatus = pIf->modeData.wddm.pfnChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); - Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettings update status %d\n", status)); - if (tmpStatus == DISP_CHANGE_SUCCESSFUL) + NTSTATUS Status = pOp->pIf->modeData.wddm.KmtCallbacks.pfnD3DKMTEscape(&EscapeData); + if (NT_SUCCESS(Status)) + winEr = ERROR_SUCCESS; + else { - if (status == DISP_CHANGE_SUCCESSFUL) - { - return NO_ERROR; - } - tmpStatus = status; + WARN(("VBoxTray: pfnD3DKMTEscape VBOXESC_REINITVIDEOMODESBYMASK failed Status 0x%x\n", Status)); + winEr = ERROR_GEN_FAILURE; } - winEr = ERROR_GEN_FAILURE; + winEr = vboxDispIfWaitDisplayDataInited(pOp, pScreenIdMask); + if (winEr != NO_ERROR) + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWaitDisplayDataInited winEr %d\n", winEr)); + return winEr; } -static DECLCALLBACK(VOID) vboxDispIfWddmScreenMonCb(void *pvCb) +DWORD vboxDispIfCancelPendingResizeWDDM(PCVBOXDISPIF const pIf) { - PVBOXDISPIF_WDDM_INTERNAL pData = (PVBOXDISPIF_WDDM_INTERNAL)pvCb; - - SetEvent(pData->hResizeEvent); + Log(("VBoxTray: cancelling pending resize\n")); + VBoxRrRetryStop(); + return NO_ERROR; } -static DWORD vboxDispIfWddmInit(PCVBOXDISPIF pIf) +static DWORD vboxDispIfWddmResizeDisplayVista(DEVMODE *paDeviceModes, DISPLAY_DEVICE *paDisplayDevices, DWORD cDevModes, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup) { - memset(&g_VBoxDispIfWddm, 0, sizeof (g_VBoxDispIfWddm)); - g_VBoxDispIfWddm.pIf = pIf; - g_VBoxDispIfWddm.hResizeEvent = CreateEvent(NULL, - FALSE, /* BOOL bManualReset */ - FALSE, /* BOOL bInitialState */ - NULL /* LPCTSTR lpName */ - ); - if (g_VBoxDispIfWddm.hResizeEvent) + /* 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++) { - HRESULT hr = VBoxScreenMonInit(vboxDispIfWddmScreenMonCb, &g_VBoxDispIfWddm); - if (SUCCEEDED(hr)) - { - /* ensure event is reset */ - WaitForSingleObject(g_VBoxDispIfWddm.hResizeEvent, 0); - return ERROR_SUCCESS; - } - CloseHandle(g_VBoxDispIfWddm.hResizeEvent); + DEVMODE tempDevMode; + ZeroMemory (&tempDevMode, sizeof (tempDevMode)); + tempDevMode.dmSize = sizeof(DEVMODE); + EnumDisplaySettings((LPSTR)paDisplayDevices[i].DeviceName, 0xffffff, &tempDevMode); + Log(("VBoxTray: ResizeDisplayDevice: EnumDisplaySettings last error %d\n", GetLastError ())); } - return ERROR_GEN_FAILURE; + + 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) + WARN(("VBoxTray: (WDDM) Failed EnableAndResizeDispDev winEr %d\n", winEr)); + + return winEr; } -static void vboxDispIfWddmTerm(PCVBOXDISPIF pIf) +static DWORD vboxDispIfResizePerform(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes) { - VBoxScreenMonTerm(); - CloseHandle(g_VBoxDispIfWddm.hResizeEvent); - memset(&g_VBoxDispIfWddm, 0, sizeof (g_VBoxDispIfWddm)); + DWORD winEr; + if (pIf->enmMode > VBOXDISPIF_MODE_WDDM) + { + winEr = vboxDispIfWddmResizeDisplay(pIf, iChangedMode, fEnable, paDisplayDevices, paDeviceModes, cDevModes); + if (winEr != NO_ERROR) + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmResizeDisplay winEr %d\n", winEr)); + } + else + { + winEr = vboxDispIfWddmResizeDisplayVista(paDeviceModes, paDisplayDevices, cDevModes, iChangedMode, fEnable, fExtDispSup); + if (winEr != NO_ERROR) + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmResizeDisplayVista winEr %d\n", winEr)); + } + return winEr; } -static DWORD vboxDispIfReninitModesWDDM(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange) +DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes) { - VBOXDISPIFESCAPE_REINITVIDEOMODESBYMASK Data = {0}; - Data.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK; - if (fReconnectDisplaysOnChange) - Data.EscapeHdr.u32CmdSpecific = VBOXWDDM_REINITVIDEOMODESBYMASK_F_RECONNECT_DISPLAYS_ON_CHANGE; + 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; - memcpy(Data.ScreenMask, pScreenIdMask, sizeof (Data.ScreenMask)); + Log(("VBoxTray: vboxDispIfResizeModesWDDM\n")); + VBoxRrRetryStop(); - DWORD err = vboxDispIfEscapeWDDM(pIf, &Data.EscapeHdr, sizeof (Data) - sizeof (Data.EscapeHdr), fReconnectDisplaysOnChange ? FALSE /* hw access must be false here, - * otherwise the miniport driver would fail - * request to prevent a deadlock */ - : TRUE); - if (err != NO_ERROR) + VBOXDISPIF_OP Op; + + winEr = vboxDispIfOpBegin(pIf, &Op); + if (winEr != NO_ERROR) { - Log((__FUNCTION__": VBoxDispIfEscape failed with err (%d)\n", err)); + WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x", winEr)); + return winEr; } - return err; + + +// 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 vboxDispIfAdjustMode(DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode) +static DWORD vboxDispIfWddmEnableDisplays(PCVBOXDISPIF const pIf, UINT cIds, UINT *pIds, BOOL fEnabled, BOOL fSetTopology, DEVMODE *pDeviceMode) { - DEVMODE CurMode; - DEVMODE BestMatchMode; - DWORD i = 0; - int64_t diffWH = INT64_MAX; - int diffBpp = INT32_MAX; - for (; ; ++i) + VBOXDISPIF_WDDM_DISPCFG DispCfg; + + DWORD winEr; + int iPath; + + winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS); + if (winEr != ERROR_SUCCESS) { - CurMode.dmSize = sizeof (CurMode); - CurMode.dmDriverExtra = 0; + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr)); + return winEr; + } - if (!EnumDisplaySettings(pDisplayDevice->DeviceName, i, &CurMode)) - break; + UINT cChangeIds = 0; + UINT *pChangeIds = (UINT*)alloca(cIds * sizeof (*pChangeIds)); + if (!pChangeIds) + { + WARN(("VBoxTray: (WDDM) Failed to alloc change ids\n")); + winEr = ERROR_OUTOFMEMORY; + goto done; + } - if (CurMode.dmPelsWidth == pDeviceMode->dmPelsWidth - && CurMode.dmPelsHeight == pDeviceMode->dmPelsHeight - && CurMode.dmBitsPerPel == pDeviceMode->dmBitsPerPel) + for (UINT i = 0; i < cIds; ++i) + { + UINT Id = pIds[i]; + bool fIsDup = false; + for (UINT j = 0; j < cChangeIds; ++j) { - Log(("Exact match found")); - *pDeviceMode = CurMode; - return NO_ERROR; + if (pChangeIds[j] == Id) + { + fIsDup = true; + break; + } } - int diffCurrW = RT_ABS((int)(CurMode.dmPelsWidth - pDeviceMode->dmPelsWidth)); - int diffCurrH = RT_ABS((int)(CurMode.dmPelsHeight - pDeviceMode->dmPelsHeight)); - int diffCurrBpp = RT_ABS((int)(CurMode.dmBitsPerPel - pDeviceMode->dmBitsPerPel) - - 1 /* <- to make higher bpp take precedence over lower ones */ - ); + if (fIsDup) + continue; - int64_t diffCurrHW = (int64_t)diffCurrW*diffCurrW + (int64_t)diffCurrH*diffCurrH; + iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id); - if (i == 0 - || diffCurrHW < diffWH - || (diffCurrHW == diffWH && diffCurrBpp < diffBpp)) + if (!((iPath >= 0) && (DispCfg.pPathInfoArray[iPath].flags & DISPLAYCONFIG_PATH_ACTIVE)) != !fEnabled) { - /* first run */ - BestMatchMode = CurMode; - diffWH = diffCurrHW; - diffBpp = diffCurrBpp; - continue; + pChangeIds[cChangeIds] = Id; + ++cChangeIds; } } - if (i == 0) + if (cChangeIds == 0) { - Log(("No modes found!")); - return NO_ERROR; + Log(("VBoxTray: (WDDM) vboxDispIfWddmEnableDisplay: settings are up to date\n")); + winEr = ERROR_SUCCESS; + goto done; } - *pDeviceMode = BestMatchMode; - return NO_ERROR; -} - -static DWORD vboxDispIfAdjustModeValues(PCVBOXDISPIF const pIf, DISPLAY_DEVICE *pDisplayDevice, DEVMODE *pDeviceMode) -{ - VBOXDISPIFESCAPE_ADJUSTVIDEOMODES Data = {0}; - Data.EscapeHdr.escapeCode = VBOXESC_REINITVIDEOMODESBYMASK; - Data.EscapeHdr.u32CmdSpecific = 1; - Data.aScreenInfos[0].Mode.Id = - Data.aScreenInfos[0].Mode.Width = pDeviceMode->dmPelsWidth; - Data.aScreenInfos[0].Mode.Height = pDeviceMode->dmPelsHeight; - Data.aScreenInfos[0].Mode.BitsPerPixel = pDeviceMode->dmBitsPerPel; - DWORD err = vboxDispIfEscapeWDDM(pIf, &Data.EscapeHdr, sizeof (Data) - sizeof (Data.EscapeHdr), TRUE); - if (err != NO_ERROR) + /* we want to set primary for every disabled for non-topoly mode only */ + winEr = vboxDispIfWddmDcSettingsIncludeAllTargets(&DispCfg); + if (winEr != ERROR_SUCCESS) { - Log((__FUNCTION__": VBoxDispIfEscape failed with err (%d)\n", err)); + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsIncludeAllTargets winEr %d\n", winEr)); + return winEr; } - return err; -} -DWORD vboxDispIfResizeModesWDDM(PCVBOXDISPIF const pIf, UINT iChangedMode, 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; - NTSTATUS Status; - DWORD winEr = NO_ERROR; - UINT i = 0; + if (fSetTopology) + vboxDispIfWddmDcSettingsInvalidateModeIndeces(&DispCfg); - for (; i < cDevModes; i++) + for (UINT i = 0; i < cChangeIds; ++i) { - PVBOXWDDM_RECOMMENDVIDPN_SCREEN_INFO pInfo = &pVidPnInfo->aScreenInfos[i]; - D3DKMT_OPENADAPTERFROMHDC OpenAdapterData = {0}; - OpenAdapterData.hDc = CreateDC(NULL, paDisplayDevices[i].DeviceName, NULL, NULL); - if (!OpenAdapterData.hDc) + UINT Id = pChangeIds[i]; + /* re-query paths */ + iPath = vboxDispIfWddmDcSearchPath(&DispCfg, -1, Id); + if (iPath < 0) { - winEr = GetLastError(); - Assert(0); - break; + WARN(("VBoxTray: (WDDM) path index not found while it should")); + winEr = ERROR_GEN_FAILURE; + goto done; } - Status = pIf->modeData.wddm.pfnD3DKMTOpenAdapterFromHdc(&OpenAdapterData); - Assert(!Status); - if (Status) + winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, pDeviceMode, !fEnabled || fSetTopology, fEnabled); + if (winEr != ERROR_SUCCESS) { - winEr = ERROR_GEN_FAILURE; - Assert(0); - break; + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsUpdate winEr %d\n", winEr)); + goto done; } + } + + if (!fSetTopology) + vboxDispIfWddmDcSettingsAttachDisbledToPrimary(&DispCfg); - pInfo->Id = OpenAdapterData.VidPnSourceId; - pInfo->Width = paDeviceModes[i].dmPelsWidth; - pInfo->Height = paDeviceModes[i].dmPelsHeight; - pInfo->BitsPerPixel = paDeviceModes[i].dmBitsPerPel; +#if 0 + /* ensure the zero-index (primary) screen is enabled */ + iPath = vboxDispIfWddmDcSearchPath(&DispCfg, 0, 0); + if (iPath < 0) + { + WARN(("VBoxTray: (WDDM) path index not found while it should")); + winEr = ERROR_GEN_FAILURE; + goto done; + } - if (!hAdapter) + winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, /* just re-use device node here*/ pDeviceMode, fSetTopology, TRUE); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) 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) { - hAdapter = OpenAdapterData.hAdapter; + WARN(("VBoxTray: (WDDM) vboxDispIfWddmDcSet validation failed winEr, trying with changes %d\n", winEr)); + fSetFlags |= SDC_ALLOW_CHANGES; } else { - D3DKMT_CLOSEADAPTER ClosaAdapterData = {0}; - ClosaAdapterData.hAdapter = OpenAdapterData.hAdapter; - Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData); - Assert(!Status); + Log(("VBoxTray: (WDDM) vboxDispIfWddmDcSet topology validation failed winEr %d\n", winEr)); + goto done; } } - BOOL fAbleToInvalidateVidPn = FALSE; + if (!fSetTopology) + fSetFlags |= SDC_SAVE_TO_DATABASE; - if (winEr == NO_ERROR) + winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_APPLY); + if (winEr != ERROR_SUCCESS) + WARN(("VBoxTray: (WDDM) 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) { - Assert(hAdapter); + if (fEnable) + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr)); + else + Log(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr)); + winEr = vboxDispIfWddmEnableDisplays(pIf, cIds, pIds, fEnable, TRUE, NULL); + if (winEr != ERROR_SUCCESS) + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplay mode winEr %d\n", winEr)); + } + + return winEr; +} - D3DKMT_INVALIDATEACTIVEVIDPN IAVidPnData = {0}; - IAVidPnData.hAdapter = hAdapter; - IAVidPnData.pPrivateDriverData = pVidPnInfo; - IAVidPnData.PrivateDriverDataSize = cbVidPnInfo; +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; - DWORD winEr = NO_ERROR; - Status = pIf->modeData.wddm.pfnD3DKMTInvalidateActiveVidPn(&IAVidPnData); - Assert(!Status); - if (Status) + winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate\n")); + return winEr; + } + + iPath = vboxDispIfWddmDcSearchActivePath(&DispCfg, Id, Id); + + if (iPath < 0) + { + vboxDispIfWddmDcTerm(&DispCfg); + + if (!fEnable) { - Log((__FUNCTION__": pfnD3DKMTInvalidateActiveVidPn failed, Status (0x%x)\n", Status)); - winEr = ERROR_GEN_FAILURE; + /* nothing to be done here, just leave */ + return ERROR_SUCCESS; } - else + + winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr)); + return winEr; + } + + winEr = vboxDispIfWddmDcCreate(&DispCfg, QDC_ONLY_ACTIVE_PATHS); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcCreate winEr %d\n", winEr)); + return winEr; + } + + iPath = vboxDispIfWddmDcSearchPath(&DispCfg, Id, Id); + if (iPath < 0) { - fAbleToInvalidateVidPn = TRUE; + WARN(("VBoxTray: (WDDM) path (%d) is still disabled, going to retry winEr %d\n", winEr)); + vboxDispIfWddmDcTerm(&DispCfg); + return ERROR_RETRY; } } - if (hAdapter) + Assert(iPath >= 0); + + if (!fEnable) { - D3DKMT_CLOSEADAPTER ClosaAdapterData = {0}; - ClosaAdapterData.hAdapter = hAdapter; - Status = pIf->modeData.wddm.pfnD3DKMTCloseAdapter(&ClosaAdapterData); - Assert(!Status); + /* need to disable it, and we are done */ + vboxDispIfWddmDcTerm(&DispCfg); + + winEr = vboxDispIfWddmEnableDisplaysTryingTopology(pIf, 1, &Id, fEnable); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmEnableDisplaysTryingTopology winEr %d\n", winEr)); + return winEr; + } + + return winEr; } -// for (i = 0; i < cDevModes; i++) -// { -// vboxDispIfAdjustMode(&paDisplayDevices[i], &paDeviceModes[i]); -// } + Assert(fEnable); - if (fAbleToInvalidateVidPn) + winEr = vboxDispIfWddmDcSettingsUpdate(&DispCfg, iPath, &paDeviceMode[Id], FALSE, fEnable); + if (winEr != ERROR_SUCCESS) { - winEr = vboxDispIfWddmValidateFixResize(pIf, paDisplayDevices, paDeviceModes, cDevModes); + WARN(("VBoxTray: (WDDM) Failed vboxDispIfWddmDcSettingsUpdate\n")); + vboxDispIfWddmDcTerm(&DispCfg); + return winEr; } - else - { - /* fallback impl needed for display-only driver - * since D3DKMTInvalidateActiveVidPn is not available for WDDM > 1.0: - * make the driver invalidate VidPn, - * which is done by emulating a monitor re-plug currently */ - /* ensure event is reset */ - WaitForSingleObject(g_VBoxDispIfWddm.hResizeEvent, 0); - uint8_t ScreenMask[VBOXWDDM_SCREENMASK_SIZE] = {0}; - ASMBitSet(ScreenMask, iChangedMode); - vboxDispIfReninitModesWDDM(pIf, ScreenMask, TRUE); - - for (UINT i = 0; i < 4; ++i) - { - WaitForSingleObject(g_VBoxDispIfWddm.hResizeEvent, 500); - winEr = vboxDispIfWddmValidateFixResize(pIf, paDisplayDevices, paDeviceModes, cDevModes); - if (winEr == NO_ERROR) - break; - } + UINT fSetFlags = SDC_USE_SUPPLIED_DISPLAY_CONFIG; + winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_VALIDATE); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr)); + fSetFlags |= SDC_ALLOW_CHANGES; + } - Assert(winEr == NO_ERROR); + winEr = vboxDispIfWddmDcSet(&DispCfg, fSetFlags | SDC_SAVE_TO_DATABASE | SDC_APPLY); + if (winEr != ERROR_SUCCESS) + { + WARN(("VBoxTray:(WDDM) pfnSetDisplayConfig Failed to validate winEr %d.\n", winEr)); } + vboxDispIfWddmDcTerm(&DispCfg); + return winEr; } + #endif /* VBOX_WITH_WDDM */ -DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes) +DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes) { switch (pIf->enmMode) { @@ -1157,28 +1679,208 @@ DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_D return ERROR_NOT_SUPPORTED; #ifdef VBOX_WITH_WDDM case VBOXDISPIF_MODE_WDDM: - return vboxDispIfResizeModesWDDM(pIf, iChangedMode, paDisplayDevices, paDeviceModes, cDevModes); + case VBOXDISPIF_MODE_WDDM_W7: + return vboxDispIfResizeModesWDDM(pIf, iChangedMode, fEnable, fExtDispSup, paDisplayDevices, paDeviceModes, cDevModes); #endif default: - Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode)); + WARN((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode)); return ERROR_INVALID_PARAMETER; } } -DWORD VBoxDispIfReninitModes(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange) +DWORD VBoxDispIfCancelPendingResize(PCVBOXDISPIF const pIf) { switch (pIf->enmMode) { case VBOXDISPIF_MODE_XPDM_NT4: - return ERROR_NOT_SUPPORTED; + return NO_ERROR; case VBOXDISPIF_MODE_XPDM: - return ERROR_NOT_SUPPORTED; + return NO_ERROR; #ifdef VBOX_WITH_WDDM case VBOXDISPIF_MODE_WDDM: - return vboxDispIfReninitModesWDDM(pIf, pScreenIdMask, fReconnectDisplaysOnChange); + case VBOXDISPIF_MODE_WDDM_W7: + return vboxDispIfCancelPendingResizeWDDM(pIf); #endif default: - Log((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode)); + WARN((__FUNCTION__": 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; + } + WARN(("VBoxTray: pfnD3DKMTEscape VBOXESC_CONFIGURETARGETS failed Status 0x%x\n", Status)); + return Status; +} + +static DWORD vboxDispIfResizeStartedWDDMOp(VBOXDISPIF_OP *pOp) +{ + DWORD NumDevices = VBoxGetDisplayConfigCount(); + if (NumDevices == 0) + { + WARN(("VBoxTray: 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) + { + WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed, %d\n", winEr)); + return winEr; + } + + if (NumDevices != DevNum) + WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: NumDevices(%d) != DevNum(%d)\n", NumDevices, DevNum)); + + + uint32_t cConnected = 0; + winEr = vboxDispIfConfigureTargetsWDDM(pOp, &cConnected); + if (winEr != NO_ERROR) + { + WARN(("VBoxTray: vboxDispIfConfigureTargetsWDDM failed winEr 0x%x\n", winEr)); + return winEr; + } + + if (!cConnected) + { + Log(("VBoxTray: all targets already connected, nothing to do\n")); + return NO_ERROR; + } + + winEr = vboxDispIfWaitDisplayDataInited(pOp, NULL); + if (winEr != NO_ERROR) + WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: vboxDispIfWaitDisplayDataInited failed winEr 0x%x\n", winEr)); + + DWORD NewNumDevices = VBoxGetDisplayConfigCount(); + if (NewNumDevices == 0) + { + WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: Zero devices found\n")); + return ERROR_GEN_FAILURE; + } + + if (NewNumDevices != NumDevices) + WARN(("VBoxTray: 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) + { + WARN(("VBoxTray: vboxDispIfResizeStartedWDDMOp: VBoxGetDisplayConfig failed for new devices, %d\n", winEr)); + return winEr; + } + + if (NewNumDevices != NewDevNum) + WARN(("VBoxTray: 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 */ + WARN(("VBoxTray: 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) + WARN(("VBoxTray: 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) + WARN(("VBoxTray: 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) + { + WARN(("VBoxTray: vboxDispIfOpBegin failed winEr 0x%x\n", winEr)); + return winEr; + } + + winEr = vboxDispIfResizeStartedWDDMOp(&Op); + if (winEr != NO_ERROR) + { + WARN(("VBoxTray: 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: + WARN((__FUNCTION__": unknown mode (%d)\n", pIf->enmMode)); return ERROR_INVALID_PARAMETER; } } @@ -1197,7 +1899,7 @@ static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf) GetVersionEx (&OSinfo); if (OSinfo.dwMajorVersion >= 5) { - HMODULE hUser = GetModuleHandle("USER32"); + HMODULE hUser = GetModuleHandle("user32.dll"); if (NULL != hUser) { bool bSupported = true; @@ -1207,19 +1909,19 @@ static DWORD vboxDispIfSwitchToXPDM(PVBOXDISPIF pIf) if (!bSupported) { - Log((__FUNCTION__": pfnChangeDisplaySettingsEx function pointer failed to initialize\n")); + WARN((__FUNCTION__": pfnChangeDisplaySettingsEx function pointer failed to initialize\n")); err = ERROR_NOT_SUPPORTED; } } else { - Log((__FUNCTION__": failed to get USER32 handle, err (%d)\n", GetLastError())); + WARN((__FUNCTION__": failed to get USER32 handle, err (%d)\n", GetLastError())); err = ERROR_NOT_SUPPORTED; } } else { - Log((__FUNCTION__": can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n")); + WARN((__FUNCTION__": can not switch to VBOXDISPIF_MODE_XPDM, because os is not >= w2k\n")); err = ERROR_NOT_SUPPORTED; } @@ -1237,9 +1939,11 @@ DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_ return NO_ERROR; #ifdef VBOX_WITH_WDDM - if (pIf->enmMode == VBOXDISPIF_MODE_WDDM) + if (pIf->enmMode >= VBOXDISPIF_MODE_WDDM) { vboxDispIfWddmTerm(pIf); + + vboxDispKmtCallbacksTerm(&pIf->modeData.wddm.KmtCallbacks); } #endif @@ -1255,7 +1959,7 @@ DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_ pIf->enmMode = VBOXDISPIF_MODE_XPDM_NT4; } else - Log((__FUNCTION__": failed to switch to XPDM_NT4 mode, err (%d)\n", err)); + WARN((__FUNCTION__": failed to switch to XPDM_NT4 mode, err (%d)\n", err)); break; case VBOXDISPIF_MODE_XPDM: Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_XPDM\n")); @@ -1266,7 +1970,7 @@ DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_ pIf->enmMode = VBOXDISPIF_MODE_XPDM; } else - Log((__FUNCTION__": failed to switch to XPDM mode, err (%d)\n", err)); + WARN((__FUNCTION__": failed to switch to XPDM mode, err (%d)\n", err)); break; #ifdef VBOX_WITH_WDDM case VBOXDISPIF_MODE_WDDM: @@ -1279,7 +1983,20 @@ DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_ pIf->enmMode = VBOXDISPIF_MODE_WDDM; } else - Log((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err)); + WARN((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err)); + break; + } + case VBOXDISPIF_MODE_WDDM_W7: + { + Log((__FUNCTION__": request to switch to VBOXDISPIF_MODE_WDDM_W7\n")); + err = vboxDispIfSwitchToWDDM_W7(pIf); + if (err == NO_ERROR) + { + Log((__FUNCTION__": successfully switched to WDDM mode\n")); + pIf->enmMode = VBOXDISPIF_MODE_WDDM_W7; + } + else + WARN((__FUNCTION__": failed to switch to WDDM mode, err (%d)\n", err)); break; } #endif @@ -1289,3 +2006,140 @@ DWORD VBoxDispIfSwitchMode(PVBOXDISPIF pIf, VBOXDISPIF_MODE enmMode, VBOXDISPIF_ } 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 + WARN(("VBoxTray: vboxDispKmtCreateContext failed hr 0x%x", hr)); + + vboxDispKmtDestroyDevice(&pSeamless->modeData.wddm.Device); + } + else + WARN(("VBoxTray: 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; + + WARN(("VBoxTray: 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: + WARN(("VBoxTray: 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: + WARN(("VBoxTray: 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) + { + WARN(("VBoxTray: 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: + WARN(("VBoxTray: VBoxDispIfSeamlesSubmit: invalid mode %d\n", pIf->enmMode)); + return ERROR_INVALID_PARAMETER; + } +} |