[Test] Added tests for CUtil::SplitParams
[xbmc.git] / xbmc / application / Application.cpp
blob3309927de025f03eb3a59c23b4ffa1d96ea124e9
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "Application.h"
11 #include "Autorun.h"
12 #include "CompileInfo.h"
13 #include "DatabaseManager.h"
14 #include "FileItem.h"
15 #include "GUIInfoManager.h"
16 #include "GUILargeTextureManager.h"
17 #include "GUIPassword.h"
18 #include "GUIUserMessages.h"
19 #include "HDRStatus.h"
20 #include "LangInfo.h"
21 #include "PartyModeManager.h"
22 #include "PlayListPlayer.h"
23 #include "SectionLoader.h"
24 #include "SeekHandler.h"
25 #include "ServiceBroker.h"
26 #include "ServiceManager.h"
27 #include "TextureCache.h"
28 #include "URL.h"
29 #include "Util.h"
30 #include "addons/AddonManager.h"
31 #include "addons/AddonSystemSettings.h"
32 #include "addons/RepositoryUpdater.h"
33 #include "addons/Service.h"
34 #include "addons/Skin.h"
35 #include "addons/VFSEntry.h"
36 #include "addons/addoninfo/AddonInfo.h"
37 #include "addons/addoninfo/AddonType.h"
38 #include "addons/gui/GUIDialogAddonSettings.h"
39 #include "application/AppInboundProtocol.h"
40 #include "application/AppParams.h"
41 #include "application/ApplicationActionListeners.h"
42 #include "application/ApplicationPlayer.h"
43 #include "application/ApplicationPowerHandling.h"
44 #include "application/ApplicationSkinHandling.h"
45 #include "application/ApplicationStackHelper.h"
46 #include "application/ApplicationVolumeHandling.h"
47 #include "cores/AudioEngine/Engines/ActiveAE/ActiveAE.h"
48 #include "cores/DataCacheCore.h"
49 #include "cores/FFmpeg.h"
50 #include "cores/IPlayer.h"
51 #include "cores/playercorefactory/PlayerCoreFactory.h"
52 #include "dialogs/GUIDialogBusy.h"
53 #include "dialogs/GUIDialogCache.h"
54 #include "dialogs/GUIDialogKaiToast.h"
55 #include "dialogs/GUIDialogSimpleMenu.h"
56 #include "events/EventLog.h"
57 #include "events/NotificationEvent.h"
58 #include "filesystem/Directory.h"
59 #include "filesystem/DirectoryCache.h"
60 #include "filesystem/DirectoryFactory.h"
61 #include "filesystem/DllLibCurl.h"
62 #include "filesystem/File.h"
63 #ifdef HAS_FILESYSTEM_NFS
64 #include "filesystem/NFSFile.h"
65 #endif
66 #include "filesystem/PluginDirectory.h"
67 #include "filesystem/SpecialProtocol.h"
68 #ifdef HAS_UPNP
69 #include "filesystem/UPnPDirectory.h"
70 #endif
71 #include "guilib/GUIAudioManager.h"
72 #include "guilib/GUIComponent.h"
73 #include "guilib/GUIControlProfiler.h"
74 #include "guilib/GUIFontManager.h"
75 #include "guilib/GUIWindowManager.h"
76 #include "guilib/LocalizeStrings.h"
77 #include "guilib/StereoscopicsManager.h"
78 #include "guilib/TextureManager.h"
79 #include "input/InertialScrollingHandler.h"
80 #include "input/InputManager.h"
81 #include "input/actions/Action.h"
82 #include "input/actions/ActionIDs.h"
83 #include "input/actions/ActionTranslator.h"
84 #include "input/keyboard/KeyboardLayoutManager.h"
85 #include "interfaces/AnnouncementManager.h"
86 #include "interfaces/builtins/Builtins.h"
87 #include "interfaces/generic/ScriptInvocationManager.h"
88 #include "interfaces/json-rpc/JSONRPC.h"
89 #ifdef HAS_PYTHON
90 #include "interfaces/python/XBPython.h"
91 #endif
92 #include "messaging/ApplicationMessenger.h"
93 #include "messaging/ThreadMessage.h"
94 #include "messaging/helpers/DialogHelper.h"
95 #include "messaging/helpers/DialogOKHelper.h"
96 #include "music/MusicLibraryQueue.h"
97 #include "music/MusicThumbLoader.h"
98 #include "music/MusicUtils.h"
99 #include "music/infoscanner/MusicInfoScanner.h"
100 #include "music/tags/MusicInfoTag.h"
101 #include "network/EventServer.h"
102 #include "network/Network.h"
103 #include "network/ZeroconfBrowser.h"
104 #ifdef HAS_UPNP
105 #include "network/upnp/UPnP.h"
106 #endif
107 #include "peripherals/Peripherals.h"
108 #include "pictures/SlideShowDelegator.h"
109 #include "platform/Environment.h"
110 #include "playlists/PlayList.h"
111 #include "playlists/PlayListFactory.h"
112 #include "playlists/SmartPlayList.h"
113 #include "powermanagement/PowerManager.h"
114 #include "profiles/ProfileManager.h"
115 #include "pvr/PVRManager.h"
116 #include "pvr/guilib/PVRGUIActionsPlayback.h"
117 #include "pvr/guilib/PVRGUIActionsPowerManagement.h"
118 #include "settings/AdvancedSettings.h"
119 #include "settings/DisplaySettings.h"
120 #include "settings/MediaSettings.h"
121 #include "settings/Settings.h"
122 #include "settings/SettingsComponent.h"
123 #include "speech/ISpeechRecognition.h"
124 #include "storage/MediaManager.h"
125 #include "threads/SingleLock.h"
126 #include "threads/SystemClock.h"
127 #include "utils/AlarmClock.h"
128 #include "utils/CPUInfo.h"
129 #include "utils/CharsetConverter.h"
130 #include "utils/ContentUtils.h"
131 #include "utils/FileExtensionProvider.h"
132 #include "utils/JobManager.h"
133 #include "utils/LangCodeExpander.h"
134 #include "utils/PlayerUtils.h"
135 #include "utils/RegExp.h"
136 #include "utils/Screenshot.h"
137 #include "utils/StringUtils.h"
138 #include "utils/SystemInfo.h"
139 #include "utils/TimeUtils.h"
140 #include "utils/URIUtils.h"
141 #include "utils/Variant.h"
142 #include "utils/XTimeUtils.h"
143 #include "utils/log.h"
144 #include "video/Bookmark.h"
145 #include "video/PlayerController.h"
146 #include "video/VideoLibraryQueue.h"
147 #include "video/dialogs/GUIDialogVideoBookmarks.h"
148 #ifdef TARGET_WINDOWS
149 #include "win32util.h"
150 #endif
151 #include "windowing/WinSystem.h"
152 #include "windowing/WindowSystemFactory.h"
154 #if defined(TARGET_ANDROID)
155 #include "platform/android/activity/XBMCApp.h"
156 #endif
157 #ifdef TARGET_DARWIN
158 #include "platform/darwin/DarwinUtils.h"
159 #endif
160 #ifdef TARGET_DARWIN_OSX
161 #ifdef HAS_XBMCHELPER
162 #include "platform/darwin/osx/XBMCHelper.h"
163 #endif
164 #endif
165 #ifdef TARGET_POSIX
166 #include "platform/posix/PlatformPosix.h"
167 #include "platform/posix/XHandle.h"
168 #endif
169 #if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
170 #include "platform/posix/filesystem/SMBFile.h"
171 #endif
172 #ifndef TARGET_POSIX
173 #include "platform/win32/threads/Win32Exception.h"
174 #endif
176 #include <cmath>
177 #include <memory>
178 #include <mutex>
180 //TODO: XInitThreads
181 #ifdef HAVE_X11
182 #include <X11/Xlib.h>
183 #endif
184 #ifdef HAS_OPTICAL_DRIVE
185 #include <cdio/logging.h>
186 #endif
188 using namespace ADDON;
189 using namespace XFILE;
190 #ifdef HAS_OPTICAL_DRIVE
191 using namespace MEDIA_DETECT;
192 #endif
193 using namespace VIDEO;
194 using namespace MUSIC_INFO;
195 using namespace EVENTSERVER;
196 using namespace JSONRPC;
197 using namespace PVR;
198 using namespace PERIPHERALS;
199 using namespace KODI;
200 using namespace KODI::MESSAGING;
201 using namespace ActiveAE;
203 using namespace XbmcThreads;
204 using namespace std::chrono_literals;
206 using KODI::MESSAGING::HELPERS::DialogResponse;
208 using namespace std::chrono_literals;
210 #define MAX_FFWD_SPEED 5
212 CApplication::CApplication(void)
214 #ifdef HAS_OPTICAL_DRIVE
215 m_Autorun(new CAutorun()),
216 #endif
217 m_pInertialScrollingHandler(new CInertialScrollingHandler()),
218 m_WaitingExternalCalls(0),
219 m_itemCurrentFile(std::make_shared<CFileItem>()),
220 m_playerEvent(true, true)
222 TiXmlBase::SetCondenseWhiteSpace(false);
224 #ifdef HAVE_X11
225 XInitThreads();
226 #endif
228 // register application components
229 RegisterComponent(std::make_shared<CApplicationActionListeners>(m_critSection));
230 RegisterComponent(std::make_shared<CApplicationPlayer>());
231 RegisterComponent(std::make_shared<CApplicationPowerHandling>());
232 RegisterComponent(std::make_shared<CApplicationSkinHandling>(this, this, m_bInitializing));
233 RegisterComponent(std::make_shared<CApplicationVolumeHandling>());
234 RegisterComponent(std::make_shared<CApplicationStackHelper>());
237 CApplication::~CApplication(void)
239 DeregisterComponent(typeid(CApplicationStackHelper));
240 DeregisterComponent(typeid(CApplicationVolumeHandling));
241 DeregisterComponent(typeid(CApplicationSkinHandling));
242 DeregisterComponent(typeid(CApplicationPowerHandling));
243 DeregisterComponent(typeid(CApplicationPlayer));
244 DeregisterComponent(typeid(CApplicationActionListeners));
247 bool CApplication::OnEvent(XBMC_Event& newEvent)
249 std::unique_lock<CCriticalSection> lock(m_portSection);
250 m_portEvents.push_back(newEvent);
251 return true;
254 void CApplication::HandlePortEvents()
256 std::unique_lock<CCriticalSection> lock(m_portSection);
257 while (!m_portEvents.empty())
259 auto newEvent = m_portEvents.front();
260 m_portEvents.pop_front();
261 CSingleExit lock(m_portSection);
262 switch(newEvent.type)
264 case XBMC_QUIT:
265 if (!m_bStop)
266 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
267 break;
268 case XBMC_VIDEORESIZE:
269 if (CServiceBroker::GetGUI()->GetWindowManager().Initialized())
271 if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
273 CServiceBroker::GetWinSystem()->GetGfxContext().ApplyWindowResize(newEvent.resize.w, newEvent.resize.h);
275 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
276 settings->SetInt(CSettings::SETTING_WINDOW_WIDTH, newEvent.resize.w);
277 settings->SetInt(CSettings::SETTING_WINDOW_HEIGHT, newEvent.resize.h);
278 settings->Save();
280 else
282 const auto& res_info = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP);
283 CServiceBroker::GetWinSystem()->ForceFullScreen(res_info);
286 break;
287 case XBMC_VIDEOMOVE:
289 CServiceBroker::GetWinSystem()->OnMove(newEvent.move.x, newEvent.move.y);
291 break;
292 case XBMC_MODECHANGE:
293 CServiceBroker::GetWinSystem()->GetGfxContext().ApplyModeChange(newEvent.mode.res);
294 break;
295 case XBMC_SCREENCHANGE:
296 CServiceBroker::GetWinSystem()->OnChangeScreen(newEvent.screen.screenIdx);
297 break;
298 case XBMC_USEREVENT:
299 CServiceBroker::GetAppMessenger()->PostMsg(static_cast<uint32_t>(newEvent.user.code));
300 break;
301 case XBMC_SETFOCUS:
303 // Reset the screensaver
304 const auto appPower = GetComponent<CApplicationPowerHandling>();
305 appPower->ResetScreenSaver();
306 appPower->WakeUpScreenSaverAndDPMS();
307 // Send a mouse motion event with no dx,dy for getting the current guiitem selected
308 OnAction(CAction(ACTION_MOUSE_MOVE, 0, static_cast<float>(newEvent.focus.x), static_cast<float>(newEvent.focus.y), 0, 0));
309 break;
311 default:
312 CServiceBroker::GetInputManager().OnEvent(newEvent);
317 extern "C" void __stdcall init_emu_environ();
318 extern "C" void __stdcall update_emu_environ();
319 extern "C" void __stdcall cleanup_emu_environ();
321 bool CApplication::Create()
323 m_bStop = false;
325 RegisterSettings();
327 CServiceBroker::RegisterCPUInfo(CCPUInfo::GetCPUInfo());
329 // Register JobManager service
330 CServiceBroker::RegisterJobManager(std::make_shared<CJobManager>());
332 // Announcement service
333 m_pAnnouncementManager = std::make_shared<ANNOUNCEMENT::CAnnouncementManager>();
334 m_pAnnouncementManager->Start();
335 CServiceBroker::RegisterAnnouncementManager(m_pAnnouncementManager);
337 const auto appMessenger = std::make_shared<CApplicationMessenger>();
338 CServiceBroker::RegisterAppMessenger(appMessenger);
340 const auto keyboardLayoutManager = std::make_shared<KEYBOARD::CKeyboardLayoutManager>();
341 CServiceBroker::RegisterKeyboardLayoutManager(keyboardLayoutManager);
343 m_ServiceManager = std::make_unique<CServiceManager>();
345 if (!m_ServiceManager->InitStageOne())
347 return false;
350 // here we register all global classes for the CApplicationMessenger,
351 // after that we can send messages to the corresponding modules
352 appMessenger->RegisterReceiver(this);
353 appMessenger->RegisterReceiver(&CServiceBroker::GetPlaylistPlayer());
354 appMessenger->SetGUIThread(CThread::GetCurrentThreadId());
355 appMessenger->SetProcessThread(CThread::GetCurrentThreadId());
357 // copy required files
358 CUtil::CopyUserDataIfNeeded("special://masterprofile/", "RssFeeds.xml");
359 CUtil::CopyUserDataIfNeeded("special://masterprofile/", "favourites.xml");
360 CUtil::CopyUserDataIfNeeded("special://masterprofile/", "Lircmap.xml");
362 CServiceBroker::GetLogging().Initialize(CSpecialProtocol::TranslatePath("special://logpath"));
364 #ifdef TARGET_POSIX //! @todo Win32 has no special://home/ mapping by default, so we
365 //! must create these here. Ideally this should be using special://home/ and
366 //! be platform agnostic (i.e. unify the InitDirectories*() functions)
367 if (!CServiceBroker::GetAppParams()->HasPlatformDirectories())
368 #endif
370 CDirectory::Create("special://xbmc/addons");
373 // Init our DllLoaders emu env
374 init_emu_environ();
376 PrintStartupLog();
378 // initialize network protocols
379 avformat_network_init();
380 // set avutil callback
381 av_log_set_callback(ff_avutil_log);
383 CLog::Log(LOGINFO, "loading settings");
384 const auto settingsComponent = CServiceBroker::GetSettingsComponent();
385 if (!settingsComponent->Load())
386 return false;
388 // Log Cache GUI settings (replacement of cache in advancedsettings.xml)
389 const auto settings = settingsComponent->GetSettings();
390 const float readFactor = settings->GetInt(CSettings::SETTING_FILECACHE_READFACTOR) / 100.0f;
391 CLog::Log(LOGINFO,
392 "New Cache GUI Settings (replacement of cache in advancedsettings.xml) are:\n Buffer "
393 "Mode: {}\n Memory Size: {} MB\n Read "
394 "Factor: {:.2f} x {}\n Chunk Size : {} bytes",
395 settings->GetInt(CSettings::SETTING_FILECACHE_BUFFERMODE),
396 settings->GetInt(CSettings::SETTING_FILECACHE_MEMORYSIZE), readFactor,
397 (readFactor < 1.0f) ? "(adaptive)" : "",
398 settings->GetInt(CSettings::SETTING_FILECACHE_CHUNKSIZE));
400 CLog::Log(LOGINFO, "creating subdirectories");
401 const std::shared_ptr<CProfileManager> profileManager = settingsComponent->GetProfileManager();
402 CLog::Log(LOGINFO, "userdata folder: {}",
403 CURL::GetRedacted(profileManager->GetProfileUserDataFolder()));
404 CLog::Log(LOGINFO, "recording folder: {}",
405 CURL::GetRedacted(settings->GetString(CSettings::SETTING_AUDIOCDS_RECORDINGPATH)));
406 CLog::Log(LOGINFO, "screenshots folder: {}",
407 CURL::GetRedacted(settings->GetString(CSettings::SETTING_DEBUG_SCREENSHOTPATH)));
408 CDirectory::Create(profileManager->GetUserDataFolder());
409 CDirectory::Create(profileManager->GetProfileUserDataFolder());
410 profileManager->CreateProfileFolders();
412 update_emu_environ();//apply the GUI settings
414 // application inbound service
415 m_pAppPort = std::make_shared<CAppInboundProtocol>(*this);
416 CServiceBroker::RegisterAppPort(m_pAppPort);
418 if (!m_ServiceManager->InitStageTwo(
419 settingsComponent->GetProfileManager()->GetProfileUserDataFolder()))
421 return false;
424 m_pActiveAE = std::make_unique<ActiveAE::CActiveAE>();
425 CServiceBroker::RegisterAE(m_pActiveAE.get());
427 // initialize m_replayGainSettings
428 GetComponent<CApplicationVolumeHandling>()->CacheReplayGainSettings(*settings);
430 // load the keyboard layouts
431 if (!keyboardLayoutManager->Load())
433 CLog::Log(LOGFATAL, "CApplication::Create: Unable to load keyboard layouts");
434 return false;
437 // set user defined CA trust bundle
438 std::string caCert =
439 CSpecialProtocol::TranslatePath(settingsComponent->GetAdvancedSettings()->m_caTrustFile);
440 if (!caCert.empty())
442 if (XFILE::CFile::Exists(caCert))
444 CEnvironment::setenv("SSL_CERT_FILE", caCert, 1);
445 CLog::Log(LOGDEBUG, "CApplication::Create - SSL_CERT_FILE: {}", caCert);
447 else
449 CLog::Log(LOGDEBUG, "CApplication::Create - Error reading SSL_CERT_FILE: {} -> ignored",
450 caCert);
454 CUtil::InitRandomSeed();
456 m_lastRenderTime = std::chrono::steady_clock::now();
457 return true;
460 bool CApplication::CreateGUI()
462 m_frameMoveGuard.lock();
464 const auto appPower = GetComponent<CApplicationPowerHandling>();
465 appPower->SetRenderGUI(true);
467 auto windowSystems = KODI::WINDOWING::CWindowSystemFactory::GetWindowSystems();
469 const std::string& windowing = CServiceBroker::GetAppParams()->GetWindowing();
471 if (!windowing.empty())
472 windowSystems = {windowing};
474 for (auto& windowSystem : windowSystems)
476 CLog::Log(LOGDEBUG, "CApplication::{} - trying to init {} windowing system", __FUNCTION__,
477 windowSystem);
478 m_pWinSystem = KODI::WINDOWING::CWindowSystemFactory::CreateWindowSystem(windowSystem);
480 if (!m_pWinSystem)
481 continue;
483 if (!windowing.empty() && windowing != windowSystem)
484 continue;
486 CServiceBroker::RegisterWinSystem(m_pWinSystem.get());
488 if (!m_pWinSystem->InitWindowSystem())
490 CLog::Log(LOGDEBUG, "CApplication::{} - unable to init {} windowing system", __FUNCTION__,
491 windowSystem);
492 m_pWinSystem->DestroyWindowSystem();
493 m_pWinSystem.reset();
494 CServiceBroker::UnregisterWinSystem();
495 continue;
497 else
499 CLog::Log(LOGINFO, "CApplication::{} - using the {} windowing system", __FUNCTION__,
500 windowSystem);
501 break;
505 if (!m_pWinSystem)
507 CLog::Log(LOGFATAL, "CApplication::{} - unable to init windowing system", __FUNCTION__);
508 CServiceBroker::UnregisterWinSystem();
509 return false;
512 // Retrieve the matching resolution based on GUI settings
513 bool sav_res = false;
514 CDisplaySettings::GetInstance().SetCurrentResolution(CDisplaySettings::GetInstance().GetDisplayResolution());
515 CLog::Log(LOGINFO, "Checking resolution {}",
516 CDisplaySettings::GetInstance().GetCurrentResolution());
517 if (!CServiceBroker::GetWinSystem()->GetGfxContext().IsValidResolution(CDisplaySettings::GetInstance().GetCurrentResolution()))
519 CLog::Log(LOGINFO, "Setting safe mode {}", RES_DESKTOP);
520 // defer saving resolution after window was created
521 CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
522 sav_res = true;
525 // update the window resolution
526 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
527 CServiceBroker::GetWinSystem()->SetWindowResolution(settings->GetInt(CSettings::SETTING_WINDOW_WIDTH), settings->GetInt(CSettings::SETTING_WINDOW_HEIGHT));
529 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_startFullScreen && CDisplaySettings::GetInstance().GetCurrentResolution() == RES_WINDOW)
531 // defer saving resolution after window was created
532 CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
533 sav_res = true;
536 if (!CServiceBroker::GetWinSystem()->GetGfxContext().IsValidResolution(CDisplaySettings::GetInstance().GetCurrentResolution()))
538 // Oh uh - doesn't look good for starting in their wanted screenmode
539 CLog::Log(LOGERROR, "The screen resolution requested is not valid, resetting to a valid mode");
540 CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
541 sav_res = true;
543 if (!InitWindow())
545 return false;
548 // Set default screen saver mode
549 auto screensaverModeSetting = std::static_pointer_cast<CSettingString>(settings->GetSetting(CSettings::SETTING_SCREENSAVER_MODE));
550 // Can only set this after windowing has been initialized since it depends on it
551 if (CServiceBroker::GetWinSystem()->GetOSScreenSaver())
553 // If OS has a screen saver, use it by default
554 screensaverModeSetting->SetDefault("");
556 else
558 // If OS has no screen saver, use Kodi one by default
559 screensaverModeSetting->SetDefault("screensaver.xbmc.builtin.dim");
562 if (sav_res)
563 CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP, true);
565 m_pGUI = std::make_unique<CGUIComponent>();
566 m_pGUI->Init();
568 // Splash requires gui component!!
569 CServiceBroker::GetRenderSystem()->ShowSplash("");
571 // The key mappings may already have been loaded by a peripheral
572 CLog::Log(LOGINFO, "load keymapping");
573 if (!CServiceBroker::GetInputManager().LoadKeymaps())
574 return false;
576 RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
577 CLog::Log(LOGINFO, "GUI format {}x{}, Display {}", info.iWidth, info.iHeight, info.strMode);
579 return true;
582 bool CApplication::InitWindow(RESOLUTION res)
584 if (res == RES_INVALID)
585 res = CDisplaySettings::GetInstance().GetCurrentResolution();
587 bool bFullScreen = res != RES_WINDOW;
588 if (!CServiceBroker::GetWinSystem()->CreateNewWindow(CSysInfo::GetAppName(),
589 bFullScreen, CDisplaySettings::GetInstance().GetResolutionInfo(res)))
591 CLog::Log(LOGFATAL, "CApplication::Create: Unable to create window");
592 return false;
595 if (!CServiceBroker::GetRenderSystem()->InitRenderSystem())
597 CLog::Log(LOGFATAL, "CApplication::Create: Unable to init rendering system");
598 return false;
600 // set GUI res and force the clear of the screen
601 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(res, false);
602 return true;
605 bool CApplication::Initialize()
607 m_pActiveAE->Start();
608 // restore AE's previous volume state
610 const auto appVolume = GetComponent<CApplicationVolumeHandling>();
611 const auto level = appVolume->GetVolumeRatio();
612 const auto muted = appVolume->IsMuted();
613 appVolume->SetHardwareVolume(level);
614 CServiceBroker::GetActiveAE()->SetMute(muted);
616 #if defined(HAS_OPTICAL_DRIVE) && \
617 !defined(TARGET_WINDOWS) // somehow this throws an "unresolved external symbol" on win32
618 // turn off cdio logging
619 cdio_loglevel_default = CDIO_LOG_ERROR;
620 #endif
622 // load the language and its translated strings
623 if (!LoadLanguage(false))
624 return false;
626 // load media manager sources (e.g. root addon type sources depend on language strings to be available)
627 CServiceBroker::GetMediaManager().LoadSources();
629 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
631 profileManager->GetEventLog().Add(EventPtr(new CNotificationEvent(
632 StringUtils::Format(g_localizeStrings.Get(177), g_sysinfo.GetAppName()),
633 StringUtils::Format(g_localizeStrings.Get(178), g_sysinfo.GetAppName()),
634 "special://xbmc/media/icon256x256.png", EventLevel::Basic)));
636 m_ServiceManager->GetNetwork().WaitForNet();
638 // initialize (and update as needed) our databases
639 CDatabaseManager &databaseManager = m_ServiceManager->GetDatabaseManager();
641 CEvent event(true);
642 CServiceBroker::GetJobManager()->Submit([&databaseManager, &event]() {
643 databaseManager.Initialize();
644 event.Set();
647 std::string localizedStr = g_localizeStrings.Get(24150);
648 int iDots = 1;
649 while (!event.Wait(1000ms))
651 if (databaseManager.IsUpgrading())
652 CServiceBroker::GetRenderSystem()->ShowSplash(std::string(iDots, ' ') + localizedStr + std::string(iDots, '.'));
654 if (iDots == 3)
655 iDots = 1;
656 else
657 ++iDots;
659 CServiceBroker::GetRenderSystem()->ShowSplash("");
661 // Initialize GUI font manager to build/update fonts cache
662 //! @todo Move GUIFontManager into service broker and drop the global reference
663 event.Reset();
664 GUIFontManager& guiFontManager = g_fontManager;
665 CServiceBroker::GetJobManager()->Submit([&guiFontManager, &event]() {
666 guiFontManager.Initialize();
667 event.Set();
669 localizedStr = g_localizeStrings.Get(39175);
670 iDots = 1;
671 while (!event.Wait(1000ms))
673 if (g_fontManager.IsUpdating())
674 CServiceBroker::GetRenderSystem()->ShowSplash(std::string(iDots, ' ') + localizedStr +
675 std::string(iDots, '.'));
677 if (iDots == 3)
678 iDots = 1;
679 else
680 ++iDots;
682 CServiceBroker::GetRenderSystem()->ShowSplash("");
684 // GUI depends on seek handler
685 GetComponent<CApplicationPlayer>()->GetSeekHandler().Configure();
687 const auto skinHandling = GetComponent<CApplicationSkinHandling>();
689 bool uiInitializationFinished = false;
691 if (CServiceBroker::GetGUI()->GetWindowManager().Initialized())
693 const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
695 CServiceBroker::GetGUI()->GetWindowManager().CreateWindows();
697 skinHandling->m_confirmSkinChange = false;
699 std::vector<AddonInfoPtr> incompatibleAddons;
700 event.Reset();
702 // Addon migration
703 if (CServiceBroker::GetAddonMgr().GetIncompatibleEnabledAddonInfos(incompatibleAddons))
705 if (CAddonSystemSettings::GetInstance().GetAddonAutoUpdateMode() == AUTO_UPDATES_ON)
707 CServiceBroker::GetJobManager()->Submit(
708 [&event, &incompatibleAddons]() {
709 if (CServiceBroker::GetRepositoryUpdater().CheckForUpdates())
710 CServiceBroker::GetRepositoryUpdater().Await();
712 incompatibleAddons = CServiceBroker::GetAddonMgr().MigrateAddons();
713 event.Set();
715 CJob::PRIORITY_DEDICATED);
716 localizedStr = g_localizeStrings.Get(24151);
717 iDots = 1;
718 while (!event.Wait(1000ms))
720 CServiceBroker::GetRenderSystem()->ShowSplash(std::string(iDots, ' ') + localizedStr +
721 std::string(iDots, '.'));
722 if (iDots == 3)
723 iDots = 1;
724 else
725 ++iDots;
727 m_incompatibleAddons = incompatibleAddons;
729 else
731 // If no update is active disable all incompatible addons during start
732 m_incompatibleAddons =
733 CServiceBroker::GetAddonMgr().DisableIncompatibleAddons(incompatibleAddons);
737 // Start splashscreen and load skin
738 CServiceBroker::GetRenderSystem()->ShowSplash("");
739 skinHandling->m_confirmSkinChange = true;
741 auto setting = settings->GetSetting(CSettings::SETTING_LOOKANDFEEL_SKIN);
742 if (!setting)
744 CLog::Log(LOGFATAL, "Failed to load setting for: {}", CSettings::SETTING_LOOKANDFEEL_SKIN);
745 return false;
748 CServiceBroker::RegisterTextureCache(std::make_shared<CTextureCache>());
750 std::string skinId = settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN);
751 if (!skinHandling->LoadSkin(skinId))
753 CLog::Log(LOGERROR, "Failed to load skin '{}'", skinId);
754 std::string defaultSkin =
755 std::static_pointer_cast<const CSettingString>(setting)->GetDefault();
756 if (!skinHandling->LoadSkin(defaultSkin))
758 CLog::Log(LOGFATAL, "Default skin '{}' could not be loaded! Terminating..", defaultSkin);
759 return false;
763 // initialize splash window after splash screen disappears
764 // because we need a real window in the background which gets
765 // rendered while we load the main window or enter the master lock key
766 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SPLASH);
768 if (settings->GetBool(CSettings::SETTING_MASTERLOCK_STARTUPLOCK) &&
769 profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
770 !profileManager->GetMasterProfile().getLockCode().empty())
772 g_passwordManager.CheckStartUpLock();
775 // check if we should use the login screen
776 if (profileManager->UsingLoginScreen())
778 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_LOGIN_SCREEN);
780 else
782 // activate the configured start window
783 int firstWindow = g_SkinInfo->GetFirstWindow();
784 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(firstWindow);
786 if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_STARTUP_ANIM))
788 CLog::Log(LOGWARNING, "CApplication::Initialize - startup.xml taints init process");
791 // the startup window is considered part of the initialization as it most likely switches to the final window
792 uiInitializationFinished = firstWindow != WINDOW_STARTUP_ANIM;
795 else //No GUI Created
797 uiInitializationFinished = true;
800 CJSONRPC::Initialize();
802 CServiceBroker::RegisterSpeechRecognition(speech::ISpeechRecognition::CreateInstance());
804 if (!m_ServiceManager->InitStageThree(profileManager))
806 CLog::Log(LOGERROR, "Application - Init3 failed");
809 g_sysinfo.Refresh();
811 CLog::Log(LOGINFO, "removing tempfiles");
812 CUtil::RemoveTempFiles();
814 if (!profileManager->UsingLoginScreen())
816 UpdateLibraries();
817 SetLoggingIn(false);
820 m_slowTimer.StartZero();
822 // register action listeners
823 const auto appListener = GetComponent<CApplicationActionListeners>();
824 const auto appPlayer = GetComponent<CApplicationPlayer>();
825 appListener->RegisterActionListener(&appPlayer->GetSeekHandler());
826 appListener->RegisterActionListener(&CPlayerController::GetInstance());
828 CServiceBroker::GetRepositoryUpdater().Start();
829 if (!profileManager->UsingLoginScreen())
830 CServiceBroker::GetServiceAddons().Start();
832 CLog::Log(LOGINFO, "initialize done");
834 const auto appPower = GetComponent<CApplicationPowerHandling>();
835 appPower->CheckOSScreenSaverInhibitionSetting();
836 // reset our screensaver (starts timers etc.)
837 appPower->ResetScreenSaver();
839 // if the user interfaces has been fully initialized let everyone know
840 if (uiInitializationFinished)
842 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UI_READY);
843 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
846 return true;
849 bool CApplication::OnSettingsSaving() const
851 // don't save settings when we're busy stopping the application
852 // a lot of screens try to save settings on deinit and deinit is
853 // called for every screen when the application is stopping
854 return !m_bStop;
857 void CApplication::Render()
859 // do not render if we are stopped or in background
860 if (m_bStop)
861 return;
863 const auto appPlayer = GetComponent<CApplicationPlayer>();
864 const auto appPower = GetComponent<CApplicationPowerHandling>();
866 bool hasRendered = false;
868 // Whether externalplayer is playing and we're unfocused
869 bool extPlayerActive = appPlayer->IsExternalPlaying() && !m_AppFocused;
871 if (!extPlayerActive && CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() &&
872 !appPlayer->IsPausedPlayback())
874 appPower->ResetScreenSaver();
877 if (!CServiceBroker::GetRenderSystem()->BeginRender())
878 return;
880 // render gui layer
881 if (appPower->GetRenderGUI() && !m_skipGuiRender)
883 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode())
885 CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_LEFT);
886 hasRendered |= CServiceBroker::GetGUI()->GetWindowManager().Render();
888 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() != RENDER_STEREO_MODE_MONO)
890 CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_RIGHT);
891 hasRendered |= CServiceBroker::GetGUI()->GetWindowManager().Render();
893 CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_OFF);
895 else
897 hasRendered |= CServiceBroker::GetGUI()->GetWindowManager().Render();
899 // execute post rendering actions (finalize window closing)
900 CServiceBroker::GetGUI()->GetWindowManager().AfterRender();
902 m_lastRenderTime = std::chrono::steady_clock::now();
905 // render video layer
906 CServiceBroker::GetGUI()->GetWindowManager().RenderEx();
908 CServiceBroker::GetRenderSystem()->EndRender();
910 // reset our info cache - we do this at the end of Render so that it is
911 // fresh for the next process(), or after a windowclose animation (where process()
912 // isn't called)
913 CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager();
914 infoMgr.ResetCache();
915 infoMgr.GetInfoProviders().GetGUIControlsInfoProvider().ResetContainerMovingCache();
917 if (hasRendered)
919 infoMgr.GetInfoProviders().GetSystemInfoProvider().UpdateFPS();
922 CServiceBroker::GetWinSystem()->GetGfxContext().Flip(hasRendered,
923 appPlayer->IsRenderingVideoLayer());
925 CTimeUtils::UpdateFrameTime(hasRendered);
928 bool CApplication::OnAction(const CAction &action)
930 // special case for switching between GUI & fullscreen mode.
931 if (action.GetID() == ACTION_SHOW_GUI)
932 { // Switch to fullscreen mode if we can
933 CGUIComponent* gui = CServiceBroker::GetGUI();
934 if (gui)
936 if (gui->GetWindowManager().SwitchToFullScreen())
938 GetComponent<CApplicationPowerHandling>()->m_navigationTimer.StartZero();
939 return true;
944 const auto appPlayer = GetComponent<CApplicationPlayer>();
946 if (action.GetID() == ACTION_TOGGLE_FULLSCREEN)
948 CServiceBroker::GetWinSystem()->GetGfxContext().ToggleFullScreen();
949 appPlayer->TriggerUpdateResolution();
950 return true;
953 if (action.IsMouse())
954 CServiceBroker::GetInputManager().SetMouseActive(true);
956 if (action.GetID() == ACTION_CREATE_EPISODE_BOOKMARK)
958 CGUIDialogVideoBookmarks::OnAddEpisodeBookmark();
960 if (action.GetID() == ACTION_CREATE_BOOKMARK)
962 CGUIDialogVideoBookmarks::OnAddBookmark();
965 // The action PLAYPAUSE behaves as ACTION_PAUSE if we are currently
966 // playing or ACTION_PLAYER_PLAY if we are seeking (FF/RW) or not playing.
967 if (action.GetID() == ACTION_PLAYER_PLAYPAUSE)
969 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
970 if ((appPlayer->IsPlaying() && appPlayer->GetPlaySpeed() == 1) ||
971 (slideShow.InSlideShow() && !slideShow.IsPaused()))
972 return OnAction(CAction(ACTION_PAUSE));
973 else
974 return OnAction(CAction(ACTION_PLAYER_PLAY));
977 //if the action would start or stop inertial scrolling
978 //by gesture - bypass the normal OnAction handler of current window
979 if( !m_pInertialScrollingHandler->CheckForInertialScrolling(&action) )
981 // in normal case
982 // just pass the action to the current window and let it handle it
983 if (CServiceBroker::GetGUI()->GetWindowManager().OnAction(action))
985 GetComponent<CApplicationPowerHandling>()->ResetNavigationTimer();
986 return true;
990 // handle extra global presses
992 // notify action listeners
993 if (GetComponent<CApplicationActionListeners>()->NotifyActionListeners(action))
994 return true;
996 // screenshot : take a screenshot :)
997 if (action.GetID() == ACTION_TAKE_SCREENSHOT)
999 CScreenShot::TakeScreenshot();
1000 return true;
1002 // Display HDR : toggle HDR on/off
1003 if (action.GetID() == ACTION_HDR_TOGGLE)
1005 // Only enables manual HDR toggle if no video is playing or auto HDR switch is disabled
1006 if (appPlayer->IsPlayingVideo() && CServiceBroker::GetWinSystem()->IsHDRDisplaySettingEnabled())
1007 return true;
1009 HDR_STATUS hdrStatus = CServiceBroker::GetWinSystem()->ToggleHDR();
1011 if (hdrStatus == HDR_STATUS::HDR_OFF)
1013 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(34220),
1014 g_localizeStrings.Get(34221));
1016 else if (hdrStatus == HDR_STATUS::HDR_ON)
1018 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(34220),
1019 g_localizeStrings.Get(34222));
1021 return true;
1023 // Tone Mapping : switch to next tone map method
1024 if (action.GetID() == ACTION_CYCLE_TONEMAP_METHOD)
1026 // Only enables tone mapping switch if display is not HDR capable or HDR is not enabled
1027 if (CServiceBroker::GetWinSystem()->IsHDRDisplaySettingEnabled())
1028 return true;
1030 if (appPlayer->IsPlayingVideo())
1032 CVideoSettings vs = appPlayer->GetVideoSettings();
1033 vs.m_ToneMapMethod = static_cast<ETONEMAPMETHOD>(static_cast<int>(vs.m_ToneMapMethod) + 1);
1034 if (vs.m_ToneMapMethod >= VS_TONEMAPMETHOD_MAX)
1035 vs.m_ToneMapMethod =
1036 static_cast<ETONEMAPMETHOD>(static_cast<int>(VS_TONEMAPMETHOD_OFF) + 1);
1038 appPlayer->SetVideoSettings(vs);
1040 int code = 0;
1041 switch (vs.m_ToneMapMethod)
1043 case VS_TONEMAPMETHOD_REINHARD:
1044 code = 36555;
1045 break;
1046 case VS_TONEMAPMETHOD_ACES:
1047 code = 36557;
1048 break;
1049 case VS_TONEMAPMETHOD_HABLE:
1050 code = 36558;
1051 break;
1052 default:
1053 throw std::logic_error("Tonemapping method not found. Did you forget to add a mapping?");
1055 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(34224),
1056 g_localizeStrings.Get(code), 1000, false, 500);
1058 return true;
1060 // built in functions : execute the built-in
1061 if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
1063 if (!CBuiltins::GetInstance().IsSystemPowerdownCommand(action.GetName()) ||
1064 CServiceBroker::GetPVRManager().Get<PVR::GUI::PowerManagement>().CanSystemPowerdown())
1066 CBuiltins::GetInstance().Execute(action.GetName());
1067 GetComponent<CApplicationPowerHandling>()->ResetNavigationTimer();
1069 return true;
1072 // reload keymaps
1073 if (action.GetID() == ACTION_RELOAD_KEYMAPS)
1074 CServiceBroker::GetInputManager().ReloadKeymaps();
1076 // show info : Shows the current video or song information
1077 if (action.GetID() == ACTION_SHOW_INFO)
1079 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().ToggleShowInfo();
1080 return true;
1083 if (action.GetID() == ACTION_SET_RATING && appPlayer->IsPlayingAudio())
1085 int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_itemCurrentFile->GetMusicInfoTag()->GetUserrating());
1086 if (userrating < 0) // Nothing selected, so user rating unchanged
1087 return true;
1088 userrating = std::min(userrating, 10);
1089 if (userrating != m_itemCurrentFile->GetMusicInfoTag()->GetUserrating())
1091 m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating);
1092 // Mirror changes to GUI item
1093 CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
1095 // Asynchronously update song userrating in music library
1096 MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, userrating);
1098 // Tell all windows (e.g. playlistplayer, media windows) to update the fileitem
1099 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
1100 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1102 return true;
1105 else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) &&
1106 appPlayer->IsPlayingAudio())
1108 int userrating = m_itemCurrentFile->GetMusicInfoTag()->GetUserrating();
1109 bool needsUpdate(false);
1110 if (userrating > 0 && action.GetID() == ACTION_DECREASE_RATING)
1112 m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating - 1);
1113 needsUpdate = true;
1115 else if (userrating < 10 && action.GetID() == ACTION_INCREASE_RATING)
1117 m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating + 1);
1118 needsUpdate = true;
1120 if (needsUpdate)
1122 // Mirror changes to current GUI item
1123 CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
1125 // Asynchronously update song userrating in music library
1126 MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, m_itemCurrentFile->GetMusicInfoTag()->GetUserrating());
1128 // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
1129 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
1130 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1133 return true;
1135 else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) &&
1136 appPlayer->IsPlayingVideo())
1138 int rating = m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating;
1139 bool needsUpdate(false);
1140 if (rating > 1 && action.GetID() == ACTION_DECREASE_RATING)
1142 m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating - 1;
1143 needsUpdate = true;
1145 else if (rating < 10 && action.GetID() == ACTION_INCREASE_RATING)
1147 m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating + 1;
1148 needsUpdate = true;
1150 if (needsUpdate)
1152 // Mirror changes to GUI item
1153 CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
1155 CVideoDatabase db;
1156 if (db.Open())
1158 db.SetVideoUserRating(m_itemCurrentFile->GetVideoInfoTag()->m_iDbId,
1159 m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating,
1160 m_itemCurrentFile->GetVideoInfoTag()->m_type);
1161 db.Close();
1163 // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
1164 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
1165 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1167 return true;
1170 // Now check with the playlist player if action can be handled.
1171 // In case of ACTION_PREV_ITEM, we only allow the playlist player to take it if we're less than ACTION_PREV_ITEM_THRESHOLD seconds into playback.
1172 if (!(action.GetID() == ACTION_PREV_ITEM && appPlayer->CanSeek() &&
1173 GetTime() > ACTION_PREV_ITEM_THRESHOLD))
1175 if (CServiceBroker::GetPlaylistPlayer().OnAction(action))
1176 return true;
1179 // Now check with the player if action can be handled.
1180 bool bIsPlayingPVRChannel = (CServiceBroker::GetPVRManager().IsStarted() &&
1181 CurrentFileItem().IsPVRChannel());
1183 bool bNotifyPlayer = false;
1184 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
1185 bNotifyPlayer = true;
1186 else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME)
1187 bNotifyPlayer = true;
1188 else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION && bIsPlayingPVRChannel)
1189 bNotifyPlayer = true;
1190 else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_DIALOG_VIDEO_OSD ||
1191 (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_DIALOG_MUSIC_OSD && bIsPlayingPVRChannel))
1193 switch (action.GetID())
1195 case ACTION_NEXT_ITEM:
1196 case ACTION_PREV_ITEM:
1197 case ACTION_CHANNEL_UP:
1198 case ACTION_CHANNEL_DOWN:
1199 bNotifyPlayer = true;
1200 break;
1201 default:
1202 break;
1205 else if (action.GetID() == ACTION_STOP)
1206 bNotifyPlayer = true;
1208 if (bNotifyPlayer)
1210 if (appPlayer->OnAction(action))
1211 return true;
1214 // stop : stops playing current audio song
1215 if (action.GetID() == ACTION_STOP)
1217 StopPlaying();
1218 return true;
1221 // In case the playlist player nor the player didn't handle PREV_ITEM, because we are past the ACTION_PREV_ITEM_THRESHOLD secs limit.
1222 // If so, we just jump to the start of the track.
1223 if (action.GetID() == ACTION_PREV_ITEM && appPlayer->CanSeek())
1225 SeekTime(0);
1226 appPlayer->SetPlaySpeed(1);
1227 return true;
1230 // forward action to graphic context and see if it can handle it
1231 if (CServiceBroker::GetGUI()->GetStereoscopicsManager().OnAction(action))
1232 return true;
1234 if (appPlayer->IsPlaying())
1236 // forward channel switches to the player - he knows what to do
1237 if (action.GetID() == ACTION_CHANNEL_UP || action.GetID() == ACTION_CHANNEL_DOWN)
1239 appPlayer->OnAction(action);
1240 return true;
1243 // pause : toggle pause action
1244 if (action.GetID() == ACTION_PAUSE)
1246 appPlayer->Pause();
1247 // go back to normal play speed on unpause
1248 if (!appPlayer->IsPaused() && appPlayer->GetPlaySpeed() != 1)
1249 appPlayer->SetPlaySpeed(1);
1251 CGUIComponent *gui = CServiceBroker::GetGUI();
1252 if (gui)
1253 gui->GetAudioManager().Enable(appPlayer->IsPaused());
1254 return true;
1256 // play: unpause or set playspeed back to normal
1257 if (action.GetID() == ACTION_PLAYER_PLAY)
1259 // if currently paused - unpause
1260 if (appPlayer->IsPaused())
1261 return OnAction(CAction(ACTION_PAUSE));
1262 // if we do a FF/RW then go back to normal speed
1263 if (appPlayer->GetPlaySpeed() != 1)
1264 appPlayer->SetPlaySpeed(1);
1265 return true;
1267 if (!appPlayer->IsPaused())
1269 if (action.GetID() == ACTION_PLAYER_FORWARD || action.GetID() == ACTION_PLAYER_REWIND)
1271 float playSpeed = appPlayer->GetPlaySpeed();
1273 if (action.GetID() == ACTION_PLAYER_REWIND && (playSpeed == 1)) // Enables Rewinding
1274 playSpeed *= -2;
1275 else if (action.GetID() == ACTION_PLAYER_REWIND && playSpeed > 1) //goes down a notch if you're FFing
1276 playSpeed /= 2;
1277 else if (action.GetID() == ACTION_PLAYER_FORWARD && playSpeed < 1) //goes up a notch if you're RWing
1278 playSpeed /= 2;
1279 else
1280 playSpeed *= 2;
1282 if (action.GetID() == ACTION_PLAYER_FORWARD && playSpeed == -1) //sets iSpeed back to 1 if -1 (didn't plan for a -1)
1283 playSpeed = 1;
1284 if (playSpeed > 32 || playSpeed < -32)
1285 playSpeed = 1;
1287 appPlayer->SetPlaySpeed(playSpeed);
1288 return true;
1290 else if ((action.GetAmount() || appPlayer->GetPlaySpeed() != 1) &&
1291 (action.GetID() == ACTION_ANALOG_REWIND || action.GetID() == ACTION_ANALOG_FORWARD))
1293 // calculate the speed based on the amount the button is held down
1294 int iPower = (int)(action.GetAmount() * MAX_FFWD_SPEED + 0.5f);
1295 // amount can be negative, for example rewind and forward share the same axis
1296 iPower = std::abs(iPower);
1297 // returns 0 -> MAX_FFWD_SPEED
1298 int iSpeed = 1 << iPower;
1299 if (iSpeed != 1 && action.GetID() == ACTION_ANALOG_REWIND)
1300 iSpeed = -iSpeed;
1301 appPlayer->SetPlaySpeed(static_cast<float>(iSpeed));
1302 if (iSpeed == 1)
1303 CLog::Log(LOGDEBUG,"Resetting playspeed");
1304 return true;
1306 else if (action.GetID() == ACTION_PLAYER_INCREASE_TEMPO)
1308 CPlayerUtils::AdvanceTempoStep(appPlayer, TempoStepChange::INCREASE);
1309 return true;
1311 else if (action.GetID() == ACTION_PLAYER_DECREASE_TEMPO)
1313 CPlayerUtils::AdvanceTempoStep(appPlayer, TempoStepChange::DECREASE);
1314 return true;
1317 // allow play to unpause
1318 else
1320 if (action.GetID() == ACTION_PLAYER_PLAY)
1322 // unpause, and set the playspeed back to normal
1323 appPlayer->Pause();
1325 CGUIComponent *gui = CServiceBroker::GetGUI();
1326 if (gui)
1327 gui->GetAudioManager().Enable(appPlayer->IsPaused());
1329 appPlayer->SetPlaySpeed(1);
1330 return true;
1336 if (action.GetID() == ACTION_SWITCH_PLAYER)
1338 const CPlayerCoreFactory &playerCoreFactory = m_ServiceManager->GetPlayerCoreFactory();
1340 if (appPlayer->IsPlaying())
1342 std::vector<std::string> players;
1343 CFileItem item(*m_itemCurrentFile.get());
1344 playerCoreFactory.GetPlayers(item, players);
1345 std::string player = playerCoreFactory.SelectPlayerDialog(players);
1346 if (!player.empty())
1348 item.SetStartOffset(CUtil::ConvertSecsToMilliSecs(GetTime()));
1349 PlayFile(item, player, true);
1352 else
1354 std::vector<std::string> players;
1355 playerCoreFactory.GetRemotePlayers(players);
1356 std::string player = playerCoreFactory.SelectPlayerDialog(players);
1357 if (!player.empty())
1359 PlayFile(CFileItem(), player, false);
1364 if (CServiceBroker::GetPeripherals().OnAction(action))
1365 return true;
1367 if (action.GetID() == ACTION_MUTE)
1369 const auto appVolume = GetComponent<CApplicationVolumeHandling>();
1370 appVolume->ToggleMute();
1371 appVolume->ShowVolumeBar(&action);
1372 return true;
1375 if (action.GetID() == ACTION_TOGGLE_DIGITAL_ANALOG)
1377 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
1378 bool passthrough = settings->GetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH);
1379 settings->SetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH, !passthrough);
1381 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SETTINGS_SYSTEM)
1383 CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0,0,WINDOW_INVALID,CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
1384 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1386 return true;
1389 // Check for global volume control
1390 if ((action.GetAmount() && (action.GetID() == ACTION_VOLUME_UP || action.GetID() == ACTION_VOLUME_DOWN)) || action.GetID() == ACTION_VOLUME_SET)
1392 const auto appVolume = GetComponent<CApplicationVolumeHandling>();
1393 if (!appPlayer->IsPassthrough())
1395 if (appVolume->IsMuted())
1396 appVolume->UnMute();
1397 float volume = appVolume->GetVolumeRatio();
1398 int volumesteps = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOOUTPUT_VOLUMESTEPS);
1399 // sanity check
1400 if (volumesteps == 0)
1401 volumesteps = 90;
1403 // Android has steps based on the max available volume level
1404 #if defined(TARGET_ANDROID)
1405 float step = (CApplicationVolumeHandling::VOLUME_MAXIMUM -
1406 CApplicationVolumeHandling::VOLUME_MINIMUM) /
1407 CXBMCApp::GetMaxSystemVolume();
1408 #else
1409 float step = (CApplicationVolumeHandling::VOLUME_MAXIMUM -
1410 CApplicationVolumeHandling::VOLUME_MINIMUM) /
1411 volumesteps;
1413 if (action.GetRepeat())
1414 step *= action.GetRepeat() * 50; // 50 fps
1415 #endif
1416 if (action.GetID() == ACTION_VOLUME_UP)
1417 volume += action.GetAmount() * action.GetAmount() * step;
1418 else if (action.GetID() == ACTION_VOLUME_DOWN)
1419 volume -= action.GetAmount() * action.GetAmount() * step;
1420 else
1421 volume = action.GetAmount() * step;
1422 if (volume != appVolume->GetVolumeRatio())
1423 appVolume->SetVolume(volume, false);
1425 // show visual feedback of volume or passthrough indicator
1426 appVolume->ShowVolumeBar(&action);
1427 return true;
1430 if (action.GetID() == ACTION_GUIPROFILE_BEGIN)
1432 CGUIControlProfiler::Instance().SetOutputFile(CSpecialProtocol::TranslatePath("special://home/guiprofiler.xml"));
1433 CGUIControlProfiler::Instance().Start();
1434 return true;
1436 if (action.GetID() == ACTION_SHOW_PLAYLIST)
1438 const PLAYLIST::Id playlistId = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
1439 if (playlistId == PLAYLIST::TYPE_VIDEO &&
1440 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_VIDEO_PLAYLIST)
1442 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_PLAYLIST);
1444 else if (playlistId == PLAYLIST::TYPE_MUSIC &&
1445 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() !=
1446 WINDOW_MUSIC_PLAYLIST)
1448 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST);
1450 return true;
1452 return false;
1455 int CApplication::GetMessageMask()
1457 return TMSG_MASK_APPLICATION;
1460 void CApplication::OnApplicationMessage(ThreadMessage* pMsg)
1462 uint32_t msg = pMsg->dwMessage;
1463 if (msg == TMSG_SYSTEM_POWERDOWN)
1465 if (CServiceBroker::GetPVRManager().Get<PVR::GUI::PowerManagement>().CanSystemPowerdown())
1466 msg = pMsg->param1; // perform requested shutdown action
1467 else
1468 return; // no shutdown
1471 const auto appPlayer = GetComponent<CApplicationPlayer>();
1473 switch (msg)
1475 case TMSG_POWERDOWN:
1476 if (Stop(EXITCODE_POWERDOWN))
1477 CServiceBroker::GetPowerManager().Powerdown();
1478 break;
1480 case TMSG_QUIT:
1481 Stop(EXITCODE_QUIT);
1482 break;
1484 case TMSG_SHUTDOWN:
1485 GetComponent<CApplicationPowerHandling>()->HandleShutdownMessage();
1486 break;
1488 case TMSG_RENDERER_FLUSH:
1489 appPlayer->FlushRenderer();
1490 break;
1492 case TMSG_HIBERNATE:
1493 CServiceBroker::GetPowerManager().Hibernate();
1494 break;
1496 case TMSG_SUSPEND:
1497 CServiceBroker::GetPowerManager().Suspend();
1498 break;
1500 case TMSG_RESTART:
1501 case TMSG_RESET:
1502 if (Stop(EXITCODE_REBOOT))
1503 CServiceBroker::GetPowerManager().Reboot();
1504 break;
1506 case TMSG_RESTARTAPP:
1507 #if defined(TARGET_WINDOWS) || defined(TARGET_LINUX)
1508 Stop(EXITCODE_RESTARTAPP);
1509 #endif
1510 break;
1512 case TMSG_INHIBITIDLESHUTDOWN:
1513 GetComponent<CApplicationPowerHandling>()->InhibitIdleShutdown(pMsg->param1 != 0);
1514 break;
1516 case TMSG_INHIBITSCREENSAVER:
1517 GetComponent<CApplicationPowerHandling>()->InhibitScreenSaver(pMsg->param1 != 0);
1518 break;
1520 case TMSG_ACTIVATESCREENSAVER:
1521 GetComponent<CApplicationPowerHandling>()->ActivateScreenSaver();
1522 break;
1524 case TMSG_RESETSCREENSAVER:
1525 GetComponent<CApplicationPowerHandling>()->m_bResetScreenSaver = true;
1526 break;
1528 case TMSG_VOLUME_SHOW:
1530 CAction action(pMsg->param1);
1531 GetComponent<CApplicationVolumeHandling>()->ShowVolumeBar(&action);
1533 break;
1535 #ifdef TARGET_ANDROID
1536 case TMSG_DISPLAY_SETUP:
1537 // We might come from a refresh rate switch destroying the native window; use the context resolution
1538 *static_cast<bool*>(pMsg->lpVoid) = InitWindow(CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution());
1539 GetComponent<CApplicationPowerHandling>()->SetRenderGUI(true);
1540 break;
1542 case TMSG_DISPLAY_DESTROY:
1543 *static_cast<bool*>(pMsg->lpVoid) = CServiceBroker::GetWinSystem()->DestroyWindow();
1544 GetComponent<CApplicationPowerHandling>()->SetRenderGUI(false);
1545 break;
1546 #endif
1548 case TMSG_START_ANDROID_ACTIVITY:
1550 #if defined(TARGET_ANDROID)
1551 if (pMsg->params.size())
1553 CXBMCApp::StartActivity(pMsg->params[0], pMsg->params.size() > 1 ? pMsg->params[1] : "",
1554 pMsg->params.size() > 2 ? pMsg->params[2] : "",
1555 pMsg->params.size() > 3 ? pMsg->params[3] : "",
1556 pMsg->params.size() > 4 ? pMsg->params[4] : "",
1557 pMsg->params.size() > 5 ? pMsg->params[5] : "",
1558 pMsg->params.size() > 6 ? pMsg->params[6] : "",
1559 pMsg->params.size() > 7 ? pMsg->params[7] : "",
1560 pMsg->params.size() > 8 ? pMsg->params[8] : "");
1562 #endif
1564 break;
1566 case TMSG_NETWORKMESSAGE:
1567 m_ServiceManager->GetNetwork().NetworkMessage(static_cast<CNetworkBase::EMESSAGE>(pMsg->param1),
1568 pMsg->param2);
1569 break;
1571 case TMSG_SETLANGUAGE:
1572 SetLanguage(pMsg->strParam);
1573 break;
1576 case TMSG_SWITCHTOFULLSCREEN:
1578 CGUIComponent* gui = CServiceBroker::GetGUI();
1579 if (gui)
1580 gui->GetWindowManager().SwitchToFullScreen(true);
1581 break;
1583 case TMSG_VIDEORESIZE:
1585 XBMC_Event newEvent = {};
1586 newEvent.type = XBMC_VIDEORESIZE;
1587 newEvent.resize.w = pMsg->param1;
1588 newEvent.resize.h = pMsg->param2;
1589 OnEvent(newEvent);
1590 CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
1592 break;
1594 case TMSG_SETVIDEORESOLUTION:
1595 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(static_cast<RESOLUTION>(pMsg->param1), pMsg->param2 == 1);
1596 break;
1598 case TMSG_TOGGLEFULLSCREEN:
1599 CServiceBroker::GetWinSystem()->GetGfxContext().ToggleFullScreen();
1600 appPlayer->TriggerUpdateResolution();
1601 break;
1603 case TMSG_MOVETOSCREEN:
1604 CServiceBroker::GetWinSystem()->MoveToScreen(static_cast<int>(pMsg->param1));
1605 break;
1607 case TMSG_MINIMIZE:
1608 CServiceBroker::GetWinSystem()->Minimize();
1609 break;
1611 case TMSG_EXECUTE_OS:
1612 // Suspend AE temporarily so exclusive or hog-mode sinks
1613 // don't block external player's access to audio device
1614 IAE *audioengine;
1615 audioengine = CServiceBroker::GetActiveAE();
1616 if (audioengine)
1618 if (!audioengine->Suspend())
1620 CLog::Log(LOGINFO, "{}: Failed to suspend AudioEngine before launching external program",
1621 __FUNCTION__);
1624 #if defined(TARGET_DARWIN)
1625 CLog::Log(LOGINFO, "ExecWait is not implemented on this platform");
1626 #elif defined(TARGET_POSIX)
1627 CUtil::RunCommandLine(pMsg->strParam, (pMsg->param1 == 1));
1628 #elif defined(TARGET_WINDOWS)
1629 CWIN32Util::XBMCShellExecute(pMsg->strParam.c_str(), (pMsg->param1 == 1));
1630 #endif
1631 // Resume AE processing of XBMC native audio
1632 if (audioengine)
1634 if (!audioengine->Resume())
1636 CLog::Log(LOGFATAL, "{}: Failed to restart AudioEngine after return from external player",
1637 __FUNCTION__);
1640 break;
1642 case TMSG_EXECUTE_SCRIPT:
1643 CScriptInvocationManager::GetInstance().ExecuteAsync(pMsg->strParam);
1644 break;
1646 case TMSG_EXECUTE_BUILT_IN:
1647 CBuiltins::GetInstance().Execute(pMsg->strParam);
1648 break;
1650 case TMSG_PICTURE_SHOW:
1652 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
1654 // stop playing file
1655 if (appPlayer->IsPlayingVideo())
1656 StopPlaying();
1658 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
1659 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
1661 const auto appPower = GetComponent<CApplicationPowerHandling>();
1662 appPower->ResetScreenSaver();
1663 appPower->WakeUpScreenSaverAndDPMS();
1665 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_SLIDESHOW)
1666 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
1667 if (URIUtils::IsZIP(pMsg->strParam) || URIUtils::IsRAR(pMsg->strParam)) // actually a cbz/cbr
1669 CFileItemList items;
1670 CURL pathToUrl;
1671 if (URIUtils::IsZIP(pMsg->strParam))
1672 pathToUrl = URIUtils::CreateArchivePath("zip", CURL(pMsg->strParam), "");
1673 else
1674 pathToUrl = URIUtils::CreateArchivePath("rar", CURL(pMsg->strParam), "");
1676 CUtil::GetRecursiveListing(pathToUrl.Get(), items, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), XFILE::DIR_FLAG_NO_FILE_DIRS);
1677 if (items.Size() > 0)
1679 slideShow.Reset();
1680 for (int i = 0; i<items.Size(); ++i)
1682 slideShow.Add(items[i].get());
1684 slideShow.Select(items[0]->GetPath());
1687 else
1689 CFileItem item(pMsg->strParam, false);
1690 slideShow.Reset();
1691 slideShow.Add(&item);
1692 slideShow.Select(pMsg->strParam);
1695 break;
1697 case TMSG_PICTURE_SLIDESHOW:
1699 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
1701 if (appPlayer->IsPlayingVideo())
1702 StopPlaying();
1704 slideShow.Reset();
1706 CFileItemList items;
1707 std::string strPath = pMsg->strParam;
1708 std::string extensions = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
1709 if (pMsg->param1)
1710 extensions += "|.tbn";
1711 CUtil::GetRecursiveListing(strPath, items, extensions);
1713 if (items.Size() > 0)
1715 for (int i = 0; i<items.Size(); ++i)
1716 slideShow.Add(items[i].get());
1717 slideShow.StartSlideShow(); //Start the slideshow!
1720 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_SLIDESHOW)
1722 if (items.Size() == 0)
1724 CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_SCREENSAVER_MODE, "screensaver.xbmc.builtin.dim");
1725 GetComponent<CApplicationPowerHandling>()->ActivateScreenSaver();
1727 else
1728 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
1732 break;
1734 case TMSG_LOADPROFILE:
1736 const int profile = pMsg->param1;
1737 if (profile >= 0)
1738 CServiceBroker::GetSettingsComponent()->GetProfileManager()->LoadProfile(static_cast<unsigned int>(profile));
1741 break;
1743 case TMSG_EVENT:
1745 if (pMsg->lpVoid)
1747 XBMC_Event* event = static_cast<XBMC_Event*>(pMsg->lpVoid);
1748 OnEvent(*event);
1749 delete event;
1752 break;
1754 case TMSG_UPDATE_PLAYER_ITEM:
1756 std::unique_ptr<CFileItem> item{static_cast<CFileItem*>(pMsg->lpVoid)};
1757 if (item)
1759 m_itemCurrentFile->UpdateInfo(*item);
1760 CServiceBroker::GetGUI()->GetInfoManager().UpdateCurrentItem(*m_itemCurrentFile);
1763 break;
1765 case TMSG_SET_VOLUME:
1767 const float volumedB = static_cast<float>(pMsg->param3);
1768 GetComponent<CApplicationVolumeHandling>()->SetVolume(volumedB);
1770 break;
1772 case TMSG_SET_MUTE:
1774 GetComponent<CApplicationVolumeHandling>()->SetMute(pMsg->param3 == 1 ? true : false);
1776 break;
1778 default:
1779 CLog::Log(LOGERROR, "{}: Unhandled threadmessage sent, {}", __FUNCTION__, msg);
1780 break;
1784 void CApplication::LockFrameMoveGuard()
1786 ++m_WaitingExternalCalls;
1787 m_frameMoveGuard.lock();
1788 ++m_ProcessedExternalCalls;
1789 CServiceBroker::GetWinSystem()->GetGfxContext().lock();
1792 void CApplication::UnlockFrameMoveGuard()
1794 --m_WaitingExternalCalls;
1795 CServiceBroker::GetWinSystem()->GetGfxContext().unlock();
1796 m_frameMoveGuard.unlock();
1799 void CApplication::FrameMove(bool processEvents, bool processGUI)
1801 const auto appPlayer = GetComponent<CApplicationPlayer>();
1802 bool renderGUI = GetComponent<CApplicationPowerHandling>()->GetRenderGUI();
1803 if (processEvents)
1805 // currently we calculate the repeat time (ie time from last similar keypress) just global as fps
1806 float frameTime = m_frameTime.GetElapsedSeconds();
1807 m_frameTime.StartZero();
1808 // never set a frametime less than 2 fps to avoid problems when debugging and on breaks
1809 if (frameTime > 0.5f)
1810 frameTime = 0.5f;
1812 if (processGUI && renderGUI)
1814 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
1815 // check if there are notifications to display
1816 CGUIDialogKaiToast *toast = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogKaiToast>(WINDOW_DIALOG_KAI_TOAST);
1817 if (toast && toast->DoWork())
1819 if (!toast->IsDialogRunning())
1821 toast->Open();
1826 HandlePortEvents();
1827 CServiceBroker::GetInputManager().Process(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(), frameTime);
1829 if (processGUI && renderGUI)
1831 m_pInertialScrollingHandler->ProcessInertialScroll(frameTime);
1832 appPlayer->GetSeekHandler().FrameMove();
1835 // Open the door for external calls e.g python exactly here.
1836 // Window size can be between 2 and 10ms and depends on number of continuous requests
1837 if (m_WaitingExternalCalls)
1839 CSingleExit ex(CServiceBroker::GetWinSystem()->GetGfxContext());
1840 m_frameMoveGuard.unlock();
1842 // Calculate a window size between 2 and 10ms, 4 continuous requests let the window grow by 1ms
1843 // When not playing video we allow it to increase to 80ms
1844 unsigned int max_sleep = 10;
1845 if (!appPlayer->IsPlayingVideo() || appPlayer->IsPausedPlayback())
1846 max_sleep = 80;
1847 unsigned int sleepTime = std::max(static_cast<unsigned int>(2), std::min(m_ProcessedExternalCalls >> 2, max_sleep));
1848 KODI::TIME::Sleep(std::chrono::milliseconds(sleepTime));
1849 m_frameMoveGuard.lock();
1850 m_ProcessedExternalDecay = 5;
1852 if (m_ProcessedExternalDecay && --m_ProcessedExternalDecay == 0)
1853 m_ProcessedExternalCalls = 0;
1856 if (processGUI && renderGUI)
1858 m_skipGuiRender = false;
1860 /*! @todo look into the possibility to use this for GBM
1861 int fps = 0;
1863 // This code reduces rendering fps of the GUI layer when playing videos in fullscreen mode
1864 // it makes only sense on architectures with multiple layers
1865 if (CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() && !m_appPlayer.IsPausedPlayback() && m_appPlayer.IsRenderingVideoLayer())
1866 fps = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_LIMITGUIUPDATE);
1868 auto now = std::chrono::steady_clock::now();
1870 auto frameTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastRenderTime).count();
1871 if (fps > 0 && frameTime * fps < 1000)
1872 m_skipGuiRender = true;
1875 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiSmartRedraw && m_guiRefreshTimer.IsTimePast())
1877 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_REFRESH_TIMER, 0, 0);
1878 m_guiRefreshTimer.Set(500ms);
1881 if (!m_bStop)
1883 if (!m_skipGuiRender)
1884 CServiceBroker::GetGUI()->GetWindowManager().Process(CTimeUtils::GetFrameTime());
1886 CServiceBroker::GetGUI()->GetWindowManager().FrameMove();
1889 appPlayer->FrameMove();
1891 // this will go away when render systems gets its own thread
1892 CServiceBroker::GetWinSystem()->DriveRenderLoop();
1896 void CApplication::ResetCurrentItem()
1898 m_itemCurrentFile->Reset();
1899 if (m_pGUI)
1900 m_pGUI->GetInfoManager().ResetCurrentItem();
1903 int CApplication::Run()
1905 CLog::Log(LOGINFO, "Running the application...");
1907 std::chrono::time_point<std::chrono::steady_clock> lastFrameTime;
1908 std::chrono::milliseconds frameTime;
1909 const unsigned int noRenderFrameTime = 15; // Simulates ~66fps
1911 CFileItemList& playlist = CServiceBroker::GetAppParams()->GetPlaylist();
1912 if (playlist.Size() > 0)
1914 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_MUSIC, playlist);
1915 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_MUSIC);
1916 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_PLAYLISTPLAYER_PLAY, -1);
1919 // Run the app
1920 while (!m_bStop)
1922 // Animate and render a frame
1924 lastFrameTime = std::chrono::steady_clock::now();
1925 Process();
1927 bool renderGUI = GetComponent<CApplicationPowerHandling>()->GetRenderGUI();
1928 if (!m_bStop)
1930 FrameMove(true, renderGUI);
1933 if (renderGUI && !m_bStop)
1935 Render();
1937 else if (!renderGUI)
1939 auto now = std::chrono::steady_clock::now();
1940 frameTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastFrameTime);
1941 if (frameTime.count() < noRenderFrameTime)
1942 KODI::TIME::Sleep(std::chrono::milliseconds(noRenderFrameTime - frameTime.count()));
1946 Cleanup();
1948 CLog::Log(LOGINFO, "Exiting the application...");
1949 return m_ExitCode;
1952 bool CApplication::Cleanup()
1956 ResetCurrentItem();
1957 StopPlaying();
1959 if (m_ServiceManager)
1960 m_ServiceManager->DeinitStageThree();
1962 CServiceBroker::UnregisterSpeechRecognition();
1964 CLog::Log(LOGINFO, "unload skin");
1965 GetComponent<CApplicationSkinHandling>()->UnloadSkin();
1967 CServiceBroker::UnregisterTextureCache();
1969 // stop all remaining scripts; must be done after skin has been unloaded,
1970 // not before some windows still need it when deinitializing during skin
1971 // unloading
1972 CScriptInvocationManager::GetInstance().Uninitialize();
1974 const auto appPower = GetComponent<CApplicationPowerHandling>();
1975 appPower->m_globalScreensaverInhibitor.Release();
1976 appPower->m_screensaverInhibitor.Release();
1978 CRenderSystemBase *renderSystem = CServiceBroker::GetRenderSystem();
1979 if (renderSystem)
1980 renderSystem->DestroyRenderSystem();
1982 CWinSystemBase *winSystem = CServiceBroker::GetWinSystem();
1983 if (winSystem)
1984 winSystem->DestroyWindow();
1986 if (m_pGUI)
1987 m_pGUI->GetWindowManager().DestroyWindows();
1989 CLog::Log(LOGINFO, "unload sections");
1991 // Shutdown as much as possible of the
1992 // application, to reduce the leaks dumped
1993 // to the vc output window before calling
1994 // _CrtDumpMemoryLeaks(). Most of the leaks
1995 // shown are no real leaks, as parts of the app
1996 // are still allocated.
1998 g_localizeStrings.Clear();
1999 g_LangCodeExpander.Clear();
2000 g_charsetConverter.clear();
2001 g_directoryCache.Clear();
2002 //CServiceBroker::GetInputManager().ClearKeymaps(); //! @todo
2003 CEventServer::RemoveInstance();
2004 CServiceBroker::GetPlaylistPlayer().Clear();
2006 if (m_ServiceManager)
2007 m_ServiceManager->DeinitStageTwo();
2009 #ifdef TARGET_POSIX
2010 CXHandle::DumpObjectTracker();
2012 #ifdef HAS_OPTICAL_DRIVE
2013 CLibcdio::ReleaseInstance();
2014 #endif
2015 #endif
2016 #ifdef _CRTDBG_MAP_ALLOC
2017 _CrtDumpMemoryLeaks();
2018 while(1); // execution ends
2019 #endif
2021 if (m_pGUI)
2023 m_pGUI->Deinit();
2024 m_pGUI.reset();
2027 if (winSystem)
2029 winSystem->DestroyWindowSystem();
2030 CServiceBroker::UnregisterWinSystem();
2031 winSystem = nullptr;
2032 m_pWinSystem.reset();
2035 // Cleanup was called more than once on exit during my tests
2036 if (m_ServiceManager)
2038 m_ServiceManager->DeinitStageOne();
2039 m_ServiceManager.reset();
2042 CServiceBroker::UnregisterKeyboardLayoutManager();
2044 CServiceBroker::UnregisterAppMessenger();
2046 CServiceBroker::UnregisterAnnouncementManager();
2047 m_pAnnouncementManager->Deinitialize();
2048 m_pAnnouncementManager.reset();
2050 CServiceBroker::UnregisterJobManager();
2051 CServiceBroker::UnregisterCPUInfo();
2053 UnregisterSettings();
2055 m_bInitializing = true;
2057 return true;
2059 catch (...)
2061 CLog::Log(LOGERROR, "Exception in CApplication::Cleanup()");
2062 return false;
2066 bool CApplication::Stop(int exitCode)
2068 #if defined(TARGET_ANDROID)
2069 // Note: On Android, the app must be stopped asynchronously, once Android has
2070 // signalled that the app shall be destroyed. See android_main() implementation.
2071 if (!CXBMCApp::Get().Stop(exitCode))
2072 return false;
2073 #endif
2075 CLog::Log(LOGINFO, "Stopping the application...");
2077 bool success = true;
2079 CLog::Log(LOGINFO, "Stopping player");
2080 const auto appPlayer = GetComponent<CApplicationPlayer>();
2081 appPlayer->ClosePlayer();
2084 // close inbound port
2085 CServiceBroker::UnregisterAppPort();
2086 XbmcThreads::EndTime<> timer(1000ms);
2087 while (m_pAppPort.use_count() > 1)
2089 KODI::TIME::Sleep(100ms);
2090 if (timer.IsTimePast())
2092 CLog::Log(LOGERROR, "CApplication::Stop - CAppPort still in use, app may crash");
2093 break;
2096 m_pAppPort.reset();
2101 m_frameMoveGuard.unlock();
2103 CVariant vExitCode(CVariant::VariantTypeObject);
2104 vExitCode["exitcode"] = exitCode;
2105 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::System, "OnQuit", vExitCode);
2107 // Abort any active screensaver
2108 GetComponent<CApplicationPowerHandling>()->WakeUpScreenSaverAndDPMS();
2110 g_alarmClock.StopThread();
2112 CLog::Log(LOGINFO, "Storing total System Uptime");
2113 g_sysinfo.SetTotalUptime(g_sysinfo.GetTotalUptime() + (int)(CTimeUtils::GetFrameTime() / 60000));
2115 // Update the settings information (volume, uptime etc. need saving)
2116 if (CFile::Exists(CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetSettingsFile()))
2118 CLog::Log(LOGINFO, "Saving settings");
2119 CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
2121 else
2122 CLog::Log(LOGINFO, "Not saving settings (settings.xml is not present)");
2124 // kodi may crash or deadlock during exit (shutdown / reboot) due to
2125 // either a bug in core or misbehaving addons. so try saving
2126 // skin settings early
2127 CLog::Log(LOGINFO, "Saving skin settings");
2128 if (g_SkinInfo != nullptr)
2129 g_SkinInfo->SaveSettings();
2131 m_bStop = true;
2132 // Add this here to keep the same ordering behaviour for now
2133 // Needs cleaning up
2134 CServiceBroker::GetAppMessenger()->Stop();
2135 m_AppFocused = false;
2136 m_ExitCode = exitCode;
2137 CLog::Log(LOGINFO, "Stopping all");
2139 // cancel any jobs from the jobmanager
2140 CServiceBroker::GetJobManager()->CancelJobs();
2142 // stop scanning before we kill the network and so on
2143 if (CMusicLibraryQueue::GetInstance().IsRunning())
2144 CMusicLibraryQueue::GetInstance().CancelAllJobs();
2146 if (CVideoLibraryQueue::GetInstance().IsRunning())
2147 CVideoLibraryQueue::GetInstance().CancelAllJobs();
2149 CServiceBroker::GetAppMessenger()->Cleanup();
2151 m_ServiceManager->GetNetwork().NetworkMessage(CNetworkBase::SERVICES_DOWN, 0);
2153 #ifdef HAS_ZEROCONF
2154 if(CZeroconfBrowser::IsInstantiated())
2156 CLog::Log(LOGINFO, "Stopping zeroconf browser");
2157 CZeroconfBrowser::GetInstance()->Stop();
2158 CZeroconfBrowser::ReleaseInstance();
2160 #endif
2162 for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
2163 vfsAddon->DisconnectAll();
2165 #if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
2166 smb.Deinit();
2167 #endif
2169 #if defined(TARGET_DARWIN_OSX) and defined(HAS_XBMCHELPER)
2170 if (XBMCHelper::GetInstance().IsAlwaysOn() == false)
2171 XBMCHelper::GetInstance().Stop();
2172 #endif
2174 // Stop services before unloading Python
2175 CServiceBroker::GetServiceAddons().Stop();
2177 // Stop any other python scripts that may be looping waiting for monitor.abortRequested()
2178 CScriptInvocationManager::GetInstance().StopRunningScripts();
2180 // unregister action listeners
2181 const auto appListener = GetComponent<CApplicationActionListeners>();
2182 appListener->UnregisterActionListener(&GetComponent<CApplicationPlayer>()->GetSeekHandler());
2183 appListener->UnregisterActionListener(&CPlayerController::GetInstance());
2185 CGUIComponent *gui = CServiceBroker::GetGUI();
2186 if (gui)
2187 gui->GetAudioManager().DeInitialize();
2189 // shutdown the AudioEngine
2190 CServiceBroker::UnregisterAE();
2191 m_pActiveAE->Shutdown();
2192 m_pActiveAE.reset();
2194 CLog::Log(LOGINFO, "Application stopped");
2196 catch (...)
2198 CLog::Log(LOGERROR, "Exception in CApplication::Stop()");
2199 success = false;
2202 cleanup_emu_environ();
2204 KODI::TIME::Sleep(200ms);
2206 return success;
2209 namespace
2211 class CCreateAndLoadPlayList : public IRunnable
2213 public:
2214 CCreateAndLoadPlayList(CFileItem& item, std::unique_ptr<PLAYLIST::CPlayList>& playlist)
2215 : m_item(item), m_playlist(playlist)
2219 void Run() override
2221 const std::unique_ptr<PLAYLIST::CPlayList> playlist(PLAYLIST::CPlayListFactory::Create(m_item));
2222 if (playlist)
2224 if (playlist->Load(m_item.GetPath()))
2225 *m_playlist = *playlist;
2229 private:
2230 CFileItem& m_item;
2231 std::unique_ptr<PLAYLIST::CPlayList>& m_playlist;
2233 } // namespace
2235 bool CApplication::PlayMedia(CFileItem& item, const std::string& player, PLAYLIST::Id playlistId)
2237 // if the item is a plugin we need to resolve the plugin paths
2238 if (URIUtils::HasPluginPath(item) && !XFILE::CPluginDirectory::GetResolvedPluginResult(item))
2239 return false;
2241 if (item.IsSmartPlayList())
2243 CFileItemList items;
2244 CUtil::GetRecursiveListing(item.GetPath(), items, "", DIR_FLAG_NO_FILE_DIRS);
2245 if (items.Size())
2247 CSmartPlaylist smartpl;
2248 //get name and type of smartplaylist, this will always succeed as GetDirectory also did this.
2249 smartpl.OpenAndReadName(item.GetURL());
2250 PLAYLIST::CPlayList playlist;
2251 playlist.Add(items);
2252 PLAYLIST::Id smartplPlaylistId = PLAYLIST::TYPE_VIDEO;
2254 if (smartpl.GetType() == "songs" || smartpl.GetType() == "albums" ||
2255 smartpl.GetType() == "artists")
2256 smartplPlaylistId = PLAYLIST::TYPE_MUSIC;
2258 return ProcessAndStartPlaylist(smartpl.GetName(), playlist, smartplPlaylistId);
2261 else if (item.IsPlayList() || item.IsInternetStream())
2263 // Not owner. Dialog auto-deletes itself.
2264 CGUIDialogCache* dlgCache =
2265 new CGUIDialogCache(5s, g_localizeStrings.Get(10214), item.GetLabel());
2267 //is or could be a playlist
2268 std::unique_ptr<PLAYLIST::CPlayList> playlist;
2269 CCreateAndLoadPlayList getPlaylist(item, playlist);
2270 bool cancelled = !CGUIDialogBusy::Wait(&getPlaylist, 100, true);
2272 if (dlgCache)
2274 dlgCache->Close();
2275 if (dlgCache->IsCanceled())
2276 cancelled = true;
2279 if (cancelled)
2280 return true;
2282 if (playlist)
2285 if (playlistId != PLAYLIST::TYPE_NONE)
2287 int track=0;
2288 if (item.HasProperty("playlist_starting_track"))
2289 track = (int)item.GetProperty("playlist_starting_track").asInteger();
2290 return ProcessAndStartPlaylist(item.GetPath(), *playlist, playlistId, track);
2292 else
2294 CLog::Log(LOGWARNING,
2295 "CApplication::PlayMedia called to play a playlist {} but no idea which playlist "
2296 "to use, playing first item",
2297 item.GetPath());
2298 if (playlist->size())
2299 return PlayFile(*(*playlist)[0], "", false);
2303 else if (item.IsPVR())
2305 return CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayMedia(item);
2308 CURL path(item.GetPath());
2309 if (path.GetProtocol() == "game")
2311 AddonPtr addon;
2312 if (CServiceBroker::GetAddonMgr().GetAddon(path.GetHostName(), addon, AddonType::GAMEDLL,
2313 OnlyEnabled::CHOICE_YES))
2315 CFileItem addonItem(addon);
2316 return PlayFile(addonItem, player, false);
2320 //nothing special just play
2321 return PlayFile(item, player, false);
2324 // PlayStack()
2325 // For playing a multi-file video. Particularly inefficient
2326 // on startup, as we are required to calculate the length
2327 // of each video, so we open + close each one in turn.
2328 // A faster calculation of video time would improve this
2329 // substantially.
2330 // return value: same with PlayFile()
2331 bool CApplication::PlayStack(CFileItem& item, bool bRestart)
2333 const auto stackHelper = GetComponent<CApplicationStackHelper>();
2334 if (!stackHelper->InitializeStack(item))
2335 return false;
2337 int startoffset = stackHelper->InitializeStackStartPartAndOffset(item);
2339 CFileItem selectedStackPart = stackHelper->GetCurrentStackPartFileItem();
2340 selectedStackPart.SetStartOffset(startoffset);
2342 if (item.HasProperty("savedplayerstate"))
2344 selectedStackPart.SetProperty("savedplayerstate", item.GetProperty("savedplayerstate")); // pass on to part
2345 item.ClearProperty("savedplayerstate");
2348 return PlayFile(selectedStackPart, "", true);
2351 bool CApplication::PlayFile(CFileItem item, const std::string& player, bool bRestart)
2353 // Ensure the MIME type has been retrieved for http:// and shout:// streams
2354 if (item.GetMimeType().empty())
2355 item.FillInMimeType();
2357 const auto appPlayer = GetComponent<CApplicationPlayer>();
2358 const auto stackHelper = GetComponent<CApplicationStackHelper>();
2360 if (!bRestart)
2362 // bRestart will be true when called from PlayStack(), skipping this block
2363 appPlayer->SetPlaySpeed(1);
2365 m_nextPlaylistItem = -1;
2366 stackHelper->Clear();
2368 if (item.IsVideo())
2369 CUtil::ClearSubtitles();
2372 if (item.IsDiscStub())
2374 return CServiceBroker::GetMediaManager().playStubFile(item);
2377 if (item.IsPlayList())
2378 return false;
2380 // Translate/Resolve the url if needed
2381 const std::unique_ptr<IDirectory> dir{CDirectoryFactory::Create(item)};
2382 if (dir && !dir->Resolve(item))
2384 return false;
2387 // if we have a stacked set of files, we need to setup our stack routines for
2388 // "seamless" seeking and total time of the movie etc.
2389 // will recall with restart set to true
2390 if (item.IsStack())
2391 return PlayStack(item, bRestart);
2393 CPlayerOptions options;
2395 if (item.HasProperty("StartPercent"))
2397 options.startpercent = item.GetProperty("StartPercent").asDouble();
2398 item.SetStartOffset(0);
2401 options.starttime = CUtil::ConvertMilliSecsToSecs(item.GetStartOffset());
2403 if (bRestart)
2405 // have to be set here due to playstack using this for starting the file
2406 if (item.HasVideoInfoTag())
2407 options.state = item.GetVideoInfoTag()->GetResumePoint().playerState;
2409 if (!bRestart || stackHelper->IsPlayingISOStack())
2411 // the following code block is only applicable when bRestart is false OR to ISO stacks
2413 if (item.IsVideo())
2415 // open the d/b and retrieve the bookmarks for the current movie
2416 CVideoDatabase dbs;
2417 dbs.Open();
2419 std::string path = item.GetPath();
2420 std::string videoInfoTagPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
2421 if (videoInfoTagPath.find("removable://") == 0 || item.IsVideoDb())
2422 path = videoInfoTagPath;
2424 // Note that we need to load the tag from database also if the item already has a tag,
2425 // because for example the (full) video info for strm files will be loaded here.
2426 dbs.LoadVideoInfo(path, *item.GetVideoInfoTag());
2428 if (item.HasProperty("savedplayerstate"))
2430 options.starttime = CUtil::ConvertMilliSecsToSecs(item.GetStartOffset());
2431 options.state = item.GetProperty("savedplayerstate").asString();
2432 item.ClearProperty("savedplayerstate");
2434 else if (item.GetStartOffset() == STARTOFFSET_RESUME)
2436 options.starttime = 0.0;
2437 if (item.IsResumePointSet())
2439 options.starttime = item.GetCurrentResumeTime();
2440 if (item.HasVideoInfoTag())
2441 options.state = item.GetVideoInfoTag()->GetResumePoint().playerState;
2443 else
2445 CBookmark bookmark;
2446 std::string path = item.GetPath();
2447 if (item.HasVideoInfoTag() && StringUtils::StartsWith(item.GetVideoInfoTag()->m_strFileNameAndPath, "removable://"))
2448 path = item.GetVideoInfoTag()->m_strFileNameAndPath;
2449 else if (item.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
2450 path = item.GetProperty("original_listitem_url").asString();
2451 if (dbs.GetResumeBookMark(path, bookmark))
2453 options.starttime = bookmark.timeInSeconds;
2454 options.state = bookmark.playerState;
2458 if (options.starttime == 0.0 && item.HasVideoInfoTag())
2460 // No resume point is set, but check if this item is part of a multi-episode file
2461 const CVideoInfoTag *tag = item.GetVideoInfoTag();
2463 if (tag->m_iBookmarkId > 0)
2465 CBookmark bookmark;
2466 dbs.GetBookMarkForEpisode(*tag, bookmark);
2467 options.starttime = bookmark.timeInSeconds;
2468 options.state = bookmark.playerState;
2472 else if (item.HasVideoInfoTag())
2474 const CVideoInfoTag *tag = item.GetVideoInfoTag();
2476 if (tag->m_iBookmarkId > 0)
2478 CBookmark bookmark;
2479 dbs.GetBookMarkForEpisode(*tag, bookmark);
2480 options.starttime = bookmark.timeInSeconds;
2481 options.state = bookmark.playerState;
2485 dbs.Close();
2489 // a disc image might be Blu-Ray disc
2490 if (!(options.startpercent > 0.0 || options.starttime > 0.0) &&
2491 (item.IsBDFile() || item.IsDiscImage()))
2493 // No video selection when using external or remote players (they handle it if supported)
2494 const bool isSimpleMenuAllowed = [&]()
2496 const std::string defaulPlayer{
2497 player.empty() ? m_ServiceManager->GetPlayerCoreFactory().GetDefaultPlayer(item)
2498 : player};
2499 const bool isExternalPlayer{
2500 m_ServiceManager->GetPlayerCoreFactory().IsExternalPlayer(defaulPlayer)};
2501 const bool isRemotePlayer{
2502 m_ServiceManager->GetPlayerCoreFactory().IsRemotePlayer(defaulPlayer)};
2503 return !isExternalPlayer && !isRemotePlayer;
2504 }();
2506 if (isSimpleMenuAllowed)
2508 // Check if we must show the simplified bd menu.
2509 if (!CGUIDialogSimpleMenu::ShowPlaySelection(item))
2510 return true;
2514 // this really aught to be inside !bRestart, but since PlayStack
2515 // uses that to init playback, we have to keep it outside
2516 const PLAYLIST::Id playlistId = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
2517 if (item.IsAudio() && playlistId == PLAYLIST::TYPE_MUSIC)
2518 { // playing from a playlist by the looks
2519 // don't switch to fullscreen if we are not playing the first item...
2520 options.fullscreen = !CServiceBroker::GetPlaylistPlayer().HasPlayedFirstFile() &&
2521 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
2522 CSettings::SETTING_MUSICFILES_SELECTACTION) &&
2523 !CMediaSettings::GetInstance().DoesMediaStartWindowed();
2525 else if (item.IsVideo() && playlistId == PLAYLIST::TYPE_VIDEO &&
2526 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size() > 1)
2527 { // playing from a playlist by the looks
2528 // don't switch to fullscreen if we are not playing the first item...
2529 options.fullscreen = !CServiceBroker::GetPlaylistPlayer().HasPlayedFirstFile() &&
2530 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreenOnMovieStart &&
2531 !CMediaSettings::GetInstance().DoesMediaStartWindowed();
2533 else if (stackHelper->IsPlayingRegularStack())
2535 //! @todo - this will fail if user seeks back to first file in stack
2536 if (stackHelper->GetCurrentPartNumber() == 0 ||
2537 stackHelper->GetRegisteredStack(item)->GetStartOffset() != 0)
2538 options.fullscreen = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->
2539 m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesMediaStartWindowed();
2540 else
2541 options.fullscreen = false;
2543 else
2544 options.fullscreen = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->
2545 m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesMediaStartWindowed();
2547 // stereo streams may have lower quality, i.e. 32bit vs 16 bit
2548 options.preferStereo = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoPreferStereoStream &&
2549 CServiceBroker::GetActiveAE()->HasStereoAudioChannelCount();
2551 // reset VideoStartWindowed as it's a temp setting
2552 CMediaSettings::GetInstance().SetMediaStartWindowed(false);
2555 // for playing a new item, previous playing item's callback may already
2556 // pushed some delay message into the threadmessage list, they are not
2557 // expected be processed after or during the new item playback starting.
2558 // so we clean up previous playing item's playback callback delay messages here.
2559 int previousMsgsIgnoredByNewPlaying[] = {
2560 GUI_MSG_PLAYBACK_STARTED,
2561 GUI_MSG_PLAYBACK_ENDED,
2562 GUI_MSG_PLAYBACK_STOPPED,
2563 GUI_MSG_PLAYLIST_CHANGED,
2564 GUI_MSG_PLAYLISTPLAYER_STOPPED,
2565 GUI_MSG_PLAYLISTPLAYER_STARTED,
2566 GUI_MSG_PLAYLISTPLAYER_CHANGED,
2567 GUI_MSG_QUEUE_NEXT_ITEM,
2570 int dMsgCount = CServiceBroker::GetGUI()->GetWindowManager().RemoveThreadMessageByMessageIds(&previousMsgsIgnoredByNewPlaying[0]);
2571 if (dMsgCount > 0)
2572 CLog::LogF(LOGDEBUG, "Ignored {} playback thread messages", dMsgCount);
2575 const auto appVolume = GetComponent<CApplicationVolumeHandling>();
2576 appPlayer->OpenFile(item, options, m_ServiceManager->GetPlayerCoreFactory(), player, *this);
2577 appPlayer->SetVolume(appVolume->GetVolumeRatio());
2578 appPlayer->SetMute(appVolume->IsMuted());
2580 #if !defined(TARGET_POSIX)
2581 CGUIComponent *gui = CServiceBroker::GetGUI();
2582 if (gui)
2583 gui->GetAudioManager().Enable(false);
2584 #endif
2586 if (item.HasPVRChannelInfoTag())
2587 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
2589 return true;
2592 void CApplication::PlaybackCleanup()
2594 const auto appPlayer = GetComponent<CApplicationPlayer>();
2595 const auto stackHelper = GetComponent<CApplicationStackHelper>();
2597 if (!appPlayer->IsPlaying())
2599 CGUIComponent *gui = CServiceBroker::GetGUI();
2600 if (gui)
2601 CServiceBroker::GetGUI()->GetAudioManager().Enable(true);
2602 appPlayer->OpenNext(m_ServiceManager->GetPlayerCoreFactory());
2605 if (!appPlayer->IsPlayingVideo())
2607 if(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
2608 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME)
2610 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
2612 else
2614 // resets to res_desktop or look&feel resolution (including refreshrate)
2615 CServiceBroker::GetWinSystem()->GetGfxContext().SetFullScreenVideo(false);
2617 #ifdef TARGET_DARWIN_EMBEDDED
2618 CDarwinUtils::SetScheduling(false);
2619 #endif
2622 const auto appPower = GetComponent<CApplicationPowerHandling>();
2624 if (!appPlayer->IsPlayingAudio() &&
2625 CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_NONE &&
2626 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION)
2628 CServiceBroker::GetSettingsComponent()->GetSettings()->Save(); // save vis settings
2629 appPower->WakeUpScreenSaverAndDPMS();
2630 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
2633 // DVD ejected while playing in vis ?
2634 if (!appPlayer->IsPlayingAudio() &&
2635 (m_itemCurrentFile->IsCDDA() || m_itemCurrentFile->IsOnDVD()) &&
2636 !CServiceBroker::GetMediaManager().IsDiscInDrive() &&
2637 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION)
2639 // yes, disable vis
2640 CServiceBroker::GetSettingsComponent()->GetSettings()->Save(); // save vis settings
2641 appPower->WakeUpScreenSaverAndDPMS();
2642 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
2645 if (!appPlayer->IsPlaying())
2647 stackHelper->Clear();
2648 appPlayer->ResetPlayer();
2651 if (CServiceBroker::GetAppParams()->IsTestMode())
2652 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
2655 bool CApplication::IsPlayingFullScreenVideo() const
2657 const auto appPlayer = GetComponent<CApplicationPlayer>();
2658 return appPlayer->IsPlayingVideo() &&
2659 CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo();
2662 bool CApplication::IsFullScreen()
2664 return IsPlayingFullScreenVideo() ||
2665 (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION) ||
2666 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW;
2669 void CApplication::StopPlaying()
2671 CGUIComponent *gui = CServiceBroker::GetGUI();
2673 if (gui)
2675 int iWin = gui->GetWindowManager().GetActiveWindow();
2676 const auto appPlayer = GetComponent<CApplicationPlayer>();
2677 if (appPlayer->IsPlaying())
2679 appPlayer->ClosePlayer();
2681 // turn off visualisation window when stopping
2682 if ((iWin == WINDOW_VISUALISATION ||
2683 iWin == WINDOW_FULLSCREEN_VIDEO ||
2684 iWin == WINDOW_FULLSCREEN_GAME) &&
2685 !m_bStop)
2686 gui->GetWindowManager().PreviousWindow();
2688 g_partyModeManager.Disable();
2693 bool CApplication::OnMessage(CGUIMessage& message)
2695 switch (message.GetMessage())
2697 case GUI_MSG_NOTIFY_ALL:
2699 if (message.GetParam1()==GUI_MSG_REMOVED_MEDIA)
2701 // Update general playlist: Remove DVD playlist items
2702 int nRemoved = CServiceBroker::GetPlaylistPlayer().RemoveDVDItems();
2703 if ( nRemoved > 0 )
2705 CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 );
2706 CServiceBroker::GetGUI()->GetWindowManager().SendMessage( msg );
2708 // stop the file if it's on dvd (will set the resume point etc)
2709 if (m_itemCurrentFile->IsOnDVD())
2710 StopPlaying();
2712 else if (message.GetParam1() == GUI_MSG_UI_READY)
2714 // remove splash window
2715 CServiceBroker::GetGUI()->GetWindowManager().Delete(WINDOW_SPLASH);
2717 // show the volumebar if the volume is muted
2718 const auto appVolume = GetComponent<CApplicationVolumeHandling>();
2719 if (appVolume->IsMuted() ||
2720 appVolume->GetVolumeRatio() <= CApplicationVolumeHandling::VOLUME_MINIMUM)
2721 appVolume->ShowVolumeBar();
2723 if (!m_incompatibleAddons.empty())
2725 // filter addons that are not dependencies
2726 std::vector<std::string> disabledAddonNames;
2727 for (const auto& addoninfo : m_incompatibleAddons)
2729 if (!CAddonType::IsDependencyType(addoninfo->MainType()))
2730 disabledAddonNames.emplace_back(addoninfo->Name());
2733 // migration (incompatible addons) dialog
2734 auto addonList = StringUtils::Join(disabledAddonNames, ", ");
2735 auto msg = StringUtils::Format(g_localizeStrings.Get(24149), addonList);
2736 HELPERS::ShowOKDialogText(CVariant{24148}, CVariant{std::move(msg)});
2737 m_incompatibleAddons.clear();
2740 // offer enabling addons at kodi startup that are disabled due to
2741 // e.g. os package manager installation on linux
2742 ConfigureAndEnableAddons();
2744 m_bInitializing = false;
2746 if (message.GetSenderId() == WINDOW_SETTINGS_PROFILES)
2747 GetComponent<CApplicationSkinHandling>()->ReloadSkin(false);
2749 else if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
2751 CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
2752 if (m_itemCurrentFile->IsSamePath(item.get()))
2754 m_itemCurrentFile->UpdateInfo(*item);
2755 CServiceBroker::GetGUI()->GetInfoManager().UpdateCurrentItem(*item);
2759 break;
2761 case GUI_MSG_PLAYBACK_STARTED:
2763 #ifdef TARGET_DARWIN_EMBEDDED
2764 // @TODO move this away to platform code
2765 CDarwinUtils::SetScheduling(GetComponent<CApplicationPlayer>()->IsPlayingVideo());
2766 #endif
2767 m_itemCurrentFile =
2768 std::make_shared<CFileItem>(*std::static_pointer_cast<CFileItem>(message.GetItem()));
2769 m_playerEvent.Reset();
2771 CServiceBroker::GetPVRManager().OnPlaybackStarted(*m_itemCurrentFile);
2773 PLAYLIST::CPlayList playList = CServiceBroker::GetPlaylistPlayer().GetPlaylist(
2774 CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist());
2776 // Update our infoManager with the new details etc.
2777 if (m_nextPlaylistItem >= 0)
2779 // playing an item which is not in the list - player might be stopped already
2780 // so do nothing
2781 if (playList.size() <= m_nextPlaylistItem)
2782 return true;
2784 // we've started a previously queued item
2785 CFileItemPtr item = playList[m_nextPlaylistItem];
2786 // update the playlist manager
2787 int currentSong = CServiceBroker::GetPlaylistPlayer().GetCurrentItemIdx();
2788 int param = ((currentSong & 0xffff) << 16) | (m_nextPlaylistItem & 0xffff);
2789 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_CHANGED, 0, 0, CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist(), param, item);
2790 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
2791 CServiceBroker::GetPlaylistPlayer().SetCurrentItemIdx(m_nextPlaylistItem);
2792 m_itemCurrentFile = std::make_shared<CFileItem>(*item);
2794 CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
2795 g_partyModeManager.OnSongChange(true);
2797 #ifdef HAS_PYTHON
2798 // informs python script currently running playback has started
2799 // (does nothing if python is not loaded)
2800 CServiceBroker::GetXBPython().OnPlayBackStarted(*m_itemCurrentFile);
2801 #endif
2803 CVariant param;
2804 param["player"]["speed"] = 1;
2805 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
2807 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPlay",
2808 m_itemCurrentFile, param);
2810 // we don't want a busy dialog when switching channels
2811 const auto appPlayer = GetComponent<CApplicationPlayer>();
2812 if (!m_itemCurrentFile->IsLiveTV() ||
2813 (!appPlayer->IsPlayingVideo() && !appPlayer->IsPlayingAudio()))
2815 CGUIDialogBusy* dialog =
2816 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogBusy>(
2817 WINDOW_DIALOG_BUSY);
2818 if (dialog && !dialog->IsDialogRunning())
2819 dialog->WaitOnEvent(m_playerEvent);
2822 return true;
2824 break;
2826 case GUI_MSG_QUEUE_NEXT_ITEM:
2828 // Check to see if our playlist player has a new item for us,
2829 // and if so, we check whether our current player wants the file
2830 int iNext = CServiceBroker::GetPlaylistPlayer().GetNextItemIdx();
2831 PLAYLIST::CPlayList& playlist = CServiceBroker::GetPlaylistPlayer().GetPlaylist(
2832 CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist());
2833 if (iNext < 0 || iNext >= playlist.size())
2835 GetComponent<CApplicationPlayer>()->OnNothingToQueueNotify();
2836 return true; // nothing to do
2839 // ok, grab the next song
2840 CFileItem file(*playlist[iNext]);
2841 // handle plugin://
2842 CURL url(file.GetDynPath());
2843 if (url.IsProtocol("plugin"))
2844 XFILE::CPluginDirectory::GetPluginResult(url.Get(), file, false);
2846 // Don't queue if next media type is different from current one
2847 bool bNothingToQueue = false;
2849 const auto appPlayer = GetComponent<CApplicationPlayer>();
2850 if (!file.IsVideo() && appPlayer->IsPlayingVideo())
2851 bNothingToQueue = true;
2852 else if ((!file.IsAudio() || file.IsVideo()) && appPlayer->IsPlayingAudio())
2853 bNothingToQueue = true;
2855 if (bNothingToQueue)
2857 appPlayer->OnNothingToQueueNotify();
2858 return true;
2861 #ifdef HAS_UPNP
2862 if (URIUtils::IsUPnP(file.GetDynPath()))
2864 if (!XFILE::CUPnPDirectory::GetResource(file.GetDynURL(), file))
2865 return true;
2867 #endif
2869 // ok - send the file to the player, if it accepts it
2870 if (appPlayer->QueueNextFile(file))
2872 // player accepted the next file
2873 m_nextPlaylistItem = iNext;
2875 else
2877 /* Player didn't accept next file: *ALWAYS* advance playlist in this case so the player can
2878 queue the next (if it wants to) and it doesn't keep looping on this song */
2879 CServiceBroker::GetPlaylistPlayer().SetCurrentItemIdx(iNext);
2882 return true;
2884 break;
2886 case GUI_MSG_PLAY_TRAILER:
2888 const CFileItem* item = dynamic_cast<CFileItem*>(message.GetItem().get());
2889 if (item == nullptr)
2891 CLog::LogF(LOGERROR, "Supplied item is not a CFileItem! Trailer cannot be played.");
2892 return false;
2895 std::unique_ptr<CFileItem> trailerItem =
2896 ContentUtils::GeneratePlayableTrailerItem(*item, g_localizeStrings.Get(20410));
2898 if (item->IsPlayList())
2900 std::unique_ptr<CFileItemList> fileitemList = std::make_unique<CFileItemList>();
2901 fileitemList->Add(std::move(trailerItem));
2902 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, -1, -1,
2903 static_cast<void*>(fileitemList.release()));
2905 else
2907 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, 1, 0,
2908 static_cast<void*>(trailerItem.release()));
2910 break;
2913 case GUI_MSG_PLAYBACK_STOPPED:
2915 CServiceBroker::GetPVRManager().OnPlaybackStopped(*m_itemCurrentFile);
2917 CVariant data(CVariant::VariantTypeObject);
2918 data["end"] = false;
2919 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnStop",
2920 m_itemCurrentFile, data);
2922 m_playerEvent.Set();
2923 ResetCurrentItem();
2924 PlaybackCleanup();
2925 #ifdef HAS_PYTHON
2926 CServiceBroker::GetXBPython().OnPlayBackStopped();
2927 #endif
2928 return true;
2931 case GUI_MSG_PLAYBACK_ENDED:
2933 CServiceBroker::GetPVRManager().OnPlaybackEnded(*m_itemCurrentFile);
2935 CVariant data(CVariant::VariantTypeObject);
2936 data["end"] = true;
2937 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnStop",
2938 m_itemCurrentFile, data);
2940 m_playerEvent.Set();
2941 const auto stackHelper = GetComponent<CApplicationStackHelper>();
2942 if (stackHelper->IsPlayingRegularStack() && stackHelper->HasNextStackPartFileItem())
2943 { // just play the next item in the stack
2944 PlayFile(stackHelper->SetNextStackPartCurrentFileItem(), "", true);
2945 return true;
2948 // For EPG playlist items we keep the player open to ensure continuous viewing experience.
2949 const bool isEpgPlaylistItem{
2950 m_itemCurrentFile->GetProperty("epg_playlist_item").asBoolean(false)};
2952 ResetCurrentItem();
2954 if (!isEpgPlaylistItem)
2956 if (!CServiceBroker::GetPlaylistPlayer().PlayNext(1, true))
2957 GetComponent<CApplicationPlayer>()->ClosePlayer();
2959 PlaybackCleanup();
2962 #ifdef HAS_PYTHON
2963 CServiceBroker::GetXBPython().OnPlayBackEnded();
2964 #endif
2965 return true;
2968 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
2969 ResetCurrentItem();
2970 if (GetComponent<CApplicationPlayer>()->IsPlaying())
2971 StopPlaying();
2972 PlaybackCleanup();
2973 return true;
2975 case GUI_MSG_PLAYBACK_AVSTARTED:
2977 CVariant param;
2978 param["player"]["speed"] = 1;
2979 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
2980 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnAVStart",
2981 m_itemCurrentFile, param);
2982 m_playerEvent.Set();
2983 #ifdef HAS_PYTHON
2984 // informs python script currently running playback has started
2985 // (does nothing if python is not loaded)
2986 CServiceBroker::GetXBPython().OnAVStarted(*m_itemCurrentFile);
2987 #endif
2988 return true;
2991 case GUI_MSG_PLAYBACK_AVCHANGE:
2993 #ifdef HAS_PYTHON
2994 // informs python script currently running playback has started
2995 // (does nothing if python is not loaded)
2996 CServiceBroker::GetXBPython().OnAVChange();
2997 #endif
2998 CVariant param;
2999 param["player"]["speed"] = 1;
3000 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3001 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnAVChange",
3002 m_itemCurrentFile, param);
3003 return true;
3006 case GUI_MSG_PLAYBACK_PAUSED:
3008 CVariant param;
3009 param["player"]["speed"] = 0;
3010 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3011 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPause",
3012 m_itemCurrentFile, param);
3013 return true;
3016 case GUI_MSG_PLAYBACK_RESUMED:
3018 CVariant param;
3019 param["player"]["speed"] = 1;
3020 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3021 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnResume",
3022 m_itemCurrentFile, param);
3023 return true;
3026 case GUI_MSG_PLAYBACK_SEEKED:
3028 CVariant param;
3029 const int64_t iTime = message.GetParam1AsI64();
3030 const int64_t seekOffset = message.GetParam2AsI64();
3031 JSONRPC::CJSONUtils::MillisecondsToTimeObject(iTime, param["player"]["time"]);
3032 JSONRPC::CJSONUtils::MillisecondsToTimeObject(seekOffset, param["player"]["seekoffset"]);
3033 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3034 const auto& components = CServiceBroker::GetAppComponents();
3035 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3036 param["player"]["speed"] = static_cast<int>(appPlayer->GetPlaySpeed());
3037 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnSeek",
3038 m_itemCurrentFile, param);
3040 CDataCacheCore::GetInstance().SeekFinished(static_cast<int>(seekOffset));
3042 return true;
3045 case GUI_MSG_PLAYBACK_SPEED_CHANGED:
3047 CVariant param;
3048 param["player"]["speed"] = message.GetParam1();
3049 param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3050 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnSpeedChanged",
3051 m_itemCurrentFile, param);
3053 return true;
3056 case GUI_MSG_PLAYBACK_ERROR:
3057 HELPERS::ShowOKDialogText(CVariant{16026}, CVariant{16027});
3058 return true;
3060 case GUI_MSG_PLAYLISTPLAYER_STARTED:
3061 case GUI_MSG_PLAYLISTPLAYER_CHANGED:
3063 return true;
3065 break;
3066 case GUI_MSG_FULLSCREEN:
3067 { // Switch to fullscreen, if we can
3068 CGUIComponent* gui = CServiceBroker::GetGUI();
3069 if (gui)
3070 gui->GetWindowManager().SwitchToFullScreen();
3072 return true;
3074 break;
3075 case GUI_MSG_EXECUTE:
3076 if (message.GetNumStringParams())
3077 return ExecuteXBMCAction(message.GetStringParam(), message.GetItem());
3078 break;
3080 return false;
3083 bool CApplication::ExecuteXBMCAction(std::string actionStr,
3084 const std::shared_ptr<CGUIListItem>& item /* = NULL */)
3086 // see if it is a user set string
3088 //We don't know if there is unsecure information in this yet, so we
3089 //postpone any logging
3090 const std::string in_actionStr(actionStr);
3091 if (item)
3092 actionStr = GUILIB::GUIINFO::CGUIInfoLabel::GetItemLabel(actionStr, item.get());
3093 else
3094 actionStr = GUILIB::GUIINFO::CGUIInfoLabel::GetLabel(actionStr, INFO::DEFAULT_CONTEXT);
3096 // user has asked for something to be executed
3097 if (CBuiltins::GetInstance().HasCommand(actionStr))
3099 if (!CBuiltins::GetInstance().IsSystemPowerdownCommand(actionStr) ||
3100 CServiceBroker::GetPVRManager().Get<PVR::GUI::PowerManagement>().CanSystemPowerdown())
3101 CBuiltins::GetInstance().Execute(actionStr);
3103 else
3105 // try translating the action from our ButtonTranslator
3106 unsigned int actionID;
3107 if (ACTION::CActionTranslator::TranslateString(actionStr, actionID))
3109 OnAction(CAction(actionID));
3110 return true;
3112 CFileItem item(actionStr, false);
3113 #ifdef HAS_PYTHON
3114 if (item.IsPythonScript())
3115 { // a python script
3116 CScriptInvocationManager::GetInstance().ExecuteAsync(item.GetPath());
3118 else
3119 #endif
3120 if (item.IsAudio() || item.IsVideo() || item.IsGame())
3121 { // an audio or video file
3122 PlayFile(item, "");
3124 else
3126 //At this point we have given up to translate, so even though
3127 //there may be insecure information, we log it.
3128 CLog::LogF(LOGDEBUG, "Tried translating, but failed to understand {}", in_actionStr);
3129 return false;
3132 return true;
3135 void CApplication::ConfigureAndEnableAddons()
3137 std::vector<std::shared_ptr<IAddon>>
3138 disabledAddons; /*!< Installed addons, but not auto-enabled via manifest */
3140 auto& addonMgr = CServiceBroker::GetAddonMgr();
3142 if (addonMgr.GetDisabledAddons(disabledAddons) && !disabledAddons.empty())
3144 // this applies to certain platforms only:
3145 // look at disabled addons with disabledReason == NONE, usually those are installed via package managers or manually.
3146 // also try to enable add-ons with disabledReason == INCOMPATIBLE at startup for all platforms.
3148 bool isConfigureAddonsAtStartupEnabled =
3149 m_ServiceManager->GetPlatform().IsConfigureAddonsAtStartupEnabled();
3151 for (const auto& addon : disabledAddons)
3153 if (addonMgr.IsAddonDisabledWithReason(addon->ID(), ADDON::AddonDisabledReason::INCOMPATIBLE))
3155 auto addonInfo = addonMgr.GetAddonInfo(addon->ID(), AddonType::UNKNOWN);
3156 if (addonInfo && addonMgr.IsCompatible(addonInfo))
3158 CLog::Log(LOGDEBUG, "CApplication::{}: enabling the compatible version of [{}].",
3159 __FUNCTION__, addon->ID());
3160 addonMgr.EnableAddon(addon->ID());
3162 continue;
3165 if (addonMgr.IsAddonDisabledExcept(addon->ID(), ADDON::AddonDisabledReason::NONE) ||
3166 CAddonType::IsDependencyType(addon->MainType()))
3168 continue;
3171 if (isConfigureAddonsAtStartupEnabled)
3173 if (HELPERS::ShowYesNoDialogLines(CVariant{24039}, // Disabled add-ons
3174 CVariant{24059}, // Would you like to enable this add-on?
3175 CVariant{addon->Name()}) == DialogResponse::CHOICE_YES)
3177 if (addon->CanHaveAddonOrInstanceSettings())
3179 if (CGUIDialogAddonSettings::ShowForAddon(addon))
3181 // only enable if settings dialog hasn't been cancelled
3182 addonMgr.EnableAddon(addon->ID());
3185 else
3187 addonMgr.EnableAddon(addon->ID());
3190 else
3192 // user chose not to configure/enable so we're not asking anymore
3193 addonMgr.UpdateDisabledReason(addon->ID(), ADDON::AddonDisabledReason::USER);
3200 void CApplication::Process()
3202 // dispatch the messages generated by python or other threads to the current window
3203 CServiceBroker::GetGUI()->GetWindowManager().DispatchThreadMessages();
3205 // process messages which have to be send to the gui
3206 // (this can only be done after CServiceBroker::GetGUI()->GetWindowManager().Render())
3207 CServiceBroker::GetAppMessenger()->ProcessWindowMessages();
3209 // handle any active scripts
3212 // Allow processing of script threads to let them shut down properly.
3213 CSingleExit ex(CServiceBroker::GetWinSystem()->GetGfxContext());
3214 m_frameMoveGuard.unlock();
3215 CScriptInvocationManager::GetInstance().Process();
3216 m_frameMoveGuard.lock();
3219 // process messages, even if a movie is playing
3220 CServiceBroker::GetAppMessenger()->ProcessMessages();
3221 if (m_bStop) return; //we're done, everything has been unloaded
3223 // do any processing that isn't needed on each run
3224 if( m_slowTimer.GetElapsedMilliseconds() > 500 )
3226 m_slowTimer.Reset();
3227 ProcessSlow();
3231 // We get called every 500ms
3232 void CApplication::ProcessSlow()
3234 // process skin resources (skin timers)
3235 GetComponent<CApplicationSkinHandling>()->ProcessSkin();
3237 CServiceBroker::GetPowerManager().ProcessEvents();
3239 // Temporarily pause pausable jobs when viewing video/picture
3240 int currentWindow = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
3241 if (CurrentFileItem().IsVideo() ||
3242 CurrentFileItem().IsPicture() ||
3243 currentWindow == WINDOW_FULLSCREEN_VIDEO ||
3244 currentWindow == WINDOW_FULLSCREEN_GAME ||
3245 currentWindow == WINDOW_SLIDESHOW)
3247 CServiceBroker::GetJobManager()->PauseJobs();
3249 else
3251 CServiceBroker::GetJobManager()->UnPauseJobs();
3254 // Check if we need to activate the screensaver / DPMS.
3255 const auto appPower = GetComponent<CApplicationPowerHandling>();
3256 appPower->CheckScreenSaverAndDPMS();
3258 // Check if we need to shutdown (if enabled).
3259 #if defined(TARGET_DARWIN)
3260 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME) &&
3261 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
3262 #else
3263 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME))
3264 #endif
3266 appPower->CheckShutdown();
3269 #if defined(TARGET_POSIX)
3270 if (CPlatformPosix::TestQuitFlag())
3272 CLog::Log(LOGINFO, "Quitting due to POSIX signal");
3273 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
3275 #endif
3277 // check if we should restart the player
3278 CheckDelayedPlayerRestart();
3280 // check if we can unload any unreferenced dlls or sections
3281 const auto appPlayer = GetComponent<CApplicationPlayer>();
3282 if (!appPlayer->IsPlayingVideo())
3283 CSectionLoader::UnloadDelayed();
3285 #ifdef TARGET_ANDROID
3286 // Pass the slow loop to droid
3287 CXBMCApp::Get().ProcessSlow();
3288 #endif
3290 // check for any idle curl connections
3291 g_curlInterface.CheckIdle();
3293 CServiceBroker::GetGUI()->GetLargeTextureManager().CleanupUnusedImages();
3295 CServiceBroker::GetGUI()->GetTextureManager().FreeUnusedTextures(5000);
3297 #ifdef HAS_OPTICAL_DRIVE
3298 // checks whats in the DVD drive and tries to autostart the content (xbox games, dvd, cdda, avi files...)
3299 if (!appPlayer->IsPlayingVideo())
3300 m_Autorun->HandleAutorun();
3301 #endif
3303 // update upnp server/renderer states
3304 #ifdef HAS_UPNP
3305 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP) && UPNP::CUPnP::IsInstantiated())
3306 UPNP::CUPnP::GetInstance()->UpdateState();
3307 #endif
3309 #if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
3310 smb.CheckIfIdle();
3311 #endif
3313 #ifdef HAS_FILESYSTEM_NFS
3314 gNfsConnection.CheckIfIdle();
3315 #endif
3317 for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
3318 vfsAddon->ClearOutIdle();
3320 CServiceBroker::GetMediaManager().ProcessEvents();
3322 // if we don't render the gui there's no reason to start the screensaver.
3323 // that way the screensaver won't kick in if we maximize the XBMC window
3324 // after the screensaver start time.
3325 if (!appPower->GetRenderGUI())
3326 appPower->ResetScreenSaverTimer();
3329 void CApplication::DelayedPlayerRestart()
3331 m_restartPlayerTimer.StartZero();
3334 void CApplication::CheckDelayedPlayerRestart()
3336 if (m_restartPlayerTimer.GetElapsedSeconds() > 3)
3338 m_restartPlayerTimer.Stop();
3339 m_restartPlayerTimer.Reset();
3340 Restart(true);
3344 void CApplication::Restart(bool bSamePosition)
3346 // this function gets called when the user changes a setting (like noninterleaved)
3347 // and which means we gotta close & reopen the current playing file
3349 // first check if we're playing a file
3350 const auto appPlayer = GetComponent<CApplicationPlayer>();
3351 if (!appPlayer->IsPlayingVideo() && !appPlayer->IsPlayingAudio())
3352 return ;
3354 if (!appPlayer->HasPlayer())
3355 return ;
3357 // do we want to return to the current position in the file
3358 if (!bSamePosition)
3360 // no, then just reopen the file and start at the beginning
3361 PlayFile(*m_itemCurrentFile, "", true);
3362 return ;
3365 // else get current position
3366 double time = GetTime();
3368 // get player state, needed for dvd's
3369 std::string state = appPlayer->GetPlayerState();
3371 // set the requested starttime
3372 m_itemCurrentFile->SetStartOffset(CUtil::ConvertSecsToMilliSecs(time));
3374 // reopen the file
3375 if (PlayFile(*m_itemCurrentFile, "", true))
3376 appPlayer->SetPlayerState(state);
3379 const std::string& CApplication::CurrentFile()
3381 return m_itemCurrentFile->GetPath();
3384 std::shared_ptr<CFileItem> CApplication::CurrentFileItemPtr()
3386 return m_itemCurrentFile;
3389 CFileItem& CApplication::CurrentFileItem()
3391 return *m_itemCurrentFile;
3394 const CFileItem& CApplication::CurrentUnstackedItem()
3396 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3398 if (stackHelper->IsPlayingISOStack() || stackHelper->IsPlayingRegularStack())
3399 return stackHelper->GetCurrentStackPartFileItem();
3400 else
3401 return *m_itemCurrentFile;
3404 // Returns the total time in seconds of the current media. Fractional
3405 // portions of a second are possible - but not necessarily supported by the
3406 // player class. This returns a double to be consistent with GetTime() and
3407 // SeekTime().
3408 double CApplication::GetTotalTime() const
3410 double rc = 0.0;
3412 const auto appPlayer = GetComponent<CApplicationPlayer>();
3413 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3415 if (appPlayer->IsPlaying())
3417 if (stackHelper->IsPlayingRegularStack())
3418 rc = stackHelper->GetStackTotalTimeMs() * 0.001;
3419 else
3420 rc = appPlayer->GetTotalTime() * 0.001;
3423 return rc;
3426 // Returns the current time in seconds of the currently playing media.
3427 // Fractional portions of a second are possible. This returns a double to
3428 // be consistent with GetTotalTime() and SeekTime().
3429 double CApplication::GetTime() const
3431 double rc = 0.0;
3433 const auto appPlayer = GetComponent<CApplicationPlayer>();
3434 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3436 if (appPlayer->IsPlaying())
3438 if (stackHelper->IsPlayingRegularStack())
3440 uint64_t startOfCurrentFile = stackHelper->GetCurrentStackPartStartTimeMs();
3441 rc = (startOfCurrentFile + appPlayer->GetTime()) * 0.001;
3443 else
3444 rc = appPlayer->GetTime() * 0.001;
3447 return rc;
3450 // Sets the current position of the currently playing media to the specified
3451 // time in seconds. Fractional portions of a second are valid. The passed
3452 // time is the time offset from the beginning of the file as opposed to a
3453 // delta from the current position. This method accepts a double to be
3454 // consistent with GetTime() and GetTotalTime().
3455 void CApplication::SeekTime( double dTime )
3457 const auto appPlayer = GetComponent<CApplicationPlayer>();
3458 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3460 if (appPlayer->IsPlaying() && (dTime >= 0.0))
3462 if (!appPlayer->CanSeek())
3463 return;
3465 if (stackHelper->IsPlayingRegularStack())
3467 // find the item in the stack we are seeking to, and load the new
3468 // file if necessary, and calculate the correct seek within the new
3469 // file. Otherwise, just fall through to the usual routine if the
3470 // time is higher than our total time.
3471 int partNumberToPlay =
3472 stackHelper->GetStackPartNumberAtTimeMs(static_cast<uint64_t>(dTime * 1000.0));
3473 uint64_t startOfNewFile = stackHelper->GetStackPartStartTimeMs(partNumberToPlay);
3474 if (partNumberToPlay == stackHelper->GetCurrentPartNumber())
3475 appPlayer->SeekTime(static_cast<uint64_t>(dTime * 1000.0) - startOfNewFile);
3476 else
3477 { // seeking to a new file
3478 stackHelper->SetStackPartCurrentFileItem(partNumberToPlay);
3479 CFileItem* item = new CFileItem(stackHelper->GetCurrentStackPartFileItem());
3480 item->SetStartOffset(static_cast<uint64_t>(dTime * 1000.0) - startOfNewFile);
3481 // don't just call "PlayFile" here, as we are quite likely called from the
3482 // player thread, so we won't be able to delete ourselves.
3483 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, 1, 0, static_cast<void*>(item));
3485 return;
3487 // convert to milliseconds and perform seek
3488 appPlayer->SeekTime(static_cast<int64_t>(dTime * 1000.0));
3492 float CApplication::GetPercentage() const
3494 const auto appPlayer = GetComponent<CApplicationPlayer>();
3495 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3497 if (appPlayer->IsPlaying())
3499 if (appPlayer->GetTotalTime() == 0 && appPlayer->IsPlayingAudio() &&
3500 m_itemCurrentFile->HasMusicInfoTag())
3502 const CMusicInfoTag& tag = *m_itemCurrentFile->GetMusicInfoTag();
3503 if (tag.GetDuration() > 0)
3504 return (float)(GetTime() / tag.GetDuration() * 100);
3507 if (stackHelper->IsPlayingRegularStack())
3509 double totalTime = GetTotalTime();
3510 if (totalTime > 0.0)
3511 return (float)(GetTime() / totalTime * 100);
3513 else
3514 return appPlayer->GetPercentage();
3516 return 0.0f;
3519 float CApplication::GetCachePercentage() const
3521 const auto appPlayer = GetComponent<CApplicationPlayer>();
3522 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3524 if (appPlayer->IsPlaying())
3526 // Note that the player returns a relative cache percentage and we want an absolute percentage
3527 if (stackHelper->IsPlayingRegularStack())
3529 float stackedTotalTime = (float) GetTotalTime();
3530 // We need to take into account the stack's total time vs. currently playing file's total time
3531 if (stackedTotalTime > 0.0f)
3532 return std::min(100.0f,
3533 GetPercentage() + (appPlayer->GetCachePercentage() *
3534 appPlayer->GetTotalTime() * 0.001f / stackedTotalTime));
3536 else
3537 return std::min(100.0f, appPlayer->GetPercentage() + appPlayer->GetCachePercentage());
3539 return 0.0f;
3542 void CApplication::SeekPercentage(float percent)
3544 const auto appPlayer = GetComponent<CApplicationPlayer>();
3545 const auto stackHelper = GetComponent<CApplicationStackHelper>();
3547 if (appPlayer->IsPlaying() && (percent >= 0.0f))
3549 if (!appPlayer->CanSeek())
3550 return;
3551 if (stackHelper->IsPlayingRegularStack())
3552 SeekTime(static_cast<double>(percent) * 0.01 * GetTotalTime());
3553 else
3554 appPlayer->SeekPercentage(percent);
3558 std::string CApplication::GetCurrentPlayer()
3560 const auto appPlayer = GetComponent<CApplicationPlayer>();
3561 return appPlayer->GetCurrentPlayer();
3564 void CApplication::UpdateLibraries()
3566 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
3567 if (settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_UPDATEONSTARTUP))
3569 CLog::LogF(LOGINFO, "Starting video library startup scan");
3570 CVideoLibraryQueue::GetInstance().ScanLibrary(
3571 "", false, !settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE));
3574 if (settings->GetBool(CSettings::SETTING_MUSICLIBRARY_UPDATEONSTARTUP))
3576 CLog::LogF(LOGINFO, "Starting music library startup scan");
3577 CMusicLibraryQueue::GetInstance().ScanLibrary(
3578 "", MUSIC_INFO::CMusicInfoScanner::SCAN_NORMAL,
3579 !settings->GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE));
3583 void CApplication::UpdateCurrentPlayArt()
3585 const auto appPlayer = GetComponent<CApplicationPlayer>();
3586 if (!appPlayer->IsPlayingAudio())
3587 return;
3588 //Clear and reload the art for the currently playing item to show updated art on OSD
3589 m_itemCurrentFile->ClearArt();
3590 CMusicThumbLoader loader;
3591 loader.LoadItem(m_itemCurrentFile.get());
3592 // Mirror changes to GUI item
3593 CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
3596 bool CApplication::ProcessAndStartPlaylist(const std::string& strPlayList,
3597 PLAYLIST::CPlayList& playlist,
3598 PLAYLIST::Id playlistId,
3599 int track)
3601 CLog::Log(LOGDEBUG, "CApplication::ProcessAndStartPlaylist({}, {})", strPlayList, playlistId);
3603 // initial exit conditions
3604 // no songs in playlist just return
3605 if (playlist.size() == 0)
3606 return false;
3608 // illegal playlist
3609 if (playlistId == PLAYLIST::TYPE_NONE || playlistId == PLAYLIST::TYPE_PICTURE)
3610 return false;
3612 // setup correct playlist
3613 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
3615 // if the playlist contains an internet stream, this file will be used
3616 // to generate a thumbnail for musicplayer.cover
3617 m_strPlayListFile = strPlayList;
3619 // add the items to the playlist player
3620 CServiceBroker::GetPlaylistPlayer().Add(playlistId, playlist);
3622 // if we have a playlist
3623 if (CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size())
3625 // start playing it
3626 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
3627 CServiceBroker::GetPlaylistPlayer().Reset();
3628 CServiceBroker::GetPlaylistPlayer().Play(track, "");
3629 return true;
3631 return false;
3634 bool CApplication::GetRenderGUI() const
3636 return GetComponent<CApplicationPowerHandling>()->GetRenderGUI();
3639 bool CApplication::SetLanguage(const std::string &strLanguage)
3641 // nothing to be done if the language hasn't changed
3642 if (strLanguage == CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE))
3643 return true;
3645 return CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_LOCALE_LANGUAGE, strLanguage);
3648 bool CApplication::LoadLanguage(bool reload)
3650 // load the configured language
3651 if (!g_langInfo.SetLanguage("", reload))
3652 return false;
3654 // set the proper audio and subtitle languages
3655 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
3656 g_langInfo.SetAudioLanguage(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE));
3657 g_langInfo.SetSubtitleLanguage(settings->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE));
3659 return true;
3662 void CApplication::SetLoggingIn(bool switchingProfiles)
3664 // don't save skin settings on unloading when logging into another profile
3665 // because in that case we have already loaded the new profile and
3666 // would therefore write the previous skin's settings into the new profile
3667 // instead of into the previous one
3668 GetComponent<CApplicationSkinHandling>()->m_saveSkinOnUnloading = !switchingProfiles;
3671 void CApplication::PrintStartupLog()
3673 CLog::Log(LOGINFO, "-----------------------------------------------------------------------");
3674 CLog::Log(LOGINFO, "Starting {} ({}). Platform: {} {} {}-bit", CSysInfo::GetAppName(),
3675 CSysInfo::GetVersion(), g_sysinfo.GetBuildTargetPlatformName(),
3676 g_sysinfo.GetBuildTargetCpuFamily(), g_sysinfo.GetXbmcBitness());
3678 std::string buildType;
3679 #if defined(_DEBUG)
3680 buildType = "Debug";
3681 #elif defined(NDEBUG)
3682 buildType = "Release";
3683 #else
3684 buildType = "Unknown";
3685 #endif
3687 CLog::Log(LOGINFO, "Using {} {} x{}", buildType, CSysInfo::GetAppName(),
3688 g_sysinfo.GetXbmcBitness());
3689 CLog::Log(LOGINFO, "{} compiled {} by {} for {} {} {}-bit {} ({})", CSysInfo::GetAppName(),
3690 CSysInfo::GetBuildDate(), g_sysinfo.GetUsedCompilerNameAndVer(),
3691 g_sysinfo.GetBuildTargetPlatformName(), g_sysinfo.GetBuildTargetCpuFamily(),
3692 g_sysinfo.GetXbmcBitness(), g_sysinfo.GetBuildTargetPlatformVersionDecoded(),
3693 g_sysinfo.GetBuildTargetPlatformVersion());
3695 std::string deviceModel(g_sysinfo.GetModelName());
3696 if (!g_sysinfo.GetManufacturerName().empty())
3697 deviceModel = g_sysinfo.GetManufacturerName() + " " +
3698 (deviceModel.empty() ? std::string("device") : deviceModel);
3699 if (!deviceModel.empty())
3700 CLog::Log(LOGINFO, "Running on {} with {}, kernel: {} {} {}-bit version {}", deviceModel,
3701 g_sysinfo.GetOsPrettyNameWithVersion(), g_sysinfo.GetKernelName(),
3702 g_sysinfo.GetKernelCpuFamily(), g_sysinfo.GetKernelBitness(),
3703 g_sysinfo.GetKernelVersionFull());
3704 else
3705 CLog::Log(LOGINFO, "Running on {}, kernel: {} {} {}-bit version {}",
3706 g_sysinfo.GetOsPrettyNameWithVersion(), g_sysinfo.GetKernelName(),
3707 g_sysinfo.GetKernelCpuFamily(), g_sysinfo.GetKernelBitness(),
3708 g_sysinfo.GetKernelVersionFull());
3710 CLog::Log(LOGINFO, "FFmpeg version/source: {}", av_version_info());
3712 std::string cpuModel(CServiceBroker::GetCPUInfo()->GetCPUModel());
3713 if (!cpuModel.empty())
3715 CLog::Log(LOGINFO, "Host CPU: {}, {} core{} available", cpuModel,
3716 CServiceBroker::GetCPUInfo()->GetCPUCount(),
3717 (CServiceBroker::GetCPUInfo()->GetCPUCount() == 1) ? "" : "s");
3719 else
3720 CLog::Log(LOGINFO, "{} CPU core{} available", CServiceBroker::GetCPUInfo()->GetCPUCount(),
3721 (CServiceBroker::GetCPUInfo()->GetCPUCount() == 1) ? "" : "s");
3723 // Any system info logging that is unique to a platform
3724 m_ServiceManager->GetPlatform().PlatformSyslog();
3726 #if defined(__arm__) || defined(__aarch64__)
3727 CLog::Log(LOGINFO, "ARM Features: Neon {}",
3728 (CServiceBroker::GetCPUInfo()->GetCPUFeatures() & CPU_FEATURE_NEON) ? "enabled"
3729 : "disabled");
3730 #endif
3731 CSpecialProtocol::LogPaths();
3733 #ifdef HAS_WEB_SERVER
3734 CLog::Log(LOGINFO, "Webserver extra whitelist paths: {}",
3735 StringUtils::Join(CCompileInfo::GetWebserverExtraWhitelist(), ", "));
3736 #endif
3738 // Check, whether libkodi.so was reused (happens on Android, where the system does not unload
3739 // the lib on activity end, but keeps it loaded (as long as there is enough memory) and reuses
3740 // it on next activity start.
3741 static bool firstRun = true;
3743 CLog::Log(LOGINFO, "The executable running is: {}{}", CUtil::ResolveExecutablePath(),
3744 firstRun ? "" : " [reused]");
3746 firstRun = false;
3748 std::string hostname("[unknown]");
3749 m_ServiceManager->GetNetwork().GetHostName(hostname);
3750 CLog::Log(LOGINFO, "Local hostname: {}", hostname);
3751 std::string lowerAppName = CCompileInfo::GetAppName();
3752 StringUtils::ToLower(lowerAppName);
3753 CLog::Log(LOGINFO, "Log File is located: {}.log",
3754 CSpecialProtocol::TranslatePath("special://logpath/" + lowerAppName));
3755 CRegExp::LogCheckUtf8Support();
3756 CLog::Log(LOGINFO, "-----------------------------------------------------------------------");
3759 void CApplication::CloseNetworkShares()
3761 CLog::Log(LOGDEBUG,"CApplication::CloseNetworkShares: Closing all network shares");
3763 #if defined(HAS_FILESYSTEM_SMB) && !defined(TARGET_WINDOWS)
3764 smb.Deinit();
3765 #endif
3767 #ifdef HAS_FILESYSTEM_NFS
3768 gNfsConnection.Deinit();
3769 #endif
3771 for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
3772 vfsAddon->DisconnectAll();