diff options
Diffstat (limited to 'src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp')
-rw-r--r-- | src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp | 1143 |
1 files changed, 1067 insertions, 76 deletions
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); +} |