VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 4 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/* $Id: VBoxCaps.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxCaps.cpp - Capability APIs.
4 */
5
6/*
7 * Copyright (C) 2013-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <iprt/log.h>
29
30#include "VBoxTray.h"
31#include "VBoxTrayInternal.h"
32#include "VBoxSeamless.h"
33
34
35typedef enum VBOXCAPS_ENTRY_ACSTATE
36{
37 /* the given cap is released */
38 VBOXCAPS_ENTRY_ACSTATE_RELEASED = 0,
39 /* the given cap acquisition is in progress */
40 VBOXCAPS_ENTRY_ACSTATE_ACQUIRING,
41 /* the given cap is acquired */
42 VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
43} VBOXCAPS_ENTRY_ACSTATE;
44
45struct VBOXCAPS_ENTRY;
46struct VBOXCAPS;
47
48typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE,(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled));
49
50typedef struct VBOXCAPS_ENTRY
51{
52 uint32_t fCap;
53 uint32_t iCap;
54 VBOXCAPS_ENTRY_FUNCSTATE enmFuncState;
55 VBOXCAPS_ENTRY_ACSTATE enmAcState;
56 PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable;
57} VBOXCAPS_ENTRY;
58
59
60typedef struct VBOXCAPS
61{
62 UINT_PTR idTimer;
63 VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
64} VBOXCAPS;
65
66static VBOXCAPS gVBoxCaps;
67
68static const char* vboxCapsFuncState2Str(VBOXCAPS_ENTRY_FUNCSTATE state)
69{
70 switch (state)
71 {
72 case VBOXCAPS_ENTRY_FUNCSTATE_UNSUPPORTED: return "unsupported";
73 case VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED: return "supported";
74 case VBOXCAPS_ENTRY_FUNCSTATE_STARTED: return "started";
75 default:
76 return "unknown";
77 }
78}
79
80static const char* vboxCapsAcquireState2Str(VBOXCAPS_ENTRY_ACSTATE state)
81{
82 switch (state)
83 {
84 case VBOXCAPS_ENTRY_ACSTATE_RELEASED: return "released";
85 case VBOXCAPS_ENTRY_ACSTATE_ACQUIRING: return "acquiring";
86 case VBOXCAPS_ENTRY_ACSTATE_ACQUIRED: return "acquired";
87 default:
88 return "unknown";
89 }
90}
91
92static const char* vboxCapsIdx2Str(uint32_t iCap)
93{
94 switch (iCap)
95 {
96 case VBOXCAPS_ENTRY_IDX_SEAMLESS: return "SEAMLESS";
97 case VBOXCAPS_ENTRY_IDX_GRAPHICS: return "GRAPHICS";
98 default:
99 return "unknown";
100 }
101}
102
103int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg)
104{
105 LogFunc(("or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg));
106 int rc = VbglR3AcquireGuestCaps(fOr, fNot, fCfg);
107 if (RT_FAILURE(rc))
108 LogFlowFunc(("VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed: %Rrc\n", rc));
109 return rc;
110}
111
112static DECLCALLBACK(void) vboxCapsOnEnableSeamless(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled)
113{
114 RT_NOREF(pConsole, pCap);
115 if (fEnabled)
116 {
117 LogFunc(("ENABLED\n"));
118 Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
119 Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
120 VBoxSeamlessEnable();
121 }
122 else
123 {
124 LogFunc(("DISABLED\n"));
125 Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
126 VBoxSeamlessDisable();
127 }
128}
129
130static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState)
131{
132 VBOXCAPS *pConsole = &gVBoxCaps;
133
134 LogFunc(("new enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
135 enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
136
137 if (pCap->enmAcState == enmAcState)
138 return;
139
140 VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState;
141 pCap->enmAcState = enmAcState;
142
143 if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
144 {
145 if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
146 {
147 if (pCap->pfnOnEnable)
148 pCap->pfnOnEnable(pConsole, pCap, TRUE);
149 }
150 }
151 else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
152 {
153 if (pCap->pfnOnEnable)
154 pCap->pfnOnEnable(pConsole, pCap, FALSE);
155 }
156
157 LogFunc(("%s %s -> %s\n", vboxCapsIdx2Str(pCap->iCap),
158 vboxCapsAcquireState2Str(enmOldAcState), vboxCapsAcquireState2Str(enmAcState)));
159}
160
161static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
162{
163 VBOXCAPS *pConsole = &gVBoxCaps;
164
165 LogFunc(("new enmFuncState(%d); fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
166 enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
167
168 if (pCap->enmFuncState == enmFuncState)
169 return;
170
171 VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState;
172
173 pCap->enmFuncState = enmFuncState;
174
175 if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
176 {
177 Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
178 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
179 {
180 if (pCap->pfnOnEnable)
181 pCap->pfnOnEnable(pConsole, pCap, TRUE);
182 }
183 }
184 else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
185 {
186 if (pCap->pfnOnEnable)
187 pCap->pfnOnEnable(pConsole, pCap, FALSE);
188 }
189
190 LogFunc((" %s %s -> %s\n", vboxCapsIdx2Str(pCap->iCap),
191 vboxCapsFuncState2Str(enmOldFuncState), vboxCapsFuncState2Str(enmFuncState)));
192}
193
194void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
195{
196 VBOXCAPS *pConsole = &gVBoxCaps;
197 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup];
198 vboxCapsEntryFuncStateSet(pCap, enmFuncState);
199}
200
201int VBoxCapsInit()
202{
203 VBOXCAPS *pConsole = &gVBoxCaps;
204 memset(pConsole, 0, sizeof (*pConsole));
205 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
206 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS;
207 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamless;
208 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
209 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS;
210 return VINF_SUCCESS;
211}
212
213int VBoxCapsReleaseAll()
214{
215 VBOXCAPS *pConsole = &gVBoxCaps;
216 Log(("VBoxCapsReleaseAll\n"));
217 int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
218 if (!RT_SUCCESS(rc))
219 {
220 LogFlowFunc(("failed rc %d\n", rc));
221 return rc;
222 }
223
224 if (pConsole->idTimer)
225 {
226 LogFunc(("killing console timer\n"));
227 KillTimer(g_hwndToolWindow, pConsole->idTimer);
228 pConsole->idTimer = 0;
229 }
230
231 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
232 {
233 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED);
234 }
235
236 return rc;
237}
238
239void VBoxCapsTerm()
240{
241 VBOXCAPS *pConsole = &gVBoxCaps;
242 VBoxCapsReleaseAll();
243 memset(pConsole, 0, sizeof (*pConsole));
244}
245
246BOOL VBoxCapsEntryIsAcquired(uint32_t iCap)
247{
248 VBOXCAPS *pConsole = &gVBoxCaps;
249 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED;
250}
251
252BOOL VBoxCapsEntryIsEnabled(uint32_t iCap)
253{
254 VBOXCAPS *pConsole = &gVBoxCaps;
255 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
256 && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED;
257}
258
259BOOL VBoxCapsCheckTimer(WPARAM wParam)
260{
261 VBOXCAPS *pConsole = &gVBoxCaps;
262 if (wParam != pConsole->idTimer)
263 return FALSE;
264
265 uint32_t u32AcquiredCaps = 0;
266 BOOL fNeedNewTimer = FALSE;
267
268 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
269 {
270 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i];
271 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING)
272 continue;
273
274 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
275 if (RT_SUCCESS(rc))
276 {
277 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
278 u32AcquiredCaps |= pCap->fCap;
279 }
280 else
281 {
282 Assert(rc == VERR_RESOURCE_BUSY);
283 fNeedNewTimer = TRUE;
284 }
285 }
286
287 if (!fNeedNewTimer)
288 {
289 KillTimer(g_hwndToolWindow, pConsole->idTimer);
290 /* cleanup timer data */
291 pConsole->idTimer = 0;
292 }
293
294 return TRUE;
295}
296
297int VBoxCapsEntryRelease(uint32_t iCap)
298{
299 VBOXCAPS *pConsole = &gVBoxCaps;
300 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
301 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED)
302 {
303 LogFlowFunc(("invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState));
304 return VERR_INVALID_STATE;
305 }
306
307 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
308 {
309 int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false);
310 AssertRC(rc);
311 }
312
313 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED);
314
315 return VINF_SUCCESS;
316}
317
318int VBoxCapsEntryAcquire(uint32_t iCap)
319{
320 VBOXCAPS *pConsole = &gVBoxCaps;
321 Assert(VBoxConsoleIsAllowed());
322 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
323 LogFunc(("%d\n", iCap));
324 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED)
325 {
326 LogFlowFunc(("invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState));
327 return VERR_INVALID_STATE;
328 }
329
330 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING);
331 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
332 if (RT_SUCCESS(rc))
333 {
334 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
335 return VINF_SUCCESS;
336 }
337
338 if (rc != VERR_RESOURCE_BUSY)
339 {
340 LogFlowFunc(("VBoxAcquireGuestCaps failed rc %d\n", rc));
341 return rc;
342 }
343
344 LogFlowFunc(("iCap %d is busy!\n", iCap));
345
346 /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session,
347 * queue the retry timer */
348 if (!pConsole->idTimer)
349 {
350 pConsole->idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL);
351 if (!pConsole->idTimer)
352 {
353 DWORD dwErr = GetLastError();
354 LogFlowFunc(("SetTimer error %08X\n", dwErr));
355 return RTErrConvertFromWin32(dwErr);
356 }
357 }
358
359 return rc;
360}
361
362int VBoxCapsAcquireAllSupported()
363{
364 VBOXCAPS *pConsole = &gVBoxCaps;
365 LogFlowFuncEnter();
366 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
367 {
368 if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED)
369 {
370 LogFunc(("acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState));
371 VBoxCapsEntryAcquire(i);
372 }
373 else
374 {
375 LogFlowFunc(("WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState));
376 }
377 }
378 return VINF_SUCCESS;
379}
380
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