VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: VBoxCaps.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxCaps.cpp - Capability APIs.
4 */
5
6/*
7 * Copyright (C) 2013-2023 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
45
46struct VBOXCAPS_ENTRY;
47struct VBOXCAPS;
48
49typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE,(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled));
50
51typedef struct VBOXCAPS_ENTRY
52{
53 uint32_t fCap;
54 uint32_t iCap;
55 VBOXCAPS_ENTRY_FUNCSTATE enmFuncState;
56 VBOXCAPS_ENTRY_ACSTATE enmAcState;
57 PFNVBOXCAPS_ENTRY_ON_ENABLE pfnOnEnable;
58} VBOXCAPS_ENTRY;
59
60
61typedef struct VBOXCAPS
62{
63 UINT_PTR idTimer;
64 VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
65} VBOXCAPS;
66
67static VBOXCAPS gVBoxCaps;
68
69
70/* we need to perform Acquire/Release using the file handled we use for rewuesting events from VBoxGuest
71 * otherwise Acquisition mechanism will treat us as different client and will not propagate necessary requests
72 * */
73int VBoxAcquireGuestCaps(uint32_t fOr, uint32_t fNot, bool fCfg)
74{
75 Log(("VBoxAcquireGuestCaps or(0x%x), not(0x%x), cfx(%d)\n", fOr, fNot, fCfg));
76 int rc = VbglR3AcquireGuestCaps(fOr, fNot, fCfg);
77 if (RT_FAILURE(rc))
78 LogFlowFunc(("VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE failed: %Rrc\n", rc));
79 return rc;
80}
81
82static DECLCALLBACK(void) vboxCapsOnEnableSeamless(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled)
83{
84 RT_NOREF(pConsole, pCap);
85 if (fEnabled)
86 {
87 Log(("vboxCapsOnEnableSeamless: ENABLED\n"));
88 Assert(pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
89 Assert(pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
90 VBoxSeamlessEnable();
91 }
92 else
93 {
94 Log(("vboxCapsOnEnableSeamless: DISABLED\n"));
95 Assert(pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRED || pCap->enmFuncState != VBOXCAPS_ENTRY_FUNCSTATE_STARTED);
96 VBoxSeamlessDisable();
97 }
98}
99
100static void vboxCapsEntryAcStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_ACSTATE enmAcState)
101{
102 VBOXCAPS *pConsole = &gVBoxCaps;
103
104 Log(("vboxCapsEntryAcStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
105 enmAcState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
106
107 if (pCap->enmAcState == enmAcState)
108 return;
109
110 VBOXCAPS_ENTRY_ACSTATE enmOldAcState = pCap->enmAcState;
111 pCap->enmAcState = enmAcState;
112
113 if (enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
114 {
115 if (pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
116 {
117 if (pCap->pfnOnEnable)
118 pCap->pfnOnEnable(pConsole, pCap, TRUE);
119 }
120 }
121 else if (enmOldAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && pCap->enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
122 {
123 if (pCap->pfnOnEnable)
124 pCap->pfnOnEnable(pConsole, pCap, FALSE);
125 }
126}
127
128static void vboxCapsEntryFuncStateSet(VBOXCAPS_ENTRY *pCap, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
129{
130 VBOXCAPS *pConsole = &gVBoxCaps;
131
132 Log(("vboxCapsEntryFuncStateSet: new state enmAcState(%d); pCap: fCap(%d), iCap(%d), enmFuncState(%d), enmAcState(%d)\n",
133 enmFuncState, pCap->fCap, pCap->iCap, pCap->enmFuncState, pCap->enmAcState));
134
135 if (pCap->enmFuncState == enmFuncState)
136 return;
137
138 VBOXCAPS_ENTRY_FUNCSTATE enmOldFuncState = pCap->enmFuncState;
139
140 pCap->enmFuncState = enmFuncState;
141
142 if (enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
143 {
144 Assert(enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED);
145 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
146 {
147 if (pCap->pfnOnEnable)
148 pCap->pfnOnEnable(pConsole, pCap, TRUE);
149 }
150 }
151 else if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED && enmOldFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED)
152 {
153 if (pCap->pfnOnEnable)
154 pCap->pfnOnEnable(pConsole, pCap, FALSE);
155 }
156}
157
158void VBoxCapsEntryFuncStateSet(uint32_t iCup, VBOXCAPS_ENTRY_FUNCSTATE enmFuncState)
159{
160 VBOXCAPS *pConsole = &gVBoxCaps;
161 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCup];
162 vboxCapsEntryFuncStateSet(pCap, enmFuncState);
163}
164
165int VBoxCapsInit()
166{
167 VBOXCAPS *pConsole = &gVBoxCaps;
168 memset(pConsole, 0, sizeof (*pConsole));
169 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].fCap = VMMDEV_GUEST_SUPPORTS_SEAMLESS;
170 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].iCap = VBOXCAPS_ENTRY_IDX_SEAMLESS;
171 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_SEAMLESS].pfnOnEnable = vboxCapsOnEnableSeamless;
172 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].fCap = VMMDEV_GUEST_SUPPORTS_GRAPHICS;
173 pConsole->aCaps[VBOXCAPS_ENTRY_IDX_GRAPHICS].iCap = VBOXCAPS_ENTRY_IDX_GRAPHICS;
174 return VINF_SUCCESS;
175}
176
177int VBoxCapsReleaseAll()
178{
179 VBOXCAPS *pConsole = &gVBoxCaps;
180 Log(("VBoxCapsReleaseAll\n"));
181 int rc = VBoxAcquireGuestCaps(0, VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GRAPHICS, false);
182 if (!RT_SUCCESS(rc))
183 {
184 LogFlowFunc(("vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
185 return rc;
186 }
187
188 if (pConsole->idTimer)
189 {
190 Log(("killing console timer\n"));
191 KillTimer(g_hwndToolWindow, pConsole->idTimer);
192 pConsole->idTimer = 0;
193 }
194
195 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
196 {
197 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_RELEASED);
198 }
199
200 return rc;
201}
202
203void VBoxCapsTerm()
204{
205 VBOXCAPS *pConsole = &gVBoxCaps;
206 VBoxCapsReleaseAll();
207 memset(pConsole, 0, sizeof (*pConsole));
208}
209
210BOOL VBoxCapsEntryIsAcquired(uint32_t iCap)
211{
212 VBOXCAPS *pConsole = &gVBoxCaps;
213 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED;
214}
215
216BOOL VBoxCapsEntryIsEnabled(uint32_t iCap)
217{
218 VBOXCAPS *pConsole = &gVBoxCaps;
219 return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED
220 && pConsole->aCaps[iCap].enmFuncState == VBOXCAPS_ENTRY_FUNCSTATE_STARTED;
221}
222
223BOOL VBoxCapsCheckTimer(WPARAM wParam)
224{
225 VBOXCAPS *pConsole = &gVBoxCaps;
226 if (wParam != pConsole->idTimer)
227 return FALSE;
228
229 uint32_t u32AcquiredCaps = 0;
230 BOOL fNeedNewTimer = FALSE;
231
232 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
233 {
234 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[i];
235 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_ACQUIRING)
236 continue;
237
238 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
239 if (RT_SUCCESS(rc))
240 {
241 vboxCapsEntryAcStateSet(&pConsole->aCaps[i], VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
242 u32AcquiredCaps |= pCap->fCap;
243 }
244 else
245 {
246 Assert(rc == VERR_RESOURCE_BUSY);
247 fNeedNewTimer = TRUE;
248 }
249 }
250
251 if (!fNeedNewTimer)
252 {
253 KillTimer(g_hwndToolWindow, pConsole->idTimer);
254 /* cleanup timer data */
255 pConsole->idTimer = 0;
256 }
257
258 return TRUE;
259}
260
261int VBoxCapsEntryRelease(uint32_t iCap)
262{
263 VBOXCAPS *pConsole = &gVBoxCaps;
264 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
265 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_RELEASED)
266 {
267 LogFlowFunc(("invalid cap[%d] state[%d] on release\n", iCap, pCap->enmAcState));
268 return VERR_INVALID_STATE;
269 }
270
271 if (pCap->enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED)
272 {
273 int rc = VBoxAcquireGuestCaps(0, pCap->fCap, false);
274 AssertRC(rc);
275 }
276
277 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_RELEASED);
278
279 return VINF_SUCCESS;
280}
281
282int VBoxCapsEntryAcquire(uint32_t iCap)
283{
284 VBOXCAPS *pConsole = &gVBoxCaps;
285 Assert(VBoxConsoleIsAllowed());
286 VBOXCAPS_ENTRY *pCap = &pConsole->aCaps[iCap];
287 Log(("VBoxCapsEntryAcquire %d\n", iCap));
288 if (pCap->enmAcState != VBOXCAPS_ENTRY_ACSTATE_RELEASED)
289 {
290 LogFlowFunc(("invalid cap[%d] state[%d] on acquire\n", iCap, pCap->enmAcState));
291 return VERR_INVALID_STATE;
292 }
293
294 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRING);
295 int rc = VBoxAcquireGuestCaps(pCap->fCap, 0, false);
296 if (RT_SUCCESS(rc))
297 {
298 vboxCapsEntryAcStateSet(pCap, VBOXCAPS_ENTRY_ACSTATE_ACQUIRED);
299 return VINF_SUCCESS;
300 }
301
302 if (rc != VERR_RESOURCE_BUSY)
303 {
304 LogFlowFunc(("vboxCapsEntryReleaseAll VBoxAcquireGuestCaps failed rc %d\n", rc));
305 return rc;
306 }
307
308 LogFlowFunc(("iCap %d is busy!\n", iCap));
309
310 /* the cap was busy, most likely it is still used by other VBoxTray instance running in another session,
311 * queue the retry timer */
312 if (!pConsole->idTimer)
313 {
314 pConsole->idTimer = SetTimer(g_hwndToolWindow, TIMERID_VBOXTRAY_CAPS_TIMER, 100, (TIMERPROC)NULL);
315 if (!pConsole->idTimer)
316 {
317 DWORD dwErr = GetLastError();
318 LogFlowFunc(("SetTimer error %08X\n", dwErr));
319 return RTErrConvertFromWin32(dwErr);
320 }
321 }
322
323 return rc;
324}
325
326int VBoxCapsAcquireAllSupported()
327{
328 VBOXCAPS *pConsole = &gVBoxCaps;
329 Log(("VBoxCapsAcquireAllSupported\n"));
330 for (int i = 0; i < RT_ELEMENTS(pConsole->aCaps); ++i)
331 {
332 if (pConsole->aCaps[i].enmFuncState >= VBOXCAPS_ENTRY_FUNCSTATE_SUPPORTED)
333 {
334 Log(("VBoxCapsAcquireAllSupported acquiring cap %d, state %d\n", i, pConsole->aCaps[i].enmFuncState));
335 VBoxCapsEntryAcquire(i);
336 }
337 else
338 {
339 LogFlowFunc(("VBoxCapsAcquireAllSupported: WARN: cap %d not supported, state %d\n", i, pConsole->aCaps[i].enmFuncState));
340 }
341 }
342 return VINF_SUCCESS;
343}
344
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use