diff options
| author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
|---|---|---|
| committer | <> | 2014-05-08 15:03:54 +0000 |
| commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
| tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Additions/WINNT/VBoxTray | |
| parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
| download | VirtualBox-master.tar.gz | |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Additions/WINNT/VBoxTray')
27 files changed, 3682 insertions, 1072 deletions
diff --git a/src/VBox/Additions/WINNT/VBoxTray/Makefile.kmk b/src/VBox/Additions/WINNT/VBoxTray/Makefile.kmk index da2b6853..6866ac32 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/Makefile.kmk +++ b/src/VBox/Additions/WINNT/VBoxTray/Makefile.kmk @@ -51,10 +51,16 @@ ifdef VBOX_WITH_SHARED_FOLDERS VBoxTray_LIBS.win += \ mpr.lib endif +ifdef VBOX_WITH_MMR + VBoxTray_DEFS += VBOX_WITH_MMR + VBoxTray_SOURCES += \ + VBoxMMR.cpp +endif ifdef VBOX_WITH_WDDM VBoxTray_DEFS += VBOX_WITH_WDDM # VBoxTray_DEFS += LOG_ENABLED + VBoxTray_SOURCES += ../Graphics/Video/disp/wddm/VBoxDispKmt.cpp endif # VBoxTray.cpp uses VBOX_SVN_REV. diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp index 971e841c..f73a7e4b 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp @@ -5,7 +5,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; @@ -609,7 +609,7 @@ static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, int VBoxClipboardInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread) { - Log(("VBoxTray: VboxClipboardInit\n")); + Log(("VBoxTray: VBoxClipboardInit\n")); if (gCtx.pEnv) { /* Clipboard was already initialized. 2 or more instances are not supported. */ @@ -675,7 +675,7 @@ unsigned __stdcall VBoxClipboardThread(void *pInstance) break; } continue; - } + } else { Log(("VBoxTray: VBoxClipboardThread: VbglR3ClipboardGetHostMsg u32Msg = %ld, u32Formats = %ld\n", u32Msg, u32Formats)); diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.h index 7757d287..10f6e4f9 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; 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; + } +} diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.h index e98edaf3..75647346 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxDispIf.h @@ -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; @@ -18,6 +18,7 @@ #ifdef VBOX_WITH_WDDM # define D3DKMDT_SPECIAL_MULTIPLATFORM_TOOL # include <d3dkmthk.h> +# include "../Graphics/Video/disp/wddm/VBoxDispKmt.h" #endif #include <VBoxDisplay.h> @@ -29,6 +30,7 @@ typedef enum VBOXDISPIF_MODE_XPDM #ifdef VBOX_WITH_WDDM , VBOXDISPIF_MODE_WDDM + , VBOXDISPIF_MODE_WDDM_W7 #endif } VBOXDISPIF_MODE; /* display driver interface abstraction for XPDM & WDDM @@ -58,15 +60,7 @@ typedef struct VBOXDISPIF /* EnumDisplayDevices does not exist in NT. isVBoxDisplayDriverActive et al. are using these functions. */ BOOL (WINAPI * pfnEnumDisplayDevices)(IN LPCSTR lpDevice, IN DWORD iDevNum, OUT PDISPLAY_DEVICEA lpDisplayDevice, IN DWORD dwFlags); - /* open adapter */ - PFND3DKMT_OPENADAPTERFROMHDC pfnD3DKMTOpenAdapterFromHdc; - PFND3DKMT_OPENADAPTERFROMGDIDISPLAYNAME pfnD3DKMTOpenAdapterFromGdiDisplayName; - /* close adapter */ - PFND3DKMT_CLOSEADAPTER pfnD3DKMTCloseAdapter; - /* escape */ - PFND3DKMT_ESCAPE pfnD3DKMTEscape; - /* auto resize support */ - PFND3DKMT_INVALIDATEACTIVEVIDPN pfnD3DKMTInvalidateActiveVidPn; + VBOXDISPKMT_CALLBACKS KmtCallbacks; } wddm; #endif } modeData; @@ -82,6 +76,36 @@ DECLINLINE(VBOXDISPIF_MODE) VBoxDispGetMode(PVBOXDISPIF pIf) { return pIf->enmMo DWORD VBoxDispIfTerm(PVBOXDISPIF pIf); DWORD VBoxDispIfEscape(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, int cbData); DWORD VBoxDispIfEscapeInOut(PCVBOXDISPIF const pIf, PVBOXDISPIFESCAPE pEscape, int cbData); -DWORD VBoxDispIfResize(PCVBOXDISPIF const pIf, ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel); -DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes); -//DWORD VBoxDispIfReninitModes(PCVBOXDISPIF const pIf, uint8_t *pScreenIdMask, BOOL fReconnectDisplaysOnChange); +DWORD VBoxDispIfResizeModes(PCVBOXDISPIF const pIf, UINT iChangedMode, BOOL fEnable, BOOL fExtDispSup, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes, UINT cDevModes); +DWORD VBoxDispIfCancelPendingResize(PCVBOXDISPIF const pIf); + +DWORD VBoxDispIfResizeStarted(PCVBOXDISPIF const pIf); + + +typedef struct VBOXDISPIF_SEAMLESS +{ + PCVBOXDISPIF pIf; + + union + { +#ifdef VBOX_WITH_WDDM + struct + { + VBOXDISPKMT_ADAPTER Adapter; +# ifdef VBOX_DISPIF_WITH_OPCONTEXT + VBOXDISPKMT_DEVICE Device; + VBOXDISPKMT_CONTEXT Context; +# endif + } wddm; +#endif + } modeData; +} VBOXDISPIF_SEAMLESS; + +DECLINLINE(bool) VBoxDispIfSeamlesIsValid(VBOXDISPIF_SEAMLESS *pSeamless) +{ + return !!pSeamless->pIf; +} + +DWORD VBoxDispIfSeamlesCreate(PCVBOXDISPIF const pIf, VBOXDISPIF_SEAMLESS *pSeamless, HANDLE hEvent); +DWORD VBoxDispIfSeamlesTerm(VBOXDISPIF_SEAMLESS *pSeamless); +DWORD VBoxDispIfSeamlesSubmit(VBOXDISPIF_SEAMLESS *pSeamless, VBOXDISPIFESCAPE *pData, int cbData); diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.cpp index b0c19261..a3473600 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -62,7 +62,7 @@ int VBoxDisplayInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStart OSinfo.dwOSVersionInfoSize = sizeof (OSinfo); GetVersionEx (&OSinfo); - HMODULE hUser = GetModuleHandle("USER32"); + HMODULE hUser = GetModuleHandle("user32.dll"); gCtx.pEnv = pEnv; @@ -89,7 +89,8 @@ int VBoxDisplayInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStart { Log(("VBoxTray: VBoxDisplayInit: WDDM driver is installed, switching display driver if to WDDM mode\n")); /* this is hacky, but the most easiest way */ - DWORD err = VBoxDispIfSwitchMode(const_cast<PVBOXDISPIF>(&pEnv->dispIf), VBOXDISPIF_MODE_WDDM, NULL /* old mode, we don't care about it */); + VBOXDISPIF_MODE enmMode = (OSinfo.dwMajorVersion > 6 || OSinfo.dwMinorVersion > 0) ? VBOXDISPIF_MODE_WDDM_W7 : VBOXDISPIF_MODE_WDDM; + DWORD err = VBoxDispIfSwitchMode(const_cast<PVBOXDISPIF>(&pEnv->dispIf), enmMode, NULL /* old mode, we don't care about it */); if (err == NO_ERROR) Log(("VBoxTray: VBoxDisplayInit: DispIf switched to WDDM mode successfully\n")); else @@ -212,19 +213,108 @@ static bool isVBoxDisplayDriverActive(VBOXDISPLAYCONTEXT *pCtx) #endif } -/* Returns TRUE to try again. */ -static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel, - VBOXDISPLAYCONTEXT *pCtx) +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) { - BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0); + DISPLAY_DEVICE displayDeviceTmp; + DISPLAY_DEVICE displayDevice; + DEVMODE deviceMode; + DWORD dwStatus = DISP_CHANGE_SUCCESSFUL; + DWORD iter ; - if (!gCtx.fAnyX) - Width &= 0xFFF8; + deviceMode = paDeviceModes[Id]; + displayDevice = paDisplayDevices[Id]; + + for (iter = 0; iter < totalDispNum; iter++) + { + if (iter != 0 && iter != Id && !(paDisplayDevices[iter].StateFlags & DISPLAY_DEVICE_ACTIVE)) + { + LogRel(("VBoxTray:Initially disabling the monitor with id = %d . Total Monitor=%d\n", iter, totalDispNum)); + DEVMODE deviceModeTmp; + ZeroMemory(&deviceModeTmp, sizeof(DEVMODE)); + deviceModeTmp.dmSize = sizeof(DEVMODE); + deviceModeTmp.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION + | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS ; + displayDeviceTmp = paDisplayDevices[iter]; + gCtx.pfnChangeDisplaySettingsEx(displayDeviceTmp.DeviceName, &deviceModeTmp, NULL, + (CDS_UPDATEREGISTRY | CDS_NORESET), NULL); + } + } + + if (fExtDispSup) /* Extended Display Support possible*/ + { + if (fEnabled) + { + /* Special case for enabling the secondary monitor. */ + if(!(displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE)) + { + LogRel(("VBoxTray: Secondary Monitor with ID=%d and name=%s Not Enabled. Enabling it.\n", Id, displayDevice.DeviceName)); + deviceMode.dmPosition.x = paDeviceModes[0].dmPelsWidth; + deviceMode.dmPosition.y = 0; + deviceMode.dmBitsPerPel = 32; + OSVERSIONINFO OSinfo; + OSinfo.dwOSVersionInfoSize = sizeof (OSinfo); + GetVersionEx (&OSinfo); + + if (OSinfo.dwMajorVersion < 6) + /* dont any more flags here as, only DM_POISITON is used to enable the secondary display */ + deviceMode.dmFields = DM_POSITION; + else /* for win 7 and above */ + /* for vista and above DM_BITSPERPEL is necessary */ + deviceMode.dmFields = DM_BITSPERPEL | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY | DM_POSITION; + + dwStatus = gCtx.pfnChangeDisplaySettingsEx((LPSTR)displayDevice.DeviceName,&deviceMode, NULL, (CDS_UPDATEREGISTRY | CDS_NORESET), NULL); + /* A second call to ChangeDisplaySettings updates the monitor.*/ + gCtx.pfnChangeDisplaySettingsEx(NULL, NULL, NULL,0, NULL); + } + else /* secondary monitor already enabled. Request to change the resolution or position. */ + { + if (aWidth !=0 && aHeight != 0) + { + LogRel(("VBoxTray: Display : %s , Change Height: %d & Width: %d\n", displayDevice.DeviceName, aWidth, aHeight)); + deviceMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL + | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS; + deviceMode.dmPelsWidth = aWidth; + deviceMode.dmPelsHeight = aHeight; + deviceMode.dmBitsPerPel = aBitsPerPixel; + } + if (aPosX != 0 || aPosY != 0) + { + LogRel(("VBoxTray: Display: %s PosX: %d, PosY: %d\n", displayDevice.DeviceName, aPosX, aPosY)); + deviceMode.dmFields |= DM_POSITION; + deviceMode.dmPosition.x = aPosX; + deviceMode.dmPosition.y = aPosY; + } + dwStatus = gCtx.pfnChangeDisplaySettingsEx((LPSTR)displayDevice.DeviceName, + &deviceMode, NULL, CDS_NORESET|CDS_UPDATEREGISTRY, NULL); + /* A second call to ChangeDisplaySettings updates the monitor. */ + gCtx.pfnChangeDisplaySettingsEx(NULL, NULL, NULL,0, NULL); + } + } + else /* Request is there to disable the monitor with ID = Id*/ + { + LogRel(("VBoxTray: Disable the Display: %d\n", displayDevice.DeviceName)); + + DEVMODE deviceModeTmp; + ZeroMemory(&deviceModeTmp, sizeof(DEVMODE)); + deviceModeTmp.dmSize = sizeof(DEVMODE); + deviceModeTmp.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION + | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS ; + displayDeviceTmp = paDisplayDevices[Id]; + dwStatus = gCtx.pfnChangeDisplaySettingsEx(displayDeviceTmp.DeviceName, &deviceModeTmp, NULL, + (CDS_UPDATEREGISTRY | CDS_NORESET), NULL); + gCtx.pfnChangeDisplaySettingsEx(NULL, NULL, NULL,0, NULL); + } + } + return dwStatus; +} +DWORD VBoxGetDisplayConfigCount() +{ DISPLAY_DEVICE DisplayDevice; - ZeroMemory(&DisplayDevice, sizeof(DisplayDevice)); - DisplayDevice.cb = sizeof(DisplayDevice); + ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE)); + DisplayDevice.cb = sizeof(DISPLAY_DEVICE); /* Find out how many display devices the system has */ DWORD NumDevices = 0; @@ -250,26 +340,21 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP i++; } - Log(("VBoxTray: ResizeDisplayDevice: Found total %d devices. err %d\n", NumDevices, GetLastError ())); - - if (NumDevices == 0 || Id >= NumDevices) - { - Log(("VBoxTray: ResizeDisplayDevice: Requested identifier %d is invalid. err %d\n", Id, GetLastError ())); - return FALSE; - } - - DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices); - DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices); - RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices); + return NumDevices; +} +DWORD VBoxGetDisplayConfig(const DWORD NumDevices, DWORD *pDevPrimaryNum, DWORD *pNumDevices, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes) +{ /* Fetch information about current devices and modes. */ DWORD DevNum = 0; DWORD DevPrimaryNum = 0; + DISPLAY_DEVICE DisplayDevice; + ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE)); DisplayDevice.cb = sizeof(DISPLAY_DEVICE); - i = 0; + DWORD i = 0; while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0)) { Log(("VBoxTray: ResizeDisplayDevice: [%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName)); @@ -293,8 +378,8 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP { if (DevNum >= NumDevices) { - Log(("VBoxTray: ResizeDisplayDevice: %d >= %d\n", NumDevices, DevNum)); - return FALSE; + WARN(("VBoxTray: ResizeDisplayDevice: %d >= %d\n", NumDevices, DevNum)); + return ERROR_BUFFER_OVERFLOW; } paDisplayDevices[DevNum] = DisplayDevice; @@ -309,7 +394,6 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum])) { Log(("VBoxTray: ResizeDisplayDevice: EnumDisplaySettings error %d\n", GetLastError ())); - return FALSE; } if ( paDeviceModes[DevNum].dmPelsWidth == 0 @@ -328,21 +412,10 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP * Do not return here, ignore the error and set the display info to 0x0x0. */ Log(("VBoxTray: ResizeDisplayDevice: EnumDisplaySettings(ENUM_CURRENT_SETTINGS) error %d\n", GetLastError ())); - ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE)); } } - Log(("VBoxTray: ResizeDisplayDevice: %dx%dx%d at %d,%d\n", - paDeviceModes[DevNum].dmPelsWidth, - paDeviceModes[DevNum].dmPelsHeight, - paDeviceModes[DevNum].dmBitsPerPel, - paDeviceModes[DevNum].dmPosition.x, - paDeviceModes[DevNum].dmPosition.y)); - - paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x; - paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y; - paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth; - paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight; + DevNum++; } @@ -351,32 +424,146 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP i++; } + *pNumDevices = DevNum; + + return NO_ERROR; +} + +/* Returns TRUE to try again. */ +static BOOL ResizeDisplayDevice(UINT Id, DWORD Width, DWORD Height, DWORD BitsPerPixel, + BOOL fEnabled, DWORD dwNewPosX, DWORD dwNewPosY, + VBOXDISPLAYCONTEXT *pCtx, BOOL fExtDispSup) +{ + BOOL fDispAlreadyEnabled = false; /* check whether the monitor with ID is already enabled. */ + BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0 && + dwNewPosX == 0 && dwNewPosY == 0); + DWORD dmFields = 0; + + Log(("VBoxTray: ResizeDisplayDevice Width= %d, Height=%d , PosX=%d and PosY=%d \ + fEnabled = %d, fExtDisSup = %d\n", + Width, Height, dwNewPosX, dwNewPosY, fEnabled, fExtDispSup)); + + if (!gCtx.fAnyX) + Width &= 0xFFF8; + + VBoxDispIfCancelPendingResize(&pCtx->pEnv->dispIf); + + DWORD NumDevices = VBoxGetDisplayConfigCount(); + + if (NumDevices == 0 || Id >= NumDevices) + { + WARN(("VBoxTray: ResizeDisplayDevice: Requested identifier %d is invalid. err %d\n", Id, GetLastError ())); + return FALSE; + } + + Log(("VBoxTray: ResizeDisplayDevice: Found total %d devices. err %d\n", NumDevices, GetLastError ())); + + DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices); + DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices); + RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices); + DWORD DevNum = 0; + DWORD DevPrimaryNum = 0; + DWORD dwStatus = VBoxGetDisplayConfig(NumDevices, &DevPrimaryNum, &DevNum, paDisplayDevices, paDeviceModes); + if (dwStatus != NO_ERROR) + { + WARN(("VBoxTray: ResizeDisplayDevice: VBoxGetDisplayConfig failed, %d\n", dwStatus)); + return dwStatus; + } + + if (NumDevices != DevNum) + WARN(("VBoxTray: ResizeDisplayDevice: NumDevices(%d) != DevNum(%d)\n", NumDevices, DevNum)); + + DWORD i = 0; + + for (i = 0; i < DevNum; ++i) + { + if (fExtDispSup) + { + LogRel(("VBoxTray: Extended Display Support.\n")); + Log(("VBoxTray: ResizeDisplayDevice1: %dx%dx%d at %d,%d . Id = %d and DevNum=%d, fEnabled=%d\n", + paDeviceModes[Id].dmPelsWidth, + paDeviceModes[Id].dmPelsHeight, + paDeviceModes[Id].dmBitsPerPel, + paDeviceModes[Id].dmPosition.x, + paDeviceModes[Id].dmPosition.y, + Id, DevNum, fEnabled)); + } + else + { + LogRel(("VBoxTray: NO Ext Display Support \n")); + } + + paRects[i].left = paDeviceModes[i].dmPosition.x; + paRects[i].top = paDeviceModes[i].dmPosition.y; + paRects[i].right = paDeviceModes[i].dmPosition.x + paDeviceModes[i].dmPelsWidth; + paRects[i].bottom = paDeviceModes[i].dmPosition.y + paDeviceModes[i].dmPelsHeight; + } + + /* Keep a record if the display with ID is already active or not. */ + if (paDisplayDevices[Id].StateFlags & DISPLAY_DEVICE_ACTIVE) + { + LogRel(("VBoxTray: Display with ID=%d already enabled\n", Id)); + fDispAlreadyEnabled = TRUE; + } + /* Width, height equal to 0 means that this value must be not changed. * Update input parameters if necessary. * Note: BitsPerPixel is taken into account later, when new rectangles * are assigned to displays. */ if (Width == 0) - { Width = paRects[Id].right - paRects[Id].left; - } + else + dmFields |= DM_PELSWIDTH; if (Height == 0) - { Height = paRects[Id].bottom - paRects[Id].top; + else + dmFields |= DM_PELSHEIGHT; + + if (BitsPerPixel == 0) + BitsPerPixel = paDeviceModes[Id].dmBitsPerPel; + else + dmFields |= DM_BITSPERPEL; + + if (!dwNewPosX && !dwNewPosY) + { + /* @fixme: zero position is a valid state, so another values should be used as a special case !!! */ + dwNewPosX = paRects[Id].left; + dwNewPosY = paRects[Id].top; } + else + dmFields |= DM_POSITION; - /* Check whether a mode reset or a change is requested. */ - if ( !fModeReset + /* Check whether a mode reset or a change is requested. + * Rectangle position is recalculated only if fEnabled is 1. + * For non extended supported modes (old Host VMs), fEnabled + * is always 1. + */ + /* Handled the case where previouseresolution of secondary monitor + * was for eg. 1024*768*32 and monitor was in disabled state. + * User gives the command + * setvideomode 1024 768 32 1 yes. + * Now in this case the resolution request is same as previous one but + * monitor is going from disabled to enabled state so the below condition + * shour return false + * The below condition will only return true , if no mode reset has + * been requested AND fEnabled is 1 and fDispAlreadyEnabled is also 1 AND + * all rect conditions are true. Thus in this case nothing has to be done. + */ + if ( !fModeReset && (!fEnabled == !fDispAlreadyEnabled) + && paRects[Id].left == dwNewPosX + && paRects[Id].top == dwNewPosY && paRects[Id].right - paRects[Id].left == Width && paRects[Id].bottom - paRects[Id].top == Height && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel) { - Log(("VBoxTray: ResizeDisplayDevice: Already at desired resolution\n")); + LogRel(("VBoxTray: Already at desired resolution. No Change.\n")); return FALSE; } - hlpResizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height); + hlpResizeRect(paRects, NumDevices, DevPrimaryNum, Id, + fEnabled ? Width : 0, fEnabled ? Height : 0, dwNewPosX, dwNewPosY); #ifdef Log for (i = 0; i < NumDevices; i++) { @@ -399,35 +586,38 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left; paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top; + if (i == Id) + paDeviceModes[i].dmBitsPerPel = BitsPerPixel; + + paDeviceModes[i].dmFields |= dmFields; + /* 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; - - if ( i == Id - && BitsPerPixel != 0) + if (!(paDeviceModes[i].dmFields & DM_BITSPERPEL)) { - /* Change dmBitsPerPel if requested. */ - paDeviceModes[i].dmBitsPerPel = BitsPerPixel; + WARN(("VBoxTray: (WDDM) no DM_BITSPERPEL\n")); + paDeviceModes[i].dmFields |= DM_BITSPERPEL; + paDeviceModes[i].dmBitsPerPel = 32; } - Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n", + Log(("VBoxTray: (WDDM) ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n", gCtx.pfnChangeDisplaySettingsEx, paDeviceModes[i].dmPelsWidth, paDeviceModes[i].dmPelsHeight, paDeviceModes[i].dmBitsPerPel, paDeviceModes[i].dmPosition.x, paDeviceModes[i].dmPosition.y)); - } - DWORD err = VBoxDispIfResizeModes(&pCtx->pEnv->dispIf, Id, paDisplayDevices, paDeviceModes, NumDevices); + Log(("VBoxTray: (WDDM) Request to resize the displa\n")); + DWORD err = VBoxDispIfResizeModes(&pCtx->pEnv->dispIf, Id, fEnabled, fExtDispSup, paDisplayDevices, paDeviceModes, DevNum); if (err == NO_ERROR || err != ERROR_RETRY) { if (err == NO_ERROR) Log(("VBoxTray: VBoxDisplayThread: (WDDM) VBoxDispIfResizeModes succeeded\n")); else - Log(("VBoxTray: VBoxDisplayThread: (WDDM) Failure VBoxDispIfResizeModes (%d)\n", err)); + WARN(("VBoxTray: VBoxDisplayThread: (WDDM) Failure VBoxDispIfResizeModes (%d)\n", err)); return FALSE; } @@ -467,8 +657,8 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP paDeviceModes[i].dmBitsPerPel = BitsPerPixel; } - Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx %x: %dx%dx%d at %d,%d\n", - gCtx.pfnChangeDisplaySettingsEx, + Log(("VBoxTray: ResizeDisplayDevice: pfnChangeDisplaySettingsEx Current MonitorId=%d: %dx%dx%d at %d,%d\n", + i, paDeviceModes[i].dmPelsWidth, paDeviceModes[i].dmPelsHeight, paDeviceModes[i].dmBitsPerPel, @@ -480,15 +670,18 @@ static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsP Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettingsEx position status %d, err %d\n", status, GetLastError ())); } - /* A second call to ChangeDisplaySettings updates the monitor. */ - LONG status = gCtx.pfnChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); - Log(("VBoxTray: ResizeDisplayDevice: ChangeDisplaySettings update status %d\n", status)); - if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE) + Log(("VBoxTray: Enable And Resize Device. Id = %d, Width=%d Height=%d, \ + dwNewPosX = %d, dwNewPosY = %d fEnabled=%d & fExtDispSupport = %d \n", + Id, Width, Height, dwNewPosX, dwNewPosY, fEnabled, fExtDispSup)); + dwStatus = EnableAndResizeDispDev(paDeviceModes, paDisplayDevices, DevNum, Id, Width, Height, BitsPerPixel, + dwNewPosX, dwNewPosY, fEnabled, fExtDispSup); + if (dwStatus == DISP_CHANGE_SUCCESSFUL || dwStatus == DISP_CHANGE_BADMODE) { - /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */ + /* Successfully set new video mode or our driver can not set + * the requested mode. Stop trying. + */ return FALSE; } - /* Retry the request. */ return TRUE; } @@ -515,15 +708,13 @@ unsigned __stdcall VBoxDisplayThread(void *pInstance) return 0; } - int rc = VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0); - if (RT_FAILURE(rc)) - { - LogRel(("VBoxTray: VBoxDisplayThread: Failed to set the graphics capability with rc=%Rrc, thread exiting\n", rc)); - return 0; - } + PostMessage(ghwndToolWindow, WM_VBOX_GRAPHICS_SUPPORTED, 0, 0); + + VBoxDispIfResizeStarted(&pCtx->pEnv->dispIf); do { + BOOL fExtDispSup = TRUE; /* Wait for a display change event. */ VBoxGuestWaitEventInfo waitEvent; waitEvent.u32TimeoutIn = 1000; @@ -552,20 +743,44 @@ unsigned __stdcall VBoxDisplayThread(void *pInstance) if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST) { Log(("VBoxTray: VBoxDisplayThread: going to get display change information\n")); + BOOL fDisplayChangeQueried; + /* We got at least one event. Read the requested resolution * and try to set it until success. New events will not be seen * but a new resolution will be read in this poll loop. */ - VMMDevDisplayChangeRequest2 displayChangeRequest = {0}; - displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest2); + /* Try if extended mode display information is available from the host. */ + VMMDevDisplayChangeRequestEx displayChangeRequest = {0}; + fExtDispSup = TRUE; + displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequestEx); displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION; - displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequest2; + displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequestEx; displayChangeRequest.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; - BOOL fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(VMMDevDisplayChangeRequest2)), &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2), + fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(VMMDevDisplayChangeRequestEx)), &displayChangeRequest, sizeof(VMMDevDisplayChangeRequestEx), + &displayChangeRequest, sizeof(VMMDevDisplayChangeRequestEx), &cbReturned, NULL); + + if (!fDisplayChangeQueried) + { + Log(("VBoxTray: Extended Display Not Supported. Trying VMMDevDisplayChangeRequest2\n")); + fExtDispSup = FALSE; /* Extended display Change request is not supported */ + + displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest2); + displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION; + displayChangeRequest.header.requestType = VMMDevReq_GetDisplayChangeRequest2; + displayChangeRequest.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(VMMDevDisplayChangeRequest2)), &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2), &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest2), &cbReturned, NULL); + displayChangeRequest.cxOrigin = 0; + displayChangeRequest.cyOrigin = 0; + displayChangeRequest.fChangeOrigin = 0; + displayChangeRequest.fEnabled = 1; /* Always Enabled for old VMs on Host.*/ + } + if (!fDisplayChangeQueried) { + Log(("VBoxTray: Extended Display Not Supported. Trying VMMDevDisplayChangeRequest\n")); + fExtDispSup = FALSE; /*Extended display Change request is not supported */ /* Try the old version of the request for old VBox hosts. */ displayChangeRequest.header.size = sizeof(VMMDevDisplayChangeRequest); displayChangeRequest.header.version = VMMDEV_REQUEST_HEADER_VERSION; @@ -574,6 +789,10 @@ unsigned __stdcall VBoxDisplayThread(void *pInstance) fDisplayChangeQueried = DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_VMMREQUEST(sizeof(VMMDevDisplayChangeRequest)), &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest), &displayChangeRequest, sizeof(VMMDevDisplayChangeRequest), &cbReturned, NULL); displayChangeRequest.display = 0; + displayChangeRequest.cxOrigin = 0; + displayChangeRequest.cyOrigin = 0; + displayChangeRequest.fChangeOrigin = 0; + displayChangeRequest.fEnabled = 1; /* Always Enabled for old VMs on Host.*/ } if (fDisplayChangeQueried) @@ -602,17 +821,31 @@ unsigned __stdcall VBoxDisplayThread(void *pInstance) if (pCtx->pfnChangeDisplaySettingsEx != 0) { Log(("VBoxTray: VBoxDisplayThread: Detected W2K or later\n")); - /* W2K or later. */ + Log(("DisplayChangeReqEx parameters aDisplay=%d x xRes=%d x yRes=%d x bpp=%d x SecondayMonEnb=%d x NewOriginX=%d x NewOriginY=%d x ChangeOrigin=%d\n", + displayChangeRequest.display, + displayChangeRequest.xres, + displayChangeRequest.yres, + displayChangeRequest.bpp, + displayChangeRequest.fEnabled, + displayChangeRequest.cxOrigin, + displayChangeRequest.cyOrigin, + displayChangeRequest.fChangeOrigin)); if (!ResizeDisplayDevice(displayChangeRequest.display, displayChangeRequest.xres, displayChangeRequest.yres, displayChangeRequest.bpp, - pCtx + displayChangeRequest.fEnabled, + displayChangeRequest.cxOrigin, + displayChangeRequest.cyOrigin, + pCtx, + fExtDispSup )) { + Log(("ResizeDipspalyDevice return 0\n")); break; } + } else { @@ -727,7 +960,8 @@ unsigned __stdcall VBoxDisplayThread(void *pInstance) } if (waitEvent.u32EventFlagsOut & VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED) hlpReloadCursor(); - } else + } + else { Log(("VBoxTray: VBoxDisplayThread: error 0 from DeviceIoControl VBOXGUEST_IOCTL_WAITEVENT\n")); /* sleep a bit to not eat too much CPU in case the above call always fails */ @@ -746,7 +980,7 @@ unsigned __stdcall VBoxDisplayThread(void *pInstance) maskInfo.u32NotMask = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED; if (!DeviceIoControl(gVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL)) Log(("VBoxTray: VBoxDisplayThread: DeviceIOControl(CtlMask - not) failed\n")); - VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_GRAPHICS); + PostMessage(ghwndToolWindow, WM_VBOX_GRAPHICS_UNSUPPORTED, 0, 0); Log(("VBoxTray: VBoxDisplayThread: finished display change request thread\n")); return 0; diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.h index b2eaa421..81c90e93 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxDisplay.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -22,6 +22,9 @@ int VBoxDisplayInit (const VBOXSERVICEENV *pEnv, void **ppInst unsigned __stdcall VBoxDisplayThread (void *pInstance); void VBoxDisplayDestroy (const VBOXSERVICEENV *pEnv, void *pInstance); +DWORD VBoxGetDisplayConfigCount(); +DWORD VBoxGetDisplayConfig(const DWORD NumDevices, DWORD *pDevPrimaryNum, DWORD *pNumDevices, DISPLAY_DEVICE *paDisplayDevices, DEVMODE *paDeviceModes); + #ifndef VBOX_WITH_WDDM static bool isVBoxDisplayDriverActive (void); #else diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.cpp index f038f51c..bb9984cb 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.cpp @@ -111,7 +111,8 @@ static unsigned hlpNextAdjacentRectYN(RECTL *paRects, unsigned nRects, unsigned } void hlpResizeRect(RECTL *paRects, unsigned nRects, unsigned uPrimary, - unsigned uResized, int iNewWidth, int iNewHeight) + unsigned uResized, int iNewWidth, int iNewHeight, + int iNewPosX, int iNewPosY) { DDCLOG(("nRects %d, iPrimary %d, iResized %d, NewWidth %d, NewHeight %d\n", nRects, uPrimary, uResized, iNewWidth, iNewHeight)); @@ -119,6 +120,10 @@ void hlpResizeRect(RECTL *paRects, unsigned nRects, unsigned uPrimary, memcpy (paNewRects, paRects, sizeof (RECTL) * nRects); paNewRects[uResized].right += iNewWidth - (paNewRects[uResized].right - paNewRects[uResized].left); paNewRects[uResized].bottom += iNewHeight - (paNewRects[uResized].bottom - paNewRects[uResized].top); + paNewRects[uResized].right += iNewPosX - paNewRects[uResized].left; + paNewRects[uResized].bottom += iNewPosY - paNewRects[uResized].top; + paNewRects[uResized].left = iNewPosX; + paNewRects[uResized].top = iNewPosY; /* Verify all pairs of originally adjacent rectangles for all 4 directions. * If the pair has a "good" delta (that is the first rectangle intersects the second) diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.h index 4924ac6d..176f8867 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxHelpers.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -28,7 +28,7 @@ extern int hlpReportStatus(VBoxGuestFacilityStatus statusCurrent); extern void hlpReloadCursor(void); -extern void hlpResizeRect(RECTL *paRects, unsigned nRects, unsigned uPrimary, unsigned uResized, int iNewWidth, int iNewHeight); +extern void hlpResizeRect(RECTL *paRects, unsigned nRects, unsigned uPrimary, unsigned uResized, int iNewWidth, int iNewHeight, int iNewPosX, int iNewPosY); extern int hlpShowBalloonTip(HINSTANCE hInst, HWND hWnd, UINT uID, const char *pszMsg, const char *pszTitle, UINT uTimeout, DWORD dwInfoFlags); #endif /* !___VBOXTRAY_HELPERS_H */ diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxHostVersion.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxHostVersion.cpp index dc429e50..792477eb 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxHostVersion.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxHostVersion.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.cpp index 3245383e..e8b8cfc1 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.cpp @@ -1,10 +1,12 @@ /* $Id: VBoxIPC.cpp $ */ /** @file - * VboxIPC - IPC thread. + * VBoxIPC - IPC thread, acts as a (purely) local IPC server. + * Multiple sessions are supported, whereas every session + * has its own thread for processing requests. */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-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; @@ -20,133 +22,132 @@ #include "VBoxHelpers.h" #include "VBoxIPC.h" +#include <iprt/asm.h> #include <iprt/assert.h> +#include <iprt/critsect.h> #include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/list.h> +#include <iprt/localipc.h> #include <iprt/mem.h> #include <VBoxGuestInternal.h> -typedef struct _VBOXIPCCONTEXT + +/** + * IPC context data. + */ +typedef struct VBOXIPCCONTEXT { - const VBOXSERVICEENV *pEnv; - HANDLE hPipe; + /** Pointer to the service environment. */ + const VBOXSERVICEENV *pEnv; + /** Handle for the local IPC server. */ + RTLOCALIPCSERVER hServer; + /** Critical section serializing access to the session list, the state, + * the response event, the session event, and the thread event. */ + RTCRITSECT CritSect; + /** List of all active IPC sessions. */ + RTLISTANCHOR SessionList; } VBOXIPCCONTEXT, *PVBOXIPCCONTEXT; - static VBOXIPCCONTEXT gCtx = {0}; +/** Function pointer for GetLastInputInfo(). */ +typedef BOOL (WINAPI *PFNGETLASTINPUTINFO)(PLASTINPUTINFO); /** - * Reads an IPC message from a connected client, represented by the IPC - * context. - * - * @return IPRT status code. - * @param pCtx The IPC context. - * @param pMessage Buffer for receiving the message to be read. - * @param cbMessage Size (in bytes) of buffer for received message. + * IPC per-session thread data. */ -int VBoxIPCReadMessage(PVBOXIPCCONTEXT pCtx, BYTE *pMessage, DWORD cbMessage) +typedef struct VBOXIPCSESSION { - int rc = VINF_SUCCESS; - do - { - DWORD dwRead; - if (!ReadFile(pCtx->hPipe, pMessage, cbMessage, &dwRead, 0)) - { - rc = RTErrConvertFromWin32(GetLastError()); - } - else - { - if (rc == VERR_MORE_DATA) - rc = VINF_SUCCESS; - pMessage += dwRead; - cbMessage -= dwRead; - } - } - while (cbMessage && RT_SUCCESS(rc)); - return rc; + /** The list node required to be part of the + * IPC session list. */ + RTLISTNODE Node; + /** Pointer to the IPC context data. */ + PVBOXIPCCONTEXT volatile pCtx; + /** The local ipc client handle. */ + RTLOCALIPCSESSION volatile hSession; + /** Indicate that the thread should terminate ASAP. */ + bool volatile fTerminate; + /** The thread handle. */ + RTTHREAD hThread; + +} VBOXIPCSESSION, *PVBOXIPCSESSION; + +/** Static pointer to GetLastInputInfo() function. */ +static PFNGETLASTINPUTINFO s_pfnGetLastInputInfo = NULL; + +int vboxIPCSessionStop(PVBOXIPCSESSION pSession); + +static int vboxIPCHandleVBoxTrayRestart(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHdr, VERR_INVALID_POINTER); + + /** @todo Not implemented yet; don't return an error here. */ + return VINF_SUCCESS; } -/** - * Skips an IPC message by reading out the outstanding message - * body to discard it. - * - * @return IPRT status code. - * @param pCtx The IPC context. - * @param pHdr The header of message to skip. - */ -int VBoxIPCSkipMessage(PVBOXIPCCONTEXT pCtx, PVBOXTRAYIPCHEADER pHdr) +static int vboxIPCHandleShowBalloonMsg(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr) { - Assert(pHdr->cbBody); - BYTE *pbBuf = (BYTE*)RTMemAlloc(pHdr->cbBody); - if (!pbBuf) - return VERR_NO_MEMORY; - int rc = VBoxIPCReadMessage(pCtx, pbBuf, pHdr->cbBody); - RTMemFree(pbBuf); + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHdr, VERR_INVALID_POINTER); + AssertReturn(pHdr->uMsgLen > 0, VERR_INVALID_PARAMETER); + + VBOXTRAYIPCMSG_SHOWBALLOONMSG ipcMsg; + int rc = RTLocalIpcSessionRead(pSession->hSession, &ipcMsg, pHdr->uMsgLen, + NULL /* Exact read, blocking */); + if (RT_SUCCESS(rc)) + { + /* Showing the balloon tooltip is not critical. */ + int rc2 = hlpShowBalloonTip(ghInstance, ghwndToolWindow, ID_TRAYICON, + ipcMsg.szMsgContent, ipcMsg.szMsgTitle, + ipcMsg.uShowMS, ipcMsg.uType); + LogFlowFunc(("Showing \"%s\" - \"%s\" (type %RU32, %RU32ms), rc=%Rrc\n", + ipcMsg.szMsgTitle, ipcMsg.szMsgContent, + ipcMsg.uType, ipcMsg.uShowMS, rc2)); + } + return rc; } -/** - * Writes an IPC message to the IPC context's client. - * - * @return IPRT status code. - * @param pCtx The IPC context. - * @param pMessage Pointer to message to send. - * @param cbMessage Size (in bytes) of message to send. - */ -int VBoxIPCWriteMessage(PVBOXIPCCONTEXT pCtx, BYTE *pMessage, DWORD cbMessage) +static int vboxIPCHandleUserLastInput(PVBOXIPCSESSION pSession, PVBOXTRAYIPCHEADER pHdr) { + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHdr, VERR_INVALID_POINTER); + /* No actual message from client. */ + int rc = VINF_SUCCESS; - while (RT_SUCCESS(rc)) + + bool fLastInputAvailable = false; + VBOXTRAYIPCRES_USERLASTINPUT ipcRes; + if (s_pfnGetLastInputInfo) { - DWORD cbWritten; - if (!WriteFile(pCtx->hPipe, pMessage, cbMessage, &cbWritten, 0)) + /* Note: This only works up to 49.7 days (= 2^32, 32-bit counter) + since Windows was started. */ + LASTINPUTINFO lastInput; + lastInput.cbSize = sizeof(LASTINPUTINFO); + BOOL fRc = s_pfnGetLastInputInfo(&lastInput); + if (fRc) + { + ipcRes.uLastInput = (GetTickCount() - lastInput.dwTime) / 1000; + fLastInputAvailable = true; + } + else rc = RTErrConvertFromWin32(GetLastError()); - pMessage += cbWritten; } - return rc; -} - -int VBoxIPCPostQuitMessage(PVBOXIPCCONTEXT pCtx) -{ - VBOXTRAYIPCHEADER hdr; - hdr.ulMsg = VBOXTRAYIPCMSGTYPE_IPC_QUIT; - return VBoxIPCWriteMessage(pCtx, (BYTE*)&hdr, sizeof(hdr)); -} -/** - * Shows a balloon tooltip message in VBoxTray's - * message area in the Windows main taskbar. - * - * @return IPRT status code. - * @param pCtx IPC context of the caller. - * @param wParam wParam of received IPC message. - * @param lParam lParam of received IPC message. - */ -int VBoxIPCMsgShowBalloonMsg(PVBOXIPCCONTEXT pCtx, UINT wParam, UINT lParam) -{ - VBOXTRAYIPCMSG_SHOWBALLOONMSG msg; - int rc = VBoxIPCReadMessage(pCtx,(BYTE*)&msg, sizeof(msg)); - if (RT_SUCCESS(rc)) + if (!fLastInputAvailable) { - hlpShowBalloonTip(ghInstance, ghwndToolWindow, ID_TRAYICON, - msg.szContent, msg.szTitle, - msg.ulShowMS, msg.ulType); + /* No last input available. */ + ipcRes.uLastInput = UINT32_MAX; } - return rc; -} -/** - * Takes action to restart VBoxTray (this application). - * - * @return IPRT status code. - * @param pCtx IPC context of the caller. - * @param wParam wParam of received IPC message. - * @param lParam lParam of received IPC message. - */ -int VBoxIPCMsgRestart(PVBOXIPCCONTEXT pCtx, UINT wParam, UINT lParam) -{ - return 0; + int rc2 = RTLocalIpcSessionWrite(pSession->hSession, &ipcRes, sizeof(ipcRes)); + if (RT_SUCCESS(rc)) + rc = rc2; + + return rc; } /** @@ -159,149 +160,405 @@ int VBoxIPCMsgRestart(PVBOXIPCCONTEXT pCtx, UINT wParam, UINT lParam) */ int VBoxIPCInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread) { - Log(("VBoxTray: VBoxIPCInit\n")); + AssertPtrReturn(pEnv, VERR_INVALID_POINTER); + /** ppInstance not used here. */ + AssertPtrReturn(pfStartThread, VERR_INVALID_POINTER); + + LogFlowFuncEnter(); *pfStartThread = false; - gCtx.pEnv = pEnv; - int rc = VINF_SUCCESS; - SECURITY_ATTRIBUTES sa; - sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)RTMemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH); - if (!sa.lpSecurityDescriptor) - rc = VERR_NO_MEMORY; - else + int rc = RTCritSectInit(&gCtx.CritSect); + if (RT_SUCCESS(rc)) { - if (!InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) + RTUTF16 wszUserName[255]; + DWORD cchUserName = sizeof(wszUserName) / sizeof(RTUTF16); + BOOL fRc = GetUserNameW(wszUserName, &cchUserName); + if (!fRc) rc = RTErrConvertFromWin32(GetLastError()); - else - { - if (!SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, (PACL)0, FALSE)) - rc = RTErrConvertFromWin32(GetLastError()); - else - { - sa.nLength = sizeof(sa); - sa.bInheritHandle = TRUE; - } - } if (RT_SUCCESS(rc)) { - gCtx.hPipe = CreateNamedPipe((LPSTR)VBOXTRAY_PIPE_IPC, - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - VBOXTRAY_PIPE_IPC_BUFSIZE, /* Output buffer size. */ - VBOXTRAY_PIPE_IPC_BUFSIZE, /* Input buffer size. */ - NMPWAIT_USE_DEFAULT_WAIT, - &sa); - if (gCtx.hPipe == INVALID_HANDLE_VALUE) - rc = RTErrConvertFromWin32(GetLastError()); - else + char *pszUserName; + rc = RTUtf16ToUtf8(wszUserName, &pszUserName); + if (RT_SUCCESS(rc)) { - *pfStartThread = true; - *ppInstance = &gCtx; + char szPipeName[255]; + if (RTStrPrintf(szPipeName, sizeof(szPipeName), "%s%s", + VBOXTRAY_IPC_PIPE_PREFIX, pszUserName)) + { + rc = RTLocalIpcServerCreate(&gCtx.hServer, szPipeName, + RTLOCALIPC_FLAGS_MULTI_SESSION); + if (RT_SUCCESS(rc)) + { + RTStrFree(pszUserName); + + gCtx.pEnv = pEnv; + RTListInit(&gCtx.SessionList); + + *ppInstance = &gCtx; + *pfStartThread = true; + + /* GetLastInputInfo only is available starting at Windows 2000. */ + s_pfnGetLastInputInfo = (PFNGETLASTINPUTINFO) + RTLdrGetSystemSymbol("User32.dll", "GetLastInputInfo"); + + LogRelFunc(("Local IPC server now running at \"%s\"\n", + szPipeName)); + return VINF_SUCCESS; + } + + } + else + rc = VERR_NO_MEMORY; + + RTStrFree(pszUserName); } } - RTMemFree(sa.lpSecurityDescriptor); + + RTCritSectDelete(&gCtx.CritSect); } + + LogRelFunc(("Creating local IPC server failed with rc=%Rrc\n", rc)); return rc; } - -void VBoxIPCDestroy(const VBOXSERVICEENV *pEnv, void *pInstance) +void VBoxIPCStop(const VBOXSERVICEENV *pEnv, void *pInstance) { - Log(("VBoxTray: VBoxIPCDestroy\n")); + AssertPtrReturnVoid(pEnv); + AssertPtrReturnVoid(pInstance); + + LogFunc(("Stopping pInstance=%p\n", pInstance)); + /* Shut down local IPC server. */ PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance; AssertPtr(pCtx); - if (pCtx->hPipe) + if (pCtx->hServer != NIL_RTLOCALIPCSERVER) + { + int rc2 = RTLocalIpcServerCancel(pCtx->hServer); + if (RT_FAILURE(rc2)) + LogFunc(("Cancelling current listening call failed with rc=%Rrc\n", rc2)); + } + + /* Stop all remaining session threads. */ + int rc = RTCritSectEnter(&pCtx->CritSect); + if (RT_SUCCESS(rc)) { - VBoxIPCPostQuitMessage(pCtx); - CloseHandle(pCtx->hPipe); + PVBOXIPCSESSION pSession; + RTListForEach(&pCtx->SessionList, pSession, VBOXIPCSESSION, Node) + { + int rc2 = vboxIPCSessionStop(pSession); + if (RT_FAILURE(rc2)) + { + LogFunc(("Stopping IPC session %p failed with rc=%Rrc\n", + pSession, rc2)); + /* Keep going. */ + } + } } - return; } -/** - * Thread function to wait for and process seamless mode change - * requests - */ -unsigned __stdcall VBoxIPCThread(void *pInstance) +void VBoxIPCDestroy(const VBOXSERVICEENV *pEnv, void *pInstance) { - Log(("VBoxTray: VBoxIPCThread\n")); + AssertPtrReturnVoid(pEnv); + AssertPtrReturnVoid(pInstance); + + LogFunc(("Destroying pInstance=%p\n", pInstance)); PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance; AssertPtr(pCtx); - bool fTerminate = false; - int rc = VINF_SUCCESS; + /* Shut down local IPC server. */ + int rc = RTCritSectEnter(&pCtx->CritSect); + if (RT_SUCCESS(rc)) + { + rc = RTLocalIpcServerDestroy(pCtx->hServer); + if (RT_FAILURE(rc)) + LogFunc(("Unable to destroy IPC server, rc=%Rrc\n", rc)); + + int rc2 = RTCritSectLeave(&pCtx->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + LogFunc(("Waiting for remaining IPC sessions to shut down ...\n")); + /* Wait for all IPC session threads to shut down. */ + bool fListIsEmpty = true; do { - DWORD dwErr = ERROR_SUCCESS; - BOOL fConnected = ConnectNamedPipe(pCtx->hPipe, NULL) - ? TRUE - : (GetLastError() == ERROR_PIPE_CONNECTED); + int rc2 = RTCritSectEnter(&pCtx->CritSect); + if (RT_SUCCESS(rc2)) + { + fListIsEmpty = RTListIsEmpty(&pCtx->SessionList); + rc2 = RTCritSectLeave(&pCtx->CritSect); + + if (!fListIsEmpty) /* Don't hog CPU while waiting. */ + RTThreadSleep(100); + } - /* Are we supposed to stop? */ - if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0) == WAIT_OBJECT_0) + if (RT_FAILURE(rc2)) break; - if (fConnected) - { - VBOXTRAYIPCHEADER hdr; - DWORD read = 0; + } while (!fListIsEmpty); + + AssertMsg(fListIsEmpty, + ("Session thread list is not empty when it should\n")); - if (!ReadFile(pCtx->hPipe, &hdr, sizeof(hdr), &read, 0)) - dwErr = GetLastError(); + LogFunc(("All remaining IPC sessions shut down\n")); - /** @todo We might want to spawn a thread per connected client - * in order to perform longer tasks. */ + int rc2 = RTCritSectDelete(&pCtx->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; - if (SUCCEEDED(dwErr)) + LogFunc(("Destroyed pInstance=%p, rc=%Rrc\n", + pInstance, rc)); +} + +/** + * Services a client session. + * + * @returns VINF_SUCCESS. + * @param hThread The thread handle. + * @param pvSession Pointer to the session instance data. + */ +static DECLCALLBACK(int) vboxIPCSessionThread(RTTHREAD hThread, void *pvSession) +{ + PVBOXIPCSESSION pThis = (PVBOXIPCSESSION)pvSession; + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + RTLOCALIPCSESSION hSession = pThis->hSession; + AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER); + + LogFunc(("pThis=%p\n", pThis)); + + int rc = VINF_SUCCESS; + + /* + * Process client requests until it quits or we're cancelled on termination. + */ + while ( !ASMAtomicUoReadBool(&pThis->fTerminate) + && RT_SUCCESS(rc)) + { + /* The next call will be cancelled via VBoxIPCStop if needed. */ + rc = RTLocalIpcSessionWaitForData(hSession, RT_INDEFINITE_WAIT); + if (RT_FAILURE(rc)) + { + if (rc == VERR_CANCELLED) { - Log(("VBoxTray: VBoxIPCThread: Received message %ld ...\n", hdr.ulMsg)); - switch (hdr.ulMsg) + LogFunc(("Session %p: Waiting for data cancelled\n", pThis)); + rc = VINF_SUCCESS; + break; + } + else + LogFunc(("Session %p: Waiting for session data failed with rc=%Rrc\n", + pThis, rc)); + } + else + { + VBOXTRAYIPCHEADER ipcHdr; + rc = RTLocalIpcSessionRead(hSession, &ipcHdr, sizeof(ipcHdr), + NULL /* Exact read, blocking */); + bool fRejected = false; /* Reject current command? */ + if (RT_SUCCESS(rc)) + fRejected = ipcHdr.uMagic != VBOXTRAY_IPC_HDR_MAGIC + || ipcHdr.uHdrVersion != 0; /* We only know version 0 commands for now. */ + + if ( !fRejected + && RT_SUCCESS(rc)) + { + switch (ipcHdr.uMsgType) { case VBOXTRAYIPCMSGTYPE_RESTART: - rc = VBoxIPCMsgRestart(pCtx, hdr.wParam, hdr.lParam); - if (RT_SUCCESS(rc)) - fTerminate = true; + rc = vboxIPCHandleVBoxTrayRestart(pThis, &ipcHdr); break; - case VBOXTRAYIPCMSGTYPE_IPC_QUIT: - fTerminate = true; + case VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG: + rc = vboxIPCHandleShowBalloonMsg(pThis, &ipcHdr); break; - case VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG: - rc = VBoxIPCMsgShowBalloonMsg(pCtx, hdr.wParam, hdr.lParam); + case VBOXTRAYIPCMSGTYPE_USERLASTINPUT: + rc = vboxIPCHandleUserLastInput(pThis, &ipcHdr); break; default: - /* Unknown message received, try to receive the body and - * just skip it. */ - Log(("VBoxTray: VBoxIPCThread: Unknown message %ld, skipping ...\n", hdr.ulMsg)); - if (hdr.cbBody) - rc = VBoxIPCSkipMessage(pCtx, &hdr); + { + /* Unknown command, reject. */ + fRejected = true; break; + } } + + if (RT_FAILURE(rc)) + LogFunc(("Session %p: Handling command %RU32 failed with rc=%Rrc\n", + pThis, ipcHdr.uMsgType, rc)); } - /* Disconnect the client from the pipe. */ - DisconnectNamedPipe(pCtx->hPipe); + if (fRejected) + { + static int s_cRejectedCmds = 0; + if (++s_cRejectedCmds <= 3) + { + LogRelFunc(("Session %p: Received invalid/unknown command %RU32 (%RU32 bytes), rejecting (%RU32/3)\n", + pThis, ipcHdr.uMsgType, ipcHdr.uMsgLen, s_cRejectedCmds + 1)); + if (ipcHdr.uMsgLen) + { + /* Get and discard payload data. */ + size_t cbRead; + uint8_t devNull[_1K]; + while (ipcHdr.uMsgLen) + { + rc = RTLocalIpcSessionRead(hSession, &devNull, sizeof(devNull), &cbRead); + if (RT_FAILURE(rc)) + break; + AssertRelease(cbRead <= ipcHdr.uMsgLen); + ipcHdr.uMsgLen -= (uint32_t)cbRead; + } + } + } + else + rc = VERR_INVALID_PARAMETER; /* Enough fun, bail out. */ + } + } + } + + LogFunc(("Session %p: Handler ended with rc=%Rrc\n", + pThis, rc)); + + /* + * Close the session. + */ + int rc2 = RTLocalIpcSessionClose(hSession); + if (RT_FAILURE(rc2)) + LogFunc(("Session %p: Failed closing session %p, rc=%Rrc\n", pThis, rc2)); + + /* + * Clean up the session. + */ + PVBOXIPCCONTEXT pCtx = ASMAtomicReadPtrT(&pThis->pCtx, PVBOXIPCCONTEXT); + AssertMsg(pCtx, ("Session %p: No context found\n", pThis)); + rc2 = RTCritSectEnter(&pCtx->CritSect); + if (RT_SUCCESS(rc2)) + { + /* Remove this session from the session list. */ + RTListNodeRemove(&pThis->Node); + + rc2 = RTCritSectLeave(&pCtx->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + LogFunc(("Session %p: Terminated with rc=%Rrc, freeing ...\n", + pThis, rc)); + + RTMemFree(pThis); + pThis = NULL; + + return rc; +} + +static int vboxIPCSessionCreate(PVBOXIPCCONTEXT pCtx, RTLOCALIPCSESSION hSession) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(hSession != NIL_RTLOCALIPCSESSION, VERR_INVALID_PARAMETER); + + int rc = RTCritSectEnter(&pCtx->CritSect); + if (RT_SUCCESS(rc)) + { + PVBOXIPCSESSION pSession = (PVBOXIPCSESSION)RTMemAllocZ(sizeof(VBOXIPCSESSION)); + if (pSession) + { + pSession->pCtx = pCtx; + pSession->hSession = hSession; + pSession->fTerminate = false; + pSession->hThread = NIL_RTTHREAD; + + /* Start IPC session thread. */ + LogFlowFunc(("Creating thread for session %p ...\n", pSession)); + rc = RTThreadCreate(&pSession->hThread, vboxIPCSessionThread, + pSession /* pvUser */, 0 /* Default stack size */, + RTTHREADTYPE_DEFAULT, 0 /* Flags */, "VBXTRYIPCSESS"); + if (RT_SUCCESS(rc)) + { + /* Add session thread to session IPC list. */ + RTListAppend(&pCtx->SessionList, &pSession->Node); + } + else + { + int rc2 = RTLocalIpcSessionClose(hSession); + if (RT_FAILURE(rc2)) + LogFunc(("Failed closing session %p, rc=%Rrc\n", pSession, rc2)); + + LogFunc(("Failed to create thread for session %p, rc=%Rrc\n", pSession, rc)); + RTMemFree(pSession); + } } else - CloseHandle(pCtx->hPipe); + rc = VERR_NO_MEMORY; + + int rc2 = RTCritSectLeave(&pCtx->CritSect); + AssertRC(rc2); + } + + return rc; +} + +static int vboxIPCSessionStop(PVBOXIPCSESSION pSession) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + + ASMAtomicWriteBool(&pSession->fTerminate, true); + + RTLOCALIPCSESSION hSession; + ASMAtomicXchgHandle(&pSession->hSession, NIL_RTLOCALIPCSESSION, &hSession); + if (hSession) + return RTLocalIpcSessionClose(hSession); + + return VINF_SUCCESS; +} + +/** + * Thread function to wait for and process seamless mode change + * requests + */ +unsigned __stdcall VBoxIPCThread(void *pInstance) +{ + LogFlowFuncEnter(); + + PVBOXIPCCONTEXT pCtx = (PVBOXIPCCONTEXT)pInstance; + AssertPtr(pCtx); + + bool fShutdown = false; + for (;;) + { + RTLOCALIPCSESSION hClientSession = NIL_RTLOCALIPCSESSION; + int rc = RTLocalIpcServerListen(pCtx->hServer, &hClientSession); + if (RT_FAILURE(rc)) + { + if (rc == VERR_CANCELLED) + { + LogFlow(("Cancelled\n")); + fShutdown = true; + } + else + LogRelFunc(("Listening failed with rc=%Rrc\n", rc)); + } - /* Sleep a bit to not eat too much CPU in case the above call always fails. */ - if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 10) == WAIT_OBJECT_0) - fTerminate = true; - if (fTerminate) - Log(("VBoxTray: VBoxIPCThread: Terminating ...\n")); - } while (!fTerminate); + if (fShutdown) + break; + rc = vboxIPCSessionCreate(pCtx, hClientSession); + if (RT_FAILURE(rc)) + { + LogRelFunc(("Creating new IPC server session failed with rc=%Rrc\n", rc)); + /* Keep going. */ + } + + AssertPtr(pCtx->pEnv); + if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 0 /* No waiting */) == WAIT_OBJECT_0) + break; + } - Log(("VBoxTray: VBoxIPCThread exited\n")); + LogFlowFuncLeave(); return 0; } diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.h index 5f3a531a..70ae2d0d 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxIPC.h @@ -1,9 +1,12 @@ +/* $Id: VBoxIPC.h $ */ /** @file - * VBoxSeamless - Seamless windows + * VBoxIPC - IPC thread, acts as a (purely) local IPC server. + * Multiple sessions are supported, whereas every session + * has its own thread for processing requests. */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -14,12 +17,12 @@ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -#ifndef __VBOXSERVICEIPC__H -#define __VBOXSERVICEIPC__H +#ifndef __VBOXTRAYIPCSERVER__H +#define __VBOXTRAYIPCSERVER__H -/* The seamless windows service prototypes */ -int VBoxIPCInit (const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread); -unsigned __stdcall VBoxIPCThread (void *pInstance); -void VBoxIPCDestroy (const VBOXSERVICEENV *pEnv, void *pInstance); +int VBoxIPCInit (const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread); +unsigned __stdcall VBoxIPCThread (void *pInstance); +void VBoxIPCStop (const VBOXSERVICEENV *pEnv, void *pInstance); +void VBoxIPCDestroy (const VBOXSERVICEENV *pEnv, void *pInstance); -#endif /* __VBOXSERVICEIPC__H */ +#endif /* __VBOXTRAYIPCSERVER__H */ diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxLA.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxLA.cpp index 5856c728..a23219b7 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxLA.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxLA.cpp @@ -26,6 +26,7 @@ #include <iprt/assert.h> #include <iprt/alloc.h> #include <iprt/list.h> +#include <iprt/ldr.h> #define LALOG(a) do { if (gCtx.fLogEnabled) LogRel(a); } while(0) #define LALOGFORCE(a) do { LogRel(a); } while(0) @@ -87,8 +88,6 @@ struct VBOXLACONTEXT char *pszPropWaitPattern; /* Which properties are monitored. */ } activeClient; - HMODULE hModuleKernel32; - BOOL (WINAPI * pfnProcessIdToSessionId)(DWORD dwProcessId, DWORD *pSessionId); }; @@ -1249,16 +1248,7 @@ int VBoxLAInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThrea RT_ZERO(gCtx.activeClient); - gCtx.hModuleKernel32 = LoadLibrary("KERNEL32"); - - if (gCtx.hModuleKernel32) - { - *(uintptr_t *)&gCtx.pfnProcessIdToSessionId = (uintptr_t)GetProcAddress(gCtx.hModuleKernel32, "ProcessIdToSessionId"); - } - else - { - gCtx.pfnProcessIdToSessionId = NULL; - } + *(void **)&gCtx.pfnProcessIdToSessionId = RTLdrGetSystemSymbol("kernel32.dll", "ProcessIdToSessionId"); *pfStartThread = true; *ppInstance = &gCtx; return VINF_SUCCESS; @@ -1279,12 +1269,7 @@ void VBoxLADestroy(const VBOXSERVICEENV *pEnv, void *pInstance) ActionExecutorDeleteActions(&pCtx->listAttachActions); ActionExecutorDeleteActions(&pCtx->listDetachActions); - if (pCtx->hModuleKernel32) - { - FreeLibrary(pCtx->hModuleKernel32); - pCtx->pfnProcessIdToSessionId = NULL; - } - pCtx->hModuleKernel32 = NULL; + pCtx->pfnProcessIdToSessionId = NULL; } /* diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxMMR.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxMMR.cpp new file mode 100644 index 00000000..4708c0bc --- /dev/null +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxMMR.cpp @@ -0,0 +1,96 @@ +/* $Id: VBoxMMR.cpp $ */ +/** @file + * VBoxMMR - Multimedia Redirection + */ + +/* + * Copyright (C) 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; + * 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 "VBoxMMR.h" +#include <iprt/ldr.h> + +struct VBOXMMRCONTEXT +{ + RTLDRMOD hModHook; + HHOOK hHook; +}; + +static VBOXMMRCONTEXT gCtx = {0}; + +static const char *g_pszMMRDLL = "VBoxMMRHook"; +static const char *g_pszMMRPROC = "CBTProc"; + +void VBoxMMRCleanup(VBOXMMRCONTEXT *pCtx) +{ + if (pCtx->hHook) + { + UnhookWindowsHookEx(pCtx->hHook); + pCtx->hHook = NULL; + } + + if (pCtx->hModHook != NIL_RTLDRMOD) + { + RTLdrClose(pCtx->hModHook); + pCtx->hModHook = NIL_RTLDRMOD; + } +} + +int VBoxMMRInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread) +{ + LogRel2(("VBoxMMR: Initializing\n")); + + int rc = RTLdrLoadAppPriv(g_pszMMRDLL, &gCtx.hModHook); + if (RT_SUCCESS(rc)) + { + HOOKPROC pHook = (HOOKPROC)RTLdrGetFunction(gCtx.hModHook, g_pszMMRPROC); + if (pHook) + { + HMODULE hMod = (HMODULE)RTLdrGetNativeHandle(gCtx.hModHook); + Assert(hMod != (HMODULE)~(uintptr_t)0); + gCtx.hHook = SetWindowsHookEx(WH_CBT, pHook, hMod, 0); + if (gCtx.hHook) + { + *ppInstance = &gCtx; + return VINF_SUCCESS; + } + + rc = RTErrConvertFromWin32(GetLastError()); + LogRel2(("VBoxMMR: Error installing hooking proc: %Rrc\n", rc)); + } + else + { + LogRel2(("VBoxMMR: Hooking proc not found\n")); + rc = VERR_NOT_FOUND; + } + + RTLdrClose(gCtx.hModHook); + gCtx.hModHook = NIL_RTLDRMOD; + } + else + LogRel2(("VBoxMMR: Hooking library not found (%Rrc)\n", rc)); + + return rc; +} + +void VBoxMMRDestroy(const VBOXSERVICEENV *pEnv, void *pInstance) +{ + VBOXMMRCONTEXT *pCtx = (VBOXMMRCONTEXT *) pInstance; + + VBoxMMRCleanup(pCtx); +} + +unsigned __stdcall VBoxMMRThread(void *pInstance) +{ + return 0; +} + diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxMMR.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxMMR.h new file mode 100644 index 00000000..40c80490 --- /dev/null +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxMMR.h @@ -0,0 +1,33 @@ +/* $Id: VBoxMMR.h $ */ +/** @file + * VBoxMMR - Multimedia Redirection + */ + +/* + * Copyright (C) 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; + * 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. + */ + +#ifndef __VBOXSERVICEMMR__H +#define __VBOXSERVICEMMR__H + +int VBoxMMRInit( + const VBOXSERVICEENV *pEnv, + void **ppInstance, + bool *pfStartThread); + +unsigned __stdcall VBoxMMRThread( + void *pInstance); + +void VBoxMMRDestroy( + const VBOXSERVICEENV *pEnv, + void *pInstance); + +#endif /* __VBOXSERVICEMMR__H */ diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.cpp index d66c5c9d..d95ba82d 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.h index 1cab5da9..7a04d0c9 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxRestore.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.cpp index 9c1fbecc..1b81c26d 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.cpp @@ -4,7 +4,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; @@ -24,16 +24,17 @@ #include <VBoxDisplay.h> #include <VBox/VMMDev.h> #include <iprt/assert.h> +#include <iprt/ldr.h> #include <VBoxGuestInternal.h> typedef struct _VBOXSEAMLESSCONTEXT { const VBOXSERVICEENV *pEnv; - HMODULE hModule; + RTLDRMOD hModHook; - BOOL (* pfnVBoxInstallHook)(HMODULE hDll); - BOOL (* pfnVBoxRemoveHook)(); + BOOL (* pfnVBoxHookInstallWindowTracker)(HMODULE hDll); + BOOL (* pfnVBoxHookRemoveWindowTracker)(); PVBOXDISPIFESCAPE lpEscapeData; } VBOXSEAMLESSCONTEXT; @@ -54,6 +55,7 @@ int VBoxSeamlessInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStar *pfStartThread = false; gCtx.pEnv = pEnv; + gCtx.hModHook = NIL_RTLDRMOD; OSVERSIONINFO OSinfo; OSinfo.dwOSVersionInfoSize = sizeof (OSinfo); @@ -71,27 +73,25 @@ int VBoxSeamlessInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStar else { /* Will fail if SetWinEventHook is not present (version < NT4 SP6 apparently) */ - gCtx.hModule = LoadLibrary(VBOXHOOK_DLL_NAME); - if (gCtx.hModule) + rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &gCtx.hModHook); + if (RT_SUCCESS(rc)) { - *(uintptr_t *)&gCtx.pfnVBoxInstallHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxInstallHook"); - *(uintptr_t *)&gCtx.pfnVBoxRemoveHook = (uintptr_t)GetProcAddress(gCtx.hModule, "VBoxRemoveHook"); + *(PFNRT *)&gCtx.pfnVBoxHookInstallWindowTracker = RTLdrGetFunction(gCtx.hModHook, "VBoxHookInstallWindowTracker"); + *(PFNRT *)&gCtx.pfnVBoxHookRemoveWindowTracker = RTLdrGetFunction(gCtx.hModHook, "VBoxHookRemoveWindowTracker"); - /* Inform the host that we support the seamless window mode. */ - rc = VbglR3SetGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS, 0); - if (RT_SUCCESS(rc)) + /* rc should contain success status */ + AssertRC(rc); + + VBoxSeamlessSetSupported(TRUE); + +// if (RT_SUCCESS(rc)) { *pfStartThread = true; *ppInstance = &gCtx; } - else - Log(("VBoxTray: VBoxSeamlessInit: Failed to set seamless capability\n")); } else - { - rc = RTErrConvertFromWin32(GetLastError()); Log(("VBoxTray: VBoxSeamlessInit: LoadLibrary of \"%s\" failed with rc=%Rrc\n", VBOXHOOK_DLL_NAME, rc)); - } } return rc; @@ -102,34 +102,36 @@ void VBoxSeamlessDestroy(const VBOXSERVICEENV *pEnv, void *pInstance) { Log(("VBoxTray: VBoxSeamlessDestroy\n")); + VBoxSeamlessSetSupported(FALSE); + /* Inform the host that we no longer support the seamless window mode. */ - int rc = VbglR3SetGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS); - if (RT_FAILURE(rc)) - Log(("VBoxTray: VBoxSeamlessDestroy: Failed to unset seamless capability, rc=%Rrc\n", rc)); - - if (gCtx.pfnVBoxRemoveHook) - gCtx.pfnVBoxRemoveHook(); - if (gCtx.hModule) - FreeLibrary(gCtx.hModule); - gCtx.hModule = 0; + if (gCtx.pfnVBoxHookRemoveWindowTracker) + gCtx.pfnVBoxHookRemoveWindowTracker(); + if (gCtx.hModHook != NIL_RTLDRMOD) + { + RTLdrClose(gCtx.hModHook); + gCtx.hModHook = NIL_RTLDRMOD; + } return; } -void VBoxSeamlessInstallHook() +static void VBoxSeamlessInstallHook() { - if (gCtx.pfnVBoxInstallHook) + if (gCtx.pfnVBoxHookInstallWindowTracker) { /* Check current visible region state */ - VBoxSeamlessCheckWindows(); + VBoxSeamlessCheckWindows(true); - gCtx.pfnVBoxInstallHook(gCtx.hModule); + HMODULE hMod = (HMODULE)RTLdrGetNativeHandle(gCtx.hModHook); + Assert(hMod != (HMODULE)~(uintptr_t)0); + gCtx.pfnVBoxHookInstallWindowTracker(hMod); } } -void VBoxSeamlessRemoveHook() +static void VBoxSeamlessRemoveHook() { - if (gCtx.pfnVBoxRemoveHook) - gCtx.pfnVBoxRemoveHook(); + if (gCtx.pfnVBoxHookRemoveWindowTracker) + gCtx.pfnVBoxHookRemoveWindowTracker(); if (gCtx.lpEscapeData) { @@ -138,6 +140,27 @@ void VBoxSeamlessRemoveHook() } } +extern HANDLE ghSeamlessKmNotifyEvent; + +static VBOXDISPIF_SEAMLESS gVBoxDispIfSeamless; + + +void VBoxSeamlessEnable() +{ + Assert(ghSeamlessKmNotifyEvent); + + VBoxDispIfSeamlesCreate(&gCtx.pEnv->dispIf, &gVBoxDispIfSeamless, ghSeamlessKmNotifyEvent); + + VBoxSeamlessInstallHook(); +} + +void VBoxSeamlessDisable() +{ + VBoxSeamlessRemoveHook(); + + VBoxDispIfSeamlesTerm(&gVBoxDispIfSeamless); +} + BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam) { PVBOX_ENUM_PARAM lpParam = (PVBOX_ENUM_PARAM)lParam; @@ -154,27 +177,60 @@ BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam) /* Only visible windows that are present on the desktop are interesting here */ if (GetWindowRect(hwnd, &rectWindow)) { - rectVisible = rectWindow; - char szWindowText[256]; szWindowText[0] = 0; + OSVERSIONINFO OSinfo; + HWND hStart = NULL; GetWindowText(hwnd, szWindowText, sizeof(szWindowText)); + OSinfo.dwOSVersionInfoSize = sizeof (OSinfo); + GetVersionEx (&OSinfo); + if (OSinfo.dwMajorVersion >= 6) + { + hStart = ::FindWindowEx(GetDesktopWindow(), NULL, "Button", "Start"); + if ( hwnd == hStart && szWindowText != NULL + && !(strcmp(szWindowText, "Start")) + ) + { + /* for vista and above. To solve the issue of small bar above + * the Start button when mouse is hovered over the start button in seamless mode. + * Difference of 7 is observed in Win 7 platform between the dimensionsof rectangle with Start title and its shadow. + */ + rectWindow.top += 7; + rectWindow.bottom -=7; + } + } + rectVisible = rectWindow; + +#ifdef LOG_ENABLED + DWORD pid = 0; + DWORD tid = GetWindowThreadProcessId(hwnd, &pid); +#endif /* Filter out Windows XP shadow windows */ /** @todo still shows inside the guest */ if ( szWindowText[0] == 0 - && dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS) - && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST)) + && ( + (dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS) + && dwExStyle == (WS_EX_LAYERED|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|WS_EX_TOPMOST)) + || (dwStyle == (WS_POPUP|WS_VISIBLE|WS_DISABLED|WS_CLIPSIBLINGS|WS_CLIPCHILDREN) + && dwExStyle == (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE)) + || (dwStyle == (WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN) + && dwExStyle == (WS_EX_TOOLWINDOW)) + )) { Log(("VBoxTray: Filter out shadow window style=%x exstyle=%x\n", dwStyle, dwExStyle)); + Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (filtered)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom)); + Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle)); + Log(("VBoxTray: pid=%d tid=%d\n", pid, tid)); return TRUE; } /** @todo will this suffice? The Program Manager window covers the whole screen */ if (strcmp(szWindowText, "Program Manager")) { - Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom)); + Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (applying)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom)); Log(("VBoxTray: title=%s style=%x exStyle=%x\n", szWindowText, dwStyle, dwExStyle)); + Log(("VBoxTray: pid=%d tid=%d\n", pid, tid)); HRGN hrgn = CreateRectRgn(0,0,0,0); @@ -203,13 +259,17 @@ BOOL CALLBACK VBoxEnumFunc(HWND hwnd, LPARAM lParam) { Log(("VBoxTray: Enum hwnd=%x rect (%d,%d) (%d,%d) (ignored)\n", hwnd, rectWindow.left, rectWindow.top, rectWindow.right, rectWindow.bottom)); Log(("VBoxTray: title=%s style=%x\n", szWindowText, dwStyle)); + Log(("VBoxTray: pid=%d tid=%d\n", pid, tid)); } } return TRUE; /* continue enumeration */ } -void VBoxSeamlessCheckWindows() +void VBoxSeamlessCheckWindows(bool fForce) { + if (!VBoxDispIfSeamlesIsValid(&gVBoxDispIfSeamless)) + return; + VBOX_ENUM_PARAM param; param.hdc = GetDC(HWND_DESKTOP); @@ -243,12 +303,13 @@ void VBoxSeamlessCheckWindows() } #endif LPRGNDATA lpCtxRgnData = VBOXDISPIFESCAPE_DATA(gCtx.lpEscapeData, RGNDATA); - if ( !gCtx.lpEscapeData + if (fForce + || !gCtx.lpEscapeData || (lpCtxRgnData->rdh.dwSize + lpCtxRgnData->rdh.nRgnSize != cbSize) || memcmp(lpCtxRgnData, lpRgnData, cbSize)) { /* send to display driver */ - VBoxDispIfEscape(&gCtx.pEnv->dispIf, lpEscapeData, cbSize); + VBoxDispIfSeamlesSubmit(&gVBoxDispIfSeamless, lpEscapeData, cbSize); if (gCtx.lpEscapeData) free(gCtx.lpEscapeData); @@ -341,7 +402,7 @@ unsigned __stdcall VBoxSeamlessThread(void *pInstance) if (!ret) Log(("VBoxTray: SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError())); } - PostMessage(ghwndToolWindow, WM_VBOX_REMOVE_SEAMLESS_HOOK, 0, 0); + PostMessage(ghwndToolWindow, WM_VBOX_SEAMLESS_DISABLE, 0, 0); break; case VMMDev_Seamless_Visible_Region: @@ -355,7 +416,7 @@ unsigned __stdcall VBoxSeamlessThread(void *pInstance) ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0); if (!ret) Log(("VBoxTray: SystemParametersInfo SPI_SETSCREENSAVEACTIVE failed with %d\n", GetLastError())); - PostMessage(ghwndToolWindow, WM_VBOX_INSTALL_SEAMLESS_HOOK, 0, 0); + PostMessage(ghwndToolWindow, WM_VBOX_SEAMLESS_ENABLE, 0, 0); break; case VMMDev_Seamless_Host_Window: diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.h index bf4bfb87..e093d042 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxSeamless.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,8 +23,10 @@ unsigned __stdcall VBoxSeamlessThread (void *pInstance); void VBoxSeamlessDestroy (const VBOXSERVICEENV *pEnv, void *pInstance); -void VBoxSeamlessInstallHook(); -void VBoxSeamlessRemoveHook(); -void VBoxSeamlessCheckWindows(); +void VBoxSeamlessEnable(); +void VBoxSeamlessDisable(); +void VBoxSeamlessCheckWindows(bool fForce); + +void VBoxSeamlessSetSupported(BOOL fSupported); #endif /* __VBOXSERVICESEAMLESS__H */ diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxSharedFolders.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxSharedFolders.cpp index 2ac9d078..400562e3 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxSharedFolders.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxSharedFolders.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-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; @@ -26,9 +26,7 @@ int VBoxSharedFoldersAutoMount(void) { uint32_t u32ClientId; int rc = VbglR3SharedFolderConnect(&u32ClientId); - if (RT_FAILURE(rc)) - Log(("VBoxTray: Failed to connect to the shared folder service, error %Rrc\n", rc)); - else + if (RT_SUCCESS(rc)) { uint32_t cMappings; VBGLR3SHAREDFOLDERMAPPING *paMappings; @@ -127,6 +125,12 @@ int VBoxSharedFoldersAutoMount(void) Log(("VBoxTray: Error while getting the shared folder mappings, rc = %Rrc\n", rc)); VbglR3SharedFolderDisconnect(u32ClientId); } + else + { + Log(("VBoxTray: Failed to connect to the shared folder service, error %Rrc\n", rc)); + /* return success, otherwise VBoxTray will not start! */ + rc = VINF_SUCCESS; + } return rc; } diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp index 7bcf3cdc..e9862389 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,6 +31,7 @@ #include "VBoxSharedFolders.h" #include "VBoxIPC.h" #include "VBoxLA.h" +#include "VBoxMMR.h" #include <VBoxHook.h> #include "resource.h" #include <malloc.h> @@ -39,7 +40,71 @@ #include <sddl.h> #include <iprt/buildconfig.h> +#include <iprt/ldr.h> +/* Default desktop state tracking */ +#include <Wtsapi32.h> + +/* + * St (session [state] tracking) functionality API + * + * !!!NOTE: this API is NOT thread-safe!!! + * it is supposed to be called & used from within the window message handler thread + * of the window passed to vboxStInit */ +static int vboxStInit(HWND hWnd); +static void vboxStTerm(void); +/* @returns true on "IsActiveConsole" state change */ +static BOOL vboxStHandleEvent(WPARAM EventID, LPARAM SessionID); +static BOOL vboxStIsActiveConsole(); +static BOOL vboxStCheckTimer(WPARAM wEvent); + +/* + * Dt (desktop [state] tracking) functionality API + * + * !!!NOTE: this API is NOT thread-safe!!! + * */ +static int vboxDtInit(); +static void vboxDtTerm(); +/* @returns true on "IsInputDesktop" state change */ +static BOOL vboxDtHandleEvent(); +/* @returns true iff the application (VBoxTray) desktop is input */ +static BOOL vboxDtIsInputDesktop(); +static HANDLE vboxDtGetNotifyEvent(); +static BOOL vboxDtCheckTimer(WPARAM wParam); + +/* caps API */ +#define VBOXCAPS_ENTRY_IDX_SEAMLESS 0 +#define VBOXCAPS_ENTRY_IDX_GRAPHICS 1 +#define VBOXCAPS_ENTRY_IDX_COUNT 2 + +typedef enum VBOXCAPS_ENTRY_FUNCSTATE +{ + /* the cap is unsupported */ + VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED = 0, + /* the cap is supported */ + VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED, + /* the cap functionality is started, it can be disabled however if its AcState is not ACQUIRED */ + VBOXCAPS_ENTRY_FUNCSTATE_STARTED, +} VBOXCAPS_ENTRY_FUNCSTATE; + + +static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState); +static int VBoxCapsInit(); +static int VBoxCapsReleaseAll(); +static void VBoxCapsTerm(); +static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap); +static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap); +static BOOL VBoxCapsCheckTimer(WPARAM wParam); +static int VBoxCapsEntryRelease(uint32_t iCap); +static int VBoxCapsEntryAcquire(uint32_t iCap); +static int VBoxCapsAcquireAllSupported(); + +/* console-related caps API */ +static BOOL VBoxConsoleIsAllowed(); +static void VBoxConsoleEnable(BOOL fEnable); +static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported); + +static void VBoxGrapicsSetSupported(BOOL fSupported); /******************************************************************************* * Internal Functions * @@ -51,13 +116,15 @@ static LRESULT CALLBACK vboxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR static int vboxTrayGlMsgTaskbarCreated(WPARAM lParam, LPARAM wParam); /*static int vboxTrayGlMsgShowBalloonMsg(WPARAM lParam, LPARAM wParam);*/ +static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg); /******************************************************************************* * Global Variables * *******************************************************************************/ HANDLE ghVBoxDriver; HANDLE ghStopSem; -HANDLE ghSeamlessNotifyEvent = 0; +HANDLE ghSeamlessWtNotifyEvent = 0; +HANDLE ghSeamlessKmNotifyEvent = 0; SERVICE_STATUS gVBoxServiceStatus; SERVICE_STATUS_HANDLE gVBoxServiceStatusHandle; HINSTANCE ghInstance; @@ -73,18 +140,21 @@ static VBOXSERVICEINFO vboxServiceTable[] = "Display", VBoxDisplayInit, VBoxDisplayThread, + NULL /* pfnStop */, VBoxDisplayDestroy }, { "Shared Clipboard", VBoxClipboardInit, VBoxClipboardThread, + NULL /* pfnStop */, VBoxClipboardDestroy }, { "Seamless Windows", VBoxSeamlessInit, VBoxSeamlessThread, + NULL /* pfnStop */, VBoxSeamlessDestroy }, #ifdef VBOX_WITH_VRDP_SESSION_HANDLING @@ -92,6 +162,7 @@ static VBOXSERVICEINFO vboxServiceTable[] = "Restore", VBoxRestoreInit, VBoxRestoreThread, + NULL /* pfnStop */, VBoxRestoreDestroy }, #endif @@ -99,20 +170,32 @@ static VBOXSERVICEINFO vboxServiceTable[] = "VRDP", VBoxVRDPInit, VBoxVRDPThread, + NULL /* pfnStop */, VBoxVRDPDestroy }, { "IPC", VBoxIPCInit, VBoxIPCThread, + VBoxIPCStop, VBoxIPCDestroy }, { "Location Awareness", VBoxLAInit, VBoxLAThread, + NULL /* pfnStop */, VBoxLADestroy }, +#ifdef VBOX_WITH_MMR + { + "Multimedia Redirection", + VBoxMMRInit, + VBoxMMRThread, + NULL /* pfnStop */, + VBoxMMRDestroy + }, +#endif { NULL } @@ -203,9 +286,14 @@ static void vboxTrayRemoveTrayIcon() static int vboxTrayStartServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable) { + AssertPtrReturn(pEnv, VERR_INVALID_POINTER); + AssertPtrReturn(pTable, VERR_INVALID_POINTER); + Log(("VBoxTray: Starting services ...\n")); - pEnv->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + /** @todo Use IPRT events here. */ + pEnv->hStopEvent = CreateEvent(NULL, TRUE /* bManualReset */, + FALSE /* bInitialState */, NULL); if (!pEnv->hStopEvent) { @@ -213,7 +301,8 @@ static int vboxTrayStartServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable) return VERR_NOT_SUPPORTED; } - while (pTable->pszName) + while ( pTable + && pTable->pszName) { Log(("VBoxTray: Starting %s ...\n", pTable->pszName)); @@ -226,24 +315,26 @@ static int vboxTrayStartServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable) pTable->fStarted = false; if (pTable->pfnInit) - rc = pTable->pfnInit (pEnv, &pTable->pInstance, &fStartThread); + rc = pTable->pfnInit(pEnv, &pTable->pInstance, &fStartThread); if (RT_FAILURE(rc)) { - Log(("VBoxTray: Failed to initialize rc = %Rrc\n", rc)); + LogRel(("VBoxTray: Failed to initialize service \"%s\", rc=%Rrc\n", + pTable->pszName, rc)); } else { - if (pTable->pfnThread && fStartThread) + if ( pTable->pfnThread + && fStartThread) { unsigned threadid; + /** @todo Use RTThread* here. */ pTable->hThread = (HANDLE)_beginthreadex(NULL, /* security */ 0, /* stacksize */ pTable->pfnThread, pTable->pInstance, 0, /* initflag */ &threadid); - if (pTable->hThread == (HANDLE)(0)) rc = VERR_NOT_SUPPORTED; } @@ -273,26 +364,42 @@ static void vboxTrayStopServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable) /* Signal to all threads. */ SetEvent(pEnv->hStopEvent); - while (pTable->pszName) + VBOXSERVICEINFO *pCurTable = pTable; + while ( pCurTable + && pCurTable->pszName) { - if (pTable->fStarted) + if (pCurTable->pfnStop) + pCurTable->pfnStop(pEnv, pCurTable->pInstance); + + /* Advance to next table element. */ + pCurTable++; + } + + pCurTable = pTable; /* Reset to first element. */ + while ( pCurTable + && pCurTable->pszName) + { + if (pCurTable->fStarted) { - if (pTable->pfnThread) + if (pCurTable->pfnThread) { /* There is a thread, wait for termination. */ - WaitForSingleObject(pTable->hThread, INFINITE); + /** @todo Use RTThread* here. */ + /** @todo Don't wait forever here. Use a sensible default. */ + WaitForSingleObject(pCurTable->hThread, INFINITE); - CloseHandle(pTable->hThread); - pTable->hThread = 0; + /** @todo Dito. */ + CloseHandle(pCurTable->hThread); + pCurTable->hThread = NULL; } - if (pTable->pfnDestroy) - pTable->pfnDestroy (pEnv, pTable->pInstance); - pTable->fStarted = false; + if (pCurTable->pfnDestroy) + pCurTable->pfnDestroy(pEnv, pCurTable->pInstance); + pCurTable->fStarted = false; } /* Advance to next table element. */ - pTable++; + pCurTable++; } CloseHandle(pEnv->hStopEvent); @@ -464,48 +571,38 @@ static int vboxTraySetupSeamless(void) if (gMajorVersion >= 6) { BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize); - - HMODULE hModule = LoadLibrary("ADVAPI32.DLL"); - if (!hModule) - { - dwErr = GetLastError(); - Log(("VBoxTray: Loading module ADVAPI32.DLL failed with last error = %08X\n", dwErr)); - } - else + *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA = + RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA"); + Log(("VBoxTray: pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA)); + if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA) { PSECURITY_DESCRIPTOR pSD; PACL pSacl = NULL; BOOL fSaclPresent = FALSE; BOOL fSaclDefaulted = FALSE; - *(uintptr_t *)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA = (uintptr_t)GetProcAddress(hModule, "ConvertStringSecurityDescriptorToSecurityDescriptorA"); - - Log(("VBoxTray: pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA)); - if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA) + fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */ + SDDL_REVISION_1, &pSD, NULL); + if (!fRC) + { + dwErr = GetLastError(); + Log(("VBoxTray: ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr)); + } + else { - fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */ - SDDL_REVISION_1, &pSD, NULL); + fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted); if (!fRC) { dwErr = GetLastError(); - Log(("VBoxTray: ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr)); + Log(("VBoxTray: GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr)); } else { - fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted); + fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE); if (!fRC) { dwErr = GetLastError(); - Log(("VBoxTray: GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr)); - } - else - { - fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE); - if (!fRC) - { - dwErr = GetLastError(); - Log(("VBoxTray: SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr)); - } + Log(("VBoxTray: SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr)); } } } @@ -515,8 +612,15 @@ static int vboxTraySetupSeamless(void) if ( dwErr == ERROR_SUCCESS && gMajorVersion >= 5) /* Only for W2K and up ... */ { - ghSeamlessNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_EVENT_NAME); - if (ghSeamlessNotifyEvent == NULL) + ghSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME); + if (ghSeamlessWtNotifyEvent == NULL) + { + dwErr = GetLastError(); + Log(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); + } + + ghSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (ghSeamlessKmNotifyEvent == NULL) { dwErr = GetLastError(); Log(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); @@ -528,10 +632,26 @@ static int vboxTraySetupSeamless(void) static void vboxTrayShutdownSeamless(void) { - if (ghSeamlessNotifyEvent) + if (ghSeamlessWtNotifyEvent) + { + CloseHandle(ghSeamlessWtNotifyEvent); + ghSeamlessWtNotifyEvent = NULL; + } + + if (ghSeamlessKmNotifyEvent) + { + CloseHandle(ghSeamlessKmNotifyEvent); + ghSeamlessKmNotifyEvent = NULL; + } +} + +static void VBoxTrayCheckDt() +{ + BOOL fOldAllowedState = VBoxConsoleIsAllowed(); + if (vboxDtHandleEvent()) { - CloseHandle(ghSeamlessNotifyEvent); - ghSeamlessNotifyEvent = NULL; + if (!VBoxConsoleIsAllowed() != !fOldAllowedState) + VBoxConsoleEnable(!fOldAllowedState); } } @@ -607,11 +727,26 @@ static int vboxTrayServiceMain(void) * Wait for the stop semaphore to be posted or a window event to arrive */ - DWORD dwEventCount = 2; - HANDLE hWaitEvent[2] = { ghStopSem, ghSeamlessNotifyEvent }; + HANDLE hWaitEvent[4] = {0}; + DWORD dwEventCount = 0; - if (0 == ghSeamlessNotifyEvent) /* If seamless mode is not active / supported, reduce event array count. */ - dwEventCount = 1; + hWaitEvent[dwEventCount++] = ghStopSem; + + /* Check if seamless mode is not active and add seamless event to the list */ + if (0 != ghSeamlessWtNotifyEvent) + { + hWaitEvent[dwEventCount++] = ghSeamlessWtNotifyEvent; + } + + if (0 != ghSeamlessKmNotifyEvent) + { + hWaitEvent[dwEventCount++] = ghSeamlessKmNotifyEvent; + } + + if (0 != vboxDtGetNotifyEvent()) + { + hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent(); + } Log(("VBoxTray: Number of events to wait in main loop: %ld\n", dwEventCount)); while (true) @@ -628,29 +763,53 @@ static int vboxTrayServiceMain(void) /* exit */ break; } - else if ( waitResult == 1 - && ghSeamlessNotifyEvent != 0) /* Only jump in, if seamless is active! */ - { - Log(("VBoxTray: Event 'Seamless' triggered\n")); - - /* seamless window notification */ - VBoxSeamlessCheckWindows(); - } else { - /* timeout or a window message, handle it */ - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + BOOL fHandled = FALSE; + if (waitResult < RT_ELEMENTS(hWaitEvent)) { - Log(("VBoxTray: msg %p\n", msg.message)); - if (msg.message == WM_QUIT) + if (hWaitEvent[waitResult]) { - Log(("VBoxTray: WM_QUIT!\n")); - SetEvent(ghStopSem); - continue; + if (hWaitEvent[waitResult] == ghSeamlessWtNotifyEvent) + { + Log(("VBoxTray: Event 'Seamless' triggered\n")); + + /* seamless window notification */ + VBoxSeamlessCheckWindows(false); + fHandled = TRUE; + } + else if (hWaitEvent[waitResult] == ghSeamlessKmNotifyEvent) + { + Log(("VBoxTray: Event 'Km Seamless' triggered\n")); + + /* seamless window notification */ + VBoxSeamlessCheckWindows(true); + fHandled = TRUE; + } + else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent()) + { + Log(("VBoxTray: Event 'Dt' triggered\n")); + VBoxTrayCheckDt(); + fHandled = TRUE; + } + } + } + + if (!fHandled) + { + /* timeout or a window message, handle it */ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + Log(("VBoxTray: msg %p\n", msg.message)); + if (msg.message == WM_QUIT) + { + Log(("VBoxTray: WM_QUIT!\n")); + SetEvent(ghStopSem); + } + TranslateMessage(&msg); + DispatchMessage(&msg); } - TranslateMessage(&msg); - DispatchMessage(&msg); } } } @@ -703,6 +862,34 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi rc = vboxTrayCreateToolWindow(); if (RT_SUCCESS(rc)) { + VBoxCapsInit(); + + rc = vboxStInit(ghwndToolWindow); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxTray: vboxStInit failed, rc %d\n")); + /* ignore the St Init failure. this can happen for < XP win that do not support WTS API + * in that case the session is treated as active connected to the physical console + * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */ + Assert(vboxStIsActiveConsole()); + } + + rc = vboxDtInit(); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxTray: vboxDtInit failed, rc %d\n")); + /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API + * in that case the session is treated as active connected to the physical console + * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */ + Assert(vboxDtIsInputDesktop()); + } + + rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxAcquireGuestCaps cfg failed rc %d, ignoring..\n", rc)); + } + rc = vboxTraySetupSeamless(); if (RT_SUCCESS(rc)) { @@ -712,6 +899,15 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi hlpReportStatus(VBoxGuestFacilityStatus_Terminating); vboxTrayShutdownSeamless(); } + + /* it should be safe to call vboxDtTerm even if vboxStInit above failed */ + vboxDtTerm(); + + /* it should be safe to call vboxStTerm even if vboxStInit above failed */ + vboxStTerm(); + + VBoxCapsTerm(); + vboxTrayDestroyToolWindow(); } if (RT_SUCCESS(rc)) @@ -763,6 +959,13 @@ static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA return 0; case WM_TIMER: + if (VBoxCapsCheckTimer(wParam)) + return 0; + if (vboxDtCheckTimer(wParam)) + return 0; + if (vboxStCheckTimer(wParam)) + return 0; + switch (wParam) { case TIMERID_VBOXTRAY_CHECK_HOSTVERSION: @@ -789,16 +992,26 @@ static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA } return 0; - case WM_VBOX_INSTALL_SEAMLESS_HOOK: - VBoxSeamlessInstallHook(); + case WM_VBOX_SEAMLESS_ENABLE: + VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED); return 0; - case WM_VBOX_REMOVE_SEAMLESS_HOOK: - VBoxSeamlessRemoveHook(); + case WM_VBOX_SEAMLESS_DISABLE: + VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED); return 0; + case WM_DISPLAYCHANGE: case WM_VBOX_SEAMLESS_UPDATE: - VBoxSeamlessCheckWindows(); + if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS)) + VBoxSeamlessCheckWindows(true); + return 0; + + case WM_VBOX_GRAPHICS_SUPPORTED: + VBoxGrapicsSetSupported(TRUE); + return 0; + + case WM_VBOX_GRAPHICS_UNSUPPORTED: + VBoxGrapicsSetSupported(FALSE); return 0; case WM_VBOXTRAY_VM_RESTORED: @@ -809,6 +1022,16 @@ static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA VBoxRestoreCheckVRDP(); return 0; + case WM_WTSSESSION_CHANGE: + { + BOOL fOldAllowedState = VBoxConsoleIsAllowed(); + if (vboxStHandleEvent(wParam, lParam)) + { + if (!VBoxConsoleIsAllowed() != !fOldAllowedState) + VBoxConsoleEnable(!fOldAllowedState); + } + return 0; + } default: /* Handle all globally registered window messages. */ @@ -825,3 +1048,771 @@ static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA return DefWindowProc(hWnd, uMsg, wParam, lParam); } +/* St (session [state] tracking) functionality API impl */ + +typedef struct VBOXST +{ + HWND hWTSAPIWnd; + RTLDRMOD hLdrModWTSAPI32; + BOOL fIsConsole; + WTS_CONNECTSTATE_CLASS enmConnectState; + UINT_PTR idDelayedInitTimer; + BOOL (WINAPI * pfnWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags); + BOOL (WINAPI * pfnWTSUnRegisterSessionNotification)(HWND hWnd); + BOOL (WINAPI * pfnWTSQuerySessionInformationA)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned); +} VBOXST; + +static VBOXST gVBoxSt; + +static int vboxStCheckState() +{ + int rc = VINF_SUCCESS; + WTS_CONNECTSTATE_CLASS *penmConnectState = NULL; + USHORT *pProtocolType = NULL; + DWORD cbBuf = 0; + if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, + (LPTSTR *)&penmConnectState, &cbBuf)) + { + if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType, + (LPTSTR *)&pProtocolType, &cbBuf)) + { + gVBoxSt.fIsConsole = (*pProtocolType == 0); + gVBoxSt.enmConnectState = *penmConnectState; + return VINF_SUCCESS; + } + + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: WTSQuerySessionInformationA WTSClientProtocolType failed, error = %08X\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + else + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: WTSQuerySessionInformationA WTSConnectState failed, error = %08X\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + + /* failure branch, set to "console-active" state */ + gVBoxSt.fIsConsole = TRUE; + gVBoxSt.enmConnectState = WTSActive; + + return rc; +} + +static int vboxStInit(HWND hWnd) +{ + RT_ZERO(gVBoxSt); + int rc = RTLdrLoadSystem("WTSAPI32.DLL", false /*fNoUnload*/, &gVBoxSt.hLdrModWTSAPI32); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSRegisterSessionNotification", + (void **)&gVBoxSt.pfnWTSRegisterSessionNotification); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSUnRegisterSessionNotification", + (void **)&gVBoxSt.pfnWTSUnRegisterSessionNotification); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSQuerySessionInformationA", + (void **)&gVBoxSt.pfnWTSQuerySessionInformationA); + if (RT_FAILURE(rc)) + WARN(("VBoxTray: WTSQuerySessionInformationA not found\n")); + } + else + WARN(("VBoxTray: WTSUnRegisterSessionNotification not found\n")); + } + else + WARN(("VBoxTray: WTSRegisterSessionNotification not found\n")); + if (RT_SUCCESS(rc)) + { + gVBoxSt.hWTSAPIWnd = hWnd; + if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION)) + vboxStCheckState(); + else + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: WTSRegisterSessionNotification failed, error = %08X\n", dwErr)); + if (dwErr == RPC_S_INVALID_BINDING) + { + gVBoxSt.idDelayedInitTimer = SetTimer(gVBoxSt.hWTSAPIWnd, TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER, + 2000, (TIMERPROC)NULL); + gVBoxSt.fIsConsole = TRUE; + gVBoxSt.enmConnectState = WTSActive; + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(dwErr); + } + + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + } + + RTLdrClose(gVBoxSt.hLdrModWTSAPI32); + } + else + WARN(("VBoxTray: WTSAPI32 load failed, rc = %Rrc\n", rc)); + + RT_ZERO(gVBoxSt); + gVBoxSt.fIsConsole = TRUE; + gVBoxSt.enmConnectState = WTSActive; + return rc; +} + +static void vboxStTerm(void) +{ + if (!gVBoxSt.hWTSAPIWnd) + { + WARN(("VBoxTray: vboxStTerm called for non-initialized St\n")); + return; + } + + if (gVBoxSt.idDelayedInitTimer) + { + /* notification is not registered, just kill timer */ + KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer); + gVBoxSt.idDelayedInitTimer = 0; + } + else + { + if (!gVBoxSt.pfnWTSUnRegisterSessionNotification(gVBoxSt.hWTSAPIWnd)) + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: WTSAPI32 load failed, error = %08X\n", dwErr)); + } + } + + RTLdrClose(gVBoxSt.hLdrModWTSAPI32); + RT_ZERO(gVBoxSt); +} + +#define VBOXST_DBG_MAKECASE(_val) case _val: return #_val; + +static const char* vboxStDbgGetString(DWORD val) +{ + switch (val) + { + VBOXST_DBG_MAKECASE(WTS_CONSOLE_CONNECT); + VBOXST_DBG_MAKECASE(WTS_CONSOLE_DISCONNECT); + VBOXST_DBG_MAKECASE(WTS_REMOTE_CONNECT); + VBOXST_DBG_MAKECASE(WTS_REMOTE_DISCONNECT); + VBOXST_DBG_MAKECASE(WTS_SESSION_LOGON); + VBOXST_DBG_MAKECASE(WTS_SESSION_LOGOFF); + VBOXST_DBG_MAKECASE(WTS_SESSION_LOCK); + VBOXST_DBG_MAKECASE(WTS_SESSION_UNLOCK); + VBOXST_DBG_MAKECASE(WTS_SESSION_REMOTE_CONTROL); + default: + WARN(("VBoxTray: invalid WTS state %d\n", val)); + return "Unknown"; + } +} + +static BOOL vboxStCheckTimer(WPARAM wEvent) +{ + if (wEvent != gVBoxSt.idDelayedInitTimer) + return FALSE; + + if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION)) + { + KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer); + gVBoxSt.idDelayedInitTimer = 0; + vboxStCheckState(); + } + else + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: timer WTSRegisterSessionNotification failed, error = %08X\n", dwErr)); + Assert(gVBoxSt.fIsConsole == TRUE); + Assert(gVBoxSt.enmConnectState == WTSActive); + } + + return TRUE; +} + + +static BOOL vboxStHandleEvent(WPARAM wEvent, LPARAM SessionID) +{ + WARN(("VBoxTray: WTS Event: %s\n", vboxStDbgGetString(wEvent))); + BOOL fOldIsActiveConsole = vboxStIsActiveConsole(); + + vboxStCheckState(); + + return !vboxStIsActiveConsole() != !fOldIsActiveConsole; +} + +static BOOL vboxStIsActiveConsole() +{ + return (gVBoxSt.enmConnectState == WTSActive && gVBoxSt.fIsConsole); +} + +/* + * Dt (desktop [state] tracking) functionality API impl + * + * !!!NOTE: this API is NOT thread-safe!!! + * */ + +typedef struct VBOXDT +{ + HANDLE hNotifyEvent; + BOOL fIsInputDesktop; + UINT_PTR idTimer; + RTLDRMOD hLdrModHook; + BOOL (* pfnVBoxHookInstallActiveDesktopTracker)(HMODULE hDll); + BOOL (* pfnVBoxHookRemoveActiveDesktopTracker)(); + HDESK (WINAPI * pfnGetThreadDesktop)(DWORD dwThreadId); + HDESK (WINAPI * pfnOpenInputDesktop)(DWORD dwFlags, BOOL fInherit, ACCESS_MASK dwDesiredAccess); + BOOL (WINAPI * pfnCloseDesktop)(HDESK hDesktop); +} VBOXDT; + +static VBOXDT gVBoxDt; + +static BOOL vboxDtCalculateIsInputDesktop() +{ + BOOL fIsInputDt = FALSE; + HDESK hInput = gVBoxDt.pfnOpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW); + if (hInput) + { +// DWORD dwThreadId = GetCurrentThreadId(); +// HDESK hThreadDt = gVBoxDt.pfnGetThreadDesktop(dwThreadId); +// if (hThreadDt) +// { + fIsInputDt = TRUE; +// } +// else +// { +// DWORD dwErr = GetLastError(); +// WARN(("VBoxTray: pfnGetThreadDesktop for Seamless failed, last error = %08X\n", dwErr)); +// } + + gVBoxDt.pfnCloseDesktop(hInput); + } + else + { + DWORD dwErr = GetLastError(); +// WARN(("VBoxTray: pfnOpenInputDesktop for Seamless failed, last error = %08X\n", dwErr)); + } + return fIsInputDt; +} + +static BOOL vboxDtCheckTimer(WPARAM wParam) +{ + if (wParam != gVBoxDt.idTimer) + return FALSE; + + VBoxTrayCheckDt(); + + return TRUE; +} + +static int vboxDtInit() +{ + int rc = VINF_SUCCESS; + OSVERSIONINFO info; + gMajorVersion = 5; /* Default to Windows XP. */ + info.dwOSVersionInfoSize = sizeof(info); + if (GetVersionEx(&info)) + { + LogRel(("VBoxTray: Windows version %ld.%ld\n", info.dwMajorVersion, info.dwMinorVersion)); + gMajorVersion = info.dwMajorVersion; + } + + RT_ZERO(gVBoxDt); + + gVBoxDt.hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, VBOXHOOK_GLOBAL_DT_EVENT_NAME); + if (gVBoxDt.hNotifyEvent != NULL) + { + /* Load the hook dll and resolve the necessary entry points. */ + rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &gVBoxDt.hLdrModHook); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookInstallActiveDesktopTracker", + (void **)&gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookRemoveActiveDesktopTracker", + (void **)&gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker); + if (RT_FAILURE(rc)) + WARN(("VBoxTray: VBoxHookRemoveActiveDesktopTracker not found\n")); + } + else + WARN(("VBoxTray: VBoxHookInstallActiveDesktopTracker not found\n")); + if (RT_SUCCESS(rc)) + { + /* Try get the system APIs we need. */ + *(void **)&gVBoxDt.pfnGetThreadDesktop = RTLdrGetSystemSymbol("user32.dll", "GetThreadDesktop"); + if (!gVBoxDt.pfnGetThreadDesktop) + { + WARN(("VBoxTray: GetThreadDesktop not found\n")); + rc = VERR_NOT_SUPPORTED; + } + + *(void **)&gVBoxDt.pfnOpenInputDesktop = RTLdrGetSystemSymbol("user32.dll", "OpenInputDesktop"); + if (!gVBoxDt.pfnOpenInputDesktop) + { + WARN(("VBoxTray: OpenInputDesktop not found\n")); + rc = VERR_NOT_SUPPORTED; + } + + *(void **)&gVBoxDt.pfnCloseDesktop = RTLdrGetSystemSymbol("user32.dll", "CloseDesktop"); + if (!gVBoxDt.pfnCloseDesktop) + { + WARN(("VBoxTray: CloseDesktop not found\n")); + rc = VERR_NOT_SUPPORTED; + } + + if (RT_SUCCESS(rc)) + { + BOOL fRc = FALSE; + /* For Vista and up we need to change the integrity of the security descriptor, too. */ + if (gMajorVersion >= 6) + { + HMODULE hModHook = (HMODULE)RTLdrGetNativeHandle(gVBoxDt.hLdrModHook); + Assert((uintptr_t)hModHook != ~(uintptr_t)0); + fRc = gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker(hModHook); + if (!fRc) + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: pfnVBoxHookInstallActiveDesktopTracker failed, last error = %08X\n", dwErr)); + } + } + + if (!fRc) + { + gVBoxDt.idTimer = SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_DT_TIMER, 500, (TIMERPROC)NULL); + if (!gVBoxDt.idTimer) + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: SetTimer error %08X\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + } + + if (RT_SUCCESS(rc)) + { + gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop(); + return VINF_SUCCESS; + } + } + } + + RTLdrClose(gVBoxDt.hLdrModHook); + } + else + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + + CloseHandle(gVBoxDt.hNotifyEvent); + } + else + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + + + RT_ZERO(gVBoxDt); + gVBoxDt.fIsInputDesktop = TRUE; + + return rc; +} + +static void vboxDtTerm() +{ + if (!gVBoxDt.hLdrModHook) + return; + + gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker(); + + RTLdrClose(gVBoxDt.hLdrModHook); + CloseHandle(gVBoxDt.hNotifyEvent); + + RT_ZERO(gVBoxDt); +} +/* @returns true on "IsInputDesktop" state change */ +static BOOL vboxDtHandleEvent() +{ + BOOL fIsInputDesktop = gVBoxDt.fIsInputDesktop; + gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop(); + return !fIsInputDesktop != !gVBoxDt.fIsInputDesktop; +} + +static HANDLE vboxDtGetNotifyEvent() +{ + return gVBoxDt.hNotifyEvent; +} + +/* @returns true iff the application (VBoxTray) desktop is input */ +static BOOL vboxDtIsInputDesktop() +{ + return gVBoxDt.fIsInputDesktop; +} + + +/* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest + * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests + * */ +static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg) +{ + DWORD cbReturned = 0; + VBoxGuestCapsAquire Info; + Log(("VBoxTray: VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg)); + Info.enmFlags = fCfg ? VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE : VBOXGUESTCAPSACQUIRE_FLAGS_NONE; + Info.rc = VERR_NOT_IMPLEMENTED; + Info.u32OrMask = fOr; + Info.u32NotMask = fNot; + if (!DeviceIoControl(ghVBoxDriver, VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE, &Info, sizeof(Info), &Info, sizeof(Info), &cbReturned, NULL)) + { + DWORD LastErr = GetLastError(); + WARN(("VBoxTray: DeviceIoControl VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed LastErr %d\n", LastErr)); + return RTErrConvertFromWin32(LastErr); + } + + int rc = Info.rc; + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxTray: VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed rc %d\n", rc)); + return rc; + } + + return rc; +} + +typedef enum VBOXCAPS_ENTRY_ACSTATE +{ + /* the given cap is released */ + VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0, + /* the given cap acquisition is in progress */ + VBOXCAPS_ENTRY_ACSTATE_ACQUIRING, + /* the given cap is acquired */ + VBOXCAPS_ENTRY_ACSTATE_ACQUIRED +} VBOXCAPS_ENTRY_ACSTATE; + + +struct VBOXCAPS_ENTRY; +struct VBOXCAPS; + +typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE)(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled); + +typedef struct VBOXCAPS_ENTRY +{ + uint32_t fCap; + uint32_t iCap; + VBOXCAPS_ENTRY_FUNCSTATE enmFuncState; + VBOXCAPS_ENTRY_ACSTATE enmAcState; + PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable; +} VBOXCAPS_ENTRY; + + +typedef struct VBOXCAPS +{ + UINT_PTR idTimer; + VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT]; +} VBOXCAPS; + +static VBOXCAPS gVBoxCaps; + +static DECLCALLBACK(void) vboxCapsOnEnableSeamles(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled) +{ + if (fEnabled) + { + Log(("VBoxTray: vboxCapsOnEnableSeamles: ENABLED\n")); + Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED); + Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED); + VBoxSeamlessEnable(); + } + else + { + Log(("VBoxTray: vboxCapsOnEnableSeamles: DISABLED\n")); + Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED); + VBoxSeamlessDisable(); + } +} + +static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + + Log(("VBoxTray: vboxCapsEntryAcStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n", + enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState)); + + if (pCap->enmAcState == enmAcState) + return; + + VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState; + pCap->enmAcState = enmAcState; + + if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED) + { + if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) + { + if (pCap->pfnOnEnable) + pCap->pfnOnEnable(pConsole, pCap, TRUE); + } + } + else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) + { + if (pCap->pfnOnEnable) + pCap->pfnOnEnable(pConsole, pCap, FALSE); + } +} + +static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + + Log(("VBoxTray: vboxCapsEntryFuncStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n", + enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState)); + + if (pCap->enmFuncState == enmFuncState) + return; + + VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState; + + pCap->enmFuncState = enmFuncState; + + if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) + { + Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED); + if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED) + { + if (pCap->pfnOnEnable) + pCap->pfnOnEnable(pConsole, pCap, TRUE); + } + } + else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED) + { + if (pCap->pfnOnEnable) + pCap->pfnOnEnable(pConsole, pCap, FALSE); + } +} + +static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup]; + vboxCapsEntryFuncStateSet(pCap, enmFuncState); +} + +static int VBoxCapsInit() +{ + VBOXCAPS *pConsole = &gVBoxCaps; + memset(pConsole, 0, sizeof (*pConsole)); + pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS; + pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS; + pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamles; + pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS; + pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS; + return VINF_SUCCESS; +} + +static int VBoxCapsReleaseAll() +{ + VBOXCAPS *pConsole = &gVBoxCaps; + Log(("VBoxTray: VBoxCapsReleaseAll\n")); + int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxTray: vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc)); + return rc; + } + + if (pConsole->idTimer) + { + Log(("VBoxTray: killing console timer\n")); + KillTimer(ghwndToolWindow, pConsole->idTimer); + pConsole->idTimer = 0; + } + + for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i) + { + vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED); + } + + return rc; +} + +static void VBoxCapsTerm() +{ + VBOXCAPS *pConsole = &gVBoxCaps; + VBoxCapsReleaseAll(); + memset(pConsole, 0, sizeof (*pConsole)); +} + +static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED; +} + +static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED + && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED; +} + +static BOOL VBoxCapsCheckTimer(WPARAM wParam) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + if (wParam != pConsole->idTimer) + return FALSE; + + uint32_t u32AcquiredCaps = 0; + BOOL fNeedNewTimer = FALSE; + + for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i) + { + VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i]; + if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING) + continue; + + int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false); + if (RT_SUCCESS(rc)) + { + vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED); + u32AcquiredCaps |= pCap->fCap; + } + else + { + Assert(rc == VERR_RESOURCE_BUSY); + fNeedNewTimer = TRUE; + } + } + + if (!fNeedNewTimer) + { + KillTimer(ghwndToolWindow, pConsole->idTimer); + /* cleanup timer data */ + pConsole->idTimer = 0; + } + + return TRUE; +} + +static int VBoxCapsEntryRelease(uint32_t iCap) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap]; + if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED) + { + WARN(("VBoxTray: invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState)); + return VERR_INVALID_STATE; + } + + if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED) + { + int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false); + AssertRC(rc); + } + + vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED); + + return VINF_SUCCESS; +} + +static int VBoxCapsEntryAcquire(uint32_t iCap) +{ + VBOXCAPS *pConsole = &gVBoxCaps; + Assert(VBoxConsoleIsAllowed()); + VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap]; + Log(("VBoxTray: VBoxCapsEntryAcquire %d\n", iCap)); + if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED) + { + WARN(("VBoxTray: invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState)); + return VERR_INVALID_STATE; + } + + vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING); + int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false); + if (RT_SUCCESS(rc)) + { + vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED); + return VINF_SUCCESS; + } + + if (rc != VERR_RESOURCE_BUSY) + { + WARN(("VBoxTray: vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc)); + return rc; + } + + WARN(("VBoxTray: iCap %d is busy!\n", iCap)); + + /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session, + * queue the retry timer */ + if (!pConsole->idTimer) + { + pConsole->idTimer = SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL); + if (!pConsole->idTimer) + { + DWORD dwErr = GetLastError(); + WARN(("VBoxTray: SetTimer error %08X\n", dwErr)); + return RTErrConvertFromWin32(dwErr); + } + } + + return rc; +} + +static int VBoxCapsAcquireAllSupported() +{ + VBOXCAPS *pConsole = &gVBoxCaps; + Log(("VBoxTray: VBoxCapsAcquireAllSupported\n")); + for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i) + { + if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED) + { + Log(("VBoxTray: VBoxCapsAcquireAllSupported acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState)); + VBoxCapsEntryAcquire(i); + } + else + { + WARN(("VBoxTray: VBoxCapsAcquireAllSupported: WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState)); + } + } + return VINF_SUCCESS; +} + +static BOOL VBoxConsoleIsAllowed() +{ + return vboxDtIsInputDesktop() && vboxStIsActiveConsole(); +} + +static void VBoxConsoleEnable(BOOL fEnable) +{ + if (fEnable) + VBoxCapsAcquireAllSupported(); + else + VBoxCapsReleaseAll(); +} + +static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported) +{ + if (fSupported) + { + VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED); + + if (VBoxConsoleIsAllowed()) + VBoxCapsEntryAcquire(iCap); + } + else + { + VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED); + + VBoxCapsEntryRelease(iCap); + } +} + +void VBoxSeamlessSetSupported(BOOL fSupported) +{ + VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_SEAMLESS, fSupported); +} + +static void VBoxGrapicsSetSupported(BOOL fSupported) +{ + VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported); +} diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.h index 132c74f8..139f962f 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 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; @@ -54,6 +54,17 @@ #include "VBoxDispIf.h" +#ifdef DEBUG_misha +#define WARN(_m) do { \ + Assert(0); \ + Log(_m); \ + } while (0) +#else +#define WARN(_m) do { \ + Log(_m); \ + } while (0) +#endif + /* * Windows messsages. */ @@ -80,6 +91,9 @@ * Timer IDs. */ #define TIMERID_VBOXTRAY_CHECK_HOSTVERSION 1000 +#define TIMERID_VBOXTRAY_CAPS_TIMER 1001 +#define TIMERID_VBOXTRAY_DT_TIMER 1002 +#define TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER 1003 /* The environment information for services. */ typedef struct _VBOXSERVICEENV @@ -97,6 +111,7 @@ typedef struct _VBOXSERVICEINFO char *pszName; int (* pfnInit) (const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread); unsigned (__stdcall * pfnThread) (void *pInstance); + void (* pfnStop) (const VBOXSERVICEENV *pEnv, void *pInstance); void (* pfnDestroy) (const VBOXSERVICEENV *pEnv, void *pInstance); /* Variables. */ diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxTrayMsg.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxTrayMsg.h index 27d86752..bef94983 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxTrayMsg.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxTrayMsg.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -18,48 +18,81 @@ #ifndef ___VBOXTRAY_MSG_H #define ___VBOXTRAY_MSG_H -#define VBOXTRAY_PIPE_IPC "\\\\.\\pipe\\VBoxTrayIPC" -#define VBOXTRAY_PIPE_IPC_BUFSIZE 64 * 1024 +/** The IPC pipe's prefix. Will be followed by the + * username VBoxTray runs under. */ +#define VBOXTRAY_IPC_PIPE_PREFIX "VBoxTrayIPC-" +/** The IPC header's magic. */ +#define VBOXTRAY_IPC_HDR_MAGIC 0x19840804 enum VBOXTRAYIPCMSGTYPE { /** Restarts VBoxTray. */ VBOXTRAYIPCMSGTYPE_RESTART = 10, - - /** Asks the IPC thread to quit. */ - VBOXTRAYIPCMSGTYPE_IPC_QUIT = 50, - /** Shows a balloon message in the tray area. */ - VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG = 100 + VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG = 100, + /** Retrieves the current user's last input + * time. This will be the user VBoxTray is running + * under. No actual message for this command + * required. */ + VBOXTRAYIPCMSGTYPE_USERLASTINPUT = 120 }; /* VBoxTray's IPC header. */ -typedef struct _VBOXTRAYIPCHEADER +typedef struct VBOXTRAYIPCHEADER { - /** Message type. */ - ULONG ulMsg; - /** Size of message body - * (without this header). */ - ULONG cbBody; - /** User-supplied wParam. */ - ULONG wParam; - /** User-supplied lParam. */ - ULONG lParam; + /** The header's magic. */ + uint32_t uMagic; + /** Header version, must be 0 by now. */ + uint32_t uHdrVersion; + /** Message type. Specifies a message + * of VBOXTRAYIPCMSGTYPE. */ + uint32_t uMsgType; + /** Message length (in bytes). This must + * include the overall message length, including + * (eventual) dynamically allocated areas which + * are passed into the message structure. + */ + uint32_t uMsgLen; + } VBOXTRAYIPCHEADER, *PVBOXTRAYIPCHEADER; -typedef struct _VBOXTRAYIPCMSG_SHOWBALLOONMSG +/** + * Tells VBoxTray to show a balloon message in Windows' + * tray area. This may or may not work depending on the + * system's configuration / set user preference. + */ +typedef struct VBOXTRAYIPCMSG_SHOWBALLOONMSG { - /** Message content. */ - TCHAR szContent[256]; - /** Message title. */ - TCHAR szTitle[64]; + /** Length of message body (in bytes). */ + uint32_t cbMsgContent; + /** Length of message title (in bytes). */ + uint32_t cbMsgTitle; /** Message type. */ - ULONG ulType; - /** Flags; not used yet. */ - ULONG ulFlags; - /** Time to show the message (in msec). */ - ULONG ulShowMS; + uint32_t uType; + /** Time to show the message (in ms). */ + uint32_t uShowMS; + /** Dynamically allocated stuff. + * + * Note: These must come at the end of the + * structure to not overwrite any important + * stuff above. + */ + /** Message body. Can be up to 256 chars + * long. */ + char szMsgContent[1]; + /** Message title. Can be up to 73 chars + * long. */ + char szMsgTitle[1]; } VBOXTRAYIPCMSG_SHOWBALLOONMSG, *PVBOXTRAYIPCMSG_SHOWBALLOONMSG; +/** + * Response telling the last input of the current user. + */ +typedef struct VBOXTRAYIPCRES_USERLASTINPUT +{ + /** Last occurred user input event (in seconds). */ + uint32_t uLastInput; +} VBOXTRAYIPCRES_USERLASTINPUT, *PVBOXTRAYIPCRES_USERLASTINPUT; + #endif /* !___VBOXTRAY_MSG_H */ diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.cpp b/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.cpp index 9f3cef91..9435a9f1 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.cpp @@ -24,6 +24,7 @@ #include <VBox/VMMDev.h> #include <VBoxGuestInternal.h> #include <iprt/assert.h> +#include <iprt/ldr.h> /* The guest receives VRDP_ACTIVE/VRDP_INACTIVE notifications. @@ -255,7 +256,7 @@ typedef struct _VBOXVRDPCONTEXT uint32_t level; BOOL fSavedThemeEnabled; - HMODULE hModule; + RTLDRMOD hModUxTheme; HRESULT (* pfnEnableTheming)(BOOL fEnable); BOOL (* pfnIsThemeActive)(VOID); @@ -273,16 +274,17 @@ int VBoxVRDPInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThr gCtx.level = VRDP_EXPERIENCE_LEVEL_FULL; gCtx.fSavedThemeEnabled = FALSE; - gCtx.hModule = LoadLibrary("UxTheme"); - - if (gCtx.hModule) + int rc = RTLdrLoadSystem("UxTheme.dll", false /*fNoUnload*/, &gCtx.hModUxTheme); + if (RT_SUCCESS(rc)) { - *(uintptr_t *)&gCtx.pfnEnableTheming = (uintptr_t)GetProcAddress(gCtx.hModule, "EnableTheming"); - *(uintptr_t *)&gCtx.pfnIsThemeActive = (uintptr_t)GetProcAddress(gCtx.hModule, "IsThemeActive"); + *(PFNRT *)&gCtx.pfnEnableTheming = RTLdrGetFunction(gCtx.hModUxTheme, "EnableTheming"); + *(PFNRT *)&gCtx.pfnIsThemeActive = RTLdrGetFunction(gCtx.hModUxTheme, "IsThemeActive"); } else { - gCtx.pfnEnableTheming = 0; + gCtx.hModUxTheme = NIL_RTLDRMOD; + gCtx.pfnEnableTheming = NULL; + gCtx.pfnIsThemeActive = NULL; } *pfStartThread = true; @@ -296,9 +298,11 @@ void VBoxVRDPDestroy(const VBOXSERVICEENV *pEnv, void *pInstance) Log(("VBoxTray: VBoxVRDPDestroy\n")); VBOXVRDPCONTEXT *pCtx = (VBOXVRDPCONTEXT *)pInstance; vboxExperienceRestore (pCtx->level); - if (gCtx.hModule) - FreeLibrary(gCtx.hModule); - gCtx.hModule = 0; + if (gCtx.hModUxTheme != NIL_RTLDRMOD) + { + RTLdrClose(gCtx.hModUxTheme); + gCtx.hModUxTheme = NIL_RTLDRMOD; + } return; } diff --git a/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.h b/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.h index dbb63439..f6c8aa3c 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.h +++ b/src/VBox/Additions/WINNT/VBoxTray/VBoxVRDP.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/WINNT/VBoxTray/resource.h b/src/VBox/Additions/WINNT/VBoxTray/resource.h index e01df6df..04c1f0ce 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/resource.h +++ b/src/VBox/Additions/WINNT/VBoxTray/resource.h @@ -2,7 +2,7 @@ * * VBoxService - Guest Additions Service * - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/WINNT/VBoxTray/testcase/tstSessionHack.cpp b/src/VBox/Additions/WINNT/VBoxTray/testcase/tstSessionHack.cpp index b0df40a9..8e160b96 100644 --- a/src/VBox/Additions/WINNT/VBoxTray/testcase/tstSessionHack.cpp +++ b/src/VBox/Additions/WINNT/VBoxTray/testcase/tstSessionHack.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; |
