VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxTray.cpp@ 48789

Last change on this file since 48789 was 48070, checked in by vboxsync, 11 years ago

wddm,vboxtray: forward-port autoresize, multimon, and seamless fixes from 4.2 r87071, r87353, r87356, r87528, r87568, r87581, r87584, r87608, r87673, r87678, r87708, r87629, r87529; additional fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.4 KB
Line 
1/* $Id: VBoxTray.cpp 48070 2013-08-26 18:13:22Z vboxsync $ */
2/** @file
3 * VBoxTray - Guest Additions Tray Application
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "VBoxTray.h"
23#include "VBoxTrayMsg.h"
24#include "VBoxHelpers.h"
25#include "VBoxSeamless.h"
26#include "VBoxClipboard.h"
27#include "VBoxDisplay.h"
28#include "VBoxRestore.h"
29#include "VBoxVRDP.h"
30#include "VBoxHostVersion.h"
31#include "VBoxSharedFolders.h"
32#include "VBoxIPC.h"
33#include "VBoxLA.h"
34#include "VBoxMMR.h"
35#include <VBoxHook.h>
36#include "resource.h"
37#include <malloc.h>
38#include <VBoxGuestInternal.h>
39
40#include <sddl.h>
41
42#include <iprt/buildconfig.h>
43#include <iprt/ldr.h>
44
45/* Default desktop state tracking */
46#include <Wtsapi32.h>
47
48/*
49 * St (session [state] tracking) functionality API
50 *
51 * !!!NOTE: this API is NOT thread-safe!!!
52 * it is supposed to be called & used from within the window message handler thread
53 * of the window passed to vboxStInit */
54static int vboxStInit(HWND hWnd);
55static void vboxStTerm(void);
56/* @returns true on "IsActiveConsole" state change */
57static BOOL vboxStHandleEvent(WPARAM EventID, LPARAM SessionID);
58static BOOL vboxStIsActiveConsole();
59static BOOL vboxStCheckTimer(WPARAM wEvent);
60
61/*
62 * Dt (desktop [state] tracking) functionality API
63 *
64 * !!!NOTE: this API is NOT thread-safe!!!
65 * */
66static int vboxDtInit();
67static void vboxDtTerm();
68/* @returns true on "IsInputDesktop" state change */
69static BOOL vboxDtHandleEvent();
70/* @returns true iff the application (VBoxTray) desktop is input */
71static BOOL vboxDtIsInputDesktop();
72static HANDLE vboxDtGetNotifyEvent();
73static BOOL vboxDtCheckTimer(WPARAM wParam);
74
75/* caps API */
76#define VBOXCAPS_ENTRY_IDX_SEAMLESS 0
77#define VBOXCAPS_ENTRY_IDX_GRAPHICS 1
78#define VBOXCAPS_ENTRY_IDX_COUNT 2
79
80typedef enum VBOXCAPS_ENTRY_FUNCSTATE
81{
82 /* the cap is unsupported */
83 VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED = 0,
84 /* the cap is supported */
85 VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED,
86 /* the cap functionality is started, it can be disabled however if its AcState is not ACQUIRED */
87 VBOXCAPS_ENTRY_FUNCSTATE_STARTED,
88} VBOXCAPS_ENTRY_FUNCSTATE;
89
90
91static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState);
92static int VBoxCapsInit();
93static int VBoxCapsReleaseAll();
94static void VBoxCapsTerm();
95static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap);
96static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap);
97static BOOL VBoxCapsCheckTimer(WPARAM wParam);
98static int VBoxCapsEntryRelease(uint32_t iCap);
99static int VBoxCapsEntryAcquire(uint32_t iCap);
100static int VBoxCapsAcquireAllSupported();
101
102/* console-related caps API */
103static BOOL VBoxConsoleIsAllowed();
104static void VBoxConsoleEnable(BOOL fEnable);
105static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported);
106
107static void VBoxGrapicsSetSupported(BOOL fSupported);
108
109/*******************************************************************************
110* Internal Functions *
111*******************************************************************************/
112static int vboxTrayCreateTrayIcon(void);
113static LRESULT CALLBACK vboxToolWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
114
115/* Global message handler prototypes. */
116static int vboxTrayGlMsgTaskbarCreated(WPARAM lParam, LPARAM wParam);
117/*static int vboxTrayGlMsgShowBalloonMsg(WPARAM lParam, LPARAM wParam);*/
118
119static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg);
120
121/*******************************************************************************
122* Global Variables *
123*******************************************************************************/
124HANDLE ghVBoxDriver;
125HANDLE ghStopSem;
126HANDLE ghSeamlessWtNotifyEvent = 0;
127HANDLE ghSeamlessKmNotifyEvent = 0;
128SERVICE_STATUS gVBoxServiceStatus;
129SERVICE_STATUS_HANDLE gVBoxServiceStatusHandle;
130HINSTANCE ghInstance;
131HWND ghwndToolWindow;
132NOTIFYICONDATA gNotifyIconData;
133DWORD gMajorVersion;
134
135
136/* The service table. */
137static VBOXSERVICEINFO vboxServiceTable[] =
138{
139 {
140 "Display",
141 VBoxDisplayInit,
142 VBoxDisplayThread,
143 NULL /* pfnStop */,
144 VBoxDisplayDestroy
145 },
146 {
147 "Shared Clipboard",
148 VBoxClipboardInit,
149 VBoxClipboardThread,
150 NULL /* pfnStop */,
151 VBoxClipboardDestroy
152 },
153 {
154 "Seamless Windows",
155 VBoxSeamlessInit,
156 VBoxSeamlessThread,
157 NULL /* pfnStop */,
158 VBoxSeamlessDestroy
159 },
160#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
161 {
162 "Restore",
163 VBoxRestoreInit,
164 VBoxRestoreThread,
165 NULL /* pfnStop */,
166 VBoxRestoreDestroy
167 },
168#endif
169 {
170 "VRDP",
171 VBoxVRDPInit,
172 VBoxVRDPThread,
173 NULL /* pfnStop */,
174 VBoxVRDPDestroy
175 },
176 {
177 "IPC",
178 VBoxIPCInit,
179 VBoxIPCThread,
180 VBoxIPCStop,
181 VBoxIPCDestroy
182 },
183 {
184 "Location Awareness",
185 VBoxLAInit,
186 VBoxLAThread,
187 NULL /* pfnStop */,
188 VBoxLADestroy
189 },
190#ifdef VBOX_WITH_MMR
191 {
192 "Multimedia Redirection",
193 VBoxMMRInit,
194 VBoxMMRThread,
195 NULL /* pfnStop */,
196 VBoxMMRDestroy
197 },
198#endif
199 {
200 NULL
201 }
202};
203
204/* The global message table. */
205static VBOXGLOBALMESSAGE s_vboxGlobalMessageTable[] =
206{
207 /* Windows specific stuff. */
208 {
209 "TaskbarCreated",
210 vboxTrayGlMsgTaskbarCreated
211 },
212
213 /* VBoxTray specific stuff. */
214 /** @todo Add new messages here! */
215
216 {
217 NULL
218 }
219};
220
221/**
222 * Gets called whenever the Windows main taskbar
223 * get (re-)created. Nice to install our tray icon.
224 *
225 * @return IPRT status code.
226 * @param wParam
227 * @param lParam
228 */
229static int vboxTrayGlMsgTaskbarCreated(WPARAM wParam, LPARAM lParam)
230{
231 return vboxTrayCreateTrayIcon();
232}
233
234static int vboxTrayCreateTrayIcon(void)
235{
236 HICON hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_VIRTUALBOX));
237 if (hIcon == NULL)
238 {
239 DWORD dwErr = GetLastError();
240 Log(("VBoxTray: Could not load tray icon, error %08X\n", dwErr));
241 return RTErrConvertFromWin32(dwErr);
242 }
243
244 /* Prepare the system tray icon. */
245 RT_ZERO(gNotifyIconData);
246 gNotifyIconData.cbSize = NOTIFYICONDATA_V1_SIZE; // sizeof(NOTIFYICONDATA);
247 gNotifyIconData.hWnd = ghwndToolWindow;
248 gNotifyIconData.uID = ID_TRAYICON;
249 gNotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
250 gNotifyIconData.uCallbackMessage = WM_VBOXTRAY_TRAY_ICON;
251 gNotifyIconData.hIcon = hIcon;
252
253 sprintf(gNotifyIconData.szTip, "%s Guest Additions %d.%d.%dr%d",
254 VBOX_PRODUCT, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
255
256 int rc = VINF_SUCCESS;
257 if (!Shell_NotifyIcon(NIM_ADD, &gNotifyIconData))
258 {
259 DWORD dwErr = GetLastError();
260 Log(("VBoxTray: Could not create tray icon, error = %08X\n", dwErr));
261 rc = RTErrConvertFromWin32(dwErr);
262 RT_ZERO(gNotifyIconData);
263 }
264
265 if (hIcon)
266 DestroyIcon(hIcon);
267 return rc;
268}
269
270static void vboxTrayRemoveTrayIcon()
271{
272 if (gNotifyIconData.cbSize > 0)
273 {
274 /* Remove the system tray icon and refresh system tray. */
275 Shell_NotifyIcon(NIM_DELETE, &gNotifyIconData);
276 HWND hTrayWnd = FindWindow("Shell_TrayWnd", NULL); /* We assume we only have one tray atm. */
277 if (hTrayWnd)
278 {
279 HWND hTrayNotifyWnd = FindWindowEx(hTrayWnd, 0, "TrayNotifyWnd", NULL);
280 if (hTrayNotifyWnd)
281 SendMessage(hTrayNotifyWnd, WM_PAINT, 0, NULL);
282 }
283 RT_ZERO(gNotifyIconData);
284 }
285}
286
287static int vboxTrayStartServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
288{
289 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
290 AssertPtrReturn(pTable, VERR_INVALID_POINTER);
291
292 Log(("VBoxTray: Starting services ...\n"));
293
294 /** @todo Use IPRT events here. */
295 pEnv->hStopEvent = CreateEvent(NULL, TRUE /* bManualReset */,
296 FALSE /* bInitialState */, NULL);
297
298 if (!pEnv->hStopEvent)
299 {
300 /* Could not create event. */
301 return VERR_NOT_SUPPORTED;
302 }
303
304 while ( pTable
305 && pTable->pszName)
306 {
307 Log(("VBoxTray: Starting %s ...\n", pTable->pszName));
308
309 int rc = VINF_SUCCESS;
310
311 bool fStartThread = false;
312
313 pTable->hThread = (HANDLE)0;
314 pTable->pInstance = NULL;
315 pTable->fStarted = false;
316
317 if (pTable->pfnInit)
318 rc = pTable->pfnInit(pEnv, &pTable->pInstance, &fStartThread);
319
320 if (RT_FAILURE(rc))
321 {
322 LogRel(("VBoxTray: Failed to initialize service \"%s\", rc=%Rrc\n",
323 pTable->pszName, rc));
324 }
325 else
326 {
327 if ( pTable->pfnThread
328 && fStartThread)
329 {
330 unsigned threadid;
331 /** @todo Use RTThread* here. */
332 pTable->hThread = (HANDLE)_beginthreadex(NULL, /* security */
333 0, /* stacksize */
334 pTable->pfnThread,
335 pTable->pInstance,
336 0, /* initflag */
337 &threadid);
338 if (pTable->hThread == (HANDLE)(0))
339 rc = VERR_NOT_SUPPORTED;
340 }
341
342 if (RT_SUCCESS(rc))
343 pTable->fStarted = true;
344 else
345 {
346 Log(("VBoxTray: Failed to start the thread\n"));
347 if (pTable->pfnDestroy)
348 pTable->pfnDestroy(pEnv, pTable->pInstance);
349 }
350 }
351
352 /* Advance to next table element. */
353 pTable++;
354 }
355
356 return VINF_SUCCESS;
357}
358
359static void vboxTrayStopServices(VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
360{
361 if (!pEnv->hStopEvent)
362 return;
363
364 /* Signal to all threads. */
365 SetEvent(pEnv->hStopEvent);
366
367 VBOXSERVICEINFO *pCurTable = pTable;
368 while ( pCurTable
369 && pCurTable->pszName)
370 {
371 if (pCurTable->pfnStop)
372 pCurTable->pfnStop(pEnv, pCurTable->pInstance);
373
374 /* Advance to next table element. */
375 pCurTable++;
376 }
377
378 pCurTable = pTable; /* Reset to first element. */
379 while ( pCurTable
380 && pCurTable->pszName)
381 {
382 if (pCurTable->fStarted)
383 {
384 if (pCurTable->pfnThread)
385 {
386 /* There is a thread, wait for termination. */
387 /** @todo Use RTThread* here. */
388 /** @todo Don't wait forever here. Use a sensible default. */
389 WaitForSingleObject(pCurTable->hThread, INFINITE);
390
391 /** @todo Dito. */
392 CloseHandle(pCurTable->hThread);
393 pCurTable->hThread = NULL;
394 }
395
396 if (pCurTable->pfnDestroy)
397 pCurTable->pfnDestroy(pEnv, pCurTable->pInstance);
398 pCurTable->fStarted = false;
399 }
400
401 /* Advance to next table element. */
402 pCurTable++;
403 }
404
405 CloseHandle(pEnv->hStopEvent);
406}
407
408static int vboxTrayRegisterGlobalMessages(PVBOXGLOBALMESSAGE pTable)
409{
410 int rc = VINF_SUCCESS;
411 if (pTable == NULL) /* No table to register? Skip. */
412 return rc;
413 while ( pTable->pszName
414 && RT_SUCCESS(rc))
415 {
416 /* Register global accessible window messages. */
417 pTable->uMsgID = RegisterWindowMessage(TEXT(pTable->pszName));
418 if (!pTable->uMsgID)
419 {
420 DWORD dwErr = GetLastError();
421 Log(("VBoxTray: Registering global message \"%s\" failed, error = %08X\n", dwErr));
422 rc = RTErrConvertFromWin32(dwErr);
423 }
424
425 /* Advance to next table element. */
426 pTable++;
427 }
428 return rc;
429}
430
431static bool vboxTrayHandleGlobalMessages(PVBOXGLOBALMESSAGE pTable, UINT uMsg,
432 WPARAM wParam, LPARAM lParam)
433{
434 if (pTable == NULL)
435 return false;
436 while (pTable && pTable->pszName)
437 {
438 if (pTable->uMsgID == uMsg)
439 {
440 if (pTable->pfnHandler)
441 pTable->pfnHandler(wParam, lParam);
442 return true;
443 }
444
445 /* Advance to next table element. */
446 pTable++;
447 }
448 return false;
449}
450
451static int vboxTrayOpenBaseDriver(void)
452{
453 /* Open VBox guest driver. */
454 DWORD dwErr = ERROR_SUCCESS;
455 ghVBoxDriver = CreateFile(VBOXGUEST_DEVICE_NAME,
456 GENERIC_READ | GENERIC_WRITE,
457 FILE_SHARE_READ | FILE_SHARE_WRITE,
458 NULL,
459 OPEN_EXISTING,
460 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
461 NULL);
462 if (ghVBoxDriver == INVALID_HANDLE_VALUE)
463 {
464 dwErr = GetLastError();
465 LogRel(("VBoxTray: Could not open VirtualBox Guest Additions driver! Please install / start it first! Error = %08X\n", dwErr));
466 }
467 return RTErrConvertFromWin32(dwErr);
468}
469
470static void vboxTrayCloseBaseDriver(void)
471{
472 if (ghVBoxDriver)
473 {
474 CloseHandle(ghVBoxDriver);
475 ghVBoxDriver = NULL;
476 }
477}
478
479static void vboxTrayDestroyToolWindow(void)
480{
481 if (ghwndToolWindow)
482 {
483 Log(("VBoxTray: Destroying tool window ...\n"));
484
485 /* Destroy the tool window. */
486 DestroyWindow(ghwndToolWindow);
487 ghwndToolWindow = NULL;
488
489 UnregisterClass("VBoxTrayToolWndClass", ghInstance);
490 }
491}
492
493static int vboxTrayCreateToolWindow(void)
494{
495 DWORD dwErr = ERROR_SUCCESS;
496
497 /* Create a custom window class. */
498 WNDCLASS windowClass = {0};
499 windowClass.style = CS_NOCLOSE;
500 windowClass.lpfnWndProc = (WNDPROC)vboxToolWndProc;
501 windowClass.hInstance = ghInstance;
502 windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
503 windowClass.lpszClassName = "VBoxTrayToolWndClass";
504 if (!RegisterClass(&windowClass))
505 {
506 dwErr = GetLastError();
507 Log(("VBoxTray: Registering invisible tool window failed, error = %08X\n", dwErr));
508 }
509 else
510 {
511 /*
512 * Create our (invisible) tool window.
513 * Note: The window name ("VBoxTrayToolWnd") and class ("VBoxTrayToolWndClass") is
514 * needed for posting globally registered messages to VBoxTray and must not be
515 * changed! Otherwise things get broken!
516 *
517 */
518 ghwndToolWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
519 "VBoxTrayToolWndClass", "VBoxTrayToolWnd",
520 WS_POPUPWINDOW,
521 -200, -200, 100, 100, NULL, NULL, ghInstance, NULL);
522 if (!ghwndToolWindow)
523 {
524 dwErr = GetLastError();
525 Log(("VBoxTray: Creating invisible tool window failed, error = %08X\n", dwErr));
526 }
527 else
528 {
529 /* Reload the cursor(s). */
530 hlpReloadCursor();
531
532 Log(("VBoxTray: Invisible tool window handle = %p\n", ghwndToolWindow));
533 }
534 }
535
536 if (dwErr != ERROR_SUCCESS)
537 vboxTrayDestroyToolWindow();
538 return RTErrConvertFromWin32(dwErr);
539}
540
541static int vboxTraySetupSeamless(void)
542{
543 OSVERSIONINFO info;
544 gMajorVersion = 5; /* Default to Windows XP. */
545 info.dwOSVersionInfoSize = sizeof(info);
546 if (GetVersionEx(&info))
547 {
548 Log(("VBoxTray: Windows version %ld.%ld\n", info.dwMajorVersion, info.dwMinorVersion));
549 gMajorVersion = info.dwMajorVersion;
550 }
551
552 /* We need to setup a security descriptor to allow other processes modify access to the seamless notification event semaphore. */
553 SECURITY_ATTRIBUTES SecAttr;
554 DWORD dwErr = ERROR_SUCCESS;
555 char secDesc[SECURITY_DESCRIPTOR_MIN_LENGTH];
556 BOOL fRC;
557
558 SecAttr.nLength = sizeof(SecAttr);
559 SecAttr.bInheritHandle = FALSE;
560 SecAttr.lpSecurityDescriptor = &secDesc;
561 InitializeSecurityDescriptor(SecAttr.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
562 fRC = SetSecurityDescriptorDacl(SecAttr.lpSecurityDescriptor, TRUE, 0, FALSE);
563 if (!fRC)
564 {
565 dwErr = GetLastError();
566 Log(("VBoxTray: SetSecurityDescriptorDacl failed with last error = %08X\n", dwErr));
567 }
568 else
569 {
570 /* For Vista and up we need to change the integrity of the security descriptor, too. */
571 if (gMajorVersion >= 6)
572 {
573 BOOL (WINAPI * pfnConvertStringSecurityDescriptorToSecurityDescriptorA)(LPCSTR StringSecurityDescriptor, DWORD StringSDRevision, PSECURITY_DESCRIPTOR *SecurityDescriptor, PULONG SecurityDescriptorSize);
574 *(void **)&pfnConvertStringSecurityDescriptorToSecurityDescriptorA =
575 RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorA");
576 Log(("VBoxTray: pfnConvertStringSecurityDescriptorToSecurityDescriptorA = %x\n", pfnConvertStringSecurityDescriptorToSecurityDescriptorA));
577 if (pfnConvertStringSecurityDescriptorToSecurityDescriptorA)
578 {
579 PSECURITY_DESCRIPTOR pSD;
580 PACL pSacl = NULL;
581 BOOL fSaclPresent = FALSE;
582 BOOL fSaclDefaulted = FALSE;
583
584 fRC = pfnConvertStringSecurityDescriptorToSecurityDescriptorA("S:(ML;;NW;;;LW)", /* this means "low integrity" */
585 SDDL_REVISION_1, &pSD, NULL);
586 if (!fRC)
587 {
588 dwErr = GetLastError();
589 Log(("VBoxTray: ConvertStringSecurityDescriptorToSecurityDescriptorA failed with last error = %08X\n", dwErr));
590 }
591 else
592 {
593 fRC = GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted);
594 if (!fRC)
595 {
596 dwErr = GetLastError();
597 Log(("VBoxTray: GetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
598 }
599 else
600 {
601 fRC = SetSecurityDescriptorSacl(SecAttr.lpSecurityDescriptor, TRUE, pSacl, FALSE);
602 if (!fRC)
603 {
604 dwErr = GetLastError();
605 Log(("VBoxTray: SetSecurityDescriptorSacl failed with last error = %08X\n", dwErr));
606 }
607 }
608 }
609 }
610 }
611
612 if ( dwErr == ERROR_SUCCESS
613 && gMajorVersion >= 5) /* Only for W2K and up ... */
614 {
615 ghSeamlessWtNotifyEvent = CreateEvent(&SecAttr, FALSE, FALSE, VBOXHOOK_GLOBAL_WT_EVENT_NAME);
616 if (ghSeamlessWtNotifyEvent == NULL)
617 {
618 dwErr = GetLastError();
619 Log(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr));
620 }
621
622 ghSeamlessKmNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
623 if (ghSeamlessKmNotifyEvent == NULL)
624 {
625 dwErr = GetLastError();
626 Log(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr));
627 }
628 }
629 }
630 return RTErrConvertFromWin32(dwErr);
631}
632
633static void vboxTrayShutdownSeamless(void)
634{
635 if (ghSeamlessWtNotifyEvent)
636 {
637 CloseHandle(ghSeamlessWtNotifyEvent);
638 ghSeamlessWtNotifyEvent = NULL;
639 }
640
641 if (ghSeamlessKmNotifyEvent)
642 {
643 CloseHandle(ghSeamlessKmNotifyEvent);
644 ghSeamlessKmNotifyEvent = NULL;
645 }
646}
647
648static void VBoxTrayCheckDt()
649{
650 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
651 if (vboxDtHandleEvent())
652 {
653 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
654 VBoxConsoleEnable(!fOldAllowedState);
655 }
656}
657
658static int vboxTrayServiceMain(void)
659{
660 int rc = VINF_SUCCESS;
661 Log(("VBoxTray: Entering vboxTrayServiceMain\n"));
662
663 ghStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
664 if (ghStopSem == NULL)
665 {
666 rc = RTErrConvertFromWin32(GetLastError());
667 Log(("VBoxTray: CreateEvent for stopping VBoxTray failed, rc=%Rrc\n", rc));
668 }
669 else
670 {
671 /*
672 * Start services listed in the vboxServiceTable.
673 */
674 VBOXSERVICEENV svcEnv;
675 svcEnv.hInstance = ghInstance;
676 svcEnv.hDriver = ghVBoxDriver;
677
678 /* Initializes disp-if to default (XPDM) mode. */
679 VBoxDispIfInit(&svcEnv.dispIf); /* Cannot fail atm. */
680 #ifdef VBOX_WITH_WDDM
681 /*
682 * For now the display mode will be adjusted to WDDM mode if needed
683 * on display service initialization when it detects the display driver type.
684 */
685 #endif
686
687 /* Finally start all the built-in services! */
688 rc = vboxTrayStartServices(&svcEnv, vboxServiceTable);
689 if (RT_FAILURE(rc))
690 {
691 /* Terminate service if something went wrong. */
692 vboxTrayStopServices(&svcEnv, vboxServiceTable);
693 }
694 else
695 {
696 rc = vboxTrayCreateTrayIcon();
697 if ( RT_SUCCESS(rc)
698 && gMajorVersion >= 5) /* Only for W2K and up ... */
699 {
700 /* We're ready to create the tooltip balloon.
701 Check in 10 seconds (@todo make seconds configurable) ... */
702 SetTimer(ghwndToolWindow,
703 TIMERID_VBOXTRAY_CHECK_HOSTVERSION,
704 10 * 1000, /* 10 seconds */
705 NULL /* No timerproc */);
706 }
707
708 if (RT_SUCCESS(rc))
709 {
710 /* Do the Shared Folders auto-mounting stuff. */
711 rc = VBoxSharedFoldersAutoMount();
712 if (RT_SUCCESS(rc))
713 {
714 /* Report the host that we're up and running! */
715 hlpReportStatus(VBoxGuestFacilityStatus_Active);
716 }
717 }
718
719 if (RT_SUCCESS(rc))
720 {
721 /* Boost thread priority to make sure we wake up early for seamless window notifications
722 * (not sure if it actually makes any difference though). */
723 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
724
725 /*
726 * Main execution loop
727 * Wait for the stop semaphore to be posted or a window event to arrive
728 */
729
730 HANDLE hWaitEvent[4] = {0};
731 DWORD dwEventCount = 0;
732
733 hWaitEvent[dwEventCount++] = ghStopSem;
734
735 /* Check if seamless mode is not active and add seamless event to the list */
736 if (0 != ghSeamlessWtNotifyEvent)
737 {
738 hWaitEvent[dwEventCount++] = ghSeamlessWtNotifyEvent;
739 }
740
741 if (0 != ghSeamlessKmNotifyEvent)
742 {
743 hWaitEvent[dwEventCount++] = ghSeamlessKmNotifyEvent;
744 }
745
746 if (0 != vboxDtGetNotifyEvent())
747 {
748 hWaitEvent[dwEventCount++] = vboxDtGetNotifyEvent();
749 }
750
751 Log(("VBoxTray: Number of events to wait in main loop: %ld\n", dwEventCount));
752 while (true)
753 {
754 DWORD waitResult = MsgWaitForMultipleObjectsEx(dwEventCount, hWaitEvent, 500, QS_ALLINPUT, 0);
755 waitResult = waitResult - WAIT_OBJECT_0;
756
757 /* Only enable for message debugging, lots of traffic! */
758 //Log(("VBoxTray: Wait result = %ld\n", waitResult));
759
760 if (waitResult == 0)
761 {
762 Log(("VBoxTray: Event 'Exit' triggered\n"));
763 /* exit */
764 break;
765 }
766 else
767 {
768 BOOL fHandled = FALSE;
769 if (waitResult < RT_ELEMENTS(hWaitEvent))
770 {
771 if (hWaitEvent[waitResult])
772 {
773 if (hWaitEvent[waitResult] == ghSeamlessWtNotifyEvent)
774 {
775 Log(("VBoxTray: Event 'Seamless' triggered\n"));
776
777 /* seamless window notification */
778 VBoxSeamlessCheckWindows(false);
779 fHandled = TRUE;
780 }
781 else if (hWaitEvent[waitResult] == ghSeamlessKmNotifyEvent)
782 {
783 Log(("VBoxTray: Event 'Km Seamless' triggered\n"));
784
785 /* seamless window notification */
786 VBoxSeamlessCheckWindows(true);
787 fHandled = TRUE;
788 }
789 else if (hWaitEvent[waitResult] == vboxDtGetNotifyEvent())
790 {
791 Log(("VBoxTray: Event 'Dt' triggered\n"));
792 VBoxTrayCheckDt();
793 fHandled = TRUE;
794 }
795 }
796 }
797
798 if (!fHandled)
799 {
800 /* timeout or a window message, handle it */
801 MSG msg;
802 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
803 {
804 Log(("VBoxTray: msg %p\n", msg.message));
805 if (msg.message == WM_QUIT)
806 {
807 Log(("VBoxTray: WM_QUIT!\n"));
808 SetEvent(ghStopSem);
809 }
810 TranslateMessage(&msg);
811 DispatchMessage(&msg);
812 }
813 }
814 }
815 }
816 Log(("VBoxTray: Returned from main loop, exiting ...\n"));
817 }
818 Log(("VBoxTray: Waiting for services to stop ...\n"));
819 vboxTrayStopServices(&svcEnv, vboxServiceTable);
820 } /* Services started */
821 CloseHandle(ghStopSem);
822 } /* Stop event created */
823
824 vboxTrayRemoveTrayIcon();
825
826 Log(("VBoxTray: Leaving vboxTrayServiceMain with rc=%Rrc\n", rc));
827 return rc;
828}
829
830/**
831 * Main function
832 */
833int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
834{
835 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
836 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxTray");
837 if ( hMutexAppRunning != NULL
838 && GetLastError() == ERROR_ALREADY_EXISTS)
839 {
840 /* Close the mutex for this application instance. */
841 CloseHandle (hMutexAppRunning);
842 hMutexAppRunning = NULL;
843 return 0;
844 }
845
846 LogRel(("VBoxTray: %s r%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()));
847
848 int rc = RTR3InitExeNoArguments(0);
849 if (RT_SUCCESS(rc))
850 {
851 rc = VbglR3Init();
852 if (RT_SUCCESS(rc))
853 rc = vboxTrayOpenBaseDriver();
854 }
855
856 if (RT_SUCCESS(rc))
857 {
858 /* Save instance handle. */
859 ghInstance = hInstance;
860
861 hlpReportStatus(VBoxGuestFacilityStatus_Init);
862 rc = vboxTrayCreateToolWindow();
863 if (RT_SUCCESS(rc))
864 {
865 VBoxCapsInit();
866
867 rc = vboxStInit(ghwndToolWindow);
868 if (!RT_SUCCESS(rc))
869 {
870 WARN(("VBoxTray: vboxStInit failed, rc %d\n"));
871 /* ignore the St Init failure. this can happen for < XP win that do not support WTS API
872 * in that case the session is treated as active connected to the physical console
873 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
874 Assert(vboxStIsActiveConsole());
875 }
876
877 rc = vboxDtInit();
878 if (!RT_SUCCESS(rc))
879 {
880 WARN(("VBoxTray: vboxDtInit failed, rc %d\n"));
881 /* ignore the Dt Init failure. this can happen for < XP win that do not support WTS API
882 * in that case the session is treated as active connected to the physical console
883 * (i.e. fallback to the old behavior that was before introduction of VBoxSt) */
884 Assert(vboxDtIsInputDesktop());
885 }
886
887 rc = VBoxAcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, true);
888 if (!RT_SUCCESS(rc))
889 {
890 WARN(("VBoxAcquireGuestCaps cfg failed rc %d, ignoring..\n", rc));
891 }
892
893 rc = vboxTraySetupSeamless();
894 if (RT_SUCCESS(rc))
895 {
896 Log(("VBoxTray: Init successful\n"));
897 rc = vboxTrayServiceMain();
898 if (RT_SUCCESS(rc))
899 hlpReportStatus(VBoxGuestFacilityStatus_Terminating);
900 vboxTrayShutdownSeamless();
901 }
902
903 /* it should be safe to call vboxDtTerm even if vboxStInit above failed */
904 vboxDtTerm();
905
906 /* it should be safe to call vboxStTerm even if vboxStInit above failed */
907 vboxStTerm();
908
909 VBoxCapsTerm();
910
911 vboxTrayDestroyToolWindow();
912 }
913 if (RT_SUCCESS(rc))
914 hlpReportStatus(VBoxGuestFacilityStatus_Terminated);
915 }
916
917 if (RT_FAILURE(rc))
918 {
919 LogRel(("VBoxTray: Error while starting, rc=%Rrc\n", rc));
920 hlpReportStatus(VBoxGuestFacilityStatus_Failed);
921 }
922 LogRel(("VBoxTray: Ended\n"));
923 vboxTrayCloseBaseDriver();
924
925 /* Release instance mutex. */
926 if (hMutexAppRunning != NULL)
927 {
928 CloseHandle(hMutexAppRunning);
929 hMutexAppRunning = NULL;
930 }
931
932 VbglR3Term();
933 return RT_SUCCESS(rc) ? 0 : 1;
934}
935
936/**
937 * Window procedure for our tool window
938 */
939static LRESULT CALLBACK vboxToolWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
940{
941 switch (uMsg)
942 {
943 case WM_CREATE:
944 {
945 Log(("VBoxTray: Tool window created\n"));
946
947 int rc = vboxTrayRegisterGlobalMessages(&s_vboxGlobalMessageTable[0]);
948 if (RT_FAILURE(rc))
949 Log(("VBoxTray: Error registering global window messages, rc=%Rrc\n", rc));
950 return 0;
951 }
952
953 case WM_CLOSE:
954 return 0;
955
956 case WM_DESTROY:
957 Log(("VBoxTray: Tool window destroyed\n"));
958 KillTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
959 return 0;
960
961 case WM_TIMER:
962 if (VBoxCapsCheckTimer(wParam))
963 return 0;
964 if (vboxDtCheckTimer(wParam))
965 return 0;
966 if (vboxStCheckTimer(wParam))
967 return 0;
968
969 switch (wParam)
970 {
971 case TIMERID_VBOXTRAY_CHECK_HOSTVERSION:
972 if (RT_SUCCESS(VBoxCheckHostVersion()))
973 {
974 /* After successful run we don't need to check again. */
975 KillTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CHECK_HOSTVERSION);
976 }
977 return 0;
978
979 default:
980 break;
981 }
982 break; /* Make sure other timers get processed the usual way! */
983
984 case WM_VBOXTRAY_TRAY_ICON:
985 switch (lParam)
986 {
987 case WM_LBUTTONDBLCLK:
988 break;
989
990 case WM_RBUTTONDOWN:
991 break;
992 }
993 return 0;
994
995 case WM_VBOX_SEAMLESS_ENABLE:
996 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
997 return 0;
998
999 case WM_VBOX_SEAMLESS_DISABLE:
1000 VBoxCapsEntryFuncStateSet(VBOXCAPS_ENTRY_IDX_SEAMLESS, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1001 return 0;
1002
1003 case WM_DISPLAYCHANGE:
1004 case WM_VBOX_SEAMLESS_UPDATE:
1005 if (VBoxCapsEntryIsEnabled(VBOXCAPS_ENTRY_IDX_SEAMLESS))
1006 VBoxSeamlessCheckWindows(true);
1007 return 0;
1008
1009 case WM_VBOX_GRAPHICS_SUPPORTED:
1010 VBoxGrapicsSetSupported(TRUE);
1011 return 0;
1012
1013 case WM_VBOX_GRAPHICS_UNSUPPORTED:
1014 VBoxGrapicsSetSupported(FALSE);
1015 return 0;
1016
1017 case WM_VBOXTRAY_VM_RESTORED:
1018 VBoxRestoreSession();
1019 return 0;
1020
1021 case WM_VBOXTRAY_VRDP_CHECK:
1022 VBoxRestoreCheckVRDP();
1023 return 0;
1024
1025 case WM_WTSSESSION_CHANGE:
1026 {
1027 BOOL fOldAllowedState = VBoxConsoleIsAllowed();
1028 if (vboxStHandleEvent(wParam, lParam))
1029 {
1030 if (!VBoxConsoleIsAllowed() != !fOldAllowedState)
1031 VBoxConsoleEnable(!fOldAllowedState);
1032 }
1033 return 0;
1034 }
1035 default:
1036
1037 /* Handle all globally registered window messages. */
1038 if (vboxTrayHandleGlobalMessages(&s_vboxGlobalMessageTable[0], uMsg,
1039 wParam, lParam))
1040 {
1041 return 0; /* We handled the message. @todo Add return value!*/
1042 }
1043 break; /* We did not handle the message, dispatch to DefWndProc. */
1044 }
1045
1046 /* Only if message was *not* handled by our switch above, dispatch
1047 * to DefWindowProc. */
1048 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1049}
1050
1051/* St (session [state] tracking) functionality API impl */
1052
1053typedef struct VBOXST
1054{
1055 HWND hWTSAPIWnd;
1056 RTLDRMOD hLdrModWTSAPI32;
1057 BOOL fIsConsole;
1058 WTS_CONNECTSTATE_CLASS enmConnectState;
1059 UINT_PTR idDelayedInitTimer;
1060 BOOL (WINAPI * pfnWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags);
1061 BOOL (WINAPI * pfnWTSUnRegisterSessionNotification)(HWND hWnd);
1062 BOOL (WINAPI * pfnWTSQuerySessionInformationA)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned);
1063} VBOXST;
1064
1065static VBOXST gVBoxSt;
1066
1067static int vboxStCheckState()
1068{
1069 int rc = VINF_SUCCESS;
1070 WTS_CONNECTSTATE_CLASS *penmConnectState = NULL;
1071 USHORT *pProtocolType = NULL;
1072 DWORD cbBuf = 0;
1073 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState,
1074 (LPTSTR *)&penmConnectState, &cbBuf))
1075 {
1076 if (gVBoxSt.pfnWTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientProtocolType,
1077 (LPTSTR *)&pProtocolType, &cbBuf))
1078 {
1079 gVBoxSt.fIsConsole = (*pProtocolType == 0);
1080 gVBoxSt.enmConnectState = *penmConnectState;
1081 return VINF_SUCCESS;
1082 }
1083
1084 DWORD dwErr = GetLastError();
1085 WARN(("VBoxTray: WTSQuerySessionInformationA WTSClientProtocolType failed, error = %08X\n", dwErr));
1086 rc = RTErrConvertFromWin32(dwErr);
1087 }
1088 else
1089 {
1090 DWORD dwErr = GetLastError();
1091 WARN(("VBoxTray: WTSQuerySessionInformationA WTSConnectState failed, error = %08X\n", dwErr));
1092 rc = RTErrConvertFromWin32(dwErr);
1093 }
1094
1095 /* failure branch, set to "console-active" state */
1096 gVBoxSt.fIsConsole = TRUE;
1097 gVBoxSt.enmConnectState = WTSActive;
1098
1099 return rc;
1100}
1101
1102static int vboxStInit(HWND hWnd)
1103{
1104 RT_ZERO(gVBoxSt);
1105 int rc = RTLdrLoadSystem("WTSAPI32.DLL", false /*fNoUnload*/, &gVBoxSt.hLdrModWTSAPI32);
1106 if (RT_SUCCESS(rc))
1107 {
1108 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSRegisterSessionNotification",
1109 (void **)&gVBoxSt.pfnWTSRegisterSessionNotification);
1110 if (RT_SUCCESS(rc))
1111 {
1112 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSUnRegisterSessionNotification",
1113 (void **)&gVBoxSt.pfnWTSUnRegisterSessionNotification);
1114 if (RT_SUCCESS(rc))
1115 {
1116 rc = RTLdrGetSymbol(gVBoxSt.hLdrModWTSAPI32, "WTSQuerySessionInformationA",
1117 (void **)&gVBoxSt.pfnWTSQuerySessionInformationA);
1118 if (RT_FAILURE(rc))
1119 WARN(("VBoxTray: WTSQuerySessionInformationA not found\n"));
1120 }
1121 else
1122 WARN(("VBoxTray: WTSUnRegisterSessionNotification not found\n"));
1123 }
1124 else
1125 WARN(("VBoxTray: WTSRegisterSessionNotification not found\n"));
1126 if (RT_SUCCESS(rc))
1127 {
1128 gVBoxSt.hWTSAPIWnd = hWnd;
1129 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1130 vboxStCheckState();
1131 else
1132 {
1133 DWORD dwErr = GetLastError();
1134 WARN(("VBoxTray: WTSRegisterSessionNotification failed, error = %08X\n", dwErr));
1135 if (dwErr == RPC_S_INVALID_BINDING)
1136 {
1137 gVBoxSt.idDelayedInitTimer = SetTimer(gVBoxSt.hWTSAPIWnd, TIMERID_VBOXTRAY_ST_DELAYED_INIT_TIMER,
1138 2000, (TIMERPROC)NULL);
1139 gVBoxSt.fIsConsole = TRUE;
1140 gVBoxSt.enmConnectState = WTSActive;
1141 rc = VINF_SUCCESS;
1142 }
1143 else
1144 rc = RTErrConvertFromWin32(dwErr);
1145 }
1146
1147 if (RT_SUCCESS(rc))
1148 return VINF_SUCCESS;
1149 }
1150
1151 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1152 }
1153 else
1154 WARN(("VBoxTray: WTSAPI32 load failed, rc = %Rrc\n", rc));
1155
1156 RT_ZERO(gVBoxSt);
1157 gVBoxSt.fIsConsole = TRUE;
1158 gVBoxSt.enmConnectState = WTSActive;
1159 return rc;
1160}
1161
1162static void vboxStTerm(void)
1163{
1164 if (!gVBoxSt.hWTSAPIWnd)
1165 {
1166 WARN(("VBoxTray: vboxStTerm called for non-initialized St\n"));
1167 return;
1168 }
1169
1170 if (gVBoxSt.idDelayedInitTimer)
1171 {
1172 /* notification is not registered, just kill timer */
1173 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1174 gVBoxSt.idDelayedInitTimer = 0;
1175 }
1176 else
1177 {
1178 if (!gVBoxSt.pfnWTSUnRegisterSessionNotification(gVBoxSt.hWTSAPIWnd))
1179 {
1180 DWORD dwErr = GetLastError();
1181 WARN(("VBoxTray: WTSAPI32 load failed, error = %08X\n", dwErr));
1182 }
1183 }
1184
1185 RTLdrClose(gVBoxSt.hLdrModWTSAPI32);
1186 RT_ZERO(gVBoxSt);
1187}
1188
1189#define VBOXST_DBG_MAKECASE(_val) case _val: return #_val;
1190
1191static const char* vboxStDbgGetString(DWORD val)
1192{
1193 switch (val)
1194 {
1195 VBOXST_DBG_MAKECASE(WTS_CONSOLE_CONNECT);
1196 VBOXST_DBG_MAKECASE(WTS_CONSOLE_DISCONNECT);
1197 VBOXST_DBG_MAKECASE(WTS_REMOTE_CONNECT);
1198 VBOXST_DBG_MAKECASE(WTS_REMOTE_DISCONNECT);
1199 VBOXST_DBG_MAKECASE(WTS_SESSION_LOGON);
1200 VBOXST_DBG_MAKECASE(WTS_SESSION_LOGOFF);
1201 VBOXST_DBG_MAKECASE(WTS_SESSION_LOCK);
1202 VBOXST_DBG_MAKECASE(WTS_SESSION_UNLOCK);
1203 VBOXST_DBG_MAKECASE(WTS_SESSION_REMOTE_CONTROL);
1204 default:
1205 WARN(("VBoxTray: invalid WTS state %d\n", val));
1206 return "Unknown";
1207 }
1208}
1209
1210static BOOL vboxStCheckTimer(WPARAM wEvent)
1211{
1212 if (wEvent != gVBoxSt.idDelayedInitTimer)
1213 return FALSE;
1214
1215 if (gVBoxSt.pfnWTSRegisterSessionNotification(gVBoxSt.hWTSAPIWnd, NOTIFY_FOR_THIS_SESSION))
1216 {
1217 KillTimer(gVBoxSt.hWTSAPIWnd, gVBoxSt.idDelayedInitTimer);
1218 gVBoxSt.idDelayedInitTimer = 0;
1219 vboxStCheckState();
1220 }
1221 else
1222 {
1223 DWORD dwErr = GetLastError();
1224 WARN(("VBoxTray: timer WTSRegisterSessionNotification failed, error = %08X\n", dwErr));
1225 Assert(gVBoxSt.fIsConsole == TRUE);
1226 Assert(gVBoxSt.enmConnectState == WTSActive);
1227 }
1228
1229 return TRUE;
1230}
1231
1232
1233static BOOL vboxStHandleEvent(WPARAM wEvent, LPARAM SessionID)
1234{
1235 WARN(("VBoxTray: WTS Event: %s\n", vboxStDbgGetString(wEvent)));
1236 BOOL fOldIsActiveConsole = vboxStIsActiveConsole();
1237
1238 vboxStCheckState();
1239
1240 return !vboxStIsActiveConsole() != !fOldIsActiveConsole;
1241}
1242
1243static BOOL vboxStIsActiveConsole()
1244{
1245 return (gVBoxSt.enmConnectState == WTSActive && gVBoxSt.fIsConsole);
1246}
1247
1248/*
1249 * Dt (desktop [state] tracking) functionality API impl
1250 *
1251 * !!!NOTE: this API is NOT thread-safe!!!
1252 * */
1253
1254typedef struct VBOXDT
1255{
1256 HANDLE hNotifyEvent;
1257 BOOL fIsInputDesktop;
1258 UINT_PTR idTimer;
1259 RTLDRMOD hLdrModHook;
1260 BOOL (* pfnVBoxHookInstallActiveDesktopTracker)(HMODULE hDll);
1261 BOOL (* pfnVBoxHookRemoveActiveDesktopTracker)();
1262 HDESK (WINAPI * pfnGetThreadDesktop)(DWORD dwThreadId);
1263 HDESK (WINAPI * pfnOpenInputDesktop)(DWORD dwFlags, BOOL fInherit, ACCESS_MASK dwDesiredAccess);
1264 BOOL (WINAPI * pfnCloseDesktop)(HDESK hDesktop);
1265} VBOXDT;
1266
1267static VBOXDT gVBoxDt;
1268
1269static BOOL vboxDtCalculateIsInputDesktop()
1270{
1271 BOOL fIsInputDt = FALSE;
1272 HDESK hInput = gVBoxDt.pfnOpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW);
1273 if (hInput)
1274 {
1275// DWORD dwThreadId = GetCurrentThreadId();
1276// HDESK hThreadDt = gVBoxDt.pfnGetThreadDesktop(dwThreadId);
1277// if (hThreadDt)
1278// {
1279 fIsInputDt = TRUE;
1280// }
1281// else
1282// {
1283// DWORD dwErr = GetLastError();
1284// WARN(("VBoxTray: pfnGetThreadDesktop for Seamless failed, last error = %08X\n", dwErr));
1285// }
1286
1287 gVBoxDt.pfnCloseDesktop(hInput);
1288 }
1289 else
1290 {
1291 DWORD dwErr = GetLastError();
1292// WARN(("VBoxTray: pfnOpenInputDesktop for Seamless failed, last error = %08X\n", dwErr));
1293 }
1294 return fIsInputDt;
1295}
1296
1297static BOOL vboxDtCheckTimer(WPARAM wParam)
1298{
1299 if (wParam != gVBoxDt.idTimer)
1300 return FALSE;
1301
1302 VBoxTrayCheckDt();
1303
1304 return TRUE;
1305}
1306
1307static int vboxDtInit()
1308{
1309 int rc = VINF_SUCCESS;
1310 OSVERSIONINFO info;
1311 gMajorVersion = 5; /* Default to Windows XP. */
1312 info.dwOSVersionInfoSize = sizeof(info);
1313 if (GetVersionEx(&info))
1314 {
1315 LogRel(("VBoxTray: Windows version %ld.%ld\n", info.dwMajorVersion, info.dwMinorVersion));
1316 gMajorVersion = info.dwMajorVersion;
1317 }
1318
1319 RT_ZERO(gVBoxDt);
1320
1321 gVBoxDt.hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, VBOXHOOK_GLOBAL_DT_EVENT_NAME);
1322 if (gVBoxDt.hNotifyEvent != NULL)
1323 {
1324 /* Load the hook dll and resolve the necessary entry points. */
1325 rc = RTLdrLoadAppPriv(VBOXHOOK_DLL_NAME, &gVBoxDt.hLdrModHook);
1326 if (RT_SUCCESS(rc))
1327 {
1328 rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookInstallActiveDesktopTracker",
1329 (void **)&gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker);
1330 if (RT_SUCCESS(rc))
1331 {
1332 rc = RTLdrGetSymbol(gVBoxDt.hLdrModHook, "VBoxHookRemoveActiveDesktopTracker",
1333 (void **)&gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker);
1334 if (RT_FAILURE(rc))
1335 WARN(("VBoxTray: VBoxHookRemoveActiveDesktopTracker not found\n"));
1336 }
1337 else
1338 WARN(("VBoxTray: VBoxHookInstallActiveDesktopTracker not found\n"));
1339 if (RT_SUCCESS(rc))
1340 {
1341 /* Try get the system APIs we need. */
1342 *(void **)&gVBoxDt.pfnGetThreadDesktop = RTLdrGetSystemSymbol("user32.dll", "GetThreadDesktop");
1343 if (!gVBoxDt.pfnGetThreadDesktop)
1344 {
1345 WARN(("VBoxTray: GetThreadDesktop not found\n"));
1346 rc = VERR_NOT_SUPPORTED;
1347 }
1348
1349 *(void **)&gVBoxDt.pfnOpenInputDesktop = RTLdrGetSystemSymbol("user32.dll", "OpenInputDesktop");
1350 if (!gVBoxDt.pfnOpenInputDesktop)
1351 {
1352 WARN(("VBoxTray: OpenInputDesktop not found\n"));
1353 rc = VERR_NOT_SUPPORTED;
1354 }
1355
1356 *(void **)&gVBoxDt.pfnCloseDesktop = RTLdrGetSystemSymbol("user32.dll", "CloseDesktop");
1357 if (!gVBoxDt.pfnCloseDesktop)
1358 {
1359 WARN(("VBoxTray: CloseDesktop not found\n"));
1360 rc = VERR_NOT_SUPPORTED;
1361 }
1362
1363 if (RT_SUCCESS(rc))
1364 {
1365 BOOL fRc = FALSE;
1366 /* For Vista and up we need to change the integrity of the security descriptor, too. */
1367 if (gMajorVersion >= 6)
1368 {
1369 HMODULE hModHook = (HMODULE)RTLdrGetNativeHandle(gVBoxDt.hLdrModHook);
1370 Assert((uintptr_t)hModHook != ~(uintptr_t)0);
1371 fRc = gVBoxDt.pfnVBoxHookInstallActiveDesktopTracker(hModHook);
1372 if (!fRc)
1373 {
1374 DWORD dwErr = GetLastError();
1375 WARN(("VBoxTray: pfnVBoxHookInstallActiveDesktopTracker failed, last error = %08X\n", dwErr));
1376 }
1377 }
1378
1379 if (!fRc)
1380 {
1381 gVBoxDt.idTimer = SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_DT_TIMER, 500, (TIMERPROC)NULL);
1382 if (!gVBoxDt.idTimer)
1383 {
1384 DWORD dwErr = GetLastError();
1385 WARN(("VBoxTray: SetTimer error %08X\n", dwErr));
1386 rc = RTErrConvertFromWin32(dwErr);
1387 }
1388 }
1389
1390 if (RT_SUCCESS(rc))
1391 {
1392 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1393 return VINF_SUCCESS;
1394 }
1395 }
1396 }
1397
1398 RTLdrClose(gVBoxDt.hLdrModHook);
1399 }
1400 else
1401 {
1402 DWORD dwErr = GetLastError();
1403 WARN(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1404 rc = RTErrConvertFromWin32(dwErr);
1405 }
1406
1407 CloseHandle(gVBoxDt.hNotifyEvent);
1408 }
1409 else
1410 {
1411 DWORD dwErr = GetLastError();
1412 WARN(("VBoxTray: CreateEvent for Seamless failed, last error = %08X\n", dwErr));
1413 rc = RTErrConvertFromWin32(dwErr);
1414 }
1415
1416
1417 RT_ZERO(gVBoxDt);
1418 gVBoxDt.fIsInputDesktop = TRUE;
1419
1420 return rc;
1421}
1422
1423static void vboxDtTerm()
1424{
1425 if (!gVBoxDt.hLdrModHook)
1426 return;
1427
1428 gVBoxDt.pfnVBoxHookRemoveActiveDesktopTracker();
1429
1430 RTLdrClose(gVBoxDt.hLdrModHook);
1431 CloseHandle(gVBoxDt.hNotifyEvent);
1432
1433 RT_ZERO(gVBoxDt);
1434}
1435/* @returns true on "IsInputDesktop" state change */
1436static BOOL vboxDtHandleEvent()
1437{
1438 BOOL fIsInputDesktop = gVBoxDt.fIsInputDesktop;
1439 gVBoxDt.fIsInputDesktop = vboxDtCalculateIsInputDesktop();
1440 return !fIsInputDesktop != !gVBoxDt.fIsInputDesktop;
1441}
1442
1443static HANDLE vboxDtGetNotifyEvent()
1444{
1445 return gVBoxDt.hNotifyEvent;
1446}
1447
1448/* @returns true iff the application (VBoxTray) desktop is input */
1449static BOOL vboxDtIsInputDesktop()
1450{
1451 return gVBoxDt.fIsInputDesktop;
1452}
1453
1454
1455/* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest
1456 * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests
1457 * */
1458static int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg)
1459{
1460 DWORD cbReturned = 0;
1461 VBoxGuestCapsAquire Info;
1462 Log(("VBoxTray: VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg));
1463 Info.enmFlags = fCfg ? VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE : VBOXGUESTCAPSACQUIRE_FLAGS_NONE;
1464 Info.rc = VERR_NOT_IMPLEMENTED;
1465 Info.u32OrMask = fOr;
1466 Info.u32NotMask = fNot;
1467 if (!DeviceIoControl(ghVBoxDriver, VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE, &Info, sizeof(Info), &Info, sizeof(Info), &cbReturned, NULL))
1468 {
1469 DWORD LastErr = GetLastError();
1470 WARN(("VBoxTray: DeviceIoControl VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed LastErr %d\n", LastErr));
1471 return RTErrConvertFromWin32(LastErr);
1472 }
1473
1474 int rc = Info.rc;
1475 if (!RT_SUCCESS(rc))
1476 {
1477 WARN(("VBoxTray: VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed rc %d\n", rc));
1478 return rc;
1479 }
1480
1481 return rc;
1482}
1483
1484typedef enum VBOXCAPS_ENTRY_ACSTATE
1485{
1486 /* the given cap is released */
1487 VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0,
1488 /* the given cap acquisition is in progress */
1489 VBOXCAPS_ENTRY_ACSTATE_ACQUIRING,
1490 /* the given cap is acquired */
1491 VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1492} VBOXCAPS_ENTRY_ACSTATE;
1493
1494
1495struct VBOXCAPS_ENTRY;
1496struct VBOXCAPS;
1497
1498typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE)(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled);
1499
1500typedef struct VBOXCAPS_ENTRY
1501{
1502 uint32_t fCap;
1503 uint32_t iCap;
1504 VBOXCAPS_ENTRY_FUNCSTATE enmFuncState;
1505 VBOXCAPS_ENTRY_ACSTATE enmAcState;
1506 PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable;
1507} VBOXCAPS_ENTRY;
1508
1509
1510typedef struct VBOXCAPS
1511{
1512 UINT_PTR idTimer;
1513 VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
1514} VBOXCAPS;
1515
1516static VBOXCAPS gVBoxCaps;
1517
1518static DECLCALLBACK(void) vboxCapsOnEnableSeamles(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled)
1519{
1520 if (fEnabled)
1521 {
1522 Log(("VBoxTray: vboxCapsOnEnableSeamles: ENABLED\n"));
1523 Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1524 Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1525 VBoxSeamlessEnable();
1526 }
1527 else
1528 {
1529 Log(("VBoxTray: vboxCapsOnEnableSeamles: DISABLED\n"));
1530 Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
1531 VBoxSeamlessDisable();
1532 }
1533}
1534
1535static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState)
1536{
1537 VBOXCAPS *pConsole = &gVBoxCaps;
1538
1539 Log(("VBoxTray: vboxCapsEntryAcStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
1540 enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
1541
1542 if (pCap->enmAcState == enmAcState)
1543 return;
1544
1545 VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState;
1546 pCap->enmAcState = enmAcState;
1547
1548 if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1549 {
1550 if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1551 {
1552 if (pCap->pfnOnEnable)
1553 pCap->pfnOnEnable(pConsole, pCap, TRUE);
1554 }
1555 }
1556 else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1557 {
1558 if (pCap->pfnOnEnable)
1559 pCap->pfnOnEnable(pConsole, pCap, FALSE);
1560 }
1561}
1562
1563static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
1564{
1565 VBOXCAPS *pConsole = &gVBoxCaps;
1566
1567 Log(("VBoxTray: vboxCapsEntryFuncStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
1568 enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
1569
1570 if (pCap->enmFuncState == enmFuncState)
1571 return;
1572
1573 VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState;
1574
1575 pCap->enmFuncState = enmFuncState;
1576
1577 if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1578 {
1579 Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1580 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1581 {
1582 if (pCap->pfnOnEnable)
1583 pCap->pfnOnEnable(pConsole, pCap, TRUE);
1584 }
1585 }
1586 else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
1587 {
1588 if (pCap->pfnOnEnable)
1589 pCap->pfnOnEnable(pConsole, pCap, FALSE);
1590 }
1591}
1592
1593static void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
1594{
1595 VBOXCAPS *pConsole = &gVBoxCaps;
1596 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup];
1597 vboxCapsEntryFuncStateSet(pCap, enmFuncState);
1598}
1599
1600static int VBoxCapsInit()
1601{
1602 VBOXCAPS *pConsole = &gVBoxCaps;
1603 memset(pConsole, 0, sizeof (*pConsole));
1604 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
1605 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS;
1606 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamles;
1607 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
1608 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS;
1609 return VINF_SUCCESS;
1610}
1611
1612static int VBoxCapsReleaseAll()
1613{
1614 VBOXCAPS *pConsole = &gVBoxCaps;
1615 Log(("VBoxTray: VBoxCapsReleaseAll\n"));
1616 int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
1617 if (!RT_SUCCESS(rc))
1618 {
1619 WARN(("VBoxTray: vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
1620 return rc;
1621 }
1622
1623 if (pConsole->idTimer)
1624 {
1625 Log(("VBoxTray: killing console timer\n"));
1626 KillTimer(ghwndToolWindow, pConsole->idTimer);
1627 pConsole->idTimer = 0;
1628 }
1629
1630 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1631 {
1632 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED);
1633 }
1634
1635 return rc;
1636}
1637
1638static void VBoxCapsTerm()
1639{
1640 VBOXCAPS *pConsole = &gVBoxCaps;
1641 VBoxCapsReleaseAll();
1642 memset(pConsole, 0, sizeof (*pConsole));
1643}
1644
1645static BOOL VBoxCapsEntryIsAcquired(uint32_t iCap)
1646{
1647 VBOXCAPS *pConsole = &gVBoxCaps;
1648 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED;
1649}
1650
1651static BOOL VBoxCapsEntryIsEnabled(uint32_t iCap)
1652{
1653 VBOXCAPS *pConsole = &gVBoxCaps;
1654 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
1655 && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED;
1656}
1657
1658static BOOL VBoxCapsCheckTimer(WPARAM wParam)
1659{
1660 VBOXCAPS *pConsole = &gVBoxCaps;
1661 if (wParam != pConsole->idTimer)
1662 return FALSE;
1663
1664 uint32_t u32AcquiredCaps = 0;
1665 BOOL fNeedNewTimer = FALSE;
1666
1667 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1668 {
1669 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i];
1670 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING)
1671 continue;
1672
1673 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
1674 if (RT_SUCCESS(rc))
1675 {
1676 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1677 u32AcquiredCaps |= pCap->fCap;
1678 }
1679 else
1680 {
1681 Assert(rc == VERR_RESOURCE_BUSY);
1682 fNeedNewTimer = TRUE;
1683 }
1684 }
1685
1686 if (!fNeedNewTimer)
1687 {
1688 KillTimer(ghwndToolWindow, pConsole->idTimer);
1689 /* cleanup timer data */
1690 pConsole->idTimer = 0;
1691 }
1692
1693 return TRUE;
1694}
1695
1696static int VBoxCapsEntryRelease(uint32_t iCap)
1697{
1698 VBOXCAPS *pConsole = &gVBoxCaps;
1699 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
1700 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED)
1701 {
1702 WARN(("VBoxTray: invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState));
1703 return VERR_INVALID_STATE;
1704 }
1705
1706 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
1707 {
1708 int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false);
1709 AssertRC(rc);
1710 }
1711
1712 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED);
1713
1714 return VINF_SUCCESS;
1715}
1716
1717static int VBoxCapsEntryAcquire(uint32_t iCap)
1718{
1719 VBOXCAPS *pConsole = &gVBoxCaps;
1720 Assert(VBoxConsoleIsAllowed());
1721 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
1722 Log(("VBoxTray: VBoxCapsEntryAcquire %d\n", iCap));
1723 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED)
1724 {
1725 WARN(("VBoxTray: invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState));
1726 return VERR_INVALID_STATE;
1727 }
1728
1729 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING);
1730 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
1731 if (RT_SUCCESS(rc))
1732 {
1733 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
1734 return VINF_SUCCESS;
1735 }
1736
1737 if (rc != VERR_RESOURCE_BUSY)
1738 {
1739 WARN(("VBoxTray: vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
1740 return rc;
1741 }
1742
1743 WARN(("VBoxTray: iCap %d is busy!\n", iCap));
1744
1745 /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session,
1746 * queue the retry timer */
1747 if (!pConsole->idTimer)
1748 {
1749 pConsole->idTimer = SetTimer(ghwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL);
1750 if (!pConsole->idTimer)
1751 {
1752 DWORD dwErr = GetLastError();
1753 WARN(("VBoxTray: SetTimer error %08X\n", dwErr));
1754 return RTErrConvertFromWin32(dwErr);
1755 }
1756 }
1757
1758 return rc;
1759}
1760
1761static int VBoxCapsAcquireAllSupported()
1762{
1763 VBOXCAPS *pConsole = &gVBoxCaps;
1764 Log(("VBoxTray: VBoxCapsAcquireAllSupported\n"));
1765 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
1766 {
1767 if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED)
1768 {
1769 Log(("VBoxTray: VBoxCapsAcquireAllSupported acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState));
1770 VBoxCapsEntryAcquire(i);
1771 }
1772 else
1773 {
1774 WARN(("VBoxTray: VBoxCapsAcquireAllSupported: WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState));
1775 }
1776 }
1777 return VINF_SUCCESS;
1778}
1779
1780static BOOL VBoxConsoleIsAllowed()
1781{
1782 return vboxDtIsInputDesktop() && vboxStIsActiveConsole();
1783}
1784
1785static void VBoxConsoleEnable(BOOL fEnable)
1786{
1787 if (fEnable)
1788 VBoxCapsAcquireAllSupported();
1789 else
1790 VBoxCapsReleaseAll();
1791}
1792
1793static void VBoxConsoleCapSetSupported(uint32_t iCap, BOOL fSupported)
1794{
1795 if (fSupported)
1796 {
1797 VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
1798
1799 if (VBoxConsoleIsAllowed())
1800 VBoxCapsEntryAcquire(iCap);
1801 }
1802 else
1803 {
1804 VBoxCapsEntryFuncStateSet(iCap, VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED);
1805
1806 VBoxCapsEntryRelease(iCap);
1807 }
1808}
1809
1810void VBoxSeamlessSetSupported(BOOL fSupported)
1811{
1812 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_SEAMLESS, fSupported);
1813}
1814
1815static void VBoxGrapicsSetSupported(BOOL fSupported)
1816{
1817 VBoxConsoleCapSetSupported(VBOXCAPS_ENTRY_IDX_GRAPHICS, fSupported);
1818}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette