VirtualBox

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

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

Main/src-server: rc -> hrc/vrc. Enabled scm rc checks. bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use