[95966] | 1 | /* $Id: VBoxCaps.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBoxCaps.cpp - Capability APIs.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2013-2023 Oracle and/or its affiliates.
|
---|
[95966] | 8 | *
|
---|
[96407] | 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
|
---|
[95966] | 26 | */
|
---|
| 27 |
|
---|
| 28 | #include <iprt/log.h>
|
---|
| 29 |
|
---|
| 30 | #include "VBoxTray.h"
|
---|
| 31 | #include "VBoxTrayInternal.h"
|
---|
| 32 | #include "VBoxSeamless.h"
|
---|
| 33 |
|
---|
| 34 |
|
---|
| 35 | typedef 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 |
|
---|
| 46 | struct VBOXCAPS_ENTRY;
|
---|
| 47 | struct VBOXCAPS;
|
---|
| 48 |
|
---|
| 49 | typedef DECLCALLBACKPTR(void, PFNVBOXCAPS_ENTRY_ON_ENABLE,(struct VBOXCAPS *pConsole, struct VBOXCAPS_ENTRY *pCap, BOOL fEnabled));
|
---|
| 50 |
|
---|
| 51 | typedef 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 |
|
---|
| 61 | typedef struct VBOXCAPS
|
---|
| 62 | {
|
---|
| 63 | UINT_PTR idTimer;
|
---|
| 64 | VBOXCAPS_ENTRY aCaps[VBOXCAPS_ENTRY_IDX_COUNT];
|
---|
| 65 | } VBOXCAPS;
|
---|
| 66 |
|
---|
| 67 | static 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 | * */
|
---|
| 73 | int 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 |
|
---|
| 82 | static 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 |
|
---|
| 100 | static 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 |
|
---|
| 128 | static 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 |
|
---|
| 158 | void 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 |
|
---|
| 165 | int 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 |
|
---|
| 177 | int 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 |
|
---|
| 203 | void VBoxCapsTerm()
|
---|
| 204 | {
|
---|
| 205 | VBOXCAPS *pConsole = &gVBoxCaps;
|
---|
| 206 | VBoxCapsReleaseAll();
|
---|
| 207 | memset(pConsole, 0, sizeof (*pConsole));
|
---|
| 208 | }
|
---|
| 209 |
|
---|
| 210 | BOOL VBoxCapsEntryIsAcquired(uint32_t iCap)
|
---|
| 211 | {
|
---|
| 212 | VBOXCAPS *pConsole = &gVBoxCaps;
|
---|
| 213 | return pConsole->aCaps[iCap].enmAcState == VBOXCAPS_ENTRY_ACSTATE_ACQUIRED;
|
---|
| 214 | }
|
---|
| 215 |
|
---|
| 216 | BOOL 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 |
|
---|
| 223 | BOOL 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 |
|
---|
| 261 | int 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 |
|
---|
| 282 | int 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 |
|
---|
| 326 | int 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 |
|
---|