summaryrefslogtreecommitdiff
path: root/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp')
-rw-r--r--src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp1143
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);
+}