VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/RecordingSettingsImpl.cpp

Last change on this file was 106061, checked in by vboxsync, 7 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: 27.8 KB
Line 
1/* $Id: RecordingSettingsImpl.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation - Machine capture settings.
5 */
6
7/*
8 * Copyright (C) 2018-2024 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29#define LOG_GROUP LOG_GROUP_MAIN_RECORDINGSETTINGS
30#include "LoggingNew.h"
31
32#include "RecordingSettingsImpl.h"
33#include "RecordingScreenSettingsImpl.h"
34#include "MachineImpl.h"
35
36#include <iprt/cpp/utils.h>
37#include <VBox/settings.h>
38
39#include "AutoStateDep.h"
40#include "AutoCaller.h"
41#include "Global.h"
42
43////////////////////////////////////////////////////////////////////////////////
44//
45// RecordSettings private data definition
46//
47////////////////////////////////////////////////////////////////////////////////
48
49struct RecordingSettings::Data
50{
51 Data()
52 : pMachine(NULL)
53 { }
54
55 Machine * const pMachine;
56 const ComObjPtr<RecordingSettings> pPeer;
57 RecordingScreenSettingsObjMap mapScreenObj;
58 /** The recording progress object.
59 * There only is one recording progress per VM, shared between multiple recording settings (if any). */
60 ComPtr<IProgress> mProgress;
61
62 // use the XML settings structure in the members for simplicity
63 Backupable<settings::RecordingCommon> bd;
64};
65
66DEFINE_EMPTY_CTOR_DTOR(RecordingSettings)
67
68HRESULT RecordingSettings::FinalConstruct()
69{
70 return BaseFinalConstruct();
71}
72
73void RecordingSettings::FinalRelease()
74{
75 uninit();
76 BaseFinalRelease();
77}
78
79/**
80 * Initializes the recording settings object.
81 *
82 * @returns COM result indicator
83 */
84HRESULT RecordingSettings::init(Machine *aParent)
85{
86 LogFlowThisFuncEnter();
87 LogFlowThisFunc(("aParent: %p\n", aParent));
88
89 ComAssertRet(aParent, E_INVALIDARG);
90
91 /* Enclose the state transition NotReady->InInit->Ready */
92 AutoInitSpan autoInitSpan(this);
93 AssertReturn(autoInitSpan.isOk(), E_FAIL);
94
95 m = new Data();
96
97 /* share the parent weakly */
98 unconst(m->pMachine) = aParent;
99
100 m->bd.allocate();
101
102 i_applyDefaults();
103
104 /* Note: The progress object gets created in i_start(). */
105
106 autoInitSpan.setSucceeded();
107
108 LogFlowThisFuncLeave();
109 return S_OK;
110}
111
112/**
113 * Initializes the capture settings object given another capture settings object
114 * (a kind of copy constructor). This object shares data with
115 * the object passed as an argument.
116 *
117 * @note This object must be destroyed before the original object
118 * it shares data with is destroyed.
119 *
120 * @note Locks @a aThat object for reading.
121 */
122HRESULT RecordingSettings::init(Machine *aParent, RecordingSettings *aThat)
123{
124 LogFlowThisFuncEnter();
125 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
126
127 ComAssertRet(aParent && aThat, E_INVALIDARG);
128
129 /* Enclose the state transition NotReady->InInit->Ready */
130 AutoInitSpan autoInitSpan(this);
131 AssertReturn(autoInitSpan.isOk(), E_FAIL);
132
133 m = new Data();
134
135 unconst(m->pMachine) = aParent;
136 unconst(m->pPeer) = aThat;
137
138 AutoCaller thatCaller(aThat);
139 AssertComRCReturnRC(thatCaller.hrc());
140
141 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
142
143 m->bd.share(aThat->m->bd);
144
145 /* Make sure to add a reference when sharing the screen objects with aThat. */
146 for (RecordingScreenSettingsObjMap::const_iterator itScreenThat = aThat->m->mapScreenObj.begin();
147 itScreenThat != aThat->m->mapScreenObj.end();
148 ++itScreenThat)
149 itScreenThat->second->i_reference();
150
151 m->mapScreenObj = aThat->m->mapScreenObj;
152 m->mProgress = aThat->m->mProgress;
153
154 autoInitSpan.setSucceeded();
155
156 LogFlowThisFuncLeave();
157 return S_OK;
158}
159
160/**
161 * Initializes the guest object given another guest object
162 * (a kind of copy constructor). This object makes a private copy of data
163 * of the original object passed as an argument.
164 *
165 * @note Locks @a aThat object for reading.
166 */
167HRESULT RecordingSettings::initCopy(Machine *aParent, RecordingSettings *aThat)
168{
169 LogFlowThisFuncEnter();
170 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
171
172 ComAssertRet(aParent && aThat, E_INVALIDARG);
173
174 /* Enclose the state transition NotReady->InInit->Ready */
175 AutoInitSpan autoInitSpan(this);
176 AssertReturn(autoInitSpan.isOk(), E_FAIL);
177
178 m = new Data();
179
180 unconst(m->pMachine) = aParent;
181 // mPeer is left null
182
183 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
184 m->bd.attachCopy(aThat->m->bd);
185
186 HRESULT hrc = S_OK;
187
188 for (RecordingScreenSettingsObjMap::const_iterator itScreenThat = aThat->m->mapScreenObj.begin();
189 itScreenThat != aThat->m->mapScreenObj.end();
190 ++itScreenThat)
191 {
192 ComObjPtr<RecordingScreenSettings> pSettings;
193 pSettings.createObject();
194 hrc = pSettings->initCopy(this, itScreenThat->second);
195 if (FAILED(hrc)) return hrc;
196
197 try
198 {
199 m->mapScreenObj[itScreenThat->first] = pSettings;
200 }
201 catch (...)
202 {
203 hrc = E_OUTOFMEMORY;
204 }
205 }
206
207 m->mProgress = aThat->m->mProgress;
208
209 if (SUCCEEDED(hrc))
210 autoInitSpan.setSucceeded();
211
212 LogFlowThisFuncLeave();
213 return hrc;
214}
215
216/**
217 * Uninitializes the instance and sets the ready flag to FALSE.
218 * Called either from FinalRelease() or by the parent when it gets destroyed.
219 */
220void RecordingSettings::uninit()
221{
222 LogFlowThisFuncEnter();
223
224 /* Enclose the state transition Ready->InUninit->NotReady */
225 AutoUninitSpan autoUninitSpan(this);
226 if (autoUninitSpan.uninitDone())
227 return;
228
229 i_reset();
230
231 m->bd.free();
232
233 unconst(m->pPeer) = NULL;
234 unconst(m->pMachine) = NULL;
235
236 delete m;
237 m = NULL;
238
239 LogFlowThisFuncLeave();
240}
241
242// IRecordSettings properties
243/////////////////////////////////////////////////////////////////////////////
244
245HRESULT RecordingSettings::getEnabled(BOOL *enabled)
246{
247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
248
249 *enabled = m->bd->fEnabled;
250
251 return S_OK;
252}
253
254HRESULT RecordingSettings::setEnabled(BOOL enable)
255{
256 /* the machine needs to be mutable */
257 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
258 if (FAILED(adep.hrc())) return adep.hrc();
259
260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
261
262 const bool fEnabled = RT_BOOL(enable);
263
264 HRESULT hrc = S_OK;
265
266 if (m->bd->fEnabled != fEnabled)
267 {
268 m->bd.backup();
269 m->bd->fEnabled = fEnabled;
270
271 alock.release();
272 }
273
274 return hrc;
275}
276
277HRESULT RecordingSettings::getScreens(std::vector<ComPtr<IRecordingScreenSettings> > &aRecordScreenSettings)
278{
279 LogFlowThisFuncEnter();
280
281 AssertPtr(m->pMachine);
282 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
283 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
284 ULONG cMonitors = 0;
285 if (!pGraphicsAdapter.isNull())
286 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
287
288 i_syncToMachineDisplays(cMonitors);
289
290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
291
292 HRESULT hrc = S_OK;
293
294 try
295 {
296 aRecordScreenSettings.clear();
297 aRecordScreenSettings.resize(m->mapScreenObj.size());
298 }
299 catch (...)
300 {
301 hrc = E_OUTOFMEMORY;
302 }
303
304 if (FAILED(hrc))
305 return hrc;
306
307 RecordingScreenSettingsObjMap::const_iterator itScreenObj = m->mapScreenObj.begin();
308 size_t i = 0;
309 while (itScreenObj != m->mapScreenObj.end())
310 {
311 itScreenObj->second.queryInterfaceTo(aRecordScreenSettings[i].asOutParam());
312 AssertBreakStmt(aRecordScreenSettings[i].isNotNull(), hrc = E_POINTER);
313 ++i;
314 ++itScreenObj;
315 }
316
317 Assert(aRecordScreenSettings.size() == m->mapScreenObj.size());
318
319 return hrc;
320}
321
322HRESULT RecordingSettings::getProgress(ComPtr<IProgress> &aProgress)
323{
324#ifndef VBOX_WITH_RECORDING
325 RT_NOREF(aProgress);
326 ReturnComNotImplemented();
327#else
328 /* the machine needs to be mutable */
329 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
330 if (FAILED(adep.hrc())) return adep.hrc();
331
332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
333
334 if (m->mProgress.isNull())
335 return setError(E_FAIL, tr("Recording not started"));
336
337 return m->mProgress.queryInterfaceTo(aProgress.asOutParam());
338#endif
339}
340
341HRESULT RecordingSettings::getScreenSettings(ULONG uScreenId, ComPtr<IRecordingScreenSettings> &aRecordScreenSettings)
342{
343 LogFlowThisFuncEnter();
344
345 AssertPtr(m->pMachine);
346 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
347 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
348 ULONG cMonitors = 0;
349 if (!pGraphicsAdapter.isNull())
350 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
351
352 i_syncToMachineDisplays(cMonitors);
353
354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
355
356 if (uScreenId + 1 > m->mapScreenObj.size())
357 return setError(E_INVALIDARG, tr("Invalid screen ID specified"));
358
359 RecordingScreenSettingsObjMap::const_iterator itScreen = m->mapScreenObj.find(uScreenId);
360 if (itScreen != m->mapScreenObj.end())
361 {
362 itScreen->second.queryInterfaceTo(aRecordScreenSettings.asOutParam());
363 return S_OK;
364 }
365
366 return VBOX_E_OBJECT_NOT_FOUND;
367}
368
369HRESULT RecordingSettings::start(ComPtr<IProgress> &aProgress)
370{
371#ifndef VBOX_WITH_RECORDING
372 RT_NOREF(aProgress);
373 ReturnComNotImplemented();
374#else
375 /* the machine needs to be mutable */
376 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
377 if (FAILED(adep.hrc())) return adep.hrc();
378
379 /* Recording not explicitly enabled before? Do so now. */
380 if (!m->bd->fEnabled)
381 {
382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
383
384 m->bd.backup();
385 m->bd->fEnabled = true;
386
387 alock.release();
388
389 /* Note: m->bd->fEnabled is transient here, i.e. we don't save the settings,
390 as this would otherwise start the recording on VM startup the next time. */
391 }
392
393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
394
395 if (m->mProgress.isNotNull())
396 {
397 BOOL fCompleted = FALSE;
398 HRESULT const hrc = m->mProgress->COMGETTER(Completed)(&fCompleted);
399 ComAssertComRCRetRC(hrc);
400 if (!fCompleted)
401 return setError(E_FAIL, tr("Recording already started"));
402 }
403
404 m->mProgress.setNull(); /* Make sure to release a dangling object from a former run. */
405
406 alock.release();
407
408 int const vrc = i_start();
409 if (RT_FAILURE(vrc))
410 {
411 /* Make the progress' error info available to the caller on failure. */
412 ComObjPtr<IVirtualBoxErrorInfo> pErrorInfo;
413 m->mProgress->COMGETTER(ErrorInfo)(pErrorInfo.asOutParam());
414 return setError(pErrorInfo);
415 }
416
417 return m->mProgress.queryInterfaceTo(aProgress.asOutParam());
418#endif
419}
420
421// IRecordSettings methods
422/////////////////////////////////////////////////////////////////////////////
423
424// public methods only for internal purposes
425/////////////////////////////////////////////////////////////////////////////
426
427/**
428 * Adds a screen settings object to a particular map.
429 *
430 * @returns IPRT status code. VERR_ALREADY_EXISTS if the object in question already exists.
431 * @param screenSettingsMap Map to add screen settings to.
432 * @param idScreen Screen ID to add settings for.
433 * @param data Recording screen settings to use for that screen.
434 */
435int RecordingSettings::i_createScreenObj(RecordingScreenSettingsObjMap &screenSettingsMap,
436 uint32_t idScreen, const settings::RecordingScreen &data)
437{
438 AssertReturn(screenSettingsMap.find(idScreen) == screenSettingsMap.end(), VERR_ALREADY_EXISTS);
439
440 int vrc = VINF_SUCCESS;
441
442 ComObjPtr<RecordingScreenSettings> recordingScreenSettings;
443 HRESULT hrc = recordingScreenSettings.createObject();
444 if (SUCCEEDED(hrc))
445 {
446 hrc = recordingScreenSettings->init(this, idScreen, data);
447 if (SUCCEEDED(hrc))
448 {
449 try
450 {
451 screenSettingsMap[idScreen] = recordingScreenSettings;
452 }
453 catch (std::bad_alloc &)
454 {
455 vrc = VERR_NO_MEMORY;
456 }
457 }
458 }
459
460 LogThisFunc(("%p: Screen %RU32 -> %Rrc\n", recordingScreenSettings.m_p, idScreen, vrc));
461 return vrc;
462}
463
464/**
465 * Removes a screen settings object from a particular map.
466 *
467 * If the internal reference count hits 0, the screen settings object will be destroyed.
468 * This means that this screen settings object is not being used anymore by other recording settings (as shared data).
469 *
470 * @returns IPRT status code.
471 * @retval VERR_NOT_FOUND if specified screen was not found.
472 * @param screenSettingsMap Map to remove screen settings from.
473 * @param idScreen ID of screen to remove.
474 */
475int RecordingSettings::i_destroyScreenObj(RecordingScreenSettingsObjMap &screenSettingsMap, uint32_t idScreen)
476{
477 AssertReturn(screenSettingsMap.find(idScreen) != screenSettingsMap.end(), VERR_NOT_FOUND);
478
479 RecordingScreenSettingsObjMap::iterator itScreen = screenSettingsMap.find(idScreen);
480
481 /* Make sure to consume the pointer before the one of the
482 * iterator gets released. */
483 ComObjPtr<RecordingScreenSettings> pScreenSettings = itScreen->second;
484
485 screenSettingsMap.erase(itScreen);
486
487 LogThisFunc(("%p: Screen %RU32, cRefs=%RI32\n", pScreenSettings.m_p, idScreen, pScreenSettings->i_getReferences()));
488
489 pScreenSettings->i_release();
490
491 /* Only destroy the object if nobody else keeps a reference to it anymore. */
492 if (pScreenSettings->i_getReferences() == 0)
493 {
494 LogThisFunc(("%p: Screen %RU32 -> Null\n", pScreenSettings.m_p, idScreen));
495 pScreenSettings.setNull();
496 }
497
498 return VINF_SUCCESS;
499}
500
501/**
502 * Destroys all screen settings objects of a particular map.
503 *
504 * @returns IPRT status code.
505 * @param screenSettingsMap Map to destroy screen settings objects for.
506 */
507int RecordingSettings::i_destroyAllScreenObj(RecordingScreenSettingsObjMap &screenSettingsMap)
508{
509 LogFlowThisFuncEnter();
510
511 int vrc = VINF_SUCCESS;
512
513 RecordingScreenSettingsObjMap::iterator itScreen = screenSettingsMap.begin();
514 while (itScreen != screenSettingsMap.end())
515 {
516 vrc = i_destroyScreenObj(screenSettingsMap, itScreen->first);
517 if (RT_FAILURE(vrc))
518 break;
519
520 itScreen = screenSettingsMap.begin();
521 }
522
523 Assert(screenSettingsMap.size() == 0);
524 return vrc;
525}
526
527/**
528 * Loads settings from the given settings.
529 * May be called once right after this object creation.
530 *
531 * @param Settings Recording settings to load from.
532 *
533 * @note Locks this object for writing.
534 */
535HRESULT RecordingSettings::i_loadSettings(const settings::Recording &Settings)
536{
537 LogFlowThisFuncEnter();
538
539 AutoCaller autoCaller(this);
540 AssertComRCReturnRC(autoCaller.hrc());
541
542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
543
544 HRESULT hrc = S_OK;
545
546 LogFlowThisFunc(("Data has %zu screens\n", Settings.mapScreens.size()));
547
548 settings::RecordingScreenSettingsMap::const_iterator itScreenData = Settings.mapScreens.begin();
549 while (itScreenData != Settings.mapScreens.end())
550 {
551 RecordingScreenSettingsObjMap::iterator itScreen = m->mapScreenObj.find(itScreenData->first);
552 if (itScreen != m->mapScreenObj.end())
553 {
554 hrc = itScreen->second->i_loadSettings(itScreenData->second);
555 if (FAILED(hrc))
556 break;
557 }
558 else
559 {
560 int vrc = i_createScreenObj(m->mapScreenObj,
561 itScreenData->first /* uScreenId */, itScreenData->second /* Settings */);
562 if (RT_FAILURE(vrc))
563 {
564 hrc = E_OUTOFMEMORY; /* Most likely. */
565 break;
566 }
567 }
568
569 ++itScreenData;
570 }
571
572 if (SUCCEEDED(hrc))
573 {
574 ComAssertComRCRet(hrc, hrc);
575 AssertReturn(m->mapScreenObj.size() == Settings.mapScreens.size(), E_UNEXPECTED);
576
577 // simply copy
578 m->bd.assignCopy(&Settings.common);
579 }
580
581 LogFlowThisFunc(("Returning %Rhrc\n", hrc));
582 return hrc;
583}
584
585/**
586 * Resets the internal object state by destroying all screen settings objects.
587 */
588void RecordingSettings::i_reset(void)
589{
590 LogFlowThisFuncEnter();
591
592 i_stop();
593
594 /* Make sure to destroy screen objects attached to this object.
595 * Note: This also decrements the refcount of a screens object, in case it's shared among other recording settings. */
596 i_destroyAllScreenObj(m->mapScreenObj);
597}
598
599/**
600 * Saves settings to the given settings.
601 *
602 * @param Settings Where to store the recording settings to.
603 *
604 * @note Locks this object for reading.
605 */
606HRESULT RecordingSettings::i_saveSettings(settings::Recording &Settings)
607{
608 LogFlowThisFuncEnter();
609
610 AutoCaller autoCaller(this);
611 AssertComRCReturnRC(autoCaller.hrc());
612
613 AssertPtr(m->pMachine);
614 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
615 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
616 ULONG cMonitors = 0;
617 if (!pGraphicsAdapter.isNull())
618 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
619
620 int vrc2 = i_syncToMachineDisplays(cMonitors);
621 AssertRC(vrc2);
622
623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
624
625 Settings.common = *m->bd.data();
626
627 HRESULT hrc = S_OK;
628
629 for (RecordingScreenSettingsObjMap::const_iterator itScreen = m->mapScreenObj.begin();
630 itScreen != m->mapScreenObj.end();
631 ++itScreen)
632 {
633 hrc = itScreen->second->i_saveSettings(Settings.mapScreens[itScreen->first /* Screen ID */]);
634 if (FAILED(hrc))
635 break;
636 }
637
638 LogFlowThisFuncLeave();
639 return hrc;
640}
641
642void RecordingSettings::i_rollback(void)
643{
644 /* sanity */
645 AutoCaller autoCaller(this);
646 AssertComRCReturnVoid(autoCaller.hrc());
647
648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
649
650 m->bd.rollback();
651
652 for (RecordingScreenSettingsObjMap::const_iterator itScreen = m->mapScreenObj.begin();
653 itScreen != m->mapScreenObj.end();
654 ++itScreen)
655 {
656 itScreen->second->i_rollback();
657 }
658}
659
660void RecordingSettings::i_commit(void)
661{
662 /* sanity */
663 AutoCaller autoCaller(this);
664 AssertComRCReturnVoid(autoCaller.hrc());
665
666 /* sanity too */
667 AutoCaller peerCaller(m->pPeer);
668 AssertComRCReturnVoid(peerCaller.hrc());
669
670 /* lock both for writing since we modify both (mPeer is "master" so locked
671 * first) */
672 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
673
674 if (m->bd.isBackedUp())
675 {
676 m->bd.commit();
677 if (m->pPeer)
678 {
679 /* attach new data to the peer and reshare it */
680 m->pPeer->m->bd.attach(m->bd);
681 }
682
683 for (RecordingScreenSettingsObjMap::const_iterator itScreenObj = m->mapScreenObj.begin();
684 itScreenObj != m->mapScreenObj.end();
685 ++itScreenObj)
686 {
687 itScreenObj->second->i_commit();
688 if (m->pPeer)
689 m->pPeer->i_commit();
690 }
691 }
692}
693
694HRESULT RecordingSettings::i_copyFrom(RecordingSettings *aThat)
695{
696 AssertPtrReturn(aThat, E_INVALIDARG);
697
698 /* sanity */
699 AutoCaller autoCaller(this);
700 AssertComRCReturn(autoCaller.hrc(), VBOX_E_INVALID_OBJECT_STATE);
701
702 /* sanity too */
703 AutoCaller thatCaller(aThat);
704 AssertComRCReturn(thatCaller.hrc(), VBOX_E_INVALID_OBJECT_STATE);
705
706 /* peer is not modified, lock it for reading (aThat is "master" so locked
707 * first) */
708 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
709 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
710
711 /* this will back up current data */
712 m->bd.assignCopy(aThat->m->bd);
713
714 HRESULT hrc = S_OK;
715
716 for (RecordingScreenSettingsObjMap::const_iterator itScreenThat = aThat->m->mapScreenObj.begin();
717 itScreenThat != aThat->m->mapScreenObj.end();
718 ++itScreenThat)
719 {
720 RecordingScreenSettingsObjMap::iterator itScreen = m->mapScreenObj.find(itScreenThat->first);
721 if (itScreen != m->mapScreenObj.end())
722 {
723 itScreen->second->i_copyFrom(itScreenThat->second);
724 }
725 else
726 {
727 int vrc = i_createScreenObj(m->mapScreenObj,
728 itScreenThat->first /* uScreenId */, itScreenThat->second->i_getData() /* Settings */);
729 if (RT_FAILURE(vrc))
730 {
731 hrc = E_OUTOFMEMORY; /* Most likely. */
732 break;
733 }
734 }
735 }
736
737 return hrc;
738}
739
740void RecordingSettings::i_applyDefaults(void)
741{
742 /* sanity */
743 AutoCaller autoCaller(this);
744 AssertComRCReturnVoid(autoCaller.hrc());
745
746 AssertPtr(m->pMachine);
747 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
748 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
749 ULONG cMonitors = 0;
750 if (!pGraphicsAdapter.isNull())
751 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
752
753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
754
755 /* Initialize default capturing settings here. */
756 m->bd->fEnabled = false;
757
758 /* First, do a reset so that all internal screen settings objects are destroyed. */
759 i_reset();
760 /* Second, sync (again) to configured machine displays to (re-)create screen settings objects. */
761 i_syncToMachineDisplays(cMonitors);
762}
763
764/**
765 * Returns the full path to the default recording file.
766 *
767 * @returns VBox status code.
768 * @param strFile Where to return the final file name on success.
769 * @param idScreen Screen ID the file is associated to.
770 * @param fWithFileExtension Whether to include the default file extension ('.webm') or not.
771 */
772int RecordingSettings::i_getDefaultFilename(Utf8Str &strFile, uint32_t idScreen, bool fWithFileExtension)
773{
774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
775
776 strFile = m->pMachine->i_getSettingsFileFull(); // path/to/machinesfolder/vmname/vmname.vbox
777 strFile.stripSuffix();
778 strFile.append(Utf8StrFmt("-screen%RU32", idScreen));
779 if (fWithFileExtension)
780 strFile.append(".webm");
781
782 return VINF_SUCCESS;
783}
784
785/**
786 * Gets a standardized file name from a given template file name.
787 *
788 * @returns VBox status code.
789 * @param strFile Where to return the final file name on success.
790 * @param idScreen Screen ID the file is associated to.
791 * @param strTemplate Template file name to use.
792 * A default file name will be used when empty.
793 */
794int RecordingSettings::i_getFilename(Utf8Str &strFile, uint32_t idScreen, const Utf8Str &strTemplate)
795{
796 strFile = strTemplate;
797
798 if (strFile.isEmpty())
799 return i_getDefaultFilename(strFile, idScreen, true /* fWithFileExtension */);
800
801 /* We force adding a .webm suffix to (hopefully) not let the user overwrite other important stuff. */
802 strFile.stripSuffix();
803
804 Utf8Str strDotExt = ".webm";
805
806 /* We also force adding the screen id suffix, at least for the moment, as FE/Qt only offers settings a single file name
807 * for *all* enabled screens. */
808 char szSuffScreen[] = "-screen";
809 Utf8Str strSuff = Utf8StrFmt("%s%RU32", szSuffScreen, idScreen);
810 if (!strFile.endsWith(strSuff, Utf8Str::CaseInsensitive))
811 {
812 /** @todo The following line checks whether there already is a screen suffix, as FE/Qt currently always works with
813 * screen 0 as the file name. Remove the following if block when FE/Qt supports this properly. */
814 Utf8Str strSuffScreen0 = Utf8StrFmt("%s%RU32", szSuffScreen, 0);
815 if (strFile.endsWith(strSuffScreen0, Utf8Str::CaseInsensitive))
816 strFile.truncate(strFile.length() - strSuffScreen0.length());
817
818 strFile += strSuff; /* Add the suffix with the correct screen ID. */
819 }
820
821 strFile += strDotExt;
822
823 LogRel2(("Recording: File name '%s' -> '%s'\n", strTemplate.c_str(), strFile.c_str()));
824
825 return VINF_SUCCESS;
826}
827
828/**
829 * Determines whether the recording settings currently can be changed or not.
830 *
831 * @returns \c true if the settings can be changed, \c false if not.
832 */
833bool RecordingSettings::i_canChangeSettings(void)
834{
835 AutoAnyStateDependency adep(m->pMachine);
836 if (FAILED(adep.hrc()))
837 return false;
838
839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 /* Only allow settings to be changed when recording is disabled when the machine is running. */
842 if ( Global::IsOnline(adep.machineState())
843 && m->bd->fEnabled)
844 {
845 return false;
846 }
847
848 return true;
849}
850
851/**
852 * Gets called when the machine object needs to know that the recording settings
853 * have been changed.
854 */
855void RecordingSettings::i_onSettingsChanged(void)
856{
857 LogFlowThisFuncEnter();
858
859 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
860 m->pMachine->i_setModified(Machine::IsModified_Recording);
861 mlock.release();
862
863 LogFlowThisFuncLeave();
864}
865
866/**
867 * Starts recording.
868 *
869 * @returns VBox status code.
870 */
871int RecordingSettings::i_start(void)
872{
873 AssertReturn(m->mProgress.isNull(), VERR_WRONG_ORDER);
874
875 HRESULT hrc = m->pMachine->i_onRecordingStateChange(TRUE /* Enable recording */, m->mProgress.asOutParam());
876 if (FAILED(hrc))
877 return VERR_RECORDING_INIT_FAILED;
878
879 return VINF_SUCCESS;
880}
881
882/**
883 * Stops recording. Does nothing if recording already has been stopped.
884 *
885 * @returns VBox status code.
886 */
887int RecordingSettings::i_stop(void)
888{
889 if (m->mProgress.isNull()) /* Not started? */
890 return VINF_SUCCESS;
891
892 /* Note: Returned progress object is just a dummy / not needed for disabling recording. */
893 HRESULT hrc = m->pMachine->i_onRecordingStateChange(FALSE /* Disable recording */, m->mProgress.asOutParam());
894 if (SUCCEEDED(hrc))
895 m->mProgress.setNull();
896
897 if (FAILED(hrc))
898 return VERR_COM_UNEXPECTED;
899
900 return VINF_SUCCESS;
901}
902
903/**
904 * Synchronizes the screen settings (COM) objects and configuration data
905 * to the number of the machine's configured displays.
906 *
907 * Note: This function ASSUMES that we always have configured VM displays
908 * as a consequtive sequence with no holes in between.
909 */
910int RecordingSettings::i_syncToMachineDisplays(uint32_t cDisplays)
911{
912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
913
914 LogThisFunc(("%p: cDisplays=%RU32 vs. %zu\n", this, cDisplays, m->mapScreenObj.size()));
915
916 /* If counts match, take a shortcut. */
917 if (cDisplays == m->mapScreenObj.size())
918 return VINF_SUCCESS;
919
920 /* Create all new screen settings objects which are not there yet. */
921 for (ULONG i = 0; i < cDisplays; i++)
922 {
923 if (m->mapScreenObj.find(i) == m->mapScreenObj.end())
924 {
925 settings::RecordingScreen defaultScreenSettings(i /* Screen ID */); /* Apply default settings. */
926
927 int vrc2 = i_createScreenObj(m->mapScreenObj, i /* Screen ID */, defaultScreenSettings);
928 AssertRC(vrc2);
929 }
930 }
931
932 /* Remove all left over screen settings objects which are not needed anymore. */
933 for (ULONG i = cDisplays; i < (ULONG)m->mapScreenObj.size(); i++)
934 {
935 int vrc2 = i_destroyScreenObj(m->mapScreenObj, i /* Screen ID */);
936 AssertRC(vrc2);
937 }
938
939 Assert(m->mapScreenObj.size() == cDisplays);
940
941 LogFlowThisFuncLeave();
942 return VINF_SUCCESS;
943}
944
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