VirtualBox

source: vbox/trunk/src/VBox/GuestHost/OpenGL/util/compositor.cpp@ 69392

Last change on this file since 69392 was 69392, checked in by vboxsync, 7 years ago

GuestHost/OpenGL: scm updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: compositor.cpp 69392 2017-10-26 17:24:52Z vboxsync $ */
2/** @file
3 * Compositor implementation.
4 */
5
6/*
7 * Copyright (C) 2013-2016 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 "../include/cr_compositor.h"
23
24
25/*********************************************************************************************************************************
26* Defined Constants And Macros *
27*********************************************************************************************************************************/
28#define VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED UINT32_MAX
29#ifdef IN_VMSVGA3D
30# define WARN AssertMsgFailed
31#endif
32
33
34
35static int crVrScrCompositorRectsAssignBuffer(PVBOXVR_SCR_COMPOSITOR pCompositor, uint32_t cRects)
36{
37 Assert(cRects);
38
39 if (pCompositor->cRectsBuffer >= cRects)
40 {
41 pCompositor->cRects = cRects;
42 return VINF_SUCCESS;
43 }
44
45 if (pCompositor->cRectsBuffer)
46 {
47 Assert(pCompositor->paSrcRects);
48 RTMemFree(pCompositor->paSrcRects);
49 pCompositor->paSrcRects = NULL;
50 Assert(pCompositor->paDstRects);
51 RTMemFree(pCompositor->paDstRects);
52 pCompositor->paDstRects = NULL;
53 Assert(pCompositor->paDstUnstretchedRects);
54 RTMemFree(pCompositor->paDstUnstretchedRects);
55 pCompositor->paDstUnstretchedRects = NULL;
56 }
57 else
58 {
59 Assert(!pCompositor->paSrcRects);
60 Assert(!pCompositor->paDstRects);
61 Assert(!pCompositor->paDstUnstretchedRects);
62 }
63
64 pCompositor->paSrcRects = (PRTRECT)RTMemAlloc(sizeof(*pCompositor->paSrcRects) * cRects);
65 if (pCompositor->paSrcRects)
66 {
67 pCompositor->paDstRects = (PRTRECT)RTMemAlloc(sizeof(*pCompositor->paDstRects) * cRects);
68 if (pCompositor->paDstRects)
69 {
70 pCompositor->paDstUnstretchedRects = (PRTRECT)RTMemAlloc(sizeof(*pCompositor->paDstUnstretchedRects) * cRects);
71 if (pCompositor->paDstUnstretchedRects)
72 {
73 pCompositor->cRects = cRects;
74 pCompositor->cRectsBuffer = cRects;
75 return VINF_SUCCESS;
76 }
77
78 RTMemFree(pCompositor->paDstRects);
79 pCompositor->paDstRects = NULL;
80 }
81 else
82 {
83 WARN(("RTMemAlloc failed!"));
84 }
85 RTMemFree(pCompositor->paSrcRects);
86 pCompositor->paSrcRects = NULL;
87 }
88 else
89 {
90 WARN(("RTMemAlloc failed!"));
91 }
92
93 pCompositor->cRects = VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED;
94 pCompositor->cRectsBuffer = 0;
95
96 return VERR_NO_MEMORY;
97}
98
99static void crVrScrCompositorRectsInvalidate(PVBOXVR_SCR_COMPOSITOR pCompositor)
100{
101 pCompositor->cRects = VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED;
102}
103
104static DECLCALLBACK(bool) crVrScrCompositorRectsCounterCb(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry,
105 void *pvVisitor)
106{
107 uint32_t* pCounter = (uint32_t*)pvVisitor;
108 (void)pCompositor; (void)pEntry;
109
110 Assert(VBoxVrListRectsCount(&pEntry->Vr));
111 *pCounter += VBoxVrListRectsCount(&pEntry->Vr);
112 return true;
113}
114
115typedef struct VBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER
116{
117 PRTRECT paSrcRects;
118 PRTRECT paDstRects;
119 PRTRECT paDstUnstretchedRects;
120 uint32_t cRects;
121} VBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER, *PVBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER;
122
123static DECLCALLBACK(bool) crVrScrCompositorRectsAssignerCb(PVBOXVR_COMPOSITOR pCCompositor, PVBOXVR_COMPOSITOR_ENTRY pCEntry,
124 void *pvVisitor)
125{
126 PVBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER pData = (PVBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER)pvVisitor;
127 PVBOXVR_SCR_COMPOSITOR pCompositor = VBOXVR_SCR_COMPOSITOR_FROM_COMPOSITOR(pCCompositor);
128 PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pCEntry);
129 pEntry->paSrcRects = pData->paSrcRects;
130 pEntry->paDstRects = pData->paDstRects;
131 pEntry->paDstUnstretchedRects = pData->paDstUnstretchedRects;
132 uint32_t cRects = VBoxVrListRectsCount(&pCEntry->Vr);
133 Assert(cRects);
134 Assert(cRects <= pData->cRects);
135 int rc = VBoxVrListRectsGet(&pCEntry->Vr, cRects, pEntry->paDstUnstretchedRects);
136 AssertRC(rc);
137
138 if (!pEntry->Rect.xLeft && !pEntry->Rect.yTop)
139 {
140 memcpy(pEntry->paSrcRects, pEntry->paDstUnstretchedRects, cRects * sizeof(*pEntry->paSrcRects));
141 }
142 else
143 {
144 for (uint32_t i = 0; i < cRects; ++i)
145 {
146 pEntry->paSrcRects[i].xLeft = (int32_t)((pEntry->paDstUnstretchedRects[i].xLeft - pEntry->Rect.xLeft));
147 pEntry->paSrcRects[i].yTop = (int32_t)((pEntry->paDstUnstretchedRects[i].yTop - pEntry->Rect.yTop));
148 pEntry->paSrcRects[i].xRight = (int32_t)((pEntry->paDstUnstretchedRects[i].xRight - pEntry->Rect.xLeft));
149 pEntry->paSrcRects[i].yBottom = (int32_t)((pEntry->paDstUnstretchedRects[i].yBottom - pEntry->Rect.yTop));
150 }
151 }
152
153#ifndef IN_RING0
154 if (pCompositor->StretchX != 1. || pCompositor->StretchY != 1.)
155 {
156 for (uint32_t i = 0; i < cRects; ++i)
157 {
158 if (pCompositor->StretchX != 1.)
159 {
160 pEntry->paDstRects[i].xLeft = (int32_t)(pEntry->paDstUnstretchedRects[i].xLeft * pCompositor->StretchX);
161 pEntry->paDstRects[i].xRight = (int32_t)(pEntry->paDstUnstretchedRects[i].xRight * pCompositor->StretchX);
162 }
163 if (pCompositor->StretchY != 1.)
164 {
165 pEntry->paDstRects[i].yTop = (int32_t)(pEntry->paDstUnstretchedRects[i].yTop * pCompositor->StretchY);
166 pEntry->paDstRects[i].yBottom = (int32_t)(pEntry->paDstUnstretchedRects[i].yBottom * pCompositor->StretchY);
167 }
168 }
169 }
170 else
171#endif
172 {
173 memcpy(pEntry->paDstRects, pEntry->paDstUnstretchedRects, cRects * sizeof(*pEntry->paDstUnstretchedRects));
174 }
175
176#if 0//ndef IN_RING0
177 bool canZeroX = (pCompositor->StretchX < 1.);
178 bool canZeroY = (pCompositor->StretchY < 1.);
179 if (canZeroX && canZeroY)
180 {
181 /* filter out zero rectangles*/
182 uint32_t iOrig, iNew;
183 for (iOrig = 0, iNew = 0; iOrig < cRects; ++iOrig)
184 {
185 PRTRECT pOrigRect = &pEntry->paDstRects[iOrig];
186 if (pOrigRect->xLeft != pOrigRect->xRight
187 && pOrigRect->yTop != pOrigRect->yBottom)
188 continue;
189
190 if (iNew != iOrig)
191 {
192 PRTRECT pNewRect = &pEntry->paSrcRects[iNew];
193 *pNewRect = *pOrigRect;
194 }
195
196 ++iNew;
197 }
198
199 Assert(iNew <= iOrig);
200
201 uint32_t cDiff = iOrig - iNew;
202
203 if (cDiff)
204 {
205 pCompositor->cRects -= cDiff;
206 cRects -= cDiff;
207 }
208 }
209#endif
210
211 pEntry->cRects = cRects;
212 pData->paDstRects += cRects;
213 pData->paSrcRects += cRects;
214 pData->paDstUnstretchedRects += cRects;
215 pData->cRects -= cRects;
216 return true;
217}
218
219static int crVrScrCompositorRectsCheckInit(PCVBOXVR_SCR_COMPOSITOR pcCompositor)
220{
221 PVBOXVR_SCR_COMPOSITOR pCompositor = const_cast<PVBOXVR_SCR_COMPOSITOR>(pcCompositor);
222
223 if (pCompositor->cRects != VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED)
224 return VINF_SUCCESS;
225
226 uint32_t cRects = 0;
227 VBoxVrCompositorVisit(&pCompositor->Compositor, crVrScrCompositorRectsCounterCb, &cRects);
228
229 if (!cRects)
230 {
231 pCompositor->cRects = 0;
232 return VINF_SUCCESS;
233 }
234
235 int rc = crVrScrCompositorRectsAssignBuffer(pCompositor, cRects);
236 if (RT_FAILURE(rc))
237 return rc;
238
239 VBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER AssignerData;
240 AssignerData.paSrcRects = pCompositor->paSrcRects;
241 AssignerData.paDstRects = pCompositor->paDstRects;
242 AssignerData.paDstUnstretchedRects = pCompositor->paDstUnstretchedRects;
243 AssignerData.cRects = pCompositor->cRects;
244 VBoxVrCompositorVisit(&pCompositor->Compositor, crVrScrCompositorRectsAssignerCb, &AssignerData);
245 Assert(!AssignerData.cRects);
246 return VINF_SUCCESS;
247}
248
249
250static int crVrScrCompositorEntryRegionsAdd(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
251 uint32_t cRegions, PCRTRECT paRegions,
252 VBOXVR_SCR_COMPOSITOR_ENTRY **ppReplacedScrEntry, uint32_t *pfChangedFlags)
253{
254 uint32_t fChangedFlags = 0;
255 PVBOXVR_COMPOSITOR_ENTRY pReplacedEntry;
256 int rc = VBoxVrCompositorEntryRegionsAdd(&pCompositor->Compositor, pEntry ? &pEntry->Ce : NULL, cRegions,
257 paRegions, &pReplacedEntry, &fChangedFlags);
258 if (RT_FAILURE(rc))
259 {
260 WARN(("VBoxVrCompositorEntryRegionsAdd failed, rc %d", rc));
261 return rc;
262 }
263
264 VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pReplacedEntry);
265
266 if (fChangedFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED)
267 crVrScrCompositorRectsInvalidate(pCompositor);
268 else if (fChangedFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED)
269 Assert(pReplacedScrEntry);
270
271 if (fChangedFlags & VBOXVR_COMPOSITOR_CF_OTHER_ENTRIES_REGIONS_CHANGED)
272 CrVrScrCompositorEntrySetAllChanged(pCompositor, true);
273 else if ((fChangedFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED) && pEntry)
274 CrVrScrCompositorEntrySetChanged(pEntry, true);
275
276 if (pfChangedFlags)
277 *pfChangedFlags = fChangedFlags;
278
279 if (ppReplacedScrEntry)
280 *ppReplacedScrEntry = pReplacedScrEntry;
281
282 return VINF_SUCCESS;
283}
284
285static int crVrScrCompositorEntryRegionsSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
286 uint32_t cRegions, PCRTRECT paRegions, bool *pfChanged)
287{
288 bool fChanged;
289 int rc = VBoxVrCompositorEntryRegionsSet(&pCompositor->Compositor, &pEntry->Ce, cRegions, paRegions, &fChanged);
290 if (RT_FAILURE(rc))
291 {
292 WARN(("VBoxVrCompositorEntryRegionsSet failed, rc %d", rc));
293 return rc;
294 }
295
296 if (fChanged)
297 {
298 CrVrScrCompositorEntrySetAllChanged(pCompositor, true);
299 if (!CrVrScrCompositorEntryIsInList(pEntry))
300 {
301 pEntry->cRects = 0;
302 pEntry->paSrcRects = NULL;
303 pEntry->paDstRects = NULL;
304 pEntry->paDstUnstretchedRects = NULL;
305 }
306 crVrScrCompositorRectsInvalidate(pCompositor);
307 }
308
309
310 if (pfChanged)
311 *pfChanged = fChanged;
312 return VINF_SUCCESS;
313}
314
315static int crVrScrCompositorEntryPositionSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
316 PCRTPOINT pPos, bool *pfChanged)
317{
318 if (pfChanged)
319 *pfChanged = false;
320 if (pEntry && (pEntry->Rect.xLeft != pPos->x || pEntry->Rect.yTop != pPos->y))
321 {
322 if (VBoxVrCompositorEntryIsInList(&pEntry->Ce))
323 {
324 int rc = VBoxVrCompositorEntryRegionsTranslate(&pCompositor->Compositor, &pEntry->Ce, pPos->x - pEntry->Rect.xLeft,
325 pPos->y - pEntry->Rect.yTop, pfChanged);
326 if (RT_FAILURE(rc))
327 {
328 WARN(("VBoxVrCompositorEntryRegionsTranslate failed rc %d", rc));
329 return rc;
330 }
331
332 crVrScrCompositorRectsInvalidate(pCompositor);
333 }
334
335 VBoxRectMove(&pEntry->Rect, pPos->x, pPos->y);
336 CrVrScrCompositorEntrySetChanged(pEntry, true);
337
338 if (pfChanged)
339 *pfChanged = true;
340 }
341 return VINF_SUCCESS;
342}
343
344static int crVrScrCompositorEntryEnsureRegionsBounds(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
345 bool *pfChanged)
346{
347 RTRECT Rect;
348 Rect.xLeft = RT_MAX(pCompositor->Rect.xLeft, pEntry->Rect.xLeft);
349 Rect.yTop = RT_MAX(pCompositor->Rect.yTop, pEntry->Rect.yTop);
350 Rect.xRight = RT_MIN(pCompositor->Rect.xRight, pEntry->Rect.xRight);
351 Rect.yBottom = RT_MIN(pCompositor->Rect.yBottom, pEntry->Rect.yBottom);
352 bool fChanged = false;
353
354 if (pfChanged)
355 *pfChanged = false;
356
357 int rc = CrVrScrCompositorEntryRegionsIntersect(pCompositor, pEntry, 1, &Rect, &fChanged);
358 if (RT_FAILURE(rc))
359 WARN(("CrVrScrCompositorEntryRegionsIntersect failed, rc %d", rc));
360
361 if (pfChanged)
362 *pfChanged = fChanged;
363 return rc;
364}
365
366VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsAdd(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
367 PCRTPOINT pPos, uint32_t cRegions, PCRTRECT paRegions,
368 bool fPosRelated, VBOXVR_SCR_COMPOSITOR_ENTRY **ppReplacedScrEntry,
369 uint32_t *pfChangeFlags)
370{
371 int rc;
372 uint32_t fChangeFlags = 0;
373 bool fPosChanged = false;
374 RTRECT *paTranslatedRects = NULL;
375 if (pPos)
376 {
377 rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, pPos, &fPosChanged);
378 if (RT_FAILURE(rc))
379 {
380 WARN(("RegionsAdd: crVrScrCompositorEntryPositionSet failed rc %d", rc));
381 return rc;
382 }
383 }
384
385 if (fPosRelated)
386 {
387 if (!pEntry)
388 {
389 WARN(("Entry is expected to be specified for pos-related regions"));
390 return VERR_INVALID_PARAMETER;
391 }
392
393 if (cRegions && (pEntry->Rect.xLeft || pEntry->Rect.yTop))
394 {
395 paTranslatedRects = (RTRECT*)RTMemAlloc(sizeof(RTRECT) * cRegions);
396 if (!paTranslatedRects)
397 {
398 WARN(("RTMemAlloc failed"));
399 return VERR_NO_MEMORY;
400 }
401 memcpy (paTranslatedRects, paRegions, sizeof(RTRECT) * cRegions);
402 for (uint32_t i = 0; i < cRegions; ++i)
403 {
404 VBoxRectTranslate(&paTranslatedRects[i], pEntry->Rect.xLeft, pEntry->Rect.yTop);
405 paRegions = paTranslatedRects;
406 }
407 }
408 }
409
410 rc = crVrScrCompositorEntryRegionsAdd(pCompositor, pEntry, cRegions, paRegions, ppReplacedScrEntry, &fChangeFlags);
411 if (RT_FAILURE(rc))
412 {
413 WARN(("crVrScrCompositorEntryRegionsAdd failed, rc %d", rc));
414 goto done;
415 }
416
417 if ((fPosChanged || (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED)) && pEntry)
418 {
419 bool fAdjusted = false;
420 rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, &fAdjusted);
421 if (RT_FAILURE(rc))
422 {
423 WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc));
424 goto done;
425 }
426
427 if (fAdjusted)
428 {
429 if (CrVrScrCompositorEntryIsUsed(pEntry))
430 {
431 fChangeFlags &= ~VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED;
432 fChangeFlags |= VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED;
433 }
434 else
435 {
436 fChangeFlags = 0;
437 }
438 }
439 }
440
441 if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED)
442 fPosChanged = false;
443 else if (ppReplacedScrEntry)
444 *ppReplacedScrEntry = NULL;
445
446 if (pfChangeFlags)
447 {
448 if (fPosChanged)
449 {
450 /* means entry was in list and was moved, so regions changed */
451 *pfChangeFlags = VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED
452 | VBOXVR_COMPOSITOR_CF_OTHER_ENTRIES_REGIONS_CHANGED;
453 }
454 else
455 *pfChangeFlags = fChangeFlags;
456 }
457
458done:
459
460 if (paTranslatedRects)
461 RTMemFree(paTranslatedRects);
462
463 return rc;
464}
465
466VBOXVREGDECL(int) CrVrScrCompositorEntryRectSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
467 PCRTRECT pRect)
468{
469 if (!memcmp(&pEntry->Rect, pRect, sizeof(*pRect)))
470 {
471 return VINF_SUCCESS;
472 }
473 RTPOINT Point = {pRect->xLeft, pRect->yTop};
474 bool fChanged = false;
475 int rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, &Point, &fChanged);
476 if (RT_FAILURE(rc))
477 {
478 WARN(("crVrScrCompositorEntryPositionSet failed %d", rc));
479 return rc;
480 }
481
482 pEntry->Rect = *pRect;
483
484 if (!CrVrScrCompositorEntryIsUsed(pEntry))
485 return VINF_SUCCESS;
486
487 rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL);
488 if (RT_FAILURE(rc))
489 {
490 WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc));
491 return rc;
492 }
493
494 return VINF_SUCCESS;
495}
496
497VBOXVREGDECL(int) CrVrScrCompositorEntryTexAssign(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
498 CR_TEXDATA *pTex)
499{
500 (void)pCompositor;
501
502 if (pEntry->pTex == pTex)
503 return VINF_SUCCESS;
504
505 if (pEntry->pTex)
506 CrTdRelease(pEntry->pTex);
507 if (pTex)
508 CrTdAddRef(pTex);
509 pEntry->pTex = pTex;
510 return VINF_SUCCESS;
511}
512
513VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
514 PCRTPOINT pPos, uint32_t cRegions, PCRTRECT paRegions,
515 bool fPosRelated, bool *pfChanged)
516{
517 /** @todo the fChanged sate calculation is really rough now, this is enough for now though */
518 bool fChanged = false, fPosChanged = false;
519 bool fWasInList = CrVrScrCompositorEntryIsInList(pEntry);
520 RTRECT *paTranslatedRects = NULL;
521 int rc = CrVrScrCompositorEntryRemove(pCompositor, pEntry);
522 if (RT_FAILURE(rc))
523 {
524 WARN(("RegionsSet: CrVrScrCompositorEntryRemove failed rc %d", rc));
525 return rc;
526 }
527
528 if (pPos)
529 {
530 rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, pPos, &fPosChanged);
531 if (RT_FAILURE(rc))
532 {
533 WARN(("RegionsSet: crVrScrCompositorEntryPositionSet failed rc %d", rc));
534 return rc;
535 }
536 }
537
538 if (fPosRelated)
539 {
540 if (!pEntry)
541 {
542 WARN(("Entry is expected to be specified for pos-related regions"));
543 return VERR_INVALID_PARAMETER;
544 }
545
546 if (cRegions && (pEntry->Rect.xLeft || pEntry->Rect.yTop))
547 {
548 paTranslatedRects = (RTRECT*)RTMemAlloc(sizeof(RTRECT) * cRegions);
549 if (!paTranslatedRects)
550 {
551 WARN(("RTMemAlloc failed"));
552 return VERR_NO_MEMORY;
553 }
554 memcpy (paTranslatedRects, paRegions, sizeof(RTRECT) * cRegions);
555 for (uint32_t i = 0; i < cRegions; ++i)
556 {
557 VBoxRectTranslate(&paTranslatedRects[i], pEntry->Rect.xLeft, pEntry->Rect.yTop);
558 paRegions = paTranslatedRects;
559 }
560 }
561 }
562
563 rc = crVrScrCompositorEntryRegionsSet(pCompositor, pEntry, cRegions, paRegions, &fChanged);
564 if (RT_SUCCESS(rc))
565 {
566 if (fChanged && CrVrScrCompositorEntryIsUsed(pEntry))
567 {
568 rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL);
569 if (RT_SUCCESS(rc))
570 {
571 if (pfChanged)
572 *pfChanged = fPosChanged || fChanged || fWasInList;
573 }
574 else
575 WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc));
576 }
577
578 }
579 else
580 WARN(("crVrScrCompositorEntryRegionsSet failed, rc %d", rc));
581
582 if (paTranslatedRects)
583 RTMemFree(paTranslatedRects);
584
585 return rc;
586}
587
588VBOXVREGDECL(int) CrVrScrCompositorEntryListIntersect(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
589 PCVBOXVR_LIST pList2, bool *pfChanged)
590{
591 bool fChanged = false;
592 int rc = VBoxVrCompositorEntryListIntersect(&pCompositor->Compositor, &pEntry->Ce, pList2, &fChanged);
593 if (RT_FAILURE(rc))
594 {
595 WARN(("RegionsIntersect: VBoxVrCompositorEntryRegionsIntersect failed rc %d", rc));
596 return rc;
597 }
598
599 if (fChanged)
600 {
601 CrVrScrCompositorEntrySetChanged(pEntry, true);
602 crVrScrCompositorRectsInvalidate(pCompositor);
603 }
604
605 if (pfChanged)
606 *pfChanged = fChanged;
607
608 return VINF_SUCCESS;
609}
610
611VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsIntersect(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
612 uint32_t cRegions, PCRTRECT paRegions, bool *pfChanged)
613{
614 bool fChanged = false;
615 int rc = VBoxVrCompositorEntryRegionsIntersect(&pCompositor->Compositor, &pEntry->Ce, cRegions, paRegions, &fChanged);
616 if (RT_FAILURE(rc))
617 {
618 WARN(("RegionsIntersect: VBoxVrCompositorEntryRegionsIntersect failed rc %d", rc));
619 return rc;
620 }
621
622 if (fChanged)
623 crVrScrCompositorRectsInvalidate(pCompositor);
624
625 if (pfChanged)
626 *pfChanged = fChanged;
627
628 return VINF_SUCCESS;
629}
630
631VBOXVREGDECL(int) CrVrScrCompositorEntryListIntersectAll(PVBOXVR_SCR_COMPOSITOR pCompositor, PCVBOXVR_LIST pList2, bool *pfChanged)
632{
633 VBOXVR_SCR_COMPOSITOR_ITERATOR Iter;
634 CrVrScrCompositorIterInit(pCompositor, &Iter);
635 PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry;
636 int rc = VINF_SUCCESS;
637 bool fChanged = false;
638
639 while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL)
640 {
641 bool fTmpChanged = false;
642 int tmpRc = CrVrScrCompositorEntryListIntersect(pCompositor, pEntry, pList2, &fTmpChanged);
643 if (RT_SUCCESS(tmpRc))
644 {
645 fChanged |= fTmpChanged;
646 }
647 else
648 {
649 WARN(("CrVrScrCompositorEntryRegionsIntersect failed, rc %d", tmpRc));
650 rc = tmpRc;
651 }
652 }
653
654 if (pfChanged)
655 *pfChanged = fChanged;
656
657 return rc;
658}
659
660VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsIntersectAll(PVBOXVR_SCR_COMPOSITOR pCompositor, uint32_t cRegions,
661 PCRTRECT paRegions, bool *pfChanged)
662{
663 VBOXVR_SCR_COMPOSITOR_ITERATOR Iter;
664 CrVrScrCompositorIterInit(pCompositor, &Iter);
665 PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry;
666 int rc = VINF_SUCCESS;
667 bool fChanged = false;
668
669 while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL)
670 {
671 bool fTmpChanged = false;
672 int tmpRc = CrVrScrCompositorEntryRegionsIntersect(pCompositor, pEntry, cRegions, paRegions, &fTmpChanged);
673 if (RT_SUCCESS(tmpRc))
674 {
675 fChanged |= fTmpChanged;
676 }
677 else
678 {
679 WARN(("CrVrScrCompositorEntryRegionsIntersect failed, rc %d", tmpRc));
680 rc = tmpRc;
681 }
682 }
683
684 if (pfChanged)
685 *pfChanged = fChanged;
686
687 return rc;
688}
689
690VBOXVREGDECL(int) CrVrScrCompositorEntryPosSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
691 PCRTPOINT pPos)
692{
693 int rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, pPos, NULL);
694 if (RT_FAILURE(rc))
695 {
696 WARN(("RegionsSet: crVrScrCompositorEntryPositionSet failed rc %d", rc));
697 return rc;
698 }
699
700 rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL);
701 if (RT_FAILURE(rc))
702 {
703 WARN(("RegionsSet: crVrScrCompositorEntryEnsureRegionsBounds failed rc %d", rc));
704 return rc;
705 }
706
707 return VINF_SUCCESS;
708}
709
710/* regions are valid until the next CrVrScrCompositor call */
711VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsGet(PCVBOXVR_SCR_COMPOSITOR pCompositor,
712 PCVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, uint32_t *pcRegions,
713 PCRTRECT *ppaSrcRegions, PCRTRECT *ppaDstRegions,
714 PCRTRECT *ppaDstUnstretchedRects)
715{
716 if (CrVrScrCompositorEntryIsUsed(pEntry))
717 {
718 int rc = crVrScrCompositorRectsCheckInit(pCompositor);
719 if (RT_FAILURE(rc))
720 {
721 WARN(("crVrScrCompositorRectsCheckInit failed, rc %d", rc));
722 return rc;
723 }
724 }
725
726 Assert(pCompositor->cRects != VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED);
727
728 *pcRegions = pEntry->cRects;
729 if (ppaSrcRegions)
730 *ppaSrcRegions = pEntry->paSrcRects;
731 if (ppaDstRegions)
732 *ppaDstRegions = pEntry->paDstRects;
733 if (ppaDstUnstretchedRects)
734 *ppaDstUnstretchedRects = pEntry->paDstUnstretchedRects;
735
736 return VINF_SUCCESS;
737}
738
739VBOXVREGDECL(uint32_t) CrVrScrCompositorEntryFlagsCombinedGet(PCVBOXVR_SCR_COMPOSITOR pCompositor,
740 PCVBOXVR_SCR_COMPOSITOR_ENTRY pEntry)
741{
742 return CRBLT_FOP_COMBINE(pCompositor->fFlags, pEntry->fFlags);
743}
744
745VBOXVREGDECL(void) CrVrScrCompositorEntryFlagsSet(PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, uint32_t fFlags)
746{
747 if (pEntry->fFlags == fFlags)
748 return;
749
750 pEntry->fFlags = fFlags;
751 CrVrScrCompositorEntrySetChanged(pEntry, true);
752}
753
754static void crVrScrCompositorEntryDataCleanup(PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry)
755{
756 pEntry->cRects = 0;
757 pEntry->paSrcRects = NULL;
758 pEntry->paDstRects = NULL;
759 pEntry->paDstUnstretchedRects = NULL;
760}
761
762static void crVrScrCompositorEntryDataCopy(PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, PVBOXVR_SCR_COMPOSITOR_ENTRY pToEntry)
763{
764 pToEntry->cRects = pEntry->cRects;
765 pToEntry->paSrcRects = pEntry->paSrcRects;
766 pToEntry->paDstRects = pEntry->paDstRects;
767 pToEntry->paDstUnstretchedRects = pEntry->paDstUnstretchedRects;
768 crVrScrCompositorEntryDataCleanup(pEntry);
769}
770
771VBOXVREGDECL(int) CrVrScrCompositorEntryRemove(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry)
772{
773 if (!VBoxVrCompositorEntryRemove(&pCompositor->Compositor, &pEntry->Ce))
774 return VINF_SUCCESS;
775
776 CrVrScrCompositorEntrySetChanged(pEntry, true);
777 crVrScrCompositorEntryDataCleanup(pEntry);
778
779 crVrScrCompositorRectsInvalidate(pCompositor);
780 return VINF_SUCCESS;
781}
782
783VBOXVREGDECL(bool) CrVrScrCompositorEntryReplace(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry,
784 PVBOXVR_SCR_COMPOSITOR_ENTRY pNewEntry)
785{
786 Assert(!CrVrScrCompositorEntryIsUsed(pNewEntry));
787
788 if (!VBoxVrCompositorEntryReplace(&pCompositor->Compositor, &pEntry->Ce, &pNewEntry->Ce))
789 return false;
790
791 CrVrScrCompositorEntrySetChanged(pEntry, true);
792 crVrScrCompositorEntryDataCopy(pEntry, pNewEntry);
793 CrVrScrCompositorEntrySetChanged(pNewEntry, true);
794
795 return true;
796}
797
798static DECLCALLBACK(void) crVrScrCompositorEntryReleasedCB(PCVBOXVR_COMPOSITOR pCompositor,
799 PVBOXVR_COMPOSITOR_ENTRY pEntry,
800 PVBOXVR_COMPOSITOR_ENTRY pReplacingEntry)
801{
802 PVBOXVR_SCR_COMPOSITOR_ENTRY pCEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pEntry);
803
804 CrVrScrCompositorEntrySetChanged(pCEntry, true);
805
806 Assert(!CrVrScrCompositorEntryIsInList(pCEntry));
807
808 if (pReplacingEntry)
809 {
810 PVBOXVR_SCR_COMPOSITOR_ENTRY pCReplacingEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pReplacingEntry);
811 Assert(CrVrScrCompositorEntryIsInList(pCReplacingEntry));
812 pCReplacingEntry->cRects = pCEntry->cRects;
813 pCReplacingEntry->paSrcRects = pCEntry->paSrcRects;
814 pCReplacingEntry->paDstRects = pCEntry->paDstRects;
815 pCReplacingEntry->paDstUnstretchedRects = pCEntry->paDstUnstretchedRects;
816 }
817
818 if (pCEntry->pfnEntryReleased)
819 {
820 PVBOXVR_SCR_COMPOSITOR_ENTRY pCReplacingEntry = pReplacingEntry
821 ? VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pReplacingEntry) : NULL;
822 PVBOXVR_SCR_COMPOSITOR pCConpositor = VBOXVR_SCR_COMPOSITOR_FROM_COMPOSITOR(pCompositor);
823 pCEntry->pfnEntryReleased(pCConpositor, pCEntry, pCReplacingEntry);
824 }
825}
826
827VBOXVREGDECL(int) CrVrScrCompositorRectSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PCRTRECT pRect, bool *pfChanged)
828{
829 if (!memcmp(&pCompositor->Rect, pRect, sizeof(pCompositor->Rect)))
830 {
831 if (pfChanged)
832 *pfChanged = false;
833 return VINF_SUCCESS;
834 }
835
836 pCompositor->Rect = *pRect;
837
838 VBOXVR_SCR_COMPOSITOR_ITERATOR Iter;
839 CrVrScrCompositorIterInit(pCompositor, &Iter);
840 PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry;
841 while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL)
842 {
843 int rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL);
844 if (RT_FAILURE(rc))
845 {
846 WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc));
847 return rc;
848 }
849 }
850
851 return VINF_SUCCESS;
852}
853
854VBOXVREGDECL(void) CrVrScrCompositorInit(PVBOXVR_SCR_COMPOSITOR pCompositor, PCRTRECT pRect)
855{
856 memset(pCompositor, 0, sizeof(*pCompositor));
857 VBoxVrCompositorInit(&pCompositor->Compositor, crVrScrCompositorEntryReleasedCB);
858 pCompositor->fFlags = CRBLT_F_LINEAR | CRBLT_F_INVERT_YCOORDS;
859 if (pRect)
860 pCompositor->Rect = *pRect;
861#ifndef IN_RING0
862 pCompositor->StretchX = 1.0;
863 pCompositor->StretchY = 1.0;
864#endif
865}
866
867VBOXVREGDECL(void) CrVrScrCompositorRegionsClear(PVBOXVR_SCR_COMPOSITOR pCompositor, bool *pfChanged)
868{
869 /* set changed flag first, while entries are in the list and we have them */
870 CrVrScrCompositorEntrySetAllChanged(pCompositor, true);
871 VBoxVrCompositorRegionsClear(&pCompositor->Compositor, pfChanged);
872 crVrScrCompositorRectsInvalidate(pCompositor);
873}
874
875VBOXVREGDECL(void) CrVrScrCompositorClear(PVBOXVR_SCR_COMPOSITOR pCompositor)
876{
877 CrVrScrCompositorRegionsClear(pCompositor, NULL);
878 if (pCompositor->paDstRects)
879 {
880 RTMemFree(pCompositor->paDstRects);
881 pCompositor->paDstRects = NULL;
882 }
883 if (pCompositor->paSrcRects)
884 {
885 RTMemFree(pCompositor->paSrcRects);
886 pCompositor->paSrcRects = NULL;
887 }
888 if (pCompositor->paDstUnstretchedRects)
889 {
890 RTMemFree(pCompositor->paDstUnstretchedRects);
891 pCompositor->paDstUnstretchedRects = NULL;
892 }
893
894 pCompositor->cRects = 0;
895 pCompositor->cRectsBuffer = 0;
896}
897
898VBOXVREGDECL(void) CrVrScrCompositorEntrySetAllChanged(PVBOXVR_SCR_COMPOSITOR pCompositor, bool fChanged)
899{
900 VBOXVR_SCR_COMPOSITOR_ITERATOR CIter;
901 PVBOXVR_SCR_COMPOSITOR_ENTRY pCurEntry;
902 CrVrScrCompositorIterInit(pCompositor, &CIter);
903
904 while ((pCurEntry = CrVrScrCompositorIterNext(&CIter)) != NULL)
905 {
906 CrVrScrCompositorEntrySetChanged(pCurEntry, fChanged);
907 }
908}
909
910#ifndef IN_RING0
911VBOXVREGDECL(void) CrVrScrCompositorSetStretching(PVBOXVR_SCR_COMPOSITOR pCompositor, float StretchX, float StretchY)
912{
913 if (pCompositor->StretchX == StretchX && pCompositor->StretchY == StretchY)
914 return;
915
916 pCompositor->StretchX = StretchX;
917 pCompositor->StretchY = StretchY;
918 crVrScrCompositorRectsInvalidate(pCompositor);
919 CrVrScrCompositorEntrySetAllChanged(pCompositor, true);
920}
921#endif
922
923/* regions are valid until the next CrVrScrCompositor call */
924VBOXVREGDECL(int) CrVrScrCompositorRegionsGet(PCVBOXVR_SCR_COMPOSITOR pCompositor, uint32_t *pcRegions,
925 PCRTRECT *ppaSrcRegions, PCRTRECT *ppaDstRegions,
926 PCRTRECT *ppaDstUnstretchedRects)
927{
928 int rc = crVrScrCompositorRectsCheckInit(pCompositor);
929 if (RT_FAILURE(rc))
930 {
931 WARN(("crVrScrCompositorRectsCheckInit failed, rc %d", rc));
932 return rc;
933 }
934
935 Assert(pCompositor->cRects != VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED);
936
937 *pcRegions = pCompositor->cRects;
938 if (ppaSrcRegions)
939 *ppaSrcRegions = pCompositor->paSrcRects;
940 if (ppaDstRegions)
941 *ppaDstRegions = pCompositor->paDstRects;
942 if (ppaDstUnstretchedRects)
943 *ppaDstUnstretchedRects = pCompositor->paDstUnstretchedRects;
944
945 return VINF_SUCCESS;
946}
947
948typedef struct VBOXVR_SCR_COMPOSITOR_VISITOR_CB
949{
950 PFNVBOXVRSCRCOMPOSITOR_VISITOR pfnVisitor;
951 void *pvVisitor;
952} VBOXVR_SCR_COMPOSITOR_VISITOR_CB, *PVBOXVR_SCR_COMPOSITOR_VISITOR_CB;
953
954static DECLCALLBACK(bool) crVrScrCompositorVisitCb(PVBOXVR_COMPOSITOR pCCompositor, PVBOXVR_COMPOSITOR_ENTRY pCEntry,
955 void *pvVisitor)
956{
957 PVBOXVR_SCR_COMPOSITOR_VISITOR_CB pData = (PVBOXVR_SCR_COMPOSITOR_VISITOR_CB)pvVisitor;
958 PVBOXVR_SCR_COMPOSITOR pCompositor = VBOXVR_SCR_COMPOSITOR_FROM_COMPOSITOR(pCCompositor);
959 PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pCEntry);
960 return pData->pfnVisitor(pCompositor, pEntry, pData->pvVisitor);
961}
962
963VBOXVREGDECL(void) CrVrScrCompositorVisit(PVBOXVR_SCR_COMPOSITOR pCompositor, PFNVBOXVRSCRCOMPOSITOR_VISITOR pfnVisitor,
964 void *pvVisitor)
965{
966 VBOXVR_SCR_COMPOSITOR_VISITOR_CB Data;
967 Data.pfnVisitor = pfnVisitor;
968 Data.pvVisitor = pvVisitor;
969 VBoxVrCompositorVisit(&pCompositor->Compositor, crVrScrCompositorVisitCb, &Data);
970}
971
972VBOXVREGDECL(int) CrVrScrCompositorClone(PCVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR pDstCompositor,
973 PFNVBOXVR_SCR_COMPOSITOR_ENTRY_FOR pfnEntryFor, void *pvEntryFor)
974{
975 /* for simplicity just copy from one to another */
976 CrVrScrCompositorInit(pDstCompositor, CrVrScrCompositorRectGet(pCompositor));
977 VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter;
978 PCVBOXVR_SCR_COMPOSITOR_ENTRY pEntry;
979 CrVrScrCompositorConstIterInit(pCompositor, &CIter);
980 int rc = VINF_SUCCESS;
981 uint32_t cRects;
982 PCRTRECT paRects;
983
984 while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL)
985 {
986 /* get source rects, that will be non-stretched and entry pos - pased */
987 rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, NULL, NULL, &paRects);
988 if (RT_FAILURE(rc))
989 {
990 WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc));
991 return rc;
992 }
993
994 PVBOXVR_SCR_COMPOSITOR_ENTRY pDstEntry = pfnEntryFor(pEntry, pvEntryFor);
995 if (!pDstEntry)
996 {
997 WARN(("pfnEntryFor failed"));
998 return VERR_INVALID_STATE;
999 }
1000
1001 rc = CrVrScrCompositorEntryRegionsSet(pDstCompositor, pDstEntry, NULL, cRects, paRects, false, NULL);
1002 if (RT_FAILURE(rc))
1003 {
1004 WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc));
1005 return rc;
1006 }
1007 }
1008
1009 return rc;
1010}
1011
1012VBOXVREGDECL(int) CrVrScrCompositorIntersectList(PVBOXVR_SCR_COMPOSITOR pCompositor, PCVBOXVR_LIST pVr, bool *pfChanged)
1013{
1014 VBOXVR_SCR_COMPOSITOR_ITERATOR CIter;
1015 PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry;
1016 CrVrScrCompositorIterInit(pCompositor, &CIter);
1017 int rc = VINF_SUCCESS;
1018 bool fChanged = false;
1019
1020 while ((pEntry = CrVrScrCompositorIterNext(&CIter)) != NULL)
1021 {
1022 bool fCurChanged = false;
1023
1024 rc = CrVrScrCompositorEntryListIntersect(pCompositor, pEntry, pVr, &fCurChanged);
1025 if (RT_FAILURE(rc))
1026 {
1027 WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc));
1028 break;
1029 }
1030
1031 fChanged |= fCurChanged;
1032 }
1033
1034 if (pfChanged)
1035 *pfChanged = fChanged;
1036
1037 return rc;
1038}
1039
1040VBOXVREGDECL(int) CrVrScrCompositorIntersectedList(PCVBOXVR_SCR_COMPOSITOR pCompositor, PCVBOXVR_LIST pVr,
1041 PVBOXVR_SCR_COMPOSITOR pDstCompositor,
1042 PFNVBOXVR_SCR_COMPOSITOR_ENTRY_FOR pfnEntryFor, void *pvEntryFor,
1043 bool *pfChanged)
1044{
1045 int rc = CrVrScrCompositorClone(pCompositor, pDstCompositor, pfnEntryFor, pvEntryFor);
1046 if (RT_FAILURE(rc))
1047 {
1048 WARN(("CrVrScrCompositorClone failed, rc %d", rc));
1049 return rc;
1050 }
1051
1052 rc = CrVrScrCompositorIntersectList(pDstCompositor, pVr, pfChanged);
1053 if (RT_FAILURE(rc))
1054 {
1055 WARN(("CrVrScrCompositorIntersectList failed, rc %d", rc));
1056 CrVrScrCompositorClear(pDstCompositor);
1057 return rc;
1058 }
1059
1060 return VINF_SUCCESS;
1061}
1062
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