1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Application.h"
10 
11 #include "AppInboundProtocol.h"
12 #include "AppParamParser.h"
13 #include "Autorun.h"
14 #include "GUIInfoManager.h"
15 #include "HDRStatus.h"
16 #include "LangInfo.h"
17 #include "PlayListPlayer.h"
18 #include "URL.h"
19 #include "Util.h"
20 #include "addons/Skin.h"
21 #include "addons/VFSEntry.h"
22 #include "cores/AudioEngine/Engines/ActiveAE/ActiveAE.h"
23 #include "cores/IPlayer.h"
24 #include "cores/playercorefactory/PlayerCoreFactory.h"
25 #include "dialogs/GUIDialogBusy.h"
26 #include "dialogs/GUIDialogKaiToast.h"
27 #include "events/EventLog.h"
28 #include "events/NotificationEvent.h"
29 #include "guilib/GUIColorManager.h"
30 #include "guilib/GUIComponent.h"
31 #include "guilib/GUIControlProfiler.h"
32 #include "guilib/GUIFontManager.h"
33 #include "guilib/StereoscopicsManager.h"
34 #include "guilib/TextureManager.h"
35 #include "interfaces/builtins/Builtins.h"
36 #include "interfaces/generic/ScriptInvocationManager.h"
37 #include "music/MusicLibraryQueue.h"
38 #include "network/EventServer.h"
39 #include "network/Network.h"
40 #include "platform/Environment.h"
41 #include "playlists/PlayListFactory.h"
42 #include "threads/SystemClock.h"
43 #include "utils/JobManager.h"
44 #include "utils/LangCodeExpander.h"
45 #include "utils/Screenshot.h"
46 #include "utils/Variant.h"
47 #include "video/Bookmark.h"
48 #include "video/VideoLibraryQueue.h"
49 #ifdef HAS_PYTHON
50 #include "interfaces/python/XBPython.h"
51 #endif
52 #include "GUILargeTextureManager.h"
53 #include "GUIPassword.h"
54 #include "GUIUserMessages.h"
55 #include "SectionLoader.h"
56 #include "SeekHandler.h"
57 #include "ServiceBroker.h"
58 #include "TextureCache.h"
59 #include "cores/DllLoader/DllLoaderContainer.h"
60 #include "filesystem/Directory.h"
61 #include "filesystem/DirectoryCache.h"
62 #include "filesystem/DllLibCurl.h"
63 #include "filesystem/PluginDirectory.h"
64 #include "filesystem/SpecialProtocol.h"
65 #include "filesystem/StackDirectory.h"
66 #include "guilib/GUIAudioManager.h"
67 #include "guilib/LocalizeStrings.h"
68 #include "input/ButtonTranslator.h"
69 #include "input/InertialScrollingHandler.h"
70 #include "input/KeyboardLayoutManager.h"
71 #include "input/actions/ActionTranslator.h"
72 #include "messaging/ApplicationMessenger.h"
73 #include "messaging/ThreadMessage.h"
74 #include "messaging/helpers/DialogHelper.h"
75 #include "messaging/helpers/DialogOKHelper.h"
76 #include "playlists/PlayList.h"
77 #include "playlists/SmartPlayList.h"
78 #include "powermanagement/DPMSSupport.h"
79 #include "powermanagement/PowerManager.h"
80 #include "powermanagement/PowerTypes.h"
81 #include "profiles/ProfileManager.h"
82 #include "settings/AdvancedSettings.h"
83 #include "settings/DisplaySettings.h"
84 #include "settings/MediaSettings.h"
85 #include "settings/Settings.h"
86 #include "settings/SettingsComponent.h"
87 #include "settings/SkinSettings.h"
88 #include "utils/CPUInfo.h"
89 #include "utils/FileExtensionProvider.h"
90 #include "utils/SystemInfo.h"
91 #include "utils/TimeUtils.h"
92 #include "utils/XTimeUtils.h"
93 #include "utils/log.h"
94 #include "windowing/WinSystem.h"
95 #include "windowing/WindowSystemFactory.h"
96 
97 #include <cmath>
98 
99 #ifdef HAS_UPNP
100 #include "network/upnp/UPnP.h"
101 #include "filesystem/UPnPDirectory.h"
102 #endif
103 #if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
104 #include "platform/posix/filesystem/SMBDirectory.h"
105 #endif
106 #ifdef HAS_FILESYSTEM_NFS
107 #include "filesystem/NFSFile.h"
108 #endif
109 #include "PartyModeManager.h"
110 #include "network/ZeroconfBrowser.h"
111 #ifndef TARGET_POSIX
112 #include "threads/platform/win/Win32Exception.h"
113 #endif
114 #ifdef HAS_DBUS
115 #include <dbus/dbus.h>
116 #endif
117 #include "interfaces/json-rpc/JSONRPC.h"
118 #include "interfaces/AnnouncementManager.h"
119 #include "peripherals/Peripherals.h"
120 #include "music/infoscanner/MusicInfoScanner.h"
121 #include "music/MusicUtils.h"
122 #include "music/MusicThumbLoader.h"
123 
124 // Windows includes
125 #include "guilib/GUIWindowManager.h"
126 #include "video/dialogs/GUIDialogVideoInfo.h"
127 #include "windows/GUIWindowScreensaver.h"
128 #include "video/PlayerController.h"
129 
130 // Dialog includes
131 #include "addons/gui/GUIDialogAddonSettings.h"
132 #include "dialogs/GUIDialogButtonMenu.h"
133 #include "dialogs/GUIDialogKaiToast.h"
134 #include "dialogs/GUIDialogSimpleMenu.h"
135 #include "dialogs/GUIDialogSubMenu.h"
136 #include "dialogs/GUIDialogVolumeBar.h"
137 #include "video/dialogs/GUIDialogVideoBookmarks.h"
138 
139 // PVR related include Files
140 #include "pvr/PVRManager.h"
141 #include "pvr/guilib/PVRGUIActions.h"
142 
143 #include "video/dialogs/GUIDialogFullScreenInfo.h"
144 #include "dialogs/GUIDialogCache.h"
145 #include "utils/URIUtils.h"
146 #include "utils/XMLUtils.h"
147 #include "addons/AddonInstaller.h"
148 #include "addons/AddonManager.h"
149 #include "addons/RepositoryUpdater.h"
150 #include "music/tags/MusicInfoTag.h"
151 #include "CompileInfo.h"
152 
153 #ifdef TARGET_WINDOWS
154 #include "win32util.h"
155 #endif
156 
157 #ifdef TARGET_DARWIN_OSX
158 #include "platform/darwin/osx/CocoaInterface.h"
159 #include "platform/darwin/osx/XBMCHelper.h"
160 #endif
161 #ifdef TARGET_DARWIN
162 #include "platform/darwin/DarwinUtils.h"
163 #endif
164 
165 #ifdef HAS_DVD_DRIVE
166 #include <cdio/logging.h>
167 #endif
168 
169 #include "storage/MediaManager.h"
170 #include "utils/SaveFileStateJob.h"
171 #include "utils/AlarmClock.h"
172 #include "utils/StringUtils.h"
173 #include "DatabaseManager.h"
174 #include "input/InputManager.h"
175 
176 #ifdef TARGET_POSIX
177 #include "platform/posix/XHandle.h"
178 #include "platform/posix/filesystem/PosixDirectory.h"
179 #include "platform/posix/PlatformPosix.h"
180 #endif
181 
182 #if defined(TARGET_ANDROID)
183 #include <androidjni/Build.h>
184 #include "platform/android/activity/XBMCApp.h"
185 #include "platform/android/activity/AndroidFeatures.h"
186 #endif
187 
188 #ifdef TARGET_WINDOWS
189 #include "platform/Environment.h"
190 #endif
191 
192 //TODO: XInitThreads
193 #ifdef HAVE_X11
194 #include <X11/Xlib.h>
195 #endif
196 
197 #include "cores/FFmpeg.h"
198 #include "utils/CharsetConverter.h"
199 #include "pictures/GUIWindowSlideShow.h"
200 #include "addons/AddonSystemSettings.h"
201 #include "FileItem.h"
202 
203 using namespace ADDON;
204 using namespace XFILE;
205 #ifdef HAS_DVD_DRIVE
206 using namespace MEDIA_DETECT;
207 #endif
208 using namespace PLAYLIST;
209 using namespace VIDEO;
210 using namespace MUSIC_INFO;
211 using namespace EVENTSERVER;
212 using namespace JSONRPC;
213 using namespace PVR;
214 using namespace PERIPHERALS;
215 using namespace KODI;
216 using namespace KODI::MESSAGING;
217 using namespace ActiveAE;
218 
219 using namespace XbmcThreads;
220 
221 using KODI::MESSAGING::HELPERS::DialogResponse;
222 
223 #define MAX_FFWD_SPEED 5
224 
CApplication(void)225 CApplication::CApplication(void)
226 :
227 #ifdef HAS_DVD_DRIVE
228   m_Autorun(new CAutorun()),
229 #endif
230   m_itemCurrentFile(new CFileItem)
231   , m_pInertialScrollingHandler(new CInertialScrollingHandler())
232   , m_WaitingExternalCalls(0)
233   , m_playerEvent(true, true)
234 {
235   TiXmlBase::SetCondenseWhiteSpace(false);
236 
237 #ifdef HAVE_X11
238   XInitThreads();
239 #endif
240 }
241 
~CApplication(void)242 CApplication::~CApplication(void)
243 {
244   delete m_pInertialScrollingHandler;
245 
246   m_actionListeners.clear();
247 }
248 
OnEvent(XBMC_Event & newEvent)249 bool CApplication::OnEvent(XBMC_Event& newEvent)
250 {
251   CSingleLock lock(m_portSection);
252   m_portEvents.push_back(newEvent);
253   return true;
254 }
255 
HandlePortEvents()256 void CApplication::HandlePortEvents()
257 {
258   CSingleLock lock(m_portSection);
259   while (!m_portEvents.empty())
260   {
261     auto newEvent = m_portEvents.front();
262     m_portEvents.pop_front();
263     CSingleExit lock(m_portSection);
264     switch(newEvent.type)
265     {
266       case XBMC_QUIT:
267         if (!m_bStop)
268           CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT);
269         break;
270       case XBMC_VIDEORESIZE:
271         if (CServiceBroker::GetGUI()->GetWindowManager().Initialized())
272         {
273           if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
274           {
275             CServiceBroker::GetWinSystem()->GetGfxContext().ApplyWindowResize(newEvent.resize.w, newEvent.resize.h);
276 
277             const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
278             settings->SetInt(CSettings::SETTING_WINDOW_WIDTH, newEvent.resize.w);
279             settings->SetInt(CSettings::SETTING_WINDOW_HEIGHT, newEvent.resize.h);
280             settings->Save();
281           }
282 #ifdef TARGET_WINDOWS
283           else
284           {
285             // this may occurs when OS tries to resize application window
286             //CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP, true);
287             //auto& gfxContext = CServiceBroker::GetWinSystem()->GetGfxContext();
288             //gfxContext.SetVideoResolution(gfxContext.GetVideoResolution(), true);
289             // try to resize window back to it's full screen size
290             auto& res_info = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP);
291             CServiceBroker::GetWinSystem()->ResizeWindow(res_info.iScreenWidth, res_info.iScreenHeight, 0, 0);
292           }
293 #endif
294         }
295         break;
296       case XBMC_VIDEOMOVE:
297       {
298         CServiceBroker::GetWinSystem()->OnMove(newEvent.move.x, newEvent.move.y);
299       }
300         break;
301       case XBMC_MODECHANGE:
302         CServiceBroker::GetWinSystem()->GetGfxContext().ApplyModeChange(newEvent.mode.res);
303         break;
304       case XBMC_USEREVENT:
305         CApplicationMessenger::GetInstance().PostMsg(static_cast<uint32_t>(newEvent.user.code));
306         break;
307       case XBMC_SETFOCUS:
308         // Reset the screensaver
309         ResetScreenSaver();
310         WakeUpScreenSaverAndDPMS();
311         // Send a mouse motion event with no dx,dy for getting the current guiitem selected
312         OnAction(CAction(ACTION_MOUSE_MOVE, 0, static_cast<float>(newEvent.focus.x), static_cast<float>(newEvent.focus.y), 0, 0));
313         break;
314       default:
315         CServiceBroker::GetInputManager().OnEvent(newEvent);
316     }
317   }
318 }
319 
320 extern "C" void __stdcall init_emu_environ();
321 extern "C" void __stdcall update_emu_environ();
322 extern "C" void __stdcall cleanup_emu_environ();
323 
Preflight()324 void CApplication::Preflight()
325 {
326 #ifdef HAS_DBUS
327   // call 'dbus_threads_init_default' before any other dbus calls in order to
328   // avoid race conditions with other threads using dbus connections
329   dbus_threads_init_default();
330 #endif
331 
332   // run any platform preflight scripts.
333 #if defined(TARGET_DARWIN_OSX)
334   std::string install_path;
335 
336   install_path = CUtil::GetHomePath();
337   setenv("KODI_HOME", install_path.c_str(), 0);
338   install_path += "/tools/darwin/runtime/preflight";
339   system(install_path.c_str());
340 #endif
341 }
342 
Create(const CAppParamParser & params)343 bool CApplication::Create(const CAppParamParser &params)
344 {
345   // Grab a handle to our thread to be used later in identifying the render thread.
346   m_threadID = CThread::GetCurrentThreadId();
347 
348   m_bPlatformDirectories = params.m_platformDirectories;
349   m_bTestMode = params.m_testmode;
350   m_bStandalone = params.m_standAlone;
351   m_windowing = params.m_windowing;
352 
353   CServiceBroker::CreateLogging();
354 
355   CServiceBroker::RegisterCPUInfo(CCPUInfo::GetCPUInfo());
356 
357   m_pSettingsComponent.reset(new CSettingsComponent());
358   m_pSettingsComponent->Init(params);
359 
360   // Announement service
361   m_pAnnouncementManager = std::make_shared<ANNOUNCEMENT::CAnnouncementManager>();
362   m_pAnnouncementManager->Start();
363   CServiceBroker::RegisterAnnouncementManager(m_pAnnouncementManager);
364 
365   m_ServiceManager.reset(new CServiceManager());
366 
367   if (!m_ServiceManager->InitStageOne())
368   {
369     return false;
370   }
371 
372   Preflight();
373 
374   // here we register all global classes for the CApplicationMessenger,
375   // after that we can send messages to the corresponding modules
376   CApplicationMessenger::GetInstance().RegisterReceiver(this);
377   CApplicationMessenger::GetInstance().RegisterReceiver(&CServiceBroker::GetPlaylistPlayer());
378   CApplicationMessenger::GetInstance().SetGUIThread(m_threadID);
379 
380   // copy required files
381   CUtil::CopyUserDataIfNeeded("special://masterprofile/", "RssFeeds.xml");
382   CUtil::CopyUserDataIfNeeded("special://masterprofile/", "favourites.xml");
383   CUtil::CopyUserDataIfNeeded("special://masterprofile/", "Lircmap.xml");
384 
385   CServiceBroker::GetLogging().Initialize(CSpecialProtocol::TranslatePath("special://logpath"));
386 
387 #ifdef TARGET_POSIX //! @todo Win32 has no special://home/ mapping by default, so we
388   //!       must create these here. Ideally this should be using special://home/ and
389   //!      be platform agnostic (i.e. unify the InitDirectories*() functions)
390   if (!m_bPlatformDirectories)
391 #endif
392   {
393     CDirectory::Create("special://xbmc/addons");
394   }
395 
396   // Init our DllLoaders emu env
397   init_emu_environ();
398 
399   CLog::Log(LOGINFO, "-----------------------------------------------------------------------");
400   CLog::Log(LOGINFO, "Starting %s (%s). Platform: %s %s %d-bit", CSysInfo::GetAppName().c_str(),
401             CSysInfo::GetVersion().c_str(), g_sysinfo.GetBuildTargetPlatformName().c_str(),
402             g_sysinfo.GetBuildTargetCpuFamily().c_str(), g_sysinfo.GetXbmcBitness());
403 
404   std::string buildType;
405 #if defined(_DEBUG)
406   buildType = "Debug";
407 #elif defined(NDEBUG)
408   buildType = "Release";
409 #else
410   buildType = "Unknown";
411 #endif
412 
413   CLog::Log(LOGINFO, "Using %s %s x%d", buildType.c_str(), CSysInfo::GetAppName().c_str(),
414             g_sysinfo.GetXbmcBitness());
415   CLog::Log(
416       LOGINFO, "%s compiled %s by %s for %s %s %d-bit %s (%s)", CSysInfo::GetAppName().c_str(),
417       CSysInfo::GetBuildDate(), g_sysinfo.GetUsedCompilerNameAndVer().c_str(),
418       g_sysinfo.GetBuildTargetPlatformName().c_str(), g_sysinfo.GetBuildTargetCpuFamily().c_str(),
419       g_sysinfo.GetXbmcBitness(), g_sysinfo.GetBuildTargetPlatformVersionDecoded().c_str(),
420       g_sysinfo.GetBuildTargetPlatformVersion().c_str());
421 
422   std::string deviceModel(g_sysinfo.GetModelName());
423   if (!g_sysinfo.GetManufacturerName().empty())
424     deviceModel = g_sysinfo.GetManufacturerName() + " " + (deviceModel.empty() ? std::string("device") : deviceModel);
425   if (!deviceModel.empty())
426     CLog::Log(LOGINFO, "Running on %s with %s, kernel: %s %s %d-bit version %s",
427               deviceModel.c_str(), g_sysinfo.GetOsPrettyNameWithVersion().c_str(),
428               g_sysinfo.GetKernelName().c_str(), g_sysinfo.GetKernelCpuFamily().c_str(),
429               g_sysinfo.GetKernelBitness(), g_sysinfo.GetKernelVersionFull().c_str());
430   else
431     CLog::Log(LOGINFO, "Running on %s, kernel: %s %s %d-bit version %s",
432               g_sysinfo.GetOsPrettyNameWithVersion().c_str(), g_sysinfo.GetKernelName().c_str(),
433               g_sysinfo.GetKernelCpuFamily().c_str(), g_sysinfo.GetKernelBitness(),
434               g_sysinfo.GetKernelVersionFull().c_str());
435 
436   CLog::Log(LOGINFO, "FFmpeg version/source: %s", av_version_info());
437 
438   std::string cpuModel(CServiceBroker::GetCPUInfo()->GetCPUModel());
439   if (!cpuModel.empty())
440     CLog::Log(LOGINFO, "Host CPU: %s, %d core%s available", cpuModel.c_str(),
441               CServiceBroker::GetCPUInfo()->GetCPUCount(),
442               (CServiceBroker::GetCPUInfo()->GetCPUCount() == 1) ? "" : "s");
443   else
444     CLog::Log(LOGINFO, "%d CPU core%s available", CServiceBroker::GetCPUInfo()->GetCPUCount(),
445               (CServiceBroker::GetCPUInfo()->GetCPUCount() == 1) ? "" : "s");
446 
447     //! @todo - move to CPlatformXXX ???
448 #if defined(TARGET_WINDOWS)
449   CLog::Log(LOGINFO, "System has {:.1f} GB of RAM installed",
450             CWIN32Util::GetSystemMemorySize() / static_cast<double>(MB));
451   CLog::Log(LOGINFO, "%s", CWIN32Util::GetResInfoString().c_str());
452   CLog::Log(LOGINFO, "Running with %s rights",
453             (CWIN32Util::IsCurrentUserLocalAdministrator() == TRUE) ? "administrator"
454                                                                     : "restricted");
455   CLog::Log(LOGINFO, "Aero is %s", (g_sysinfo.IsAeroDisabled() == true) ? "disabled" : "enabled");
456   HDR_STATUS hdrStatus = CWIN32Util::GetWindowsHDRStatus();
457   if (hdrStatus == HDR_STATUS::HDR_UNSUPPORTED)
458     CLog::Log(LOGINFO, "Display is not HDR capable or cannot be detected");
459   else
460     CLog::Log(LOGINFO, "Display HDR capable is detected and Windows HDR switch is %s",
461               (hdrStatus == HDR_STATUS::HDR_ON) ? "ON" : "OFF");
462 #endif
463 #if defined(TARGET_ANDROID)
464   CLog::Log(
465       LOGINFO,
466       "Product: %s, Device: %s, Board: %s - Manufacturer: %s, Brand: %s, Model: %s, Hardware: %s",
467       CJNIBuild::PRODUCT.c_str(), CJNIBuild::DEVICE.c_str(), CJNIBuild::BOARD.c_str(),
468       CJNIBuild::MANUFACTURER.c_str(), CJNIBuild::BRAND.c_str(), CJNIBuild::MODEL.c_str(),
469       CJNIBuild::HARDWARE.c_str());
470   std::string extstorage;
471   bool extready = CXBMCApp::GetExternalStorage(extstorage);
472   CLog::Log(LOGINFO, "External storage path = %s; status = %s", extstorage.c_str(),
473             extready ? "ok" : "nok");
474 #endif
475 
476 #if defined(__arm__) || defined(__aarch64__)
477   if (CServiceBroker::GetCPUInfo()->GetCPUFeatures() & CPU_FEATURE_NEON)
478     CLog::Log(LOGINFO, "ARM Features: Neon enabled");
479   else
480     CLog::Log(LOGINFO, "ARM Features: Neon disabled");
481 #endif
482   CSpecialProtocol::LogPaths();
483 
484   std::string executable = CUtil::ResolveExecutablePath();
485   CLog::Log(LOGINFO, "The executable running is: %s", executable.c_str());
486   std::string hostname("[unknown]");
487   m_ServiceManager->GetNetwork().GetHostName(hostname);
488   CLog::Log(LOGINFO, "Local hostname: %s", hostname.c_str());
489   std::string lowerAppName = CCompileInfo::GetAppName();
490   StringUtils::ToLower(lowerAppName);
491   CLog::Log(LOGINFO, "Log File is located: %s.log",
492             CSpecialProtocol::TranslatePath("special://logpath/" + lowerAppName).c_str());
493   CRegExp::LogCheckUtf8Support();
494   CLog::Log(LOGINFO, "-----------------------------------------------------------------------");
495 
496   std::string strExecutablePath = CUtil::GetHomePath();
497 
498   // initialize network protocols
499   avformat_network_init();
500   // set avutil callback
501   av_log_set_callback(ff_avutil_log);
502 
503   CLog::Log(LOGINFO, "loading settings");
504   if (!m_pSettingsComponent->Load())
505     return false;
506 
507   CLog::Log(LOGINFO, "creating subdirectories");
508   const std::shared_ptr<CProfileManager> profileManager = m_pSettingsComponent->GetProfileManager();
509   const std::shared_ptr<CSettings> settings = m_pSettingsComponent->GetSettings();
510   CLog::Log(LOGINFO, "userdata folder: %s", CURL::GetRedacted(profileManager->GetProfileUserDataFolder()).c_str());
511   CLog::Log(LOGINFO, "recording folder: %s", CURL::GetRedacted(settings->GetString(CSettings::SETTING_AUDIOCDS_RECORDINGPATH)).c_str());
512   CLog::Log(LOGINFO, "screenshots folder: %s", CURL::GetRedacted(settings->GetString(CSettings::SETTING_DEBUG_SCREENSHOTPATH)).c_str());
513   CDirectory::Create(profileManager->GetUserDataFolder());
514   CDirectory::Create(profileManager->GetProfileUserDataFolder());
515   profileManager->CreateProfileFolders();
516 
517   update_emu_environ();//apply the GUI settings
518 
519   // application inbound service
520   m_pAppPort = std::make_shared<CAppInboundProtocol>(*this);
521   CServiceBroker::RegisterAppPort(m_pAppPort);
522 
523   if (!m_ServiceManager->InitStageTwo(params, m_pSettingsComponent->GetProfileManager()->GetProfileUserDataFolder()))
524   {
525     return false;
526   }
527 
528   m_pActiveAE.reset(new ActiveAE::CActiveAE());
529   m_pActiveAE->Start();
530   CServiceBroker::RegisterAE(m_pActiveAE.get());
531 
532   // restore AE's previous volume state
533   SetHardwareVolume(m_volumeLevel);
534   CServiceBroker::GetActiveAE()->SetMute(m_muted);
535 
536   // initialize m_replayGainSettings
537   m_replayGainSettings.iType = settings->GetInt(CSettings::SETTING_MUSICPLAYER_REPLAYGAINTYPE);
538   m_replayGainSettings.iPreAmp = settings->GetInt(CSettings::SETTING_MUSICPLAYER_REPLAYGAINPREAMP);
539   m_replayGainSettings.iNoGainPreAmp = settings->GetInt(CSettings::SETTING_MUSICPLAYER_REPLAYGAINNOGAINPREAMP);
540   m_replayGainSettings.bAvoidClipping = settings->GetBool(CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING);
541 
542   // load the keyboard layouts
543   if (!CKeyboardLayoutManager::GetInstance().Load())
544   {
545     CLog::Log(LOGFATAL, "CApplication::Create: Unable to load keyboard layouts");
546     return false;
547   }
548 
549   // set user defined CA trust bundle
550   std::string caCert =
551       CSpecialProtocol::TranslatePath(m_pSettingsComponent->GetAdvancedSettings()->m_caTrustFile);
552   if (!caCert.empty())
553   {
554     if (XFILE::CFile::Exists(caCert))
555     {
556       CEnvironment::setenv("SSL_CERT_FILE", caCert.c_str(), 1);
557       CLog::Log(LOGDEBUG, "CApplication::Create - SSL_CERT_FILE: {}", caCert);
558     }
559     else
560     {
561       CLog::Log(LOGDEBUG, "CApplication::Create - Error reading SSL_CERT_FILE: {} -> ignored",
562                 caCert);
563     }
564   }
565 
566   CUtil::InitRandomSeed();
567 
568   m_lastRenderTime = XbmcThreads::SystemClockMillis();
569   return true;
570 }
571 
CreateGUI()572 bool CApplication::CreateGUI()
573 {
574   m_frameMoveGuard.lock();
575 
576   m_renderGUI = true;
577 
578   auto windowSystems = KODI::WINDOWING::CWindowSystemFactory::GetWindowSystems();
579 
580   if (!m_windowing.empty())
581     windowSystems = {m_windowing};
582 
583   for (auto& windowSystem : windowSystems)
584   {
585     CLog::Log(LOGDEBUG, "CApplication::{} - trying to init {} windowing system", __FUNCTION__,
586               windowSystem);
587     m_pWinSystem = KODI::WINDOWING::CWindowSystemFactory::CreateWindowSystem(windowSystem);
588 
589     if (!m_pWinSystem)
590       continue;
591 
592     if (!m_windowing.empty() && m_windowing != windowSystem)
593       continue;
594 
595     CServiceBroker::RegisterWinSystem(m_pWinSystem.get());
596 
597     if (!m_pWinSystem->InitWindowSystem())
598     {
599       CLog::Log(LOGDEBUG, "CApplication::{} - unable to init {} windowing system", __FUNCTION__,
600                 windowSystem);
601       m_pWinSystem->DestroyWindowSystem();
602       m_pWinSystem.reset();
603       CServiceBroker::UnregisterWinSystem();
604       continue;
605     }
606     else
607     {
608       CLog::Log(LOGINFO, "CApplication::{} - using the {} windowing system", __FUNCTION__,
609                 windowSystem);
610       break;
611     }
612   }
613 
614   if (!m_pWinSystem)
615   {
616     CLog::Log(LOGFATAL, "CApplication::{} - unable to init windowing system", __FUNCTION__);
617     CServiceBroker::UnregisterWinSystem();
618     return false;
619   }
620 
621   // Retrieve the matching resolution based on GUI settings
622   bool sav_res = false;
623   CDisplaySettings::GetInstance().SetCurrentResolution(CDisplaySettings::GetInstance().GetDisplayResolution());
624   CLog::Log(LOGINFO, "Checking resolution %i",
625             CDisplaySettings::GetInstance().GetCurrentResolution());
626   if (!CServiceBroker::GetWinSystem()->GetGfxContext().IsValidResolution(CDisplaySettings::GetInstance().GetCurrentResolution()))
627   {
628     CLog::Log(LOGINFO, "Setting safe mode %i", RES_DESKTOP);
629     // defer saving resolution after window was created
630     CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
631     sav_res = true;
632   }
633 
634   // update the window resolution
635   const std::shared_ptr<CSettings> settings = m_pSettingsComponent->GetSettings();
636   CServiceBroker::GetWinSystem()->SetWindowResolution(settings->GetInt(CSettings::SETTING_WINDOW_WIDTH), settings->GetInt(CSettings::SETTING_WINDOW_HEIGHT));
637 
638   if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_startFullScreen && CDisplaySettings::GetInstance().GetCurrentResolution() == RES_WINDOW)
639   {
640     // defer saving resolution after window was created
641     CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
642     sav_res = true;
643   }
644 
645   if (!CServiceBroker::GetWinSystem()->GetGfxContext().IsValidResolution(CDisplaySettings::GetInstance().GetCurrentResolution()))
646   {
647     // Oh uh - doesn't look good for starting in their wanted screenmode
648     CLog::Log(LOGERROR, "The screen resolution requested is not valid, resetting to a valid mode");
649     CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
650     sav_res = true;
651   }
652   if (!InitWindow())
653   {
654     return false;
655   }
656 
657   // Set default screen saver mode
658   auto screensaverModeSetting = std::static_pointer_cast<CSettingString>(settings->GetSetting(CSettings::SETTING_SCREENSAVER_MODE));
659   // Can only set this after windowing has been initialized since it depends on it
660   if (CServiceBroker::GetWinSystem()->GetOSScreenSaver())
661   {
662     // If OS has a screen saver, use it by default
663     screensaverModeSetting->SetDefault("");
664   }
665   else
666   {
667     // If OS has no screen saver, use Kodi one by default
668     screensaverModeSetting->SetDefault("screensaver.xbmc.builtin.dim");
669   }
670 
671   if (sav_res)
672     CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP, true);
673 
674   m_pGUI.reset(new CGUIComponent());
675   m_pGUI->Init();
676 
677   // Splash requires gui component!!
678   CServiceBroker::GetRenderSystem()->ShowSplash("");
679 
680   // The key mappings may already have been loaded by a peripheral
681   CLog::Log(LOGINFO, "load keymapping");
682   if (!CServiceBroker::GetInputManager().LoadKeymaps())
683     return false;
684 
685   RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
686   CLog::Log(LOGINFO, "GUI format %ix%i, Display %s",
687             info.iWidth,
688             info.iHeight,
689             info.strMode.c_str());
690 
691   return true;
692 }
693 
InitWindow(RESOLUTION res)694 bool CApplication::InitWindow(RESOLUTION res)
695 {
696   if (res == RES_INVALID)
697     res = CDisplaySettings::GetInstance().GetCurrentResolution();
698 
699   bool bFullScreen = res != RES_WINDOW;
700   if (!CServiceBroker::GetWinSystem()->CreateNewWindow(CSysInfo::GetAppName(),
701                                                       bFullScreen, CDisplaySettings::GetInstance().GetResolutionInfo(res)))
702   {
703     CLog::Log(LOGFATAL, "CApplication::Create: Unable to create window");
704     return false;
705   }
706 
707   if (!CServiceBroker::GetRenderSystem()->InitRenderSystem())
708   {
709     CLog::Log(LOGFATAL, "CApplication::Create: Unable to init rendering system");
710     return false;
711   }
712   // set GUI res and force the clear of the screen
713   CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(res, false);
714   return true;
715 }
716 
Initialize()717 bool CApplication::Initialize()
718 {
719 #if defined(HAS_DVD_DRIVE) && !defined(TARGET_WINDOWS) // somehow this throws an "unresolved external symbol" on win32
720   // turn off cdio logging
721   cdio_loglevel_default = CDIO_LOG_ERROR;
722 #endif
723 
724   // load the language and its translated strings
725   if (!LoadLanguage(false))
726     return false;
727 
728   const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
729 
730   profileManager->GetEventLog().Add(EventPtr(new CNotificationEvent(
731     StringUtils::Format(g_localizeStrings.Get(177).c_str(), g_sysinfo.GetAppName().c_str()),
732     StringUtils::Format(g_localizeStrings.Get(178).c_str(), g_sysinfo.GetAppName().c_str()),
733     "special://xbmc/media/icon256x256.png", EventLevel::Basic)));
734 
735   m_ServiceManager->GetNetwork().WaitForNet();
736 
737   // initialize (and update as needed) our databases
738   CDatabaseManager &databaseManager = m_ServiceManager->GetDatabaseManager();
739 
740   CEvent event(true);
741   CJobManager::GetInstance().Submit([&databaseManager, &event]() {
742     databaseManager.Initialize();
743     event.Set();
744   });
745 
746   std::string localizedStr = g_localizeStrings.Get(24150);
747   int iDots = 1;
748   while (!event.WaitMSec(1000))
749   {
750     if (databaseManager.IsUpgrading())
751       CServiceBroker::GetRenderSystem()->ShowSplash(std::string(iDots, ' ') + localizedStr + std::string(iDots, '.'));
752 
753     if (iDots == 3)
754       iDots = 1;
755     else
756       ++iDots;
757   }
758   CServiceBroker::GetRenderSystem()->ShowSplash("");
759 
760   StartServices();
761 
762   // GUI depends on seek handler
763   m_appPlayer.GetSeekHandler().Configure();
764 
765   bool uiInitializationFinished = false;
766 
767   if (CServiceBroker::GetGUI()->GetWindowManager().Initialized())
768   {
769     const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
770 
771     CServiceBroker::GetGUI()->GetWindowManager().CreateWindows();
772 
773     m_confirmSkinChange = false;
774 
775     std::vector<AddonInfoPtr> incompatibleAddons;
776     event.Reset();
777 
778     // Addon migration
779     if (CServiceBroker::GetAddonMgr().GetIncompatibleEnabledAddonInfos(incompatibleAddons))
780     {
781       if (CAddonSystemSettings::GetInstance().GetAddonAutoUpdateMode() == AUTO_UPDATES_ON)
782       {
783         CJobManager::GetInstance().Submit(
784             [&event, &incompatibleAddons]() {
785               if (CServiceBroker::GetRepositoryUpdater().CheckForUpdates())
786                 CServiceBroker::GetRepositoryUpdater().Await();
787 
788               incompatibleAddons = CServiceBroker::GetAddonMgr().MigrateAddons();
789               event.Set();
790             },
791             CJob::PRIORITY_DEDICATED);
792         localizedStr = g_localizeStrings.Get(24151);
793         iDots = 1;
794         while (!event.WaitMSec(1000))
795         {
796           CServiceBroker::GetRenderSystem()->ShowSplash(std::string(iDots, ' ') + localizedStr +
797                                                         std::string(iDots, '.'));
798           if (iDots == 3)
799             iDots = 1;
800           else
801             ++iDots;
802         }
803         m_incompatibleAddons = incompatibleAddons;
804       }
805       else
806       {
807         // If no update is active disable all incompatible addons during start
808         m_incompatibleAddons =
809             CServiceBroker::GetAddonMgr().DisableIncompatibleAddons(incompatibleAddons);
810       }
811     }
812 
813     // Start splashscreen and load skin
814     CServiceBroker::GetRenderSystem()->ShowSplash("");
815     m_confirmSkinChange = true;
816 
817     auto setting = settings->GetSetting(CSettings::SETTING_LOOKANDFEEL_SKIN);
818     if (!setting)
819     {
820       CLog::Log(LOGFATAL, "Failed to load setting for: {}", CSettings::SETTING_LOOKANDFEEL_SKIN);
821       return false;
822     }
823 
824     std::string defaultSkin = std::static_pointer_cast<const CSettingString>(setting)->GetDefault();
825     if (!LoadSkin(settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN)))
826     {
827       CLog::Log(LOGERROR, "Failed to load skin '%s'", settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN).c_str());
828       if (!LoadSkin(defaultSkin))
829       {
830         CLog::Log(LOGFATAL, "Default skin '%s' could not be loaded! Terminating..", defaultSkin.c_str());
831         return false;
832       }
833     }
834 
835     // initialize splash window after splash screen disappears
836     // because we need a real window in the background which gets
837     // rendered while we load the main window or enter the master lock key
838     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SPLASH);
839 
840     if (settings->GetBool(CSettings::SETTING_MASTERLOCK_STARTUPLOCK) &&
841         profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
842         !profileManager->GetMasterProfile().getLockCode().empty())
843     {
844       g_passwordManager.CheckStartUpLock();
845     }
846 
847     // check if we should use the login screen
848     if (profileManager->UsingLoginScreen())
849     {
850       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_LOGIN_SCREEN);
851     }
852     else
853     {
854       // activate the configured start window
855       int firstWindow = g_SkinInfo->GetFirstWindow();
856       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(firstWindow);
857 
858       if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_STARTUP_ANIM))
859       {
860         CLog::Log(LOGWARNING, "CApplication::Initialize - startup.xml taints init process");
861       }
862 
863       // the startup window is considered part of the initialization as it most likely switches to the final window
864       uiInitializationFinished = firstWindow != WINDOW_STARTUP_ANIM;
865     }
866   }
867   else //No GUI Created
868   {
869     uiInitializationFinished = true;
870   }
871 
872   CJSONRPC::Initialize();
873 
874   if (!m_ServiceManager->InitStageThree(profileManager))
875   {
876     CLog::Log(LOGERROR, "Application - Init3 failed");
877   }
878 
879   g_sysinfo.Refresh();
880 
881   CLog::Log(LOGINFO, "removing tempfiles");
882   CUtil::RemoveTempFiles();
883 
884   if (!profileManager->UsingLoginScreen())
885   {
886     UpdateLibraries();
887     SetLoggingIn(false);
888   }
889 
890   m_slowTimer.StartZero();
891 
892   // register action listeners
893   RegisterActionListener(&m_appPlayer.GetSeekHandler());
894   RegisterActionListener(&CPlayerController::GetInstance());
895 
896   CServiceBroker::GetRepositoryUpdater().Start();
897   if (!profileManager->UsingLoginScreen())
898     CServiceBroker::GetServiceAddons().Start();
899 
900   CLog::Log(LOGINFO, "initialize done");
901 
902   CheckOSScreenSaverInhibitionSetting();
903   // reset our screensaver (starts timers etc.)
904   ResetScreenSaver();
905 
906   // if the user interfaces has been fully initialized let everyone know
907   if (uiInitializationFinished)
908   {
909     CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UI_READY);
910     CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
911   }
912 
913   return true;
914 }
915 
StartServer(enum ESERVERS eServer,bool bStart,bool bWait)916 bool CApplication::StartServer(enum ESERVERS eServer, bool bStart, bool bWait/* = false*/)
917 {
918   bool ret = false;
919   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
920   switch(eServer)
921   {
922     case ES_WEBSERVER:
923       // the callback will take care of starting/stopping webserver
924       ret = settings->SetBool(CSettings::SETTING_SERVICES_WEBSERVER, bStart);
925       break;
926 
927     case ES_AIRPLAYSERVER:
928       // the callback will take care of starting/stopping airplay
929       ret = settings->SetBool(CSettings::SETTING_SERVICES_AIRPLAY, bStart);
930       break;
931 
932     case ES_JSONRPCSERVER:
933       // the callback will take care of starting/stopping jsonrpc server
934       ret = settings->SetBool(CSettings::SETTING_SERVICES_ESENABLED, bStart);
935       break;
936 
937     case ES_UPNPSERVER:
938       // the callback will take care of starting/stopping upnp server
939       ret = settings->SetBool(CSettings::SETTING_SERVICES_UPNPSERVER, bStart);
940       break;
941 
942     case ES_UPNPRENDERER:
943       // the callback will take care of starting/stopping upnp renderer
944       ret = settings->SetBool(CSettings::SETTING_SERVICES_UPNPRENDERER, bStart);
945       break;
946 
947     case ES_EVENTSERVER:
948       // the callback will take care of starting/stopping event server
949       ret = settings->SetBool(CSettings::SETTING_SERVICES_ESENABLED, bStart);
950       break;
951 
952     case ES_ZEROCONF:
953       // the callback will take care of starting/stopping zeroconf
954       ret = settings->SetBool(CSettings::SETTING_SERVICES_ZEROCONF, bStart);
955       break;
956 
957     default:
958       ret = false;
959       break;
960   }
961   settings->Save();
962 
963   return ret;
964 }
965 
StartServices()966 void CApplication::StartServices()
967 {
968 #if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE)
969   // Start Thread for DVD Mediatype detection
970   CLog::Log(LOGINFO, "start dvd mediatype detection");
971   m_DetectDVDType.Create(false);
972 #endif
973 }
974 
StopServices()975 void CApplication::StopServices()
976 {
977   m_ServiceManager->GetNetwork().NetworkMessage(CNetwork::SERVICES_DOWN, 0);
978 
979 #if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE)
980   CLog::Log(LOGINFO, "stop dvd detect media");
981   m_DetectDVDType.StopThread();
982 #endif
983 }
984 
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)985 void CApplication::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
986 {
987   if (setting == NULL)
988     return;
989 
990   const std::string &settingId = setting->GetId();
991 
992   if (settingId == CSettings::SETTING_LOOKANDFEEL_SKIN ||
993       settingId == CSettings::SETTING_LOOKANDFEEL_FONT ||
994       settingId == CSettings::SETTING_LOOKANDFEEL_SKINTHEME ||
995       settingId == CSettings::SETTING_LOOKANDFEEL_SKINCOLORS)
996   {
997     // check if we should ignore this change event due to changing skins in which case we have to
998     // change several settings and each one of them could lead to a complete skin reload which would
999     // result in multiple skin reloads. Therefore we manually specify to ignore specific settings
1000     // which are going to be changed.
1001     if (m_ignoreSkinSettingChanges)
1002       return;
1003 
1004     // if the skin changes and the current color/theme/font is not the default one, reset
1005     // the it to the default value
1006     if (settingId == CSettings::SETTING_LOOKANDFEEL_SKIN)
1007     {
1008       const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
1009       SettingPtr skinRelatedSetting = settings->GetSetting(CSettings::SETTING_LOOKANDFEEL_SKINCOLORS);
1010       if (!skinRelatedSetting->IsDefault())
1011       {
1012         m_ignoreSkinSettingChanges = true;
1013         skinRelatedSetting->Reset();
1014       }
1015 
1016       skinRelatedSetting = settings->GetSetting(CSettings::SETTING_LOOKANDFEEL_SKINTHEME);
1017       if (!skinRelatedSetting->IsDefault())
1018       {
1019         m_ignoreSkinSettingChanges = true;
1020         skinRelatedSetting->Reset();
1021       }
1022 
1023       skinRelatedSetting = settings->GetSetting(CSettings::SETTING_LOOKANDFEEL_FONT);
1024       if (!skinRelatedSetting->IsDefault())
1025       {
1026         m_ignoreSkinSettingChanges = true;
1027         skinRelatedSetting->Reset();
1028       }
1029     }
1030     else if (settingId == CSettings::SETTING_LOOKANDFEEL_SKINTHEME)
1031     {
1032       std::shared_ptr<CSettingString> skinColorsSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_LOOKANDFEEL_SKINCOLORS));
1033       m_ignoreSkinSettingChanges = true;
1034 
1035       // we also need to adjust the skin color setting
1036       std::string colorTheme = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
1037       URIUtils::RemoveExtension(colorTheme);
1038       if (setting->IsDefault() || StringUtils::EqualsNoCase(colorTheme, "Textures"))
1039         skinColorsSetting->Reset();
1040       else
1041         skinColorsSetting->SetValue(colorTheme);
1042     }
1043 
1044     m_ignoreSkinSettingChanges = false;
1045 
1046     if (g_SkinInfo)
1047     {
1048       // now we can finally reload skins
1049       std::string builtin("ReloadSkin");
1050       if (settingId == CSettings::SETTING_LOOKANDFEEL_SKIN && m_confirmSkinChange)
1051         builtin += "(confirm)";
1052       CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, builtin);
1053     }
1054   }
1055   else if (settingId == CSettings::SETTING_LOOKANDFEEL_SKINZOOM)
1056   {
1057     CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE);
1058     CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
1059   }
1060   else if (settingId == CSettings::SETTING_SCREENSAVER_MODE)
1061   {
1062     CheckOSScreenSaverInhibitionSetting();
1063   }
1064   else if (settingId == CSettings::SETTING_VIDEOSCREEN_FAKEFULLSCREEN)
1065   {
1066     if (CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
1067       CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution(), true);
1068   }
1069   else if (settingId == CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH)
1070   {
1071     CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_RESTART);
1072   }
1073   else if (StringUtils::EqualsNoCase(settingId, CSettings::SETTING_MUSICPLAYER_REPLAYGAINTYPE))
1074     m_replayGainSettings.iType = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1075   else if (StringUtils::EqualsNoCase(settingId, CSettings::SETTING_MUSICPLAYER_REPLAYGAINPREAMP))
1076     m_replayGainSettings.iPreAmp = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1077   else if (StringUtils::EqualsNoCase(settingId, CSettings::SETTING_MUSICPLAYER_REPLAYGAINNOGAINPREAMP))
1078     m_replayGainSettings.iNoGainPreAmp = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1079   else if (StringUtils::EqualsNoCase(settingId, CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING))
1080     m_replayGainSettings.bAvoidClipping = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
1081 }
1082 
OnSettingAction(const std::shared_ptr<const CSetting> & setting)1083 void CApplication::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
1084 {
1085   if (setting == NULL)
1086     return;
1087 
1088   const std::string &settingId = setting->GetId();
1089   if (settingId == CSettings::SETTING_LOOKANDFEEL_SKINSETTINGS)
1090     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SKIN_SETTINGS);
1091   else if (settingId == CSettings::SETTING_SCREENSAVER_PREVIEW)
1092     ActivateScreenSaver(true);
1093   else if (settingId == CSettings::SETTING_SCREENSAVER_SETTINGS)
1094   {
1095     AddonPtr addon;
1096     if (CServiceBroker::GetAddonMgr().GetAddon(
1097             CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
1098                 CSettings::SETTING_SCREENSAVER_MODE),
1099             addon, ADDON_SCREENSAVER, OnlyEnabled::YES))
1100       CGUIDialogAddonSettings::ShowForAddon(addon);
1101   }
1102   else if (settingId == CSettings::SETTING_AUDIOCDS_SETTINGS)
1103   {
1104     AddonPtr addon;
1105     if (CServiceBroker::GetAddonMgr().GetAddon(
1106             CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
1107                 CSettings::SETTING_AUDIOCDS_ENCODER),
1108             addon, ADDON_AUDIOENCODER, OnlyEnabled::YES))
1109       CGUIDialogAddonSettings::ShowForAddon(addon);
1110   }
1111   else if (settingId == CSettings::SETTING_VIDEOSCREEN_GUICALIBRATION)
1112     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SCREEN_CALIBRATION);
1113   else if (settingId == CSettings::SETTING_SOURCE_VIDEOS)
1114   {
1115     std::vector<std::string> params{"library://video/files.xml", "return"};
1116     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_NAV, params);
1117   }
1118   else if (settingId == CSettings::SETTING_SOURCE_MUSIC)
1119   {
1120     std::vector<std::string> params{"library://music/files.xml", "return"};
1121     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_NAV, params);
1122   }
1123   else if (settingId == CSettings::SETTING_SOURCE_PICTURES)
1124     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_PICTURES);
1125 }
1126 
OnSettingUpdate(const std::shared_ptr<CSetting> & setting,const char * oldSettingId,const TiXmlNode * oldSettingNode)1127 bool CApplication::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
1128                                    const char* oldSettingId,
1129                                    const TiXmlNode* oldSettingNode)
1130 {
1131   if (setting == NULL)
1132     return false;
1133 
1134 #if defined(TARGET_DARWIN_OSX)
1135   if (setting->GetId() == CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE)
1136   {
1137     std::shared_ptr<CSettingString> audioDevice = std::static_pointer_cast<CSettingString>(setting);
1138     // Gotham and older didn't enumerate audio devices per stream on osx
1139     // add stream0 per default which should be ok for all old settings.
1140     if (!StringUtils::EqualsNoCase(audioDevice->GetValue(), "DARWINOSX:default") &&
1141         StringUtils::FindWords(audioDevice->GetValue().c_str(), ":stream") == std::string::npos)
1142     {
1143       std::string newSetting = audioDevice->GetValue();
1144       newSetting += ":stream0";
1145       return audioDevice->SetValue(newSetting);
1146     }
1147   }
1148 #endif
1149 
1150   return false;
1151 }
1152 
OnSettingsSaving() const1153 bool CApplication::OnSettingsSaving() const
1154 {
1155   // don't save settings when we're busy stopping the application
1156   // a lot of screens try to save settings on deinit and deinit is
1157   // called for every screen when the application is stopping
1158   return !m_bStop;
1159 }
1160 
ReloadSkin(bool confirm)1161 void CApplication::ReloadSkin(bool confirm/*=false*/)
1162 {
1163   if (!g_SkinInfo || m_bInitializing)
1164     return; // Don't allow reload before skin is loaded by system
1165 
1166   std::string oldSkin = g_SkinInfo->ID();
1167 
1168   CGUIMessage msg(GUI_MSG_LOAD_SKIN, -1, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
1169   CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1170 
1171   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
1172   std::string newSkin = settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN);
1173   if (LoadSkin(newSkin))
1174   {
1175     /* The Reset() or SetString() below will cause recursion, so the m_confirmSkinChange boolean is set so as to not prompt the
1176        user as to whether they want to keep the current skin. */
1177     if (confirm && m_confirmSkinChange)
1178     {
1179       if (HELPERS::ShowYesNoDialogText(CVariant{13123}, CVariant{13111}, CVariant{""}, CVariant{""}, 10000) !=
1180         DialogResponse::YES)
1181       {
1182         m_confirmSkinChange = false;
1183         settings->SetString(CSettings::SETTING_LOOKANDFEEL_SKIN, oldSkin);
1184       }
1185       else
1186         CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_STARTUP_ANIM);
1187     }
1188   }
1189   else
1190   {
1191     // skin failed to load - we revert to the default only if we didn't fail loading the default
1192     auto setting = settings->GetSetting(CSettings::SETTING_LOOKANDFEEL_SKIN);
1193     if (!setting)
1194     {
1195       CLog::Log(LOGFATAL, "Failed to load setting for: {}", CSettings::SETTING_LOOKANDFEEL_SKIN);
1196       return;
1197     }
1198 
1199     std::string defaultSkin = std::static_pointer_cast<CSettingString>(setting)->GetDefault();
1200     if (newSkin != defaultSkin)
1201     {
1202       m_confirmSkinChange = false;
1203       setting->Reset();
1204       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(24102), g_localizeStrings.Get(24103));
1205     }
1206   }
1207   m_confirmSkinChange = true;
1208 }
1209 
Load(const TiXmlNode * settings)1210 bool CApplication::Load(const TiXmlNode *settings)
1211 {
1212   if (settings == NULL)
1213     return false;
1214 
1215   const TiXmlElement *audioElement = settings->FirstChildElement("audio");
1216   if (audioElement != NULL)
1217   {
1218     XMLUtils::GetBoolean(audioElement, "mute", m_muted);
1219     if (!XMLUtils::GetFloat(audioElement, "fvolumelevel", m_volumeLevel, VOLUME_MINIMUM, VOLUME_MAXIMUM))
1220       m_volumeLevel = VOLUME_MAXIMUM;
1221   }
1222 
1223   return true;
1224 }
1225 
Save(TiXmlNode * settings) const1226 bool CApplication::Save(TiXmlNode *settings) const
1227 {
1228   if (settings == NULL)
1229     return false;
1230 
1231   TiXmlElement volumeNode("audio");
1232   TiXmlNode *audioNode = settings->InsertEndChild(volumeNode);
1233   if (audioNode == NULL)
1234     return false;
1235 
1236   XMLUtils::SetBoolean(audioNode, "mute", m_muted);
1237   XMLUtils::SetFloat(audioNode, "fvolumelevel", m_volumeLevel);
1238 
1239   return true;
1240 }
1241 
LoadSkin(const std::string & skinID)1242 bool CApplication::LoadSkin(const std::string& skinID)
1243 {
1244   SkinPtr skin;
1245   {
1246     AddonPtr addon;
1247     if (!CServiceBroker::GetAddonMgr().GetAddon(skinID, addon, ADDON_SKIN, OnlyEnabled::YES))
1248       return false;
1249     skin = std::static_pointer_cast<ADDON::CSkinInfo>(addon);
1250   }
1251 
1252   // store player and rendering state
1253   bool bPreviousPlayingState = false;
1254 
1255   enum class RENDERING_STATE
1256   {
1257     NONE,
1258     VIDEO,
1259     GAME,
1260   } previousRenderingState = RENDERING_STATE::NONE;
1261 
1262   if (m_appPlayer.IsPlayingVideo())
1263   {
1264     bPreviousPlayingState = !m_appPlayer.IsPausedPlayback();
1265     if (bPreviousPlayingState)
1266       m_appPlayer.Pause();
1267     m_appPlayer.FlushRenderer();
1268     if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
1269     {
1270       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME);
1271       previousRenderingState = RENDERING_STATE::VIDEO;
1272     }
1273     else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME)
1274     {
1275       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME);
1276       previousRenderingState = RENDERING_STATE::GAME;
1277     }
1278 
1279   }
1280 
1281   CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
1282 
1283   // store current active window with its focused control
1284   int currentWindowID = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
1285   int currentFocusedControlID = -1;
1286   if (currentWindowID != WINDOW_INVALID)
1287   {
1288     CGUIWindow* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(currentWindowID);
1289     if (pWindow)
1290       currentFocusedControlID = pWindow->GetFocusedControlID();
1291   }
1292 
1293   UnloadSkin();
1294 
1295   skin->Start();
1296 
1297   // migrate any skin-specific settings that are still stored in guisettings.xml
1298   CSkinSettings::GetInstance().MigrateSettings(skin);
1299 
1300   // check if the skin has been properly loaded and if it has a Home.xml
1301   if (!skin->HasSkinFile("Home.xml"))
1302   {
1303     CLog::Log(LOGERROR, "failed to load requested skin '%s'", skin->ID().c_str());
1304     return false;
1305   }
1306 
1307   CLog::Log(LOGINFO, "  load skin from: %s (version: %s)", skin->Path().c_str(),
1308             skin->Version().asString().c_str());
1309   g_SkinInfo = skin;
1310 
1311   CLog::Log(LOGINFO, "  load fonts for skin...");
1312   CServiceBroker::GetWinSystem()->GetGfxContext().SetMediaDir(skin->Path());
1313   g_directoryCache.ClearSubPaths(skin->Path());
1314 
1315   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
1316   CServiceBroker::GetGUI()->GetColorManager().Load(settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKINCOLORS));
1317 
1318   g_SkinInfo->LoadIncludes();
1319 
1320   g_fontManager.LoadFonts(settings->GetString(CSettings::SETTING_LOOKANDFEEL_FONT));
1321 
1322   // load in the skin strings
1323   std::string langPath = URIUtils::AddFileToFolder(skin->Path(), "language");
1324   URIUtils::AddSlashAtEnd(langPath);
1325 
1326   g_localizeStrings.LoadSkinStrings(langPath, settings->GetString(CSettings::SETTING_LOCALE_LANGUAGE));
1327 
1328 
1329   int64_t start;
1330   start = CurrentHostCounter();
1331 
1332   CLog::Log(LOGINFO, "  load new skin...");
1333 
1334   // Load custom windows
1335   LoadCustomWindows();
1336 
1337   int64_t end, freq;
1338   end = CurrentHostCounter();
1339   freq = CurrentHostFrequency();
1340   CLog::Log(LOGDEBUG,"Load Skin XML: %.2fms", 1000.f * (end - start) / freq);
1341 
1342   CLog::Log(LOGINFO, "  initialize new skin...");
1343   CServiceBroker::GetGUI()->GetWindowManager().AddMsgTarget(this);
1344   CServiceBroker::GetGUI()->GetWindowManager().AddMsgTarget(&CServiceBroker::GetPlaylistPlayer());
1345   CServiceBroker::GetGUI()->GetWindowManager().AddMsgTarget(&g_fontManager);
1346   CServiceBroker::GetGUI()->GetWindowManager().AddMsgTarget(&CServiceBroker::GetGUI()->GetStereoscopicsManager());
1347   CServiceBroker::GetGUI()->GetWindowManager().SetCallback(*this);
1348   //@todo should be done by GUIComponents
1349   CServiceBroker::GetGUI()->GetWindowManager().Initialize();
1350   CTextureCache::GetInstance().Initialize();
1351   CServiceBroker::GetGUI()->GetAudioManager().Enable(true);
1352   CServiceBroker::GetGUI()->GetAudioManager().Load();
1353 
1354   if (g_SkinInfo->HasSkinFile("DialogFullScreenInfo.xml"))
1355     CServiceBroker::GetGUI()->GetWindowManager().Add(new CGUIDialogFullScreenInfo);
1356 
1357   CLog::Log(LOGINFO, "  skin loaded...");
1358 
1359   // leave the graphics lock
1360   lock.Leave();
1361 
1362   // restore active window
1363   if (currentWindowID != WINDOW_INVALID)
1364   {
1365     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(currentWindowID);
1366     if (currentFocusedControlID != -1)
1367     {
1368       CGUIWindow *pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(currentWindowID);
1369       if (pWindow && pWindow->HasSaveLastControl())
1370       {
1371         CGUIMessage msg(GUI_MSG_SETFOCUS, currentWindowID, currentFocusedControlID, 0);
1372         pWindow->OnMessage(msg);
1373       }
1374     }
1375   }
1376 
1377   // restore player and rendering state
1378   if (m_appPlayer.IsPlayingVideo())
1379   {
1380     if (bPreviousPlayingState)
1381       m_appPlayer.Pause();
1382 
1383     switch (previousRenderingState)
1384     {
1385     case RENDERING_STATE::VIDEO:
1386       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_FULLSCREEN_VIDEO);
1387       break;
1388     case RENDERING_STATE::GAME:
1389       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_FULLSCREEN_GAME);
1390       break;
1391     default:
1392       break;
1393     }
1394   }
1395 
1396   return true;
1397 }
1398 
UnloadSkin()1399 void CApplication::UnloadSkin()
1400 {
1401   if (g_SkinInfo != nullptr && m_saveSkinOnUnloading)
1402     g_SkinInfo->SaveSettings();
1403   else if (!m_saveSkinOnUnloading)
1404     m_saveSkinOnUnloading = true;
1405 
1406   CGUIComponent *gui = CServiceBroker::GetGUI();
1407   if (gui)
1408   {
1409     gui->GetAudioManager().Enable(false);
1410 
1411     gui->GetWindowManager().DeInitialize();
1412     CTextureCache::GetInstance().Deinitialize();
1413 
1414     // remove the skin-dependent window
1415     gui->GetWindowManager().Delete(WINDOW_DIALOG_FULLSCREEN_INFO);
1416 
1417     gui->GetTextureManager().Cleanup();
1418     gui->GetLargeTextureManager().CleanupUnusedImages(true);
1419 
1420     g_fontManager.Clear();
1421 
1422     gui->GetColorManager().Clear();
1423 
1424     gui->GetInfoManager().Clear();
1425   }
1426 
1427 //  The g_SkinInfo shared_ptr ought to be reset here
1428 // but there are too many places it's used without checking for NULL
1429 // and as a result a race condition on exit can cause a crash.
1430 
1431   CLog::Log(LOGINFO, "Unloaded skin");
1432 }
1433 
LoadCustomWindows()1434 bool CApplication::LoadCustomWindows()
1435 {
1436   // Start from wherever home.xml is
1437   std::vector<std::string> vecSkinPath;
1438   g_SkinInfo->GetSkinPaths(vecSkinPath);
1439 
1440   for (const auto &skinPath : vecSkinPath)
1441   {
1442     CLog::Log(LOGINFO, "Loading custom window XMLs from skin path %s", skinPath.c_str());
1443 
1444     CFileItemList items;
1445     if (CDirectory::GetDirectory(skinPath, items, ".xml", DIR_FLAG_NO_FILE_DIRS))
1446     {
1447       for (const auto &item : items)
1448       {
1449         if (item->m_bIsFolder)
1450           continue;
1451 
1452         std::string skinFile = URIUtils::GetFileName(item->GetPath());
1453         if (StringUtils::StartsWithNoCase(skinFile, "custom"))
1454         {
1455           CXBMCTinyXML xmlDoc;
1456           if (!xmlDoc.LoadFile(item->GetPath()))
1457           {
1458             CLog::Log(LOGERROR, "Unable to load custom window XML %s. Line %d\n%s", item->GetPath().c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
1459             continue;
1460           }
1461 
1462           // Root element should be <window>
1463           TiXmlElement* pRootElement = xmlDoc.RootElement();
1464           std::string strValue = pRootElement->Value();
1465           if (!StringUtils::EqualsNoCase(strValue, "window"))
1466           {
1467             CLog::Log(LOGERROR, "No <window> root element found for custom window in %s", skinFile.c_str());
1468             continue;
1469           }
1470 
1471           int id = WINDOW_INVALID;
1472 
1473           // Read the type attribute or element to get the window type to create
1474           // If no type is specified, create a CGUIWindow as default
1475           std::string strType;
1476           if (pRootElement->Attribute("type"))
1477             strType = pRootElement->Attribute("type");
1478           else
1479           {
1480             const TiXmlNode *pType = pRootElement->FirstChild("type");
1481             if (pType && pType->FirstChild())
1482               strType = pType->FirstChild()->Value();
1483           }
1484 
1485           // Read the id attribute or element to get the window id
1486           if (!pRootElement->Attribute("id", &id))
1487           {
1488             const TiXmlNode *pType = pRootElement->FirstChild("id");
1489             if (pType && pType->FirstChild())
1490               id = atol(pType->FirstChild()->Value());
1491           }
1492 
1493           int windowId = id + WINDOW_HOME;
1494           if (id == WINDOW_INVALID || CServiceBroker::GetGUI()->GetWindowManager().GetWindow(windowId))
1495           {
1496             // No id specified or id already in use
1497             CLog::Log(LOGERROR, "No id specified or id already in use for custom window in %s", skinFile.c_str());
1498             continue;
1499           }
1500 
1501           CGUIWindow* pWindow = NULL;
1502           bool hasVisibleCondition = false;
1503 
1504           if (StringUtils::EqualsNoCase(strType, "dialog"))
1505           {
1506             hasVisibleCondition = pRootElement->FirstChildElement("visible") != nullptr;
1507             pWindow = new CGUIDialog(windowId, skinFile);
1508           }
1509           else if (StringUtils::EqualsNoCase(strType, "submenu"))
1510           {
1511             pWindow = new CGUIDialogSubMenu(windowId, skinFile);
1512           }
1513           else if (StringUtils::EqualsNoCase(strType, "buttonmenu"))
1514           {
1515             pWindow = new CGUIDialogButtonMenu(windowId, skinFile);
1516           }
1517           else
1518           {
1519             pWindow = new CGUIWindow(windowId, skinFile);
1520           }
1521 
1522           if (!pWindow)
1523           {
1524             CLog::Log(LOGERROR, "Failed to create custom window from %s", skinFile.c_str());
1525             continue;
1526           }
1527 
1528           pWindow->SetCustom(true);
1529 
1530           // Determining whether our custom dialog is modeless (visible condition is present)
1531           // will be done on load. Therefore we need to initialize the custom dialog on gui init.
1532           pWindow->SetLoadType(hasVisibleCondition ? CGUIWindow::LOAD_ON_GUI_INIT : CGUIWindow::KEEP_IN_MEMORY);
1533 
1534           CServiceBroker::GetGUI()->GetWindowManager().AddCustomWindow(pWindow);
1535         }
1536       }
1537     }
1538   }
1539   return true;
1540 }
1541 
Render()1542 void CApplication::Render()
1543 {
1544   // do not render if we are stopped or in background
1545   if (m_bStop)
1546     return;
1547 
1548   bool hasRendered = false;
1549 
1550   // Whether externalplayer is playing and we're unfocused
1551   bool extPlayerActive = m_appPlayer.IsExternalPlaying() && !m_AppFocused;
1552 
1553   if (!extPlayerActive && CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() && !m_appPlayer.IsPausedPlayback())
1554   {
1555     ResetScreenSaver();
1556   }
1557 
1558   if(!CServiceBroker::GetRenderSystem()->BeginRender())
1559     return;
1560 
1561   // render gui layer
1562   if (m_renderGUI && !m_skipGuiRender)
1563   {
1564     if (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode())
1565     {
1566       CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_LEFT);
1567       hasRendered |= CServiceBroker::GetGUI()->GetWindowManager().Render();
1568 
1569       if (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() != RENDER_STEREO_MODE_MONO)
1570       {
1571         CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_RIGHT);
1572         hasRendered |= CServiceBroker::GetGUI()->GetWindowManager().Render();
1573       }
1574       CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoView(RENDER_STEREO_VIEW_OFF);
1575     }
1576     else
1577     {
1578       hasRendered |= CServiceBroker::GetGUI()->GetWindowManager().Render();
1579     }
1580     // execute post rendering actions (finalize window closing)
1581     CServiceBroker::GetGUI()->GetWindowManager().AfterRender();
1582 
1583     m_lastRenderTime = XbmcThreads::SystemClockMillis();
1584   }
1585 
1586   // render video layer
1587   CServiceBroker::GetGUI()->GetWindowManager().RenderEx();
1588 
1589   CServiceBroker::GetRenderSystem()->EndRender();
1590 
1591   // reset our info cache - we do this at the end of Render so that it is
1592   // fresh for the next process(), or after a windowclose animation (where process()
1593   // isn't called)
1594   CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager();
1595   infoMgr.ResetCache();
1596   infoMgr.GetInfoProviders().GetGUIControlsInfoProvider().ResetContainerMovingCache();
1597 
1598   if (hasRendered)
1599   {
1600     infoMgr.GetInfoProviders().GetSystemInfoProvider().UpdateFPS();
1601   }
1602 
1603   CServiceBroker::GetWinSystem()->GetGfxContext().Flip(hasRendered, m_appPlayer.IsRenderingVideoLayer());
1604 
1605   CTimeUtils::UpdateFrameTime(hasRendered);
1606 }
1607 
OnAction(const CAction & action)1608 bool CApplication::OnAction(const CAction &action)
1609 {
1610   // special case for switching between GUI & fullscreen mode.
1611   if (action.GetID() == ACTION_SHOW_GUI)
1612   { // Switch to fullscreen mode if we can
1613     if (SwitchToFullScreen())
1614     {
1615       m_navigationTimer.StartZero();
1616       return true;
1617     }
1618   }
1619 
1620   if (action.GetID() == ACTION_TOGGLE_FULLSCREEN)
1621   {
1622     CServiceBroker::GetWinSystem()->GetGfxContext().ToggleFullScreen();
1623     m_appPlayer.TriggerUpdateResolution();
1624     return true;
1625   }
1626 
1627   if (action.IsMouse())
1628     CServiceBroker::GetInputManager().SetMouseActive(true);
1629 
1630   if (action.GetID() == ACTION_CREATE_EPISODE_BOOKMARK)
1631   {
1632     CGUIDialogVideoBookmarks::OnAddEpisodeBookmark();
1633   }
1634   if (action.GetID() == ACTION_CREATE_BOOKMARK)
1635   {
1636     CGUIDialogVideoBookmarks::OnAddBookmark();
1637   }
1638 
1639   // The action PLAYPAUSE behaves as ACTION_PAUSE if we are currently
1640   // playing or ACTION_PLAYER_PLAY if we are seeking (FF/RW) or not playing.
1641   if (action.GetID() == ACTION_PLAYER_PLAYPAUSE)
1642   {
1643     CGUIWindowSlideShow* pSlideShow = CServiceBroker::GetGUI()->
1644                          GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
1645     if ((m_appPlayer.IsPlaying() && m_appPlayer.GetPlaySpeed() == 1) ||
1646          (pSlideShow && pSlideShow->InSlideShow() && !pSlideShow->IsPaused()))
1647       return OnAction(CAction(ACTION_PAUSE));
1648     else
1649       return OnAction(CAction(ACTION_PLAYER_PLAY));
1650   }
1651 
1652   //if the action would start or stop inertial scrolling
1653   //by gesture - bypass the normal OnAction handler of current window
1654   if( !m_pInertialScrollingHandler->CheckForInertialScrolling(&action) )
1655   {
1656     // in normal case
1657     // just pass the action to the current window and let it handle it
1658     if (CServiceBroker::GetGUI()->GetWindowManager().OnAction(action))
1659     {
1660       m_navigationTimer.StartZero();
1661       return true;
1662     }
1663   }
1664 
1665   // handle extra global presses
1666 
1667   // notify action listeners
1668   if (NotifyActionListeners(action))
1669     return true;
1670 
1671   // screenshot : take a screenshot :)
1672   if (action.GetID() == ACTION_TAKE_SCREENSHOT)
1673   {
1674     CScreenShot::TakeScreenshot();
1675     return true;
1676   }
1677   // Display HDR : toggle HDR on/off
1678   if (action.GetID() == ACTION_HDR_TOGGLE)
1679   {
1680     // Only enables manual HDR toggle if no video is playing or auto HDR switch is disabled
1681     if (m_appPlayer.IsPlayingVideo() &&
1682         CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
1683             CServiceBroker::GetWinSystem()->SETTING_WINSYSTEM_IS_HDR_DISPLAY))
1684       return true;
1685 
1686     HDR_STATUS hdrStatus = CServiceBroker::GetWinSystem()->ToggleHDR();
1687 
1688     if (hdrStatus == HDR_STATUS::HDR_OFF)
1689     {
1690       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(34220),
1691                                             g_localizeStrings.Get(34221));
1692     }
1693     else if (hdrStatus == HDR_STATUS::HDR_ON)
1694     {
1695       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(34220),
1696                                             g_localizeStrings.Get(34222));
1697     }
1698     return true;
1699   }
1700   // Tone Mapping : switch to next tone map method
1701   if (action.GetID() == ACTION_CYCLE_TONEMAP_METHOD)
1702   {
1703     // Only enables tone mapping switch if display is not HDR capable or HDR is not enabled
1704     if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
1705             CServiceBroker::GetWinSystem()->SETTING_WINSYSTEM_IS_HDR_DISPLAY) &&
1706         CServiceBroker::GetWinSystem()->IsHDRDisplay())
1707       return true;
1708 
1709     if (m_appPlayer.IsPlayingVideo())
1710     {
1711       CVideoSettings vs = m_appPlayer.GetVideoSettings();
1712       vs.m_ToneMapMethod++;
1713       if (vs.m_ToneMapMethod >= VS_TONEMAPMETHOD_MAX)
1714         vs.m_ToneMapMethod = VS_TONEMAPMETHOD_OFF + 1;
1715       m_appPlayer.SetVideoSettings(vs);
1716 
1717       int code = 0;
1718       switch (vs.m_ToneMapMethod)
1719       {
1720         case VS_TONEMAPMETHOD_REINHARD:
1721           code = 36555;
1722           break;
1723         case VS_TONEMAPMETHOD_ACES:
1724           code = 36557;
1725           break;
1726         case VS_TONEMAPMETHOD_HABLE:
1727           code = 36558;
1728           break;
1729       }
1730       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(34224),
1731                                             g_localizeStrings.Get(code), 1000, false, 500);
1732     }
1733     return true;
1734   }
1735   // built in functions : execute the built-in
1736   if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
1737   {
1738     if (!CBuiltins::GetInstance().IsSystemPowerdownCommand(action.GetName()) ||
1739         CServiceBroker::GetPVRManager().GUIActions()->CanSystemPowerdown())
1740     {
1741       CBuiltins::GetInstance().Execute(action.GetName());
1742       m_navigationTimer.StartZero();
1743     }
1744     return true;
1745   }
1746 
1747   // reload keymaps
1748   if (action.GetID() == ACTION_RELOAD_KEYMAPS)
1749     CServiceBroker::GetInputManager().ReloadKeymaps();
1750 
1751   // show info : Shows the current video or song information
1752   if (action.GetID() == ACTION_SHOW_INFO)
1753   {
1754     CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().ToggleShowInfo();
1755     return true;
1756   }
1757 
1758   if ((action.GetID() == ACTION_SET_RATING) && m_appPlayer.IsPlayingAudio())
1759   {
1760     int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_itemCurrentFile->GetMusicInfoTag()->GetUserrating());
1761     if (userrating < 0) // Nothing selected, so user rating unchanged
1762       return true;
1763     userrating = std::min(userrating, 10);
1764     if (userrating != m_itemCurrentFile->GetMusicInfoTag()->GetUserrating())
1765     {
1766       m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating);
1767       // Mirror changes to GUI item
1768       CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
1769 
1770       // Asynchronously update song userrating in music library
1771       MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, userrating);
1772 
1773       // Tell all windows (e.g. playlistplayer, media windows) to update the fileitem
1774       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
1775       CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1776     }
1777     return true;
1778   }
1779 
1780   else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_appPlayer.IsPlayingAudio())
1781   {
1782     int userrating = m_itemCurrentFile->GetMusicInfoTag()->GetUserrating();
1783     bool needsUpdate(false);
1784     if (userrating > 0 && action.GetID() == ACTION_DECREASE_RATING)
1785     {
1786       m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating - 1);
1787       needsUpdate = true;
1788     }
1789     else if (userrating < 10 && action.GetID() == ACTION_INCREASE_RATING)
1790     {
1791       m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating + 1);
1792       needsUpdate = true;
1793     }
1794     if (needsUpdate)
1795     {
1796       // Mirror changes to current GUI item
1797       CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
1798 
1799       // Asynchronously update song userrating in music library
1800       MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, m_itemCurrentFile->GetMusicInfoTag()->GetUserrating());
1801 
1802       // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
1803       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
1804       CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1805     }
1806 
1807     return true;
1808   }
1809   else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_appPlayer.IsPlayingVideo())
1810   {
1811     int rating = m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating;
1812     bool needsUpdate(false);
1813     if (rating > 1 && action.GetID() == ACTION_DECREASE_RATING)
1814     {
1815       m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating - 1;
1816       needsUpdate = true;
1817     }
1818     else if (rating < 10 && action.GetID() == ACTION_INCREASE_RATING)
1819     {
1820       m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating + 1;
1821       needsUpdate = true;
1822     }
1823     if (needsUpdate)
1824     {
1825       // Mirror changes to GUI item
1826       CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
1827 
1828       CVideoDatabase db;
1829       if (db.Open())
1830       {
1831         db.SetVideoUserRating(m_itemCurrentFile->GetVideoInfoTag()->m_iDbId,
1832                               m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating,
1833                               m_itemCurrentFile->GetVideoInfoTag()->m_type);
1834         db.Close();
1835       }
1836       // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows)
1837       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
1838       CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
1839     }
1840     return true;
1841   }
1842 
1843   // Now check with the playlist player if action can be handled.
1844   // 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.
1845   if (!(action.GetID() == ACTION_PREV_ITEM && m_appPlayer.CanSeek() && GetTime() > ACTION_PREV_ITEM_THRESHOLD) )
1846   {
1847     if (CServiceBroker::GetPlaylistPlayer().OnAction(action))
1848       return true;
1849   }
1850 
1851   // Now check with the player if action can be handled.
1852   bool bIsPlayingPVRChannel = (CServiceBroker::GetPVRManager().IsStarted() &&
1853                                CurrentFileItem().IsPVRChannel());
1854 
1855   bool bNotifyPlayer = false;
1856   if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
1857     bNotifyPlayer = true;
1858   else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME)
1859     bNotifyPlayer = true;
1860   else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION && bIsPlayingPVRChannel)
1861     bNotifyPlayer = true;
1862   else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_DIALOG_VIDEO_OSD ||
1863           (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_DIALOG_MUSIC_OSD && bIsPlayingPVRChannel))
1864   {
1865     switch (action.GetID())
1866     {
1867       case ACTION_NEXT_ITEM:
1868       case ACTION_PREV_ITEM:
1869       case ACTION_CHANNEL_UP:
1870       case ACTION_CHANNEL_DOWN:
1871         bNotifyPlayer = true;
1872         break;
1873       default:
1874         break;
1875     }
1876   }
1877   else if (action.GetID() == ACTION_STOP)
1878     bNotifyPlayer = true;
1879 
1880   if (bNotifyPlayer)
1881   {
1882     if (m_appPlayer.OnAction(action))
1883       return true;
1884   }
1885 
1886   // stop : stops playing current audio song
1887   if (action.GetID() == ACTION_STOP)
1888   {
1889     StopPlaying();
1890     return true;
1891   }
1892 
1893   // In case the playlist player nor the player didn't handle PREV_ITEM, because we are past the ACTION_PREV_ITEM_THRESHOLD secs limit.
1894   // If so, we just jump to the start of the track.
1895   if (action.GetID() == ACTION_PREV_ITEM && m_appPlayer.CanSeek())
1896   {
1897     SeekTime(0);
1898     m_appPlayer.SetPlaySpeed(1);
1899     return true;
1900   }
1901 
1902   // forward action to graphic context and see if it can handle it
1903   if (CServiceBroker::GetGUI()->GetStereoscopicsManager().OnAction(action))
1904     return true;
1905 
1906   if (m_appPlayer.IsPlaying())
1907   {
1908     // forward channel switches to the player - he knows what to do
1909     if (action.GetID() == ACTION_CHANNEL_UP || action.GetID() == ACTION_CHANNEL_DOWN)
1910     {
1911       m_appPlayer.OnAction(action);
1912       return true;
1913     }
1914 
1915     // pause : toggle pause action
1916     if (action.GetID() == ACTION_PAUSE)
1917     {
1918       m_appPlayer.Pause();
1919       // go back to normal play speed on unpause
1920       if (!m_appPlayer.IsPaused() && m_appPlayer.GetPlaySpeed() != 1)
1921         m_appPlayer.SetPlaySpeed(1);
1922 
1923       CGUIComponent *gui = CServiceBroker::GetGUI();
1924       if (gui)
1925         gui->GetAudioManager().Enable(m_appPlayer.IsPaused());
1926       return true;
1927     }
1928     // play: unpause or set playspeed back to normal
1929     if (action.GetID() == ACTION_PLAYER_PLAY)
1930     {
1931       // if currently paused - unpause
1932       if (m_appPlayer.IsPaused())
1933         return OnAction(CAction(ACTION_PAUSE));
1934       // if we do a FF/RW then go back to normal speed
1935       if (m_appPlayer.GetPlaySpeed() != 1)
1936         m_appPlayer.SetPlaySpeed(1);
1937       return true;
1938     }
1939     if (!m_appPlayer.IsPaused())
1940     {
1941       if (action.GetID() == ACTION_PLAYER_FORWARD || action.GetID() == ACTION_PLAYER_REWIND)
1942       {
1943         float playSpeed = m_appPlayer.GetPlaySpeed();
1944 
1945         if (action.GetID() == ACTION_PLAYER_REWIND && (playSpeed == 1)) // Enables Rewinding
1946           playSpeed *= -2;
1947         else if (action.GetID() == ACTION_PLAYER_REWIND && playSpeed > 1) //goes down a notch if you're FFing
1948           playSpeed /= 2;
1949         else if (action.GetID() == ACTION_PLAYER_FORWARD && playSpeed < 1) //goes up a notch if you're RWing
1950           playSpeed /= 2;
1951         else
1952           playSpeed *= 2;
1953 
1954         if (action.GetID() == ACTION_PLAYER_FORWARD && playSpeed == -1) //sets iSpeed back to 1 if -1 (didn't plan for a -1)
1955           playSpeed = 1;
1956         if (playSpeed > 32 || playSpeed < -32)
1957           playSpeed = 1;
1958 
1959         m_appPlayer.SetPlaySpeed(playSpeed);
1960         return true;
1961       }
1962       else if ((action.GetAmount() || m_appPlayer.GetPlaySpeed() != 1) && (action.GetID() == ACTION_ANALOG_REWIND || action.GetID() == ACTION_ANALOG_FORWARD))
1963       {
1964         // calculate the speed based on the amount the button is held down
1965         int iPower = (int)(action.GetAmount() * MAX_FFWD_SPEED + 0.5f);
1966         // amount can be negative, for example rewind and forward share the same axis
1967         iPower = std::abs(iPower);
1968         // returns 0 -> MAX_FFWD_SPEED
1969         int iSpeed = 1 << iPower;
1970         if (iSpeed != 1 && action.GetID() == ACTION_ANALOG_REWIND)
1971           iSpeed = -iSpeed;
1972         m_appPlayer.SetPlaySpeed(static_cast<float>(iSpeed));
1973         if (iSpeed == 1)
1974           CLog::Log(LOGDEBUG,"Resetting playspeed");
1975         return true;
1976       }
1977     }
1978     // allow play to unpause
1979     else
1980     {
1981       if (action.GetID() == ACTION_PLAYER_PLAY)
1982       {
1983         // unpause, and set the playspeed back to normal
1984         m_appPlayer.Pause();
1985 
1986         CGUIComponent *gui = CServiceBroker::GetGUI();
1987         if (gui)
1988           gui->GetAudioManager().Enable(m_appPlayer.IsPaused());
1989 
1990         m_appPlayer.SetPlaySpeed(1);
1991         return true;
1992       }
1993     }
1994   }
1995 
1996 
1997   if (action.GetID() == ACTION_SWITCH_PLAYER)
1998   {
1999     const CPlayerCoreFactory &playerCoreFactory = m_ServiceManager->GetPlayerCoreFactory();
2000 
2001     if(m_appPlayer.IsPlaying())
2002     {
2003       std::vector<std::string> players;
2004       CFileItem item(*m_itemCurrentFile.get());
2005       playerCoreFactory.GetPlayers(item, players);
2006       std::string player = playerCoreFactory.SelectPlayerDialog(players);
2007       if (!player.empty())
2008       {
2009         item.m_lStartOffset = CUtil::ConvertSecsToMilliSecs(GetTime());
2010         PlayFile(item, player, true);
2011       }
2012     }
2013     else
2014     {
2015       std::vector<std::string> players;
2016       playerCoreFactory.GetRemotePlayers(players);
2017       std::string player = playerCoreFactory.SelectPlayerDialog(players);
2018       if (!player.empty())
2019       {
2020         PlayFile(CFileItem(), player, false);
2021       }
2022     }
2023   }
2024 
2025   if (CServiceBroker::GetPeripherals().OnAction(action))
2026     return true;
2027 
2028   if (action.GetID() == ACTION_MUTE)
2029   {
2030     ToggleMute();
2031     ShowVolumeBar(&action);
2032     return true;
2033   }
2034 
2035   if (action.GetID() == ACTION_TOGGLE_DIGITAL_ANALOG)
2036   {
2037     const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
2038     bool passthrough = settings->GetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH);
2039     settings->SetBool(CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH, !passthrough);
2040 
2041     if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SETTINGS_SYSTEM)
2042     {
2043       CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0,0,WINDOW_INVALID,CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
2044       CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
2045     }
2046     return true;
2047   }
2048 
2049   // Check for global volume control
2050   if ((action.GetAmount() && (action.GetID() == ACTION_VOLUME_UP || action.GetID() == ACTION_VOLUME_DOWN)) || action.GetID() == ACTION_VOLUME_SET)
2051   {
2052     if (!m_appPlayer.IsPassthrough())
2053     {
2054       if (m_muted)
2055         UnMute();
2056       float volume = m_volumeLevel;
2057       int volumesteps = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_AUDIOOUTPUT_VOLUMESTEPS);
2058       // sanity check
2059       if (volumesteps == 0)
2060         volumesteps = 90;
2061 
2062 // Android has steps based on the max available volume level
2063 #if defined(TARGET_ANDROID)
2064       float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / CXBMCApp::GetMaxSystemVolume();
2065 #else
2066       float step   = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / volumesteps;
2067 
2068       if (action.GetRepeat())
2069         step *= action.GetRepeat() * 50; // 50 fps
2070 #endif
2071       if (action.GetID() == ACTION_VOLUME_UP)
2072         volume += action.GetAmount() * action.GetAmount() * step;
2073       else if (action.GetID() == ACTION_VOLUME_DOWN)
2074         volume -= action.GetAmount() * action.GetAmount() * step;
2075       else
2076         volume = action.GetAmount() * step;
2077       if (volume != m_volumeLevel)
2078         SetVolume(volume, false);
2079     }
2080     // show visual feedback of volume or passthrough indicator
2081     ShowVolumeBar(&action);
2082     return true;
2083   }
2084   if (action.GetID() == ACTION_GUIPROFILE_BEGIN)
2085   {
2086     CGUIControlProfiler::Instance().SetOutputFile(CSpecialProtocol::TranslatePath("special://home/guiprofiler.xml"));
2087     CGUIControlProfiler::Instance().Start();
2088     return true;
2089   }
2090   if (action.GetID() == ACTION_SHOW_PLAYLIST)
2091   {
2092     int iPlaylist = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
2093     if (iPlaylist == PLAYLIST_VIDEO && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_VIDEO_PLAYLIST)
2094       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VIDEO_PLAYLIST);
2095     else if (iPlaylist == PLAYLIST_MUSIC && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_MUSIC_PLAYLIST)
2096       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST);
2097     return true;
2098   }
2099   return false;
2100 }
2101 
GetMessageMask()2102 int CApplication::GetMessageMask()
2103 {
2104   return TMSG_MASK_APPLICATION;
2105 }
2106 
OnApplicationMessage(ThreadMessage * pMsg)2107 void CApplication::OnApplicationMessage(ThreadMessage* pMsg)
2108 {
2109   uint32_t msg = pMsg->dwMessage;
2110   if (msg == TMSG_SYSTEM_POWERDOWN)
2111   {
2112     if (CServiceBroker::GetPVRManager().GUIActions()->CanSystemPowerdown())
2113       msg = pMsg->param1; // perform requested shutdown action
2114     else
2115       return; // no shutdown
2116   }
2117 
2118   switch (msg)
2119   {
2120   case TMSG_POWERDOWN:
2121     Stop(EXITCODE_POWERDOWN);
2122     CServiceBroker::GetPowerManager().Powerdown();
2123     break;
2124 
2125   case TMSG_QUIT:
2126     Stop(EXITCODE_QUIT);
2127     break;
2128 
2129   case TMSG_SHUTDOWN:
2130     HandleShutdownMessage();
2131     break;
2132 
2133   case TMSG_RENDERER_FLUSH:
2134     m_appPlayer.FlushRenderer();
2135     break;
2136 
2137   case TMSG_HIBERNATE:
2138     CServiceBroker::GetPowerManager().Hibernate();
2139     break;
2140 
2141   case TMSG_SUSPEND:
2142     CServiceBroker::GetPowerManager().Suspend();
2143     break;
2144 
2145   case TMSG_RESTART:
2146   case TMSG_RESET:
2147     Stop(EXITCODE_REBOOT);
2148     CServiceBroker::GetPowerManager().Reboot();
2149     break;
2150 
2151   case TMSG_RESTARTAPP:
2152 #if defined(TARGET_WINDOWS) || defined(TARGET_LINUX)
2153     Stop(EXITCODE_RESTARTAPP);
2154 #endif
2155     break;
2156 
2157   case TMSG_INHIBITIDLESHUTDOWN:
2158     InhibitIdleShutdown(pMsg->param1 != 0);
2159     break;
2160 
2161   case TMSG_INHIBITSCREENSAVER:
2162     InhibitScreenSaver(pMsg->param1 != 0);
2163     break;
2164 
2165   case TMSG_ACTIVATESCREENSAVER:
2166     ActivateScreenSaver();
2167     break;
2168 
2169   case TMSG_VOLUME_SHOW:
2170   {
2171     CAction action(pMsg->param1);
2172     ShowVolumeBar(&action);
2173   }
2174   break;
2175 
2176 #ifdef TARGET_ANDROID
2177   case TMSG_DISPLAY_SETUP:
2178     // We might come from a refresh rate switch destroying the native window; use the context resolution
2179     *static_cast<bool*>(pMsg->lpVoid) = InitWindow(CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution());
2180     SetRenderGUI(true);
2181     break;
2182 
2183   case TMSG_DISPLAY_DESTROY:
2184     *static_cast<bool*>(pMsg->lpVoid) = CServiceBroker::GetWinSystem()->DestroyWindow();
2185     SetRenderGUI(false);
2186     break;
2187 #endif
2188 
2189   case TMSG_START_ANDROID_ACTIVITY:
2190   {
2191 #if defined(TARGET_ANDROID)
2192     if (pMsg->params.size())
2193     {
2194       CXBMCApp::StartActivity(pMsg->params[0],
2195         pMsg->params.size() > 1 ? pMsg->params[1] : "",
2196         pMsg->params.size() > 2 ? pMsg->params[2] : "",
2197         pMsg->params.size() > 3 ? pMsg->params[3] : "");
2198     }
2199 #endif
2200   }
2201   break;
2202 
2203   case TMSG_NETWORKMESSAGE:
2204     m_ServiceManager->GetNetwork().NetworkMessage((CNetwork::EMESSAGE)pMsg->param1, pMsg->param2);
2205     break;
2206 
2207   case TMSG_SETLANGUAGE:
2208     SetLanguage(pMsg->strParam);
2209     break;
2210 
2211 
2212   case TMSG_SWITCHTOFULLSCREEN:
2213     SwitchToFullScreen(true);
2214     break;
2215 
2216   case TMSG_VIDEORESIZE:
2217   {
2218     XBMC_Event newEvent;
2219     memset(&newEvent, 0, sizeof(newEvent));
2220     newEvent.type = XBMC_VIDEORESIZE;
2221     newEvent.resize.w = pMsg->param1;
2222     newEvent.resize.h = pMsg->param2;
2223     OnEvent(newEvent);
2224     CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
2225   }
2226     break;
2227 
2228   case TMSG_SETVIDEORESOLUTION:
2229     CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(static_cast<RESOLUTION>(pMsg->param1), pMsg->param2 == 1);
2230     break;
2231 
2232   case TMSG_TOGGLEFULLSCREEN:
2233     CServiceBroker::GetWinSystem()->GetGfxContext().ToggleFullScreen();
2234     m_appPlayer.TriggerUpdateResolution();
2235     break;
2236 
2237   case TMSG_MINIMIZE:
2238     Minimize();
2239     break;
2240 
2241   case TMSG_EXECUTE_OS:
2242     // Suspend AE temporarily so exclusive or hog-mode sinks
2243     // don't block external player's access to audio device
2244     IAE *audioengine;
2245     audioengine = CServiceBroker::GetActiveAE();
2246     if (audioengine)
2247     {
2248       if (!audioengine->Suspend())
2249       {
2250         CLog::Log(LOGINFO, "%s: Failed to suspend AudioEngine before launching external program",
2251                   __FUNCTION__);
2252       }
2253     }
2254 #if defined(TARGET_DARWIN)
2255     CLog::Log(LOGINFO, "ExecWait is not implemented on this platform");
2256 #elif defined(TARGET_POSIX)
2257     CUtil::RunCommandLine(pMsg->strParam.c_str(), (pMsg->param1 == 1));
2258 #elif defined(TARGET_WINDOWS)
2259     CWIN32Util::XBMCShellExecute(pMsg->strParam.c_str(), (pMsg->param1 == 1));
2260 #endif
2261     // Resume AE processing of XBMC native audio
2262     if (audioengine)
2263     {
2264       if (!audioengine->Resume())
2265       {
2266         CLog::Log(LOGFATAL, "%s: Failed to restart AudioEngine after return from external player", __FUNCTION__);
2267       }
2268     }
2269     break;
2270 
2271   case TMSG_EXECUTE_SCRIPT:
2272     CScriptInvocationManager::GetInstance().ExecuteAsync(pMsg->strParam);
2273     break;
2274 
2275   case TMSG_EXECUTE_BUILT_IN:
2276     CBuiltins::GetInstance().Execute(pMsg->strParam.c_str());
2277     break;
2278 
2279   case TMSG_PICTURE_SHOW:
2280   {
2281     CGUIWindowSlideShow *pSlideShow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
2282     if (!pSlideShow) return;
2283 
2284     // stop playing file
2285     if (m_appPlayer.IsPlayingVideo()) StopPlaying();
2286 
2287     if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
2288       CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
2289 
2290     ResetScreenSaver();
2291     WakeUpScreenSaverAndDPMS();
2292 
2293     if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_SLIDESHOW)
2294       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
2295     if (URIUtils::IsZIP(pMsg->strParam) || URIUtils::IsRAR(pMsg->strParam)) // actually a cbz/cbr
2296     {
2297       CFileItemList items;
2298       CURL pathToUrl;
2299       if (URIUtils::IsZIP(pMsg->strParam))
2300         pathToUrl = URIUtils::CreateArchivePath("zip", CURL(pMsg->strParam), "");
2301       else
2302         pathToUrl = URIUtils::CreateArchivePath("rar", CURL(pMsg->strParam), "");
2303 
2304       CUtil::GetRecursiveListing(pathToUrl.Get(), items, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), XFILE::DIR_FLAG_NO_FILE_DIRS);
2305       if (items.Size() > 0)
2306       {
2307         pSlideShow->Reset();
2308         for (int i = 0; i<items.Size(); ++i)
2309         {
2310           pSlideShow->Add(items[i].get());
2311         }
2312         pSlideShow->Select(items[0]->GetPath());
2313       }
2314     }
2315     else
2316     {
2317       CFileItem item(pMsg->strParam, false);
2318       pSlideShow->Reset();
2319       pSlideShow->Add(&item);
2320       pSlideShow->Select(pMsg->strParam);
2321     }
2322   }
2323   break;
2324 
2325   case TMSG_PICTURE_SLIDESHOW:
2326   {
2327     CGUIWindowSlideShow *pSlideShow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
2328     if (!pSlideShow) return;
2329 
2330     if (m_appPlayer.IsPlayingVideo())
2331       StopPlaying();
2332 
2333     pSlideShow->Reset();
2334 
2335     CFileItemList items;
2336     std::string strPath = pMsg->strParam;
2337     std::string extensions = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
2338     if (pMsg->param1)
2339       extensions += "|.tbn";
2340     CUtil::GetRecursiveListing(strPath, items, extensions);
2341 
2342     if (items.Size() > 0)
2343     {
2344       for (int i = 0; i<items.Size(); ++i)
2345         pSlideShow->Add(items[i].get());
2346       pSlideShow->StartSlideShow(); //Start the slideshow!
2347     }
2348 
2349     if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_SLIDESHOW)
2350     {
2351       if (items.Size() == 0)
2352       {
2353         CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_SCREENSAVER_MODE, "screensaver.xbmc.builtin.dim");
2354         ActivateScreenSaver();
2355       }
2356       else
2357         CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
2358     }
2359 
2360   }
2361   break;
2362 
2363   case TMSG_LOADPROFILE:
2364     {
2365       const int profile = pMsg->param1;
2366       if (profile >= 0)
2367         CServiceBroker::GetSettingsComponent()->GetProfileManager()->LoadProfile(static_cast<unsigned int>(profile));
2368     }
2369 
2370     break;
2371 
2372   case TMSG_EVENT:
2373   {
2374     if (pMsg->lpVoid)
2375     {
2376       XBMC_Event* event = static_cast<XBMC_Event*>(pMsg->lpVoid);
2377       OnEvent(*event);
2378       delete event;
2379     }
2380   }
2381   break;
2382 
2383   default:
2384     CLog::Log(LOGERROR, "%s: Unhandled threadmessage sent, %u", __FUNCTION__, msg);
2385     break;
2386   }
2387 }
2388 
HandleShutdownMessage()2389 void CApplication::HandleShutdownMessage()
2390 {
2391   switch (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE))
2392   {
2393   case POWERSTATE_SHUTDOWN:
2394     CApplicationMessenger::GetInstance().PostMsg(TMSG_POWERDOWN);
2395     break;
2396 
2397   case POWERSTATE_SUSPEND:
2398     CApplicationMessenger::GetInstance().PostMsg(TMSG_SUSPEND);
2399     break;
2400 
2401   case POWERSTATE_HIBERNATE:
2402     CApplicationMessenger::GetInstance().PostMsg(TMSG_HIBERNATE);
2403     break;
2404 
2405   case POWERSTATE_QUIT:
2406     CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT);
2407     break;
2408 
2409   case POWERSTATE_MINIMIZE:
2410     CApplicationMessenger::GetInstance().PostMsg(TMSG_MINIMIZE);
2411     break;
2412 
2413   default:
2414     CLog::Log(LOGERROR, "%s: No valid shutdownstate matched", __FUNCTION__);
2415     break;
2416   }
2417 }
2418 
LockFrameMoveGuard()2419 void CApplication::LockFrameMoveGuard()
2420 {
2421   ++m_WaitingExternalCalls;
2422   m_frameMoveGuard.lock();
2423   ++m_ProcessedExternalCalls;
2424   CServiceBroker::GetWinSystem()->GetGfxContext().lock();
2425 };
2426 
UnlockFrameMoveGuard()2427 void CApplication::UnlockFrameMoveGuard()
2428 {
2429   --m_WaitingExternalCalls;
2430   CServiceBroker::GetWinSystem()->GetGfxContext().unlock();
2431   m_frameMoveGuard.unlock();
2432 };
2433 
FrameMove(bool processEvents,bool processGUI)2434 void CApplication::FrameMove(bool processEvents, bool processGUI)
2435 {
2436   if (processEvents)
2437   {
2438     // currently we calculate the repeat time (ie time from last similar keypress) just global as fps
2439     float frameTime = m_frameTime.GetElapsedSeconds();
2440     m_frameTime.StartZero();
2441     // never set a frametime less than 2 fps to avoid problems when debugging and on breaks
2442     if( frameTime > 0.5 )
2443       frameTime = 0.5;
2444 
2445     if (processGUI && m_renderGUI)
2446     {
2447       CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
2448       // check if there are notifications to display
2449       CGUIDialogKaiToast *toast = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogKaiToast>(WINDOW_DIALOG_KAI_TOAST);
2450       if (toast && toast->DoWork())
2451       {
2452         if (!toast->IsDialogRunning())
2453         {
2454           toast->Open();
2455         }
2456       }
2457     }
2458 
2459     HandlePortEvents();
2460     CServiceBroker::GetInputManager().Process(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(), frameTime);
2461 
2462     if (processGUI && m_renderGUI)
2463     {
2464       m_pInertialScrollingHandler->ProcessInertialScroll(frameTime);
2465       m_appPlayer.GetSeekHandler().FrameMove();
2466     }
2467 
2468     // Open the door for external calls e.g python exactly here.
2469     // Window size can be between 2 and 10ms and depends on number of continuous requests
2470     if (m_WaitingExternalCalls)
2471     {
2472       CSingleExit ex(CServiceBroker::GetWinSystem()->GetGfxContext());
2473       m_frameMoveGuard.unlock();
2474 
2475       // Calculate a window size between 2 and 10ms, 4 continuous requests let the window grow by 1ms
2476       // When not playing video we allow it to increase to 80ms
2477       unsigned int max_sleep = 10;
2478       if (!m_appPlayer.IsPlayingVideo() || m_appPlayer.IsPausedPlayback())
2479         max_sleep = 80;
2480       unsigned int sleepTime = std::max(static_cast<unsigned int>(2), std::min(m_ProcessedExternalCalls >> 2, max_sleep));
2481       KODI::TIME::Sleep(sleepTime);
2482       m_frameMoveGuard.lock();
2483       m_ProcessedExternalDecay = 5;
2484     }
2485     if (m_ProcessedExternalDecay && --m_ProcessedExternalDecay == 0)
2486       m_ProcessedExternalCalls = 0;
2487   }
2488 
2489   if (processGUI && m_renderGUI)
2490   {
2491     m_skipGuiRender = false;
2492 
2493     /*! @todo look into the possibility to use this for GBM
2494     int fps = 0;
2495 
2496     // This code reduces rendering fps of the GUI layer when playing videos in fullscreen mode
2497     // it makes only sense on architectures with multiple layers
2498     if (CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() && !m_appPlayer.IsPausedPlayback() && m_appPlayer.IsRenderingVideoLayer())
2499       fps = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_LIMITGUIUPDATE);
2500 
2501     unsigned int now = XbmcThreads::SystemClockMillis();
2502     unsigned int frameTime = now - m_lastRenderTime;
2503     if (fps > 0 && frameTime * fps < 1000)
2504       m_skipGuiRender = true;
2505     */
2506 
2507     if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiSmartRedraw && m_guiRefreshTimer.IsTimePast())
2508     {
2509       CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_REFRESH_TIMER, 0, 0);
2510       m_guiRefreshTimer.Set(500);
2511     }
2512 
2513     if (!m_bStop)
2514     {
2515       if (!m_skipGuiRender)
2516         CServiceBroker::GetGUI()->GetWindowManager().Process(CTimeUtils::GetFrameTime());
2517     }
2518     CServiceBroker::GetGUI()->GetWindowManager().FrameMove();
2519   }
2520 
2521   m_appPlayer.FrameMove();
2522 
2523   // this will go away when render systems gets its own thread
2524   CServiceBroker::GetWinSystem()->DriveRenderLoop();
2525 }
2526 
2527 
ResetCurrentItem()2528 void CApplication::ResetCurrentItem()
2529 {
2530   m_itemCurrentFile->Reset();
2531   if (m_pGUI)
2532     m_pGUI->GetInfoManager().ResetCurrentItem();
2533 }
2534 
Cleanup()2535 bool CApplication::Cleanup()
2536 {
2537   try
2538   {
2539     ResetCurrentItem();
2540     StopPlaying();
2541 
2542     if (m_ServiceManager)
2543       m_ServiceManager->DeinitStageThree();
2544 
2545     CLog::Log(LOGINFO, "unload skin");
2546     UnloadSkin();
2547 
2548     // stop all remaining scripts; must be done after skin has been unloaded,
2549     // not before some windows still need it when deinitializing during skin
2550     // unloading
2551     CScriptInvocationManager::GetInstance().Uninitialize();
2552 
2553     m_globalScreensaverInhibitor.Release();
2554     m_screensaverInhibitor.Release();
2555 
2556     CRenderSystemBase *renderSystem = CServiceBroker::GetRenderSystem();
2557     if (renderSystem)
2558       renderSystem->DestroyRenderSystem();
2559 
2560     CWinSystemBase *winSystem = CServiceBroker::GetWinSystem();
2561     if (winSystem)
2562       winSystem->DestroyWindow();
2563 
2564     if (m_pGUI)
2565       m_pGUI->GetWindowManager().DestroyWindows();
2566 
2567     CLog::Log(LOGINFO, "unload sections");
2568 
2569     //  Shutdown as much as possible of the
2570     //  application, to reduce the leaks dumped
2571     //  to the vc output window before calling
2572     //  _CrtDumpMemoryLeaks(). Most of the leaks
2573     //  shown are no real leaks, as parts of the app
2574     //  are still allocated.
2575 
2576     g_localizeStrings.Clear();
2577     g_LangCodeExpander.Clear();
2578     g_charsetConverter.clear();
2579     g_directoryCache.Clear();
2580     //CServiceBroker::GetInputManager().ClearKeymaps(); //! @todo
2581     CEventServer::RemoveInstance();
2582     DllLoaderContainer::Clear();
2583     CServiceBroker::GetPlaylistPlayer().Clear();
2584 
2585     if (m_ServiceManager)
2586       m_ServiceManager->DeinitStageTwo();
2587 
2588 #ifdef TARGET_POSIX
2589     CXHandle::DumpObjectTracker();
2590 
2591 #ifdef HAS_DVD_DRIVE
2592     CLibcdio::ReleaseInstance();
2593 #endif
2594 #endif
2595 #ifdef _CRTDBG_MAP_ALLOC
2596     _CrtDumpMemoryLeaks();
2597     while(1); // execution ends
2598 #endif
2599 
2600     if (m_pGUI)
2601     {
2602       m_pGUI->Deinit();
2603       m_pGUI.reset();
2604     }
2605 
2606     if (winSystem)
2607     {
2608       winSystem->DestroyWindowSystem();
2609       CServiceBroker::UnregisterWinSystem();
2610       winSystem = nullptr;
2611       m_pWinSystem.reset();
2612     }
2613 
2614     // Cleanup was called more than once on exit during my tests
2615     if (m_ServiceManager)
2616     {
2617       m_ServiceManager->DeinitStageOne();
2618       m_ServiceManager.reset();
2619     }
2620 
2621     m_pAnnouncementManager->Deinitialize();
2622     m_pAnnouncementManager.reset();
2623 
2624     m_pSettingsComponent->Deinit();
2625     m_pSettingsComponent.reset();
2626 
2627     CServiceBroker::UnregisterCPUInfo();
2628 
2629     return true;
2630   }
2631   catch (...)
2632   {
2633     CLog::Log(LOGERROR, "Exception in CApplication::Cleanup()");
2634     return false;
2635   }
2636 }
2637 
Stop(int exitCode)2638 void CApplication::Stop(int exitCode)
2639 {
2640   CLog::Log(LOGINFO, "Stopping player");
2641   m_appPlayer.ClosePlayer();
2642 
2643   {
2644     // close inbound port
2645     CServiceBroker::UnregisterAppPort();
2646     XbmcThreads::EndTime timer(1000);
2647     while (m_pAppPort.use_count() > 1)
2648     {
2649       KODI::TIME::Sleep(100);
2650       if (timer.IsTimePast())
2651       {
2652         CLog::Log(LOGERROR, "CApplication::Stop - CAppPort still in use, app may crash");
2653         break;
2654       }
2655     }
2656     m_pAppPort.reset();
2657   }
2658 
2659   try
2660   {
2661     m_frameMoveGuard.unlock();
2662 
2663     CVariant vExitCode(CVariant::VariantTypeObject);
2664     vExitCode["exitcode"] = exitCode;
2665     CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::System, "OnQuit", vExitCode);
2666 
2667     // Abort any active screensaver
2668     WakeUpScreenSaverAndDPMS();
2669 
2670     g_alarmClock.StopThread();
2671 
2672     CLog::Log(LOGINFO, "Storing total System Uptime");
2673     g_sysinfo.SetTotalUptime(g_sysinfo.GetTotalUptime() + (int)(CTimeUtils::GetFrameTime() / 60000));
2674 
2675     // Update the settings information (volume, uptime etc. need saving)
2676     if (CFile::Exists(CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetSettingsFile()))
2677     {
2678       CLog::Log(LOGINFO, "Saving settings");
2679       CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
2680     }
2681     else
2682       CLog::Log(LOGINFO, "Not saving settings (settings.xml is not present)");
2683 
2684     // kodi may crash or deadlock during exit (shutdown / reboot) due to
2685     // either a bug in core or misbehaving addons. so try saving
2686     // skin settings early
2687     CLog::Log(LOGINFO, "Saving skin settings");
2688     if (g_SkinInfo != nullptr)
2689       g_SkinInfo->SaveSettings();
2690 
2691     m_bStop = true;
2692     // Add this here to keep the same ordering behaviour for now
2693     // Needs cleaning up
2694     CApplicationMessenger::GetInstance().Stop();
2695     m_AppFocused = false;
2696     m_ExitCode = exitCode;
2697     CLog::Log(LOGINFO, "Stopping all");
2698 
2699     // cancel any jobs from the jobmanager
2700     CJobManager::GetInstance().CancelJobs();
2701 
2702     // stop scanning before we kill the network and so on
2703     if (CMusicLibraryQueue::GetInstance().IsRunning())
2704       CMusicLibraryQueue::GetInstance().CancelAllJobs();
2705 
2706     if (CVideoLibraryQueue::GetInstance().IsRunning())
2707       CVideoLibraryQueue::GetInstance().CancelAllJobs();
2708 
2709     CApplicationMessenger::GetInstance().Cleanup();
2710 
2711     StopServices();
2712 
2713 #ifdef HAS_ZEROCONF
2714     if(CZeroconfBrowser::IsInstantiated())
2715     {
2716       CLog::Log(LOGINFO, "Stopping zeroconf browser");
2717       CZeroconfBrowser::GetInstance()->Stop();
2718       CZeroconfBrowser::ReleaseInstance();
2719     }
2720 #endif
2721 
2722     for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
2723       vfsAddon->DisconnectAll();
2724 
2725 #if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
2726     smb.Deinit();
2727 #endif
2728 
2729 #if defined(TARGET_DARWIN_OSX)
2730     if (XBMCHelper::GetInstance().IsAlwaysOn() == false)
2731       XBMCHelper::GetInstance().Stop();
2732 #endif
2733 
2734     // Stop services before unloading Python
2735     CServiceBroker::GetServiceAddons().Stop();
2736 
2737     // Stop any other python scripts that may be looping waiting for monitor.abortRequested()
2738     CScriptInvocationManager::GetInstance().StopRunningScripts();
2739 
2740     // unregister action listeners
2741     UnregisterActionListener(&m_appPlayer.GetSeekHandler());
2742     UnregisterActionListener(&CPlayerController::GetInstance());
2743 
2744     CGUIComponent *gui = CServiceBroker::GetGUI();
2745     if (gui)
2746       gui->GetAudioManager().DeInitialize();
2747 
2748     // shutdown the AudioEngine
2749     CServiceBroker::UnregisterAE();
2750     m_pActiveAE->Shutdown();
2751     m_pActiveAE.reset();
2752 
2753     CLog::Log(LOGINFO, "Application stopped");
2754   }
2755   catch (...)
2756   {
2757     CLog::Log(LOGERROR, "Exception in CApplication::Stop()");
2758   }
2759 
2760   cleanup_emu_environ();
2761 
2762   KODI::TIME::Sleep(200);
2763 }
2764 
PlayMedia(CFileItem & item,const std::string & player,int iPlaylist)2765 bool CApplication::PlayMedia(CFileItem& item, const std::string &player, int iPlaylist)
2766 {
2767   // If item is a plugin, expand out
2768   for (int i=0; URIUtils::IsPlugin(item.GetDynPath()) && i<5; ++i)
2769   {
2770     bool resume = item.m_lStartOffset == STARTOFFSET_RESUME;
2771 
2772     if (!XFILE::CPluginDirectory::GetPluginResult(item.GetDynPath(), item, resume) ||
2773         item.GetDynPath() == item.GetPath()) // GetPluginResult resolved to an empty path
2774       return false;
2775   }
2776   // if after the 5 resolution attempts the item is still a plugin just return, it isn't playable
2777   if (URIUtils::IsPlugin(item.GetDynPath()))
2778     return false;
2779 
2780   if (item.IsSmartPlayList())
2781   {
2782     CFileItemList items;
2783     CUtil::GetRecursiveListing(item.GetPath(), items, "", DIR_FLAG_NO_FILE_DIRS);
2784     if (items.Size())
2785     {
2786       CSmartPlaylist smartpl;
2787       //get name and type of smartplaylist, this will always succeed as GetDirectory also did this.
2788       smartpl.OpenAndReadName(item.GetURL());
2789       CPlayList playlist;
2790       playlist.Add(items);
2791       int iPlaylist = PLAYLIST_VIDEO;
2792       if (smartpl.GetType() == "songs" || smartpl.GetType() == "albums" ||
2793         smartpl.GetType() == "artists")
2794         iPlaylist = PLAYLIST_MUSIC;
2795       return ProcessAndStartPlaylist(smartpl.GetName(), playlist, iPlaylist);
2796     }
2797   }
2798   else if (item.IsPlayList() || item.IsInternetStream())
2799   {
2800     CGUIDialogCache* dlgCache = new CGUIDialogCache(5000, g_localizeStrings.Get(10214), item.GetLabel());
2801 
2802     //is or could be a playlist
2803     std::unique_ptr<CPlayList> pPlayList (CPlayListFactory::Create(item));
2804     bool gotPlayList = (pPlayList.get() && pPlayList->Load(item.GetPath()));
2805 
2806     if (dlgCache)
2807     {
2808        dlgCache->Close();
2809        if (dlgCache->IsCanceled())
2810           return true;
2811     }
2812 
2813     if (gotPlayList)
2814     {
2815 
2816       if (iPlaylist != PLAYLIST_NONE)
2817       {
2818         int track=0;
2819         if (item.HasProperty("playlist_starting_track"))
2820           track = (int)item.GetProperty("playlist_starting_track").asInteger();
2821         return ProcessAndStartPlaylist(item.GetPath(), *pPlayList, iPlaylist, track);
2822       }
2823       else
2824       {
2825         CLog::Log(LOGWARNING, "CApplication::PlayMedia called to play a playlist %s but no idea which playlist to use, playing first item", item.GetPath().c_str());
2826         if(pPlayList->size())
2827           return PlayFile(*(*pPlayList)[0], "", false);
2828       }
2829     }
2830   }
2831   else if (item.IsPVR())
2832   {
2833     return CServiceBroker::GetPVRManager().GUIActions()->PlayMedia(CFileItemPtr(new CFileItem(item)));
2834   }
2835 
2836   CURL path(item.GetPath());
2837   if (path.GetProtocol() == "game")
2838   {
2839     AddonPtr addon;
2840     if (CServiceBroker::GetAddonMgr().GetAddon(path.GetHostName(), addon, ADDON_GAMEDLL,
2841                                                OnlyEnabled::YES))
2842     {
2843       CFileItem addonItem(addon);
2844       return PlayFile(addonItem, player, false);
2845     }
2846   }
2847 
2848   //nothing special just play
2849   return PlayFile(item, player, false);
2850 }
2851 
2852 // PlayStack()
2853 // For playing a multi-file video.  Particularly inefficient
2854 // on startup, as we are required to calculate the length
2855 // of each video, so we open + close each one in turn.
2856 // A faster calculation of video time would improve this
2857 // substantially.
2858 // return value: same with PlayFile()
PlayStack(CFileItem & item,bool bRestart)2859 bool CApplication::PlayStack(CFileItem& item, bool bRestart)
2860 {
2861   if (!m_stackHelper.InitializeStack(item))
2862     return false;
2863 
2864   int startoffset = m_stackHelper.InitializeStackStartPartAndOffset(item);
2865 
2866   CFileItem selectedStackPart = m_stackHelper.GetCurrentStackPartFileItem();
2867   selectedStackPart.m_lStartOffset = startoffset;
2868 
2869   if (item.HasProperty("savedplayerstate"))
2870   {
2871     selectedStackPart.SetProperty("savedplayerstate", item.GetProperty("savedplayerstate")); // pass on to part
2872     item.ClearProperty("savedplayerstate");
2873   }
2874 
2875   return PlayFile(selectedStackPart, "", true);
2876 }
2877 
PlayFile(CFileItem item,const std::string & player,bool bRestart)2878 bool CApplication::PlayFile(CFileItem item, const std::string& player, bool bRestart)
2879 {
2880   // Ensure the MIME type has been retrieved for http:// and shout:// streams
2881   if (item.GetMimeType().empty())
2882     item.FillInMimeType();
2883 
2884   if (!bRestart)
2885   {
2886     // bRestart will be true when called from PlayStack(), skipping this block
2887     m_appPlayer.SetPlaySpeed(1);
2888 
2889     m_nextPlaylistItem = -1;
2890     m_stackHelper.Clear();
2891 
2892     if (item.IsVideo())
2893       CUtil::ClearSubtitles();
2894   }
2895 
2896   if (item.IsDiscStub())
2897   {
2898     return CServiceBroker::GetMediaManager().playStubFile(item);
2899   }
2900 
2901   if (item.IsPlayList())
2902     return false;
2903 
2904   for (int i=0; URIUtils::IsPlugin(item.GetDynPath()) && i<5; ++i)
2905   { // we modify the item so that it becomes a real URL
2906     bool resume = item.m_lStartOffset == STARTOFFSET_RESUME;
2907 
2908     if (!XFILE::CPluginDirectory::GetPluginResult(item.GetDynPath(), item, resume) ||
2909         item.GetDynPath() == item.GetPath()) // GetPluginResult resolved to an empty path
2910       return false;
2911   }
2912   // if after the 5 resolution attempts the item is still a plugin just return, it isn't playable
2913   if (URIUtils::IsPlugin(item.GetDynPath()))
2914     return false;
2915 
2916 #ifdef HAS_UPNP
2917   if (URIUtils::IsUPnP(item.GetPath()))
2918   {
2919     if (!XFILE::CUPnPDirectory::GetResource(item.GetURL(), item))
2920       return false;
2921   }
2922 #endif
2923 
2924   // if we have a stacked set of files, we need to setup our stack routines for
2925   // "seamless" seeking and total time of the movie etc.
2926   // will recall with restart set to true
2927   if (item.IsStack())
2928     return PlayStack(item, bRestart);
2929 
2930   CPlayerOptions options;
2931 
2932   if (item.HasProperty("StartPercent"))
2933   {
2934     options.startpercent = item.GetProperty("StartPercent").asDouble();
2935     item.m_lStartOffset = 0;
2936   }
2937 
2938   options.starttime = CUtil::ConvertMilliSecsToSecs(item.m_lStartOffset);
2939 
2940   if (bRestart)
2941   {
2942     // have to be set here due to playstack using this for starting the file
2943     if (item.HasVideoInfoTag())
2944       options.state = item.GetVideoInfoTag()->GetResumePoint().playerState;
2945   }
2946   if (!bRestart || m_stackHelper.IsPlayingISOStack())
2947   {
2948     // the following code block is only applicable when bRestart is false OR to ISO stacks
2949 
2950     if (item.IsVideo())
2951     {
2952       // open the d/b and retrieve the bookmarks for the current movie
2953       CVideoDatabase dbs;
2954       dbs.Open();
2955 
2956       std::string path = item.GetPath();
2957       std::string videoInfoTagPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
2958       if (videoInfoTagPath.find("removable://") == 0 || item.IsVideoDb())
2959         path = videoInfoTagPath;
2960       dbs.LoadVideoInfo(path, *item.GetVideoInfoTag());
2961 
2962       if (item.HasProperty("savedplayerstate"))
2963       {
2964         options.starttime = CUtil::ConvertMilliSecsToSecs(item.m_lStartOffset);
2965         options.state = item.GetProperty("savedplayerstate").asString();
2966         item.ClearProperty("savedplayerstate");
2967       }
2968       else if (item.m_lStartOffset == STARTOFFSET_RESUME)
2969       {
2970         options.starttime = 0.0f;
2971         if (item.IsResumePointSet())
2972         {
2973           options.starttime = item.GetCurrentResumeTime();
2974           if (item.HasVideoInfoTag())
2975             options.state = item.GetVideoInfoTag()->GetResumePoint().playerState;
2976         }
2977         else
2978         {
2979           CBookmark bookmark;
2980           std::string path = item.GetPath();
2981           if (item.HasVideoInfoTag() && StringUtils::StartsWith(item.GetVideoInfoTag()->m_strFileNameAndPath, "removable://"))
2982             path = item.GetVideoInfoTag()->m_strFileNameAndPath;
2983           else if (item.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
2984             path = item.GetProperty("original_listitem_url").asString();
2985           if (dbs.GetResumeBookMark(path, bookmark))
2986           {
2987             options.starttime = bookmark.timeInSeconds;
2988             options.state = bookmark.playerState;
2989           }
2990         }
2991 
2992         if (options.starttime == 0.0f && item.HasVideoInfoTag())
2993         {
2994           // No resume point is set, but check if this item is part of a multi-episode file
2995           const CVideoInfoTag *tag = item.GetVideoInfoTag();
2996 
2997           if (tag->m_iBookmarkId > 0)
2998           {
2999             CBookmark bookmark;
3000             dbs.GetBookMarkForEpisode(*tag, bookmark);
3001             options.starttime = bookmark.timeInSeconds;
3002             options.state = bookmark.playerState;
3003           }
3004         }
3005       }
3006       else if (item.HasVideoInfoTag())
3007       {
3008         const CVideoInfoTag *tag = item.GetVideoInfoTag();
3009 
3010         if (tag->m_iBookmarkId > 0)
3011         {
3012           CBookmark bookmark;
3013           dbs.GetBookMarkForEpisode(*tag, bookmark);
3014           options.starttime = bookmark.timeInSeconds;
3015           options.state = bookmark.playerState;
3016         }
3017       }
3018 
3019       dbs.Close();
3020     }
3021   }
3022 
3023   // a disc image might be Blu-Ray disc
3024   if (!(options.startpercent > 0.0f || options.starttime > 0.0f) && (item.IsBDFile() || item.IsDiscImage()))
3025   {
3026     //check if we must show the simplified bd menu
3027     if (!CGUIDialogSimpleMenu::ShowPlaySelection(item))
3028       return true;
3029   }
3030 
3031   // this really aught to be inside !bRestart, but since PlayStack
3032   // uses that to init playback, we have to keep it outside
3033   int playlist = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3034   if (item.IsAudio() && playlist == PLAYLIST_MUSIC)
3035   { // playing from a playlist by the looks
3036     // don't switch to fullscreen if we are not playing the first item...
3037     options.fullscreen = !CServiceBroker::GetPlaylistPlayer().HasPlayedFirstFile() &&
3038         CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
3039         CSettings::SETTING_MUSICFILES_SELECTACTION) &&
3040         !CMediaSettings::GetInstance().DoesMediaStartWindowed();
3041   }
3042   else if (item.IsVideo() && playlist == PLAYLIST_VIDEO &&
3043       CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlist).size() > 1)
3044   { // playing from a playlist by the looks
3045     // don't switch to fullscreen if we are not playing the first item...
3046     options.fullscreen = !CServiceBroker::GetPlaylistPlayer().HasPlayedFirstFile() &&
3047         CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreenOnMovieStart &&
3048         !CMediaSettings::GetInstance().DoesMediaStartWindowed();
3049   }
3050   else if(m_stackHelper.IsPlayingRegularStack())
3051   {
3052     //! @todo - this will fail if user seeks back to first file in stack
3053     if(m_stackHelper.GetCurrentPartNumber() == 0 || m_stackHelper.GetRegisteredStack(item)->m_lStartOffset != 0)
3054       options.fullscreen = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->
3055           m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesMediaStartWindowed();
3056     else
3057       options.fullscreen = false;
3058   }
3059   else
3060     options.fullscreen = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->
3061         m_fullScreenOnMovieStart && !CMediaSettings::GetInstance().DoesMediaStartWindowed();
3062 
3063   // stereo streams may have lower quality, i.e. 32bit vs 16 bit
3064   options.preferStereo = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoPreferStereoStream &&
3065                          CServiceBroker::GetActiveAE()->HasStereoAudioChannelCount();
3066 
3067   // reset VideoStartWindowed as it's a temp setting
3068   CMediaSettings::GetInstance().SetMediaStartWindowed(false);
3069 
3070   {
3071     // for playing a new item, previous playing item's callback may already
3072     // pushed some delay message into the threadmessage list, they are not
3073     // expected be processed after or during the new item playback starting.
3074     // so we clean up previous playing item's playback callback delay messages here.
3075     int previousMsgsIgnoredByNewPlaying[] = {
3076       GUI_MSG_PLAYBACK_STARTED,
3077       GUI_MSG_PLAYBACK_ENDED,
3078       GUI_MSG_PLAYBACK_STOPPED,
3079       GUI_MSG_PLAYLIST_CHANGED,
3080       GUI_MSG_PLAYLISTPLAYER_STOPPED,
3081       GUI_MSG_PLAYLISTPLAYER_STARTED,
3082       GUI_MSG_PLAYLISTPLAYER_CHANGED,
3083       GUI_MSG_QUEUE_NEXT_ITEM,
3084       0
3085     };
3086     int dMsgCount = CServiceBroker::GetGUI()->GetWindowManager().RemoveThreadMessageByMessageIds(&previousMsgsIgnoredByNewPlaying[0]);
3087     if (dMsgCount > 0)
3088       CLog::LogF(LOGDEBUG,"Ignored %d playback thread messages", dMsgCount);
3089   }
3090 
3091   m_appPlayer.OpenFile(item, options, m_ServiceManager->GetPlayerCoreFactory(), player, *this);
3092   m_appPlayer.SetVolume(m_volumeLevel);
3093   m_appPlayer.SetMute(m_muted);
3094 
3095 #if !defined(TARGET_POSIX)
3096   CGUIComponent *gui = CServiceBroker::GetGUI();
3097   if (gui)
3098     gui->GetAudioManager().Enable(false);
3099 #endif
3100 
3101   if (item.HasPVRChannelInfoTag())
3102     CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST_NONE);
3103 
3104   return true;
3105 }
3106 
PlaybackCleanup()3107 void CApplication::PlaybackCleanup()
3108 {
3109   if (!m_appPlayer.IsPlaying())
3110   {
3111     CGUIComponent *gui = CServiceBroker::GetGUI();
3112     if (gui)
3113       CServiceBroker::GetGUI()->GetAudioManager().Enable(true);
3114     m_appPlayer.OpenNext(m_ServiceManager->GetPlayerCoreFactory());
3115   }
3116 
3117   if (!m_appPlayer.IsPlayingVideo())
3118   {
3119     if(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
3120        CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME)
3121     {
3122       CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
3123     }
3124     else
3125     {
3126       //  resets to res_desktop or look&feel resolution (including refreshrate)
3127       CServiceBroker::GetWinSystem()->GetGfxContext().SetFullScreenVideo(false);
3128     }
3129 #ifdef TARGET_DARWIN_EMBEDDED
3130     CDarwinUtils::SetScheduling(false);
3131 #endif
3132   }
3133 
3134   if (!m_appPlayer.IsPlayingAudio() && CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST_NONE && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION)
3135   {
3136     CServiceBroker::GetSettingsComponent()->GetSettings()->Save();  // save vis settings
3137     WakeUpScreenSaverAndDPMS();
3138     CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
3139   }
3140 
3141   // DVD ejected while playing in vis ?
3142   if (!m_appPlayer.IsPlayingAudio() &&
3143       (m_itemCurrentFile->IsCDDA() || m_itemCurrentFile->IsOnDVD()) &&
3144       !CServiceBroker::GetMediaManager().IsDiscInDrive() &&
3145       CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION)
3146   {
3147     // yes, disable vis
3148     CServiceBroker::GetSettingsComponent()->GetSettings()->Save();    // save vis settings
3149     WakeUpScreenSaverAndDPMS();
3150     CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
3151   }
3152 
3153   if (!m_appPlayer.IsPlaying())
3154   {
3155     m_stackHelper.Clear();
3156     m_appPlayer.ResetPlayer();
3157   }
3158 
3159   if (IsEnableTestMode())
3160     CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT);
3161 }
3162 
OnPlayBackEnded()3163 void CApplication::OnPlayBackEnded()
3164 {
3165   CLog::LogF(LOGDEBUG ,"CApplication::OnPlayBackEnded");
3166 
3167   CServiceBroker::GetPVRManager().OnPlaybackEnded(m_itemCurrentFile);
3168 
3169   CVariant data(CVariant::VariantTypeObject);
3170   data["end"] = true;
3171   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnStop",
3172                                                      m_itemCurrentFile, data);
3173 
3174   CGUIMessage msg(GUI_MSG_PLAYBACK_ENDED, 0, 0);
3175   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3176 }
3177 
OnPlayBackStarted(const CFileItem & file)3178 void CApplication::OnPlayBackStarted(const CFileItem &file)
3179 {
3180   CLog::LogF(LOGDEBUG,"CApplication::OnPlayBackStarted");
3181 
3182   // check if VideoPlayer should set file item stream details from its current streams
3183   if (file.GetProperty("get_stream_details_from_player").asBoolean()
3184       || ((!file.HasVideoInfoTag() || !file.GetVideoInfoTag()->HasStreamDetails())
3185       && (URIUtils::IsBluray(file.GetPath())
3186       || file.IsDVDFile()
3187       || file.IsDiscImage())))
3188     m_appPlayer.SetUpdateStreamDetails();
3189 
3190   if (m_stackHelper.IsPlayingISOStack() || m_stackHelper.IsPlayingRegularStack())
3191     m_itemCurrentFile.reset(new CFileItem(*m_stackHelper.GetRegisteredStack(file)));
3192   else
3193     m_itemCurrentFile.reset(new CFileItem(file));
3194 
3195   /* When playing video pause any low priority jobs, they will be unpaused  when playback stops.
3196    * This should speed up player startup for files on internet filesystems (eg. webdav) and
3197    * increase performance on low powered systems (Atom/ARM).
3198    */
3199   if (file.IsVideo() || file.IsGame())
3200   {
3201     CJobManager::GetInstance().PauseJobs();
3202   }
3203 
3204   CServiceBroker::GetPVRManager().OnPlaybackStarted(m_itemCurrentFile);
3205   m_stackHelper.OnPlayBackStarted(file);
3206 
3207   m_playerEvent.Reset();
3208 
3209   CGUIMessage msg(GUI_MSG_PLAYBACK_STARTED, 0, 0);
3210   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3211 }
3212 
OnPlayerCloseFile(const CFileItem & file,const CBookmark & bookmarkParam)3213 void CApplication::OnPlayerCloseFile(const CFileItem &file, const CBookmark &bookmarkParam)
3214 {
3215   CSingleLock lock(m_stackHelper.m_critSection);
3216 
3217   CFileItem fileItem(file);
3218   CBookmark bookmark = bookmarkParam;
3219   CBookmark resumeBookmark;
3220   bool playCountUpdate = false;
3221   float percent = 0.0f;
3222 
3223   // Make sure we don't reset existing bookmark etc. on eg. player start failure
3224   if (bookmark.timeInSeconds == 0.0f)
3225     return;
3226 
3227   if (m_stackHelper.GetRegisteredStack(fileItem) != nullptr && m_stackHelper.GetRegisteredStackTotalTimeMs(fileItem) > 0)
3228   {
3229     // regular stack case: we have to save the bookmark on the stack
3230     fileItem = *m_stackHelper.GetRegisteredStack(file);
3231     // the bookmark coming from the player is only relative to the current part, thus needs to be corrected with these attributes (start time will be 0 for non-stackparts)
3232     bookmark.timeInSeconds += m_stackHelper.GetRegisteredStackPartStartTimeMs(file) / 1000.0;
3233     if (m_stackHelper.GetRegisteredStackTotalTimeMs(file) > 0)
3234       bookmark.totalTimeInSeconds = m_stackHelper.GetRegisteredStackTotalTimeMs(file) / 1000.0;
3235     bookmark.partNumber = m_stackHelper.GetRegisteredStackPartNumber(file);
3236   }
3237 
3238   percent = bookmark.timeInSeconds / bookmark.totalTimeInSeconds * 100;
3239 
3240   const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
3241 
3242   if ((fileItem.IsAudio() && advancedSettings->m_audioPlayCountMinimumPercent > 0 &&
3243        percent >= advancedSettings->m_audioPlayCountMinimumPercent) ||
3244       (fileItem.IsVideo() && advancedSettings->m_videoPlayCountMinimumPercent > 0 &&
3245        percent >= advancedSettings->m_videoPlayCountMinimumPercent))
3246   {
3247     playCountUpdate = true;
3248   }
3249 
3250   if (advancedSettings->m_videoIgnorePercentAtEnd > 0 &&
3251       bookmark.totalTimeInSeconds - bookmark.timeInSeconds <
3252         0.01f * advancedSettings->m_videoIgnorePercentAtEnd * bookmark.totalTimeInSeconds)
3253   {
3254     resumeBookmark.timeInSeconds = -1.0f;
3255   }
3256   else if (bookmark.timeInSeconds > advancedSettings->m_videoIgnoreSecondsAtStart)
3257   {
3258     resumeBookmark = bookmark;
3259     if (m_stackHelper.GetRegisteredStack(file) != nullptr)
3260     {
3261       // also update video info tag with total time
3262       fileItem.GetVideoInfoTag()->m_streamDetails.SetVideoDuration(0, resumeBookmark.totalTimeInSeconds);
3263     }
3264   }
3265   else
3266   {
3267     resumeBookmark.timeInSeconds = 0.0f;
3268   }
3269 
3270   if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteDatabases())
3271   {
3272     CSaveFileState::DoWork(fileItem, resumeBookmark, playCountUpdate);
3273   }
3274 }
3275 
OnQueueNextItem()3276 void CApplication::OnQueueNextItem()
3277 {
3278   CLog::LogF(LOGDEBUG,"CApplication::OnQueueNextItem");
3279 
3280   // informs python script currently running that we are requesting the next track
3281   // (does nothing if python is not loaded)
3282 #ifdef HAS_PYTHON
3283   CServiceBroker::GetXBPython().OnQueueNextItem(); // currently unimplemented
3284 #endif
3285 
3286   CGUIMessage msg(GUI_MSG_QUEUE_NEXT_ITEM, 0, 0);
3287   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3288 }
3289 
OnPlayBackStopped()3290 void CApplication::OnPlayBackStopped()
3291 {
3292   CLog::LogF(LOGDEBUG, "CApplication::OnPlayBackStopped");
3293 
3294   CServiceBroker::GetPVRManager().OnPlaybackStopped(m_itemCurrentFile);
3295 
3296   CVariant data(CVariant::VariantTypeObject);
3297   data["end"] = false;
3298   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnStop",
3299                                                      m_itemCurrentFile, data);
3300 
3301   CGUIMessage msg(GUI_MSG_PLAYBACK_STOPPED, 0, 0);
3302   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3303 }
3304 
OnPlayBackError()3305 void CApplication::OnPlayBackError()
3306 {
3307   //@todo Playlists can be continued by calling OnPlaybackEnded instead
3308   // open error dialog
3309   CGUIMessage msg(GUI_MSG_PLAYBACK_ERROR, 0, 0);
3310   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3311   OnPlayBackStopped();
3312 }
3313 
OnPlayBackPaused()3314 void CApplication::OnPlayBackPaused()
3315 {
3316 #ifdef HAS_PYTHON
3317   CServiceBroker::GetXBPython().OnPlayBackPaused();
3318 #endif
3319 
3320   CVariant param;
3321   param["player"]["speed"] = 0;
3322   param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3323   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPause",
3324                                                      m_itemCurrentFile, param);
3325 }
3326 
OnPlayBackResumed()3327 void CApplication::OnPlayBackResumed()
3328 {
3329 #ifdef HAS_PYTHON
3330   CServiceBroker::GetXBPython().OnPlayBackResumed();
3331 #endif
3332 
3333   CVariant param;
3334   param["player"]["speed"] = 1;
3335   param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3336   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnResume",
3337                                                      m_itemCurrentFile, param);
3338 }
3339 
OnPlayBackSpeedChanged(int iSpeed)3340 void CApplication::OnPlayBackSpeedChanged(int iSpeed)
3341 {
3342 #ifdef HAS_PYTHON
3343   CServiceBroker::GetXBPython().OnPlayBackSpeedChanged(iSpeed);
3344 #endif
3345 
3346   CVariant param;
3347   param["player"]["speed"] = iSpeed;
3348   param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3349   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnSpeedChanged",
3350                                                      m_itemCurrentFile, param);
3351 }
3352 
OnPlayBackSeek(int64_t iTime,int64_t seekOffset)3353 void CApplication::OnPlayBackSeek(int64_t iTime, int64_t seekOffset)
3354 {
3355 #ifdef HAS_PYTHON
3356   CServiceBroker::GetXBPython().OnPlayBackSeek(static_cast<int>(iTime),
3357                                                static_cast<int>(seekOffset));
3358 #endif
3359 
3360   CVariant param;
3361   CJSONUtils::MillisecondsToTimeObject(iTime, param["player"]["time"]);
3362   CJSONUtils::MillisecondsToTimeObject(seekOffset, param["player"]["seekoffset"]);
3363   param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3364   param["player"]["speed"] = (int)m_appPlayer.GetPlaySpeed();
3365   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnSeek",
3366                                                      m_itemCurrentFile, param);
3367   CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().SetDisplayAfterSeek(2500, static_cast<int>(seekOffset));
3368 }
3369 
OnPlayBackSeekChapter(int iChapter)3370 void CApplication::OnPlayBackSeekChapter(int iChapter)
3371 {
3372 #ifdef HAS_PYTHON
3373   CServiceBroker::GetXBPython().OnPlayBackSeekChapter(iChapter);
3374 #endif
3375 }
3376 
OnAVStarted(const CFileItem & file)3377 void CApplication::OnAVStarted(const CFileItem &file)
3378 {
3379   CLog::LogF(LOGDEBUG, "CApplication::OnAVStarted");
3380 
3381   CGUIMessage msg(GUI_MSG_PLAYBACK_AVSTARTED, 0, 0);
3382   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3383 
3384   CVariant param;
3385   param["player"]["speed"] = 1;
3386   param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3387   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnAVStart",
3388                                                      m_itemCurrentFile, param);
3389 }
3390 
OnAVChange()3391 void CApplication::OnAVChange()
3392 {
3393   CLog::LogF(LOGDEBUG, "CApplication::OnAVChange");
3394 
3395   CServiceBroker::GetGUI()->GetStereoscopicsManager().OnStreamChange();
3396 
3397   CGUIMessage msg(GUI_MSG_PLAYBACK_AVCHANGE, 0, 0);
3398   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3399 
3400   CVariant param;
3401   param["player"]["speed"] = 1;
3402   param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3403   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnAVChange",
3404                                                      m_itemCurrentFile, param);
3405 }
3406 
RequestVideoSettings(const CFileItem & fileItem)3407 void CApplication::RequestVideoSettings(const CFileItem &fileItem)
3408 {
3409   CVideoDatabase dbs;
3410   if (dbs.Open())
3411   {
3412     CLog::Log(LOGDEBUG, "Loading settings for %s", CURL::GetRedacted(fileItem.GetPath()).c_str());
3413 
3414     // Load stored settings if they exist, otherwise use default
3415     CVideoSettings vs;
3416     if (!dbs.GetVideoSettings(fileItem, vs))
3417       vs = CMediaSettings::GetInstance().GetDefaultVideoSettings();
3418 
3419     m_appPlayer.SetVideoSettings(vs);
3420 
3421     dbs.Close();
3422   }
3423 }
3424 
StoreVideoSettings(const CFileItem & fileItem,CVideoSettings vs)3425 void CApplication::StoreVideoSettings(const CFileItem &fileItem, CVideoSettings vs)
3426 {
3427   CVideoDatabase dbs;
3428   if (dbs.Open())
3429   {
3430     if (vs != CMediaSettings::GetInstance().GetDefaultVideoSettings())
3431     {
3432       dbs.SetVideoSettings(fileItem, vs);
3433     }
3434     else
3435     {
3436       dbs.EraseVideoSettings(fileItem);
3437     }
3438     dbs.Close();
3439   }
3440 }
3441 
IsPlayingFullScreenVideo() const3442 bool CApplication::IsPlayingFullScreenVideo() const
3443 {
3444   return m_appPlayer.IsPlayingVideo() && CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo();
3445 }
3446 
IsFullScreen()3447 bool CApplication::IsFullScreen()
3448 {
3449   return IsPlayingFullScreenVideo() ||
3450         (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION) ||
3451          CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW;
3452 }
3453 
StopPlaying()3454 void CApplication::StopPlaying()
3455 {
3456   CGUIComponent *gui = CServiceBroker::GetGUI();
3457 
3458   if (gui)
3459   {
3460     int iWin = gui->GetWindowManager().GetActiveWindow();
3461     if (m_appPlayer.IsPlaying())
3462     {
3463       m_appPlayer.ClosePlayer();
3464 
3465       // turn off visualisation window when stopping
3466       if ((iWin == WINDOW_VISUALISATION ||
3467            iWin == WINDOW_FULLSCREEN_VIDEO ||
3468            iWin == WINDOW_FULLSCREEN_GAME) &&
3469            !m_bStop)
3470         gui->GetWindowManager().PreviousWindow();
3471 
3472       g_partyModeManager.Disable();
3473     }
3474   }
3475 }
3476 
ResetSystemIdleTimer()3477 void CApplication::ResetSystemIdleTimer()
3478 {
3479   // reset system idle timer
3480   m_idleTimer.StartZero();
3481 }
3482 
ResetScreenSaver()3483 void CApplication::ResetScreenSaver()
3484 {
3485   // reset our timers
3486   m_shutdownTimer.StartZero();
3487 
3488   // screen saver timer is reset only if we're not already in screensaver or
3489   // DPMS mode
3490   if ((!m_screensaverActive && m_iScreenSaveLock == 0) && !m_dpmsIsActive)
3491     ResetScreenSaverTimer();
3492 }
3493 
ResetScreenSaverTimer()3494 void CApplication::ResetScreenSaverTimer()
3495 {
3496   m_screenSaverTimer.StartZero();
3497 }
3498 
StopScreenSaverTimer()3499 void CApplication::StopScreenSaverTimer()
3500 {
3501   m_screenSaverTimer.Stop();
3502 }
3503 
ToggleDPMS(bool manual)3504 bool CApplication::ToggleDPMS(bool manual)
3505 {
3506   auto winSystem = CServiceBroker::GetWinSystem();
3507   if (!winSystem)
3508     return false;
3509 
3510   std::shared_ptr<CDPMSSupport> dpms = winSystem->GetDPMSManager();
3511   if (!dpms)
3512     return false;
3513 
3514   if (manual || (m_dpmsIsManual == manual))
3515   {
3516     if (m_dpmsIsActive)
3517     {
3518       m_dpmsIsActive = false;
3519       m_dpmsIsManual = false;
3520       SetRenderGUI(true);
3521       CheckOSScreenSaverInhibitionSetting();
3522       CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI, "OnDPMSDeactivated");
3523       return dpms->DisablePowerSaving();
3524     }
3525     else
3526     {
3527       if (dpms->EnablePowerSaving(dpms->GetSupportedModes()[0]))
3528       {
3529         m_dpmsIsActive = true;
3530         m_dpmsIsManual = manual;
3531         SetRenderGUI(false);
3532         CheckOSScreenSaverInhibitionSetting();
3533         CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI, "OnDPMSActivated");
3534         return true;
3535       }
3536     }
3537   }
3538   return false;
3539 }
3540 
WakeUpScreenSaverAndDPMS(bool bPowerOffKeyPressed)3541 bool CApplication::WakeUpScreenSaverAndDPMS(bool bPowerOffKeyPressed /* = false */)
3542 {
3543   bool result = false;
3544 
3545   // First reset DPMS, if active
3546   if (m_dpmsIsActive)
3547   {
3548     if (m_dpmsIsManual)
3549       return false;
3550     //! @todo if screensaver lock is specified but screensaver is not active
3551     //! (DPMS came first), activate screensaver now.
3552     ToggleDPMS(false);
3553     ResetScreenSaverTimer();
3554     result = !m_screensaverActive || WakeUpScreenSaver(bPowerOffKeyPressed);
3555   }
3556   else if (m_screensaverActive)
3557     result = WakeUpScreenSaver(bPowerOffKeyPressed);
3558 
3559   if(result)
3560   {
3561     // allow listeners to ignore the deactivation if it precedes a powerdown/suspend etc
3562     CVariant data(CVariant::VariantTypeObject);
3563     data["shuttingdown"] = bPowerOffKeyPressed;
3564     CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI,
3565                                                        "OnScreensaverDeactivated", data);
3566   }
3567 
3568   return result;
3569 }
3570 
WakeUpScreenSaver(bool bPowerOffKeyPressed)3571 bool CApplication::WakeUpScreenSaver(bool bPowerOffKeyPressed /* = false */)
3572 {
3573   if (m_iScreenSaveLock == 2)
3574     return false;
3575 
3576   // if Screen saver is active
3577   if (m_screensaverActive && !m_screensaverIdInUse.empty())
3578   {
3579     if (m_iScreenSaveLock == 0)
3580     {
3581       const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
3582       if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
3583           (profileManager->UsingLoginScreen() || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MASTERLOCK_STARTUPLOCK)) &&
3584           profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE &&
3585           m_screensaverIdInUse != "screensaver.xbmc.builtin.dim" && m_screensaverIdInUse != "screensaver.xbmc.builtin.black" && m_screensaverIdInUse != "visualization")
3586       {
3587         m_iScreenSaveLock = 2;
3588         CGUIMessage msg(GUI_MSG_CHECK_LOCK,0,0);
3589 
3590         CGUIWindow* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_SCREENSAVER);
3591         if (pWindow)
3592           pWindow->OnMessage(msg);
3593       }
3594     }
3595     if (m_iScreenSaveLock == -1)
3596     {
3597       m_iScreenSaveLock = 0;
3598       return true;
3599     }
3600 
3601     // disable screensaver
3602     m_screensaverActive = false;
3603     m_iScreenSaveLock = 0;
3604     ResetScreenSaverTimer();
3605 
3606     if (m_screensaverIdInUse == "visualization")
3607     {
3608       // we can just continue as usual from vis mode
3609       return false;
3610     }
3611     else if (m_screensaverIdInUse == "screensaver.xbmc.builtin.dim" ||
3612              m_screensaverIdInUse == "screensaver.xbmc.builtin.black" ||
3613              m_screensaverIdInUse.empty())
3614     {
3615       return true;
3616     }
3617     else if (!m_screensaverIdInUse.empty())
3618     { // we're in screensaver window
3619       if (m_pythonScreenSaver)
3620       {
3621         // What sound does a python screensaver make?
3622         #define SCRIPT_ALARM "sssssscreensaver"
3623         #define SCRIPT_TIMEOUT 15 // seconds
3624 
3625         /* FIXME: This is a hack but a proper fix is non-trivial. Basically this code
3626         * makes sure the addon gets terminated after we've moved out of the screensaver window.
3627         * If we don't do this, we may simply lockup.
3628         */
3629         g_alarmClock.Start(SCRIPT_ALARM, SCRIPT_TIMEOUT, "StopScript(" + m_pythonScreenSaver->LibPath() + ")", true, false);
3630         m_pythonScreenSaver.reset();
3631       }
3632       if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SCREENSAVER)
3633         CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();  // show the previous window
3634       else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW)
3635         CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1, static_cast<void*>(new CAction(ACTION_STOP)));
3636     }
3637     return true;
3638   }
3639   else
3640     return false;
3641 }
3642 
CheckOSScreenSaverInhibitionSetting()3643 void CApplication::CheckOSScreenSaverInhibitionSetting()
3644 {
3645   // Kodi screen saver overrides OS one: always inhibit OS screen saver then
3646   // except when DPMS is active (inhibiting the screen saver then might also
3647   // disable DPMS again)
3648   if (!m_dpmsIsActive &&
3649       !CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SCREENSAVER_MODE).empty() &&
3650       CServiceBroker::GetWinSystem()->GetOSScreenSaver())
3651   {
3652     if (!m_globalScreensaverInhibitor)
3653     {
3654       m_globalScreensaverInhibitor = CServiceBroker::GetWinSystem()->GetOSScreenSaver()->CreateInhibitor();
3655     }
3656   }
3657   else if (m_globalScreensaverInhibitor)
3658   {
3659     m_globalScreensaverInhibitor.Release();
3660   }
3661 }
3662 
CheckScreenSaverAndDPMS()3663 void CApplication::CheckScreenSaverAndDPMS()
3664 {
3665   bool maybeScreensaver = true;
3666   if (m_dpmsIsActive)
3667     maybeScreensaver = false;
3668   else if (m_screensaverActive)
3669     maybeScreensaver = false;
3670   else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SCREENSAVER_MODE).empty())
3671     maybeScreensaver = false;
3672 
3673   auto winSystem = CServiceBroker::GetWinSystem();
3674   if (!winSystem)
3675     return;
3676 
3677   std::shared_ptr<CDPMSSupport> dpms = winSystem->GetDPMSManager();
3678 
3679   bool maybeDPMS = true;
3680   if (m_dpmsIsActive)
3681     maybeDPMS = false;
3682   else if (!dpms || !dpms->IsSupported())
3683     maybeDPMS = false;
3684   else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) <= 0)
3685     maybeDPMS = false;
3686 
3687   // whether the current state of the application should be regarded as active even when there is no
3688   // explicit user activity such as input
3689   bool haveIdleActivity = false;
3690 
3691   // When inhibit screensaver is enabled prevent screensaver from kicking in
3692   if (m_bInhibitScreenSaver)
3693     haveIdleActivity = true;
3694 
3695   // Are we playing a video and it is not paused?
3696   if (m_appPlayer.IsPlayingVideo() && !m_appPlayer.IsPaused())
3697     haveIdleActivity = true;
3698 
3699   // Are we playing some music in fullscreen vis?
3700   else if (m_appPlayer.IsPlayingAudio() &&
3701            CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION &&
3702            !CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_MUSICPLAYER_VISUALISATION).empty())
3703   {
3704     haveIdleActivity = true;
3705   }
3706 
3707   // Handle OS screen saver state
3708   if (haveIdleActivity && CServiceBroker::GetWinSystem()->GetOSScreenSaver())
3709   {
3710     // Always inhibit OS screen saver during these kinds of activities
3711     if (!m_screensaverInhibitor)
3712     {
3713       m_screensaverInhibitor = CServiceBroker::GetWinSystem()->GetOSScreenSaver()->CreateInhibitor();
3714     }
3715   }
3716   else if (m_screensaverInhibitor)
3717   {
3718     m_screensaverInhibitor.Release();
3719   }
3720 
3721   // Has the screen saver window become active?
3722   if (maybeScreensaver && CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_SCREENSAVER))
3723   {
3724     m_screensaverActive = true;
3725     maybeScreensaver = false;
3726   }
3727 
3728   if (m_screensaverActive && haveIdleActivity)
3729   {
3730     WakeUpScreenSaverAndDPMS();
3731     return;
3732   }
3733 
3734   if (!maybeScreensaver && !maybeDPMS) return;  // Nothing to do.
3735 
3736   // See if we need to reset timer.
3737   if (haveIdleActivity)
3738   {
3739     ResetScreenSaverTimer();
3740     return;
3741   }
3742 
3743   float elapsed = m_screenSaverTimer.IsRunning() ? m_screenSaverTimer.GetElapsedSeconds() : 0.f;
3744 
3745   // DPMS has priority (it makes the screensaver not needed)
3746   if (maybeDPMS
3747       && elapsed > CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) * 60)
3748   {
3749     ToggleDPMS(false);
3750     WakeUpScreenSaver();
3751   }
3752   else if (maybeScreensaver
3753            && elapsed > CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SCREENSAVER_TIME) * 60)
3754   {
3755     ActivateScreenSaver();
3756   }
3757 }
3758 
3759 // activate the screensaver.
3760 // if forceType is true, we ignore the various conditions that can alter
3761 // the type of screensaver displayed
ActivateScreenSaver(bool forceType)3762 void CApplication::ActivateScreenSaver(bool forceType /*= false */)
3763 {
3764   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
3765   if (m_appPlayer.IsPlayingAudio() && settings->GetBool(CSettings::SETTING_SCREENSAVER_USEMUSICVISINSTEAD) &&
3766       !settings->GetString(CSettings::SETTING_MUSICPLAYER_VISUALISATION).empty())
3767   { // just activate the visualisation if user toggled the usemusicvisinstead option
3768     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_VISUALISATION);
3769     return;
3770   }
3771 
3772   m_screensaverActive = true;
3773   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI, "OnScreensaverActivated");
3774 
3775   // disable screensaver lock from the login screen
3776   m_iScreenSaveLock = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN ? 1 : 0;
3777 
3778   m_screensaverIdInUse = settings->GetString(CSettings::SETTING_SCREENSAVER_MODE);
3779 
3780   if (!forceType)
3781   {
3782     if (m_screensaverIdInUse == "screensaver.xbmc.builtin.dim" ||
3783         m_screensaverIdInUse == "screensaver.xbmc.builtin.black" ||
3784         m_screensaverIdInUse.empty())
3785     {
3786       return;
3787     }
3788 
3789     // Enforce Dim for special cases.
3790     bool bUseDim = false;
3791     if (CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true))
3792       bUseDim = true;
3793     else if (m_appPlayer.IsPlayingVideo() && settings->GetBool(CSettings::SETTING_SCREENSAVER_USEDIMONPAUSE))
3794       bUseDim = true;
3795     else if (CServiceBroker::GetPVRManager().GUIActions()->IsRunningChannelScan())
3796       bUseDim = true;
3797 
3798     if (bUseDim)
3799       m_screensaverIdInUse = "screensaver.xbmc.builtin.dim";
3800   }
3801 
3802   if (m_screensaverIdInUse == "screensaver.xbmc.builtin.dim" ||
3803       m_screensaverIdInUse == "screensaver.xbmc.builtin.black" ||
3804       m_screensaverIdInUse.empty())
3805   {
3806     return;
3807   }
3808   else if (CServiceBroker::GetAddonMgr().GetAddon(m_screensaverIdInUse, m_pythonScreenSaver,
3809                                                   ADDON_SCREENSAVER, OnlyEnabled::YES))
3810   {
3811     std::string libPath = m_pythonScreenSaver->LibPath();
3812     if (CScriptInvocationManager::GetInstance().HasLanguageInvoker(libPath))
3813     {
3814       CLog::Log(LOGDEBUG, "using python screensaver add-on %s", m_screensaverIdInUse.c_str());
3815 
3816       // Don't allow a previously-scheduled alarm to kill our new screensaver
3817       g_alarmClock.Stop(SCRIPT_ALARM, true);
3818 
3819       if (!CScriptInvocationManager::GetInstance().Stop(libPath))
3820         CScriptInvocationManager::GetInstance().ExecuteAsync(libPath, AddonPtr(new CAddon(dynamic_cast<ADDON::CAddon&>(*m_pythonScreenSaver))));
3821       return;
3822     }
3823     m_pythonScreenSaver.reset();
3824   }
3825 
3826   CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SCREENSAVER);
3827 }
3828 
InhibitScreenSaver(bool inhibit)3829 void CApplication::InhibitScreenSaver(bool inhibit)
3830 {
3831   m_bInhibitScreenSaver = inhibit;
3832 }
3833 
IsScreenSaverInhibited() const3834 bool CApplication::IsScreenSaverInhibited() const
3835 {
3836   return m_bInhibitScreenSaver;
3837 }
3838 
CheckShutdown()3839 void CApplication::CheckShutdown()
3840 {
3841   // first check if we should reset the timer
3842   if (m_bInhibitIdleShutdown
3843       || m_appPlayer.IsPlaying() || m_appPlayer.IsPausedPlayback() // is something playing?
3844       || CMusicLibraryQueue::GetInstance().IsRunning()
3845       || CVideoLibraryQueue::GetInstance().IsRunning()
3846       || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_PROGRESS) // progress dialog is onscreen
3847       || !CServiceBroker::GetPVRManager().GUIActions()->CanSystemPowerdown(false))
3848   {
3849     m_shutdownTimer.StartZero();
3850     return;
3851   }
3852 
3853   float elapsed = m_shutdownTimer.IsRunning() ? m_shutdownTimer.GetElapsedSeconds() : 0.f;
3854   if ( elapsed > CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME) * 60 )
3855   {
3856     // Since it is a sleep instead of a shutdown, let's set everything to reset when we wake up.
3857     m_shutdownTimer.Stop();
3858 
3859     // Sleep the box
3860     CApplicationMessenger::GetInstance().PostMsg(TMSG_SHUTDOWN);
3861   }
3862 }
3863 
InhibitIdleShutdown(bool inhibit)3864 void CApplication::InhibitIdleShutdown(bool inhibit)
3865 {
3866   m_bInhibitIdleShutdown = inhibit;
3867 }
3868 
IsIdleShutdownInhibited() const3869 bool CApplication::IsIdleShutdownInhibited() const
3870 {
3871   return m_bInhibitIdleShutdown;
3872 }
3873 
OnMessage(CGUIMessage & message)3874 bool CApplication::OnMessage(CGUIMessage& message)
3875 {
3876   switch ( message.GetMessage() )
3877   {
3878   case GUI_MSG_NOTIFY_ALL:
3879     {
3880       if (message.GetParam1()==GUI_MSG_REMOVED_MEDIA)
3881       {
3882         // Update general playlist: Remove DVD playlist items
3883         int nRemoved = CServiceBroker::GetPlaylistPlayer().RemoveDVDItems();
3884         if ( nRemoved > 0 )
3885         {
3886           CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 );
3887           CServiceBroker::GetGUI()->GetWindowManager().SendMessage( msg );
3888         }
3889         // stop the file if it's on dvd (will set the resume point etc)
3890         if (m_itemCurrentFile->IsOnDVD())
3891           StopPlaying();
3892       }
3893       else if (message.GetParam1() == GUI_MSG_UI_READY)
3894       {
3895         // remove splash window
3896         CServiceBroker::GetGUI()->GetWindowManager().Delete(WINDOW_SPLASH);
3897 
3898         // show the volumebar if the volume is muted
3899         if (IsMuted() || GetVolumeRatio() <= VOLUME_MINIMUM)
3900           ShowVolumeBar();
3901 
3902         if (!m_incompatibleAddons.empty())
3903         {
3904           // filter addons that are not dependencies
3905           std::vector<std::string> disabledAddonNames;
3906           for (const auto& addoninfo : m_incompatibleAddons)
3907           {
3908             if (!CAddonType::IsDependencyType(addoninfo->MainType()))
3909               disabledAddonNames.emplace_back(addoninfo->Name());
3910           }
3911 
3912           // migration (incompatible addons) dialog
3913           auto addonList = StringUtils::Join(disabledAddonNames, ", ");
3914           auto msg = StringUtils::Format(g_localizeStrings.Get(24149).c_str(), addonList.c_str());
3915           HELPERS::ShowOKDialogText(CVariant{24148}, CVariant{std::move(msg)});
3916           m_incompatibleAddons.clear();
3917         }
3918 
3919         // show info dialog about moved configuration files if needed
3920         ShowAppMigrationMessage();
3921 
3922         // offer enabling addons at kodi startup that are disabled due to
3923         // e.g. os package manager installation on linux
3924         ConfigureAndEnableAddons();
3925 
3926         m_bInitializing = false;
3927 
3928         if (message.GetSenderId() == WINDOW_SETTINGS_PROFILES)
3929           g_application.ReloadSkin(false);
3930       }
3931       else if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
3932       {
3933         CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
3934         if (m_itemCurrentFile->IsSamePath(item.get()))
3935         {
3936           m_itemCurrentFile->UpdateInfo(*item);
3937           CServiceBroker::GetGUI()->GetInfoManager().UpdateCurrentItem(*item);
3938         }
3939       }
3940     }
3941     break;
3942 
3943   case GUI_MSG_PLAYBACK_STARTED:
3944     {
3945 #ifdef TARGET_DARWIN_EMBEDDED
3946       // @TODO move this away to platform code
3947       CDarwinUtils::SetScheduling(m_appPlayer.IsPlayingVideo());
3948 #endif
3949       CPlayList playList = CServiceBroker::GetPlaylistPlayer().GetPlaylist(CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist());
3950 
3951       // Update our infoManager with the new details etc.
3952       if (m_nextPlaylistItem >= 0)
3953       {
3954         // playing an item which is not in the list - player might be stopped already
3955         // so do nothing
3956         if (playList.size() <= m_nextPlaylistItem)
3957           return true;
3958 
3959         // we've started a previously queued item
3960         CFileItemPtr item = playList[m_nextPlaylistItem];
3961         // update the playlist manager
3962         int currentSong = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
3963         int param = ((currentSong & 0xffff) << 16) | (m_nextPlaylistItem & 0xffff);
3964         CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_CHANGED, 0, 0, CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist(), param, item);
3965         CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
3966         CServiceBroker::GetPlaylistPlayer().SetCurrentSong(m_nextPlaylistItem);
3967         m_itemCurrentFile.reset(new CFileItem(*item));
3968       }
3969       CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
3970       g_partyModeManager.OnSongChange(true);
3971 
3972 #ifdef HAS_PYTHON
3973       // informs python script currently running playback has started
3974       // (does nothing if python is not loaded)
3975       CServiceBroker::GetXBPython().OnPlayBackStarted(*m_itemCurrentFile);
3976 #endif
3977 
3978       CVariant param;
3979       param["player"]["speed"] = 1;
3980       param["player"]["playerid"] = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
3981       CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPlay",
3982                                                          m_itemCurrentFile, param);
3983 
3984       // we don't want a busy dialog when switching channels
3985       if (!m_itemCurrentFile->IsLiveTV())
3986       {
3987         CGUIDialogBusy* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogBusy>(WINDOW_DIALOG_BUSY);
3988         if (dialog && !dialog->IsDialogRunning())
3989           dialog->WaitOnEvent(m_playerEvent);
3990       }
3991 
3992       return true;
3993     }
3994     break;
3995 
3996   case GUI_MSG_QUEUE_NEXT_ITEM:
3997     {
3998       // Check to see if our playlist player has a new item for us,
3999       // and if so, we check whether our current player wants the file
4000       int iNext = CServiceBroker::GetPlaylistPlayer().GetNextSong();
4001       CPlayList& playlist = CServiceBroker::GetPlaylistPlayer().GetPlaylist(CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist());
4002       if (iNext < 0 || iNext >= playlist.size())
4003       {
4004         m_appPlayer.OnNothingToQueueNotify();
4005         return true; // nothing to do
4006       }
4007 
4008       // ok, grab the next song
4009       CFileItem file(*playlist[iNext]);
4010       // handle plugin://
4011       CURL url(file.GetDynPath());
4012       if (url.IsProtocol("plugin"))
4013         XFILE::CPluginDirectory::GetPluginResult(url.Get(), file, false);
4014 
4015       // Don't queue if next media type is different from current one
4016       bool bNothingToQueue = false;
4017 
4018       if (!file.IsVideo() && m_appPlayer.IsPlayingVideo())
4019         bNothingToQueue = true;
4020       else if ((!file.IsAudio() || file.IsVideo()) && m_appPlayer.IsPlayingAudio())
4021         bNothingToQueue = true;
4022 
4023       if (bNothingToQueue)
4024       {
4025         m_appPlayer.OnNothingToQueueNotify();
4026         return true;
4027       }
4028 
4029 #ifdef HAS_UPNP
4030       if (URIUtils::IsUPnP(file.GetDynPath()))
4031       {
4032         if (!XFILE::CUPnPDirectory::GetResource(file.GetDynURL(), file))
4033           return true;
4034       }
4035 #endif
4036 
4037       // ok - send the file to the player, if it accepts it
4038       if (m_appPlayer.QueueNextFile(file))
4039       {
4040         // player accepted the next file
4041         m_nextPlaylistItem = iNext;
4042       }
4043       else
4044       {
4045         /* Player didn't accept next file: *ALWAYS* advance playlist in this case so the player can
4046             queue the next (if it wants to) and it doesn't keep looping on this song */
4047         CServiceBroker::GetPlaylistPlayer().SetCurrentSong(iNext);
4048       }
4049 
4050       return true;
4051     }
4052     break;
4053 
4054   case GUI_MSG_PLAYBACK_STOPPED:
4055     m_playerEvent.Set();
4056     ResetCurrentItem();
4057     PlaybackCleanup();
4058 #ifdef HAS_PYTHON
4059     CServiceBroker::GetXBPython().OnPlayBackStopped();
4060 #endif
4061      return true;
4062 
4063   case GUI_MSG_PLAYBACK_ENDED:
4064     m_playerEvent.Set();
4065     if (m_stackHelper.IsPlayingRegularStack() && m_stackHelper.HasNextStackPartFileItem())
4066     { // just play the next item in the stack
4067       PlayFile(m_stackHelper.SetNextStackPartCurrentFileItem(), "", true);
4068       return true;
4069     }
4070     ResetCurrentItem();
4071     if (!CServiceBroker::GetPlaylistPlayer().PlayNext(1, true))
4072       m_appPlayer.ClosePlayer();
4073 
4074     PlaybackCleanup();
4075 
4076 #ifdef HAS_PYTHON
4077     CServiceBroker::GetXBPython().OnPlayBackEnded();
4078 #endif
4079     return true;
4080 
4081   case GUI_MSG_PLAYLISTPLAYER_STOPPED:
4082     ResetCurrentItem();
4083     if (m_appPlayer.IsPlaying())
4084       StopPlaying();
4085     PlaybackCleanup();
4086     return true;
4087 
4088   case GUI_MSG_PLAYBACK_AVSTARTED:
4089     m_playerEvent.Set();
4090 #ifdef HAS_PYTHON
4091     // informs python script currently running playback has started
4092     // (does nothing if python is not loaded)
4093     CServiceBroker::GetXBPython().OnAVStarted(*m_itemCurrentFile);
4094 #endif
4095     return true;
4096 
4097   case GUI_MSG_PLAYBACK_AVCHANGE:
4098 #ifdef HAS_PYTHON
4099     // informs python script currently running playback has started
4100     // (does nothing if python is not loaded)
4101     CServiceBroker::GetXBPython().OnAVChange();
4102 #endif
4103       return true;
4104 
4105   case GUI_MSG_PLAYBACK_ERROR:
4106     HELPERS::ShowOKDialogText(CVariant{16026}, CVariant{16027});
4107     return true;
4108 
4109   case GUI_MSG_PLAYLISTPLAYER_STARTED:
4110   case GUI_MSG_PLAYLISTPLAYER_CHANGED:
4111     {
4112       return true;
4113     }
4114     break;
4115   case GUI_MSG_FULLSCREEN:
4116     { // Switch to fullscreen, if we can
4117       SwitchToFullScreen();
4118       return true;
4119     }
4120     break;
4121   case GUI_MSG_EXECUTE:
4122     if (message.GetNumStringParams())
4123       return ExecuteXBMCAction(message.GetStringParam(), message.GetItem());
4124     break;
4125   }
4126   return false;
4127 }
4128 
ExecuteXBMCAction(std::string actionStr,const CGUIListItemPtr & item)4129 bool CApplication::ExecuteXBMCAction(std::string actionStr, const CGUIListItemPtr &item /* = NULL */)
4130 {
4131   // see if it is a user set string
4132 
4133   //We don't know if there is unsecure information in this yet, so we
4134   //postpone any logging
4135   const std::string in_actionStr(actionStr);
4136   if (item)
4137     actionStr = GUILIB::GUIINFO::CGUIInfoLabel::GetItemLabel(actionStr, item.get());
4138   else
4139     actionStr = GUILIB::GUIINFO::CGUIInfoLabel::GetLabel(actionStr);
4140 
4141   // user has asked for something to be executed
4142   if (CBuiltins::GetInstance().HasCommand(actionStr))
4143   {
4144     if (!CBuiltins::GetInstance().IsSystemPowerdownCommand(actionStr) ||
4145         CServiceBroker::GetPVRManager().GUIActions()->CanSystemPowerdown())
4146       CBuiltins::GetInstance().Execute(actionStr);
4147   }
4148   else
4149   {
4150     // try translating the action from our ButtonTranslator
4151     unsigned int actionID;
4152     if (CActionTranslator::TranslateString(actionStr, actionID))
4153     {
4154       OnAction(CAction(actionID));
4155       return true;
4156     }
4157     CFileItem item(actionStr, false);
4158 #ifdef HAS_PYTHON
4159     if (item.IsPythonScript())
4160     { // a python script
4161       CScriptInvocationManager::GetInstance().ExecuteAsync(item.GetPath());
4162     }
4163     else
4164 #endif
4165     if (item.IsAudio() || item.IsVideo() || item.IsGame())
4166     { // an audio or video file
4167       PlayFile(item, "");
4168     }
4169     else
4170     {
4171       //At this point we have given up to translate, so even though
4172       //there may be insecure information, we log it.
4173       CLog::LogF(LOGDEBUG,"Tried translating, but failed to understand %s", in_actionStr.c_str());
4174       return false;
4175     }
4176   }
4177   return true;
4178 }
4179 
4180 // inform the user that the configuration data has moved from old XBMC location
4181 // to new Kodi location - if applicable
ShowAppMigrationMessage()4182 void CApplication::ShowAppMigrationMessage()
4183 {
4184   // .kodi_migration_complete will be created from the installer/packaging
4185   // once an old XBMC configuration was moved to the new Kodi location
4186   // if this is the case show the migration info to the user once which
4187   // tells him to have a look into the wiki where the move of configuration
4188   // is further explained.
4189   if (CFile::Exists("special://home/.kodi_data_was_migrated") &&
4190       !CFile::Exists("special://home/.kodi_migration_info_shown"))
4191   {
4192     HELPERS::ShowOKDialogText(CVariant{24128}, CVariant{24129});
4193     CFile tmpFile;
4194     // create the file which will prevent this dialog from appearing in the future
4195     tmpFile.OpenForWrite("special://home/.kodi_migration_info_shown");
4196     tmpFile.Close();
4197   }
4198 }
4199 
ConfigureAndEnableAddons()4200 void CApplication::ConfigureAndEnableAddons()
4201 {
4202   std::vector<std::shared_ptr<IAddon>>
4203       disabledAddons; /*!< Installed addons, but not auto-enabled via manifest */
4204 
4205   auto& addonMgr = CServiceBroker::GetAddonMgr();
4206 
4207   if (addonMgr.GetDisabledAddons(disabledAddons) && !disabledAddons.empty())
4208   {
4209     // this applies to certain platforms only:
4210     // look at disabled addons with disabledReason == NONE, usually those are installed via package managers or manually.
4211     // also try to enable add-ons with disabledReason == INCOMPATIBLE at startup for all platforms.
4212 
4213     bool isConfigureAddonsAtStartupEnabled =
4214         m_ServiceManager->GetPlatform().IsConfigureAddonsAtStartupEnabled();
4215 
4216     for (const auto& addon : disabledAddons)
4217     {
4218       if (addonMgr.IsAddonDisabledWithReason(addon->ID(), ADDON::AddonDisabledReason::INCOMPATIBLE))
4219       {
4220         auto addonInfo = addonMgr.GetAddonInfo(addon->ID());
4221         if (addonInfo && addonMgr.IsCompatible(addonInfo))
4222         {
4223           CLog::Log(LOGDEBUG, "CApplication::{}: enabling the compatible version of [{}].",
4224                     __FUNCTION__, addon->ID());
4225           addonMgr.EnableAddon(addon->ID());
4226         }
4227         continue;
4228       }
4229 
4230       if (addonMgr.IsAddonDisabledExcept(addon->ID(), ADDON::AddonDisabledReason::NONE) ||
4231           CAddonType::IsDependencyType(addon->MainType()))
4232       {
4233         continue;
4234       }
4235 
4236       if (isConfigureAddonsAtStartupEnabled)
4237       {
4238         if (HELPERS::ShowYesNoDialogLines(CVariant{24039}, // Disabled add-ons
4239                                           CVariant{24059}, // Would you like to enable this add-on?
4240                                           CVariant{addon->Name()}) == DialogResponse::YES)
4241         {
4242           if (addon->HasSettings())
4243           {
4244             if (CGUIDialogAddonSettings::ShowForAddon(addon))
4245             {
4246               // only enable if settings dialog hasn't been cancelled
4247               addonMgr.EnableAddon(addon->ID());
4248             }
4249           }
4250           else
4251           {
4252             addonMgr.EnableAddon(addon->ID());
4253           }
4254         }
4255         else
4256         {
4257           // user chose not to configure/enable so we're not asking anymore
4258           addonMgr.UpdateDisabledReason(addon->ID(), ADDON::AddonDisabledReason::USER);
4259         }
4260       }
4261     }
4262   }
4263 }
4264 
Process()4265 void CApplication::Process()
4266 {
4267   // dispatch the messages generated by python or other threads to the current window
4268   CServiceBroker::GetGUI()->GetWindowManager().DispatchThreadMessages();
4269 
4270   // process messages which have to be send to the gui
4271   // (this can only be done after CServiceBroker::GetGUI()->GetWindowManager().Render())
4272   CApplicationMessenger::GetInstance().ProcessWindowMessages();
4273 
4274   // handle any active scripts
4275 
4276   {
4277     // Allow processing of script threads to let them shut down properly.
4278     CSingleExit ex(CServiceBroker::GetWinSystem()->GetGfxContext());
4279     m_frameMoveGuard.unlock();
4280     CScriptInvocationManager::GetInstance().Process();
4281     m_frameMoveGuard.lock();
4282   }
4283 
4284   // process messages, even if a movie is playing
4285   CApplicationMessenger::GetInstance().ProcessMessages();
4286   if (m_bStop) return; //we're done, everything has been unloaded
4287 
4288   // update sound
4289   m_appPlayer.DoAudioWork();
4290 
4291   // do any processing that isn't needed on each run
4292   if( m_slowTimer.GetElapsedMilliseconds() > 500 )
4293   {
4294     m_slowTimer.Reset();
4295     ProcessSlow();
4296   }
4297 }
4298 
4299 // We get called every 500ms
ProcessSlow()4300 void CApplication::ProcessSlow()
4301 {
4302   CServiceBroker::GetPowerManager().ProcessEvents();
4303 
4304 #if defined(TARGET_DARWIN_OSX)
4305   // There is an issue on OS X that several system services ask the cursor to become visible
4306   // during their startup routines.  Given that we can't control this, we hack it in by
4307   // forcing the
4308   if (CServiceBroker::GetWinSystem()->IsFullScreen())
4309   { // SDL thinks it's hidden
4310     Cocoa_HideMouse();
4311   }
4312 #endif
4313 
4314   // Temporarily pause pausable jobs when viewing video/picture
4315   int currentWindow = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
4316   if (CurrentFileItem().IsVideo() ||
4317       CurrentFileItem().IsPicture() ||
4318       currentWindow == WINDOW_FULLSCREEN_VIDEO ||
4319       currentWindow == WINDOW_FULLSCREEN_GAME ||
4320       currentWindow == WINDOW_SLIDESHOW)
4321   {
4322     CJobManager::GetInstance().PauseJobs();
4323   }
4324   else
4325   {
4326     CJobManager::GetInstance().UnPauseJobs();
4327   }
4328 
4329   // Check if we need to activate the screensaver / DPMS.
4330   CheckScreenSaverAndDPMS();
4331 
4332   // Check if we need to shutdown (if enabled).
4333 #if defined(TARGET_DARWIN)
4334   if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME) &&
4335       CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
4336 #else
4337   if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME))
4338 #endif
4339   {
4340     CheckShutdown();
4341   }
4342 
4343 #if defined(TARGET_POSIX)
4344   if (CPlatformPosix::TestQuitFlag())
4345   {
4346     CLog::Log(LOGINFO, "Quitting due to POSIX signal");
4347     CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT);
4348   }
4349 #endif
4350 
4351   // check if we should restart the player
4352   CheckDelayedPlayerRestart();
4353 
4354   //  check if we can unload any unreferenced dlls or sections
4355   if (!m_appPlayer.IsPlayingVideo())
4356     CSectionLoader::UnloadDelayed();
4357 
4358 #ifdef TARGET_ANDROID
4359   // Pass the slow loop to droid
4360   CXBMCApp::get()->ProcessSlow();
4361 #endif
4362 
4363   // check for any idle curl connections
4364   g_curlInterface.CheckIdle();
4365 
4366   CServiceBroker::GetGUI()->GetLargeTextureManager().CleanupUnusedImages();
4367 
4368   CServiceBroker::GetGUI()->GetTextureManager().FreeUnusedTextures(5000);
4369 
4370 #ifdef HAS_DVD_DRIVE
4371   // checks whats in the DVD drive and tries to autostart the content (xbox games, dvd, cdda, avi files...)
4372   if (!m_appPlayer.IsPlayingVideo())
4373     m_Autorun->HandleAutorun();
4374 #endif
4375 
4376   // update upnp server/renderer states
4377 #ifdef HAS_UPNP
4378   if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP) && UPNP::CUPnP::IsInstantiated())
4379     UPNP::CUPnP::GetInstance()->UpdateState();
4380 #endif
4381 
4382 #if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB)
4383   smb.CheckIfIdle();
4384 #endif
4385 
4386 #ifdef HAS_FILESYSTEM_NFS
4387   gNfsConnection.CheckIfIdle();
4388 #endif
4389 
4390   for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
4391     vfsAddon->ClearOutIdle();
4392 
4393   CServiceBroker::GetMediaManager().ProcessEvents();
4394 
4395   // if we don't render the gui there's no reason to start the screensaver.
4396   // that way the screensaver won't kick in if we maximize the XBMC window
4397   // after the screensaver start time.
4398   if(!m_renderGUI)
4399     ResetScreenSaverTimer();
4400 }
4401 
4402 // Global Idle Time in Seconds
4403 // idle time will be reset if on any OnKey()
4404 // int return: system Idle time in seconds! 0 is no idle!
GlobalIdleTime()4405 int CApplication::GlobalIdleTime()
4406 {
4407   if(!m_idleTimer.IsRunning())
4408     m_idleTimer.StartZero();
4409   return (int)m_idleTimer.GetElapsedSeconds();
4410 }
4411 
NavigationIdleTime()4412 float CApplication::NavigationIdleTime()
4413 {
4414   if (!m_navigationTimer.IsRunning())
4415     m_navigationTimer.StartZero();
4416   return m_navigationTimer.GetElapsedSeconds();
4417 }
4418 
DelayedPlayerRestart()4419 void CApplication::DelayedPlayerRestart()
4420 {
4421   m_restartPlayerTimer.StartZero();
4422 }
4423 
CheckDelayedPlayerRestart()4424 void CApplication::CheckDelayedPlayerRestart()
4425 {
4426   if (m_restartPlayerTimer.GetElapsedSeconds() > 3)
4427   {
4428     m_restartPlayerTimer.Stop();
4429     m_restartPlayerTimer.Reset();
4430     Restart(true);
4431   }
4432 }
4433 
Restart(bool bSamePosition)4434 void CApplication::Restart(bool bSamePosition)
4435 {
4436   // this function gets called when the user changes a setting (like noninterleaved)
4437   // and which means we gotta close & reopen the current playing file
4438 
4439   // first check if we're playing a file
4440   if (!m_appPlayer.IsPlayingVideo() && !m_appPlayer.IsPlayingAudio())
4441     return ;
4442 
4443   if (!m_appPlayer.HasPlayer())
4444     return ;
4445 
4446   // do we want to return to the current position in the file
4447   if (!bSamePosition)
4448   {
4449     // no, then just reopen the file and start at the beginning
4450     PlayFile(*m_itemCurrentFile, "", true);
4451     return ;
4452   }
4453 
4454   // else get current position
4455   double time = GetTime();
4456 
4457   // get player state, needed for dvd's
4458   std::string state = m_appPlayer.GetPlayerState();
4459 
4460   // set the requested starttime
4461   m_itemCurrentFile->m_lStartOffset = CUtil::ConvertSecsToMilliSecs(time);
4462 
4463   // reopen the file
4464   if (PlayFile(*m_itemCurrentFile, "", true))
4465     m_appPlayer.SetPlayerState(state);
4466 }
4467 
CurrentFile()4468 const std::string& CApplication::CurrentFile()
4469 {
4470   return m_itemCurrentFile->GetPath();
4471 }
4472 
CurrentFileItemPtr()4473 std::shared_ptr<CFileItem> CApplication::CurrentFileItemPtr()
4474 {
4475   return m_itemCurrentFile;
4476 }
4477 
CurrentFileItem()4478 CFileItem& CApplication::CurrentFileItem()
4479 {
4480   return *m_itemCurrentFile;
4481 }
4482 
CurrentUnstackedItem()4483 CFileItem& CApplication::CurrentUnstackedItem()
4484 {
4485   if (m_stackHelper.IsPlayingISOStack() || m_stackHelper.IsPlayingRegularStack())
4486     return m_stackHelper.GetCurrentStackPartFileItem();
4487   else
4488     return *m_itemCurrentFile;
4489 }
4490 
ShowVolumeBar(const CAction * action)4491 void CApplication::ShowVolumeBar(const CAction *action)
4492 {
4493   CGUIDialogVolumeBar *volumeBar = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVolumeBar>(WINDOW_DIALOG_VOLUME_BAR);
4494   if (volumeBar != nullptr && volumeBar->IsVolumeBarEnabled())
4495   {
4496     volumeBar->Open();
4497     if (action)
4498       volumeBar->OnAction(*action);
4499   }
4500 }
4501 
IsMuted() const4502 bool CApplication::IsMuted() const
4503 {
4504   if (CServiceBroker::GetPeripherals().IsMuted())
4505     return true;
4506   IAE* ae = CServiceBroker::GetActiveAE();
4507   if (ae)
4508     return ae->IsMuted();
4509   return true;
4510 }
4511 
ToggleMute(void)4512 void CApplication::ToggleMute(void)
4513 {
4514   if (m_muted)
4515     UnMute();
4516   else
4517     Mute();
4518 }
4519 
SetMute(bool mute)4520 void CApplication::SetMute(bool mute)
4521 {
4522   if (m_muted != mute)
4523   {
4524     ToggleMute();
4525     m_muted = mute;
4526   }
4527 }
4528 
Mute()4529 void CApplication::Mute()
4530 {
4531   if (CServiceBroker::GetPeripherals().Mute())
4532     return;
4533 
4534   IAE* ae = CServiceBroker::GetActiveAE();
4535   if (ae)
4536     ae->SetMute(true);
4537   m_muted = true;
4538   VolumeChanged();
4539 }
4540 
UnMute()4541 void CApplication::UnMute()
4542 {
4543   if (CServiceBroker::GetPeripherals().UnMute())
4544     return;
4545 
4546   IAE* ae = CServiceBroker::GetActiveAE();
4547   if (ae)
4548     ae->SetMute(false);
4549   m_muted = false;
4550   VolumeChanged();
4551 }
4552 
SetVolume(float iValue,bool isPercentage)4553 void CApplication::SetVolume(float iValue, bool isPercentage/*=true*/)
4554 {
4555   float hardwareVolume = iValue;
4556 
4557   if(isPercentage)
4558     hardwareVolume /= 100.0f;
4559 
4560   SetHardwareVolume(hardwareVolume);
4561   VolumeChanged();
4562 }
4563 
SetHardwareVolume(float hardwareVolume)4564 void CApplication::SetHardwareVolume(float hardwareVolume)
4565 {
4566   hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, hardwareVolume));
4567   m_volumeLevel = hardwareVolume;
4568 
4569   IAE* ae = CServiceBroker::GetActiveAE();
4570   if (ae)
4571     ae->SetVolume(hardwareVolume);
4572 }
4573 
GetVolumePercent() const4574 float CApplication::GetVolumePercent() const
4575 {
4576   // converts the hardware volume to a percentage
4577   return m_volumeLevel * 100.0f;
4578 }
4579 
GetVolumeRatio() const4580 float CApplication::GetVolumeRatio() const
4581 {
4582   return m_volumeLevel;
4583 }
4584 
VolumeChanged()4585 void CApplication::VolumeChanged()
4586 {
4587   CVariant data(CVariant::VariantTypeObject);
4588   data["volume"] = static_cast<int>(std::lroundf(GetVolumePercent()));
4589   data["muted"] = m_muted;
4590   CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Application, "OnVolumeChanged",
4591                                                      data);
4592 
4593   // if player has volume control, set it.
4594   m_appPlayer.SetVolume(m_volumeLevel);
4595   m_appPlayer.SetMute(m_muted);
4596 }
4597 
GetSubtitleDelay()4598 int CApplication::GetSubtitleDelay()
4599 {
4600   // converts subtitle delay to a percentage
4601   return int(((m_appPlayer.GetVideoSettings().m_SubtitleDelay + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange)) / (2 * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoSubsDelayRange)*100.0f + 0.5f);
4602 }
4603 
GetAudioDelay()4604 int CApplication::GetAudioDelay()
4605 {
4606   // converts audio delay to a percentage
4607   return int(((m_appPlayer.GetVideoSettings().m_AudioDelay + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAudioDelayRange)) / (2 * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAudioDelayRange)*100.0f + 0.5f);
4608 }
4609 
4610 // Returns the total time in seconds of the current media.  Fractional
4611 // portions of a second are possible - but not necessarily supported by the
4612 // player class.  This returns a double to be consistent with GetTime() and
4613 // SeekTime().
GetTotalTime() const4614 double CApplication::GetTotalTime() const
4615 {
4616   double rc = 0.0;
4617 
4618   if (m_appPlayer.IsPlaying())
4619   {
4620     if (m_stackHelper.IsPlayingRegularStack())
4621       rc = m_stackHelper.GetStackTotalTimeMs() * 0.001f;
4622     else
4623       rc = static_cast<double>(m_appPlayer.GetTotalTime() * 0.001f);
4624   }
4625 
4626   return rc;
4627 }
4628 
StopShutdownTimer()4629 void CApplication::StopShutdownTimer()
4630 {
4631   m_shutdownTimer.Stop();
4632 }
4633 
ResetShutdownTimers()4634 void CApplication::ResetShutdownTimers()
4635 {
4636   // reset system shutdown timer
4637   m_shutdownTimer.StartZero();
4638 
4639   // delete custom shutdown timer
4640   if (g_alarmClock.HasAlarm("shutdowntimer"))
4641     g_alarmClock.Stop("shutdowntimer", true);
4642 }
4643 
4644 // Returns the current time in seconds of the currently playing media.
4645 // Fractional portions of a second are possible.  This returns a double to
4646 // be consistent with GetTotalTime() and SeekTime().
GetTime() const4647 double CApplication::GetTime() const
4648 {
4649   double rc = 0.0;
4650 
4651   if (m_appPlayer.IsPlaying())
4652   {
4653     if (m_stackHelper.IsPlayingRegularStack())
4654     {
4655       uint64_t startOfCurrentFile = m_stackHelper.GetCurrentStackPartStartTimeMs();
4656       rc = (startOfCurrentFile + m_appPlayer.GetTime()) * 0.001f;
4657     }
4658     else
4659       rc = static_cast<double>(m_appPlayer.GetTime() * 0.001f);
4660   }
4661 
4662   return rc;
4663 }
4664 
4665 // Sets the current position of the currently playing media to the specified
4666 // time in seconds.  Fractional portions of a second are valid.  The passed
4667 // time is the time offset from the beginning of the file as opposed to a
4668 // delta from the current position.  This method accepts a double to be
4669 // consistent with GetTime() and GetTotalTime().
SeekTime(double dTime)4670 void CApplication::SeekTime( double dTime )
4671 {
4672   if (m_appPlayer.IsPlaying() && (dTime >= 0.0))
4673   {
4674     if (!m_appPlayer.CanSeek())
4675       return;
4676     if (m_stackHelper.IsPlayingRegularStack())
4677     {
4678       // find the item in the stack we are seeking to, and load the new
4679       // file if necessary, and calculate the correct seek within the new
4680       // file.  Otherwise, just fall through to the usual routine if the
4681       // time is higher than our total time.
4682       int partNumberToPlay = m_stackHelper.GetStackPartNumberAtTimeMs(static_cast<uint64_t>(dTime * 1000.0));
4683       uint64_t startOfNewFile = m_stackHelper.GetStackPartStartTimeMs(partNumberToPlay);
4684       if (partNumberToPlay == m_stackHelper.GetCurrentPartNumber())
4685         m_appPlayer.SeekTime(static_cast<uint64_t>(dTime * 1000.0) - startOfNewFile);
4686       else
4687       { // seeking to a new file
4688         m_stackHelper.SetStackPartCurrentFileItem(partNumberToPlay);
4689         CFileItem *item = new CFileItem(m_stackHelper.GetCurrentStackPartFileItem());
4690         item->m_lStartOffset = static_cast<uint64_t>(dTime * 1000.0) - startOfNewFile;
4691         // don't just call "PlayFile" here, as we are quite likely called from the
4692         // player thread, so we won't be able to delete ourselves.
4693         CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_PLAY, 1, 0, static_cast<void*>(item));
4694       }
4695       return;
4696     }
4697     // convert to milliseconds and perform seek
4698     m_appPlayer.SeekTime( static_cast<int64_t>( dTime * 1000.0 ) );
4699   }
4700 }
4701 
GetPercentage() const4702 float CApplication::GetPercentage() const
4703 {
4704   if (m_appPlayer.IsPlaying())
4705   {
4706     if (m_appPlayer.GetTotalTime() == 0 && m_appPlayer.IsPlayingAudio() && m_itemCurrentFile->HasMusicInfoTag())
4707     {
4708       const CMusicInfoTag& tag = *m_itemCurrentFile->GetMusicInfoTag();
4709       if (tag.GetDuration() > 0)
4710         return (float)(GetTime() / tag.GetDuration() * 100);
4711     }
4712 
4713     if (m_stackHelper.IsPlayingRegularStack())
4714     {
4715       double totalTime = GetTotalTime();
4716       if (totalTime > 0.0f)
4717         return (float)(GetTime() / totalTime * 100);
4718     }
4719     else
4720       return m_appPlayer.GetPercentage();
4721   }
4722   return 0.0f;
4723 }
4724 
GetCachePercentage() const4725 float CApplication::GetCachePercentage() const
4726 {
4727   if (m_appPlayer.IsPlaying())
4728   {
4729     // Note that the player returns a relative cache percentage and we want an absolute percentage
4730     if (m_stackHelper.IsPlayingRegularStack())
4731     {
4732       float stackedTotalTime = (float) GetTotalTime();
4733       // We need to take into account the stack's total time vs. currently playing file's total time
4734       if (stackedTotalTime > 0.0f)
4735         return std::min( 100.0f, GetPercentage() + (m_appPlayer.GetCachePercentage() * m_appPlayer.GetTotalTime() * 0.001f / stackedTotalTime ) );
4736     }
4737     else
4738       return std::min( 100.0f, m_appPlayer.GetPercentage() + m_appPlayer.GetCachePercentage() );
4739   }
4740   return 0.0f;
4741 }
4742 
SeekPercentage(float percent)4743 void CApplication::SeekPercentage(float percent)
4744 {
4745   if (m_appPlayer.IsPlaying() && (percent >= 0.0))
4746   {
4747     if (!m_appPlayer.CanSeek())
4748       return;
4749     if (m_stackHelper.IsPlayingRegularStack())
4750       SeekTime(percent * 0.01 * GetTotalTime());
4751     else
4752       m_appPlayer.SeekPercentage(percent);
4753   }
4754 }
4755 
4756 // SwitchToFullScreen() returns true if a switch is made, else returns false
SwitchToFullScreen(bool force)4757 bool CApplication::SwitchToFullScreen(bool force /* = false */)
4758 {
4759   // don't switch if the slideshow is active
4760   if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_SLIDESHOW))
4761     return false;
4762 
4763   // if playing from the video info window, close it first!
4764   if (CServiceBroker::GetGUI()->GetWindowManager().IsModalDialogTopmost(WINDOW_DIALOG_VIDEO_INFO))
4765   {
4766     CGUIDialogVideoInfo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVideoInfo>(WINDOW_DIALOG_VIDEO_INFO);
4767     if (pDialog) pDialog->Close(true);
4768   }
4769 
4770   // if playing from the album info window, close it first!
4771   if (CServiceBroker::GetGUI()->GetWindowManager().IsModalDialogTopmost(WINDOW_DIALOG_MUSIC_INFO))
4772   {
4773     CGUIDialogVideoInfo* pDialog = CServiceBroker::GetGUI()->
4774         GetWindowManager().GetWindow<CGUIDialogVideoInfo>(WINDOW_DIALOG_MUSIC_INFO);
4775     if (pDialog)
4776       pDialog->Close(true);
4777   }
4778 
4779   // if playing from the song info window, close it first!
4780   if (CServiceBroker::GetGUI()->GetWindowManager().IsModalDialogTopmost(WINDOW_DIALOG_SONG_INFO))
4781   {
4782     CGUIDialogVideoInfo* pDialog = CServiceBroker::GetGUI()->
4783         GetWindowManager().GetWindow<CGUIDialogVideoInfo>(WINDOW_DIALOG_SONG_INFO);
4784     if (pDialog)
4785       pDialog->Close(true);
4786   }
4787 
4788   const int activeWindowID = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
4789   int windowID = WINDOW_INVALID;
4790 
4791   // See if we're playing a game
4792   if (activeWindowID != WINDOW_FULLSCREEN_GAME && m_appPlayer.IsPlayingGame())
4793     windowID = WINDOW_FULLSCREEN_GAME;
4794 
4795   // See if we're playing a video
4796   else if (activeWindowID != WINDOW_FULLSCREEN_VIDEO && m_appPlayer.IsPlayingVideo())
4797     windowID = WINDOW_FULLSCREEN_VIDEO;
4798 
4799   // See if we're playing an audio song
4800   if (activeWindowID != WINDOW_VISUALISATION && m_appPlayer.IsPlayingAudio())
4801     windowID = WINDOW_VISUALISATION;
4802 
4803   if (windowID != WINDOW_INVALID && (force || windowID != activeWindowID))
4804   {
4805     if (force)
4806       CServiceBroker::GetGUI()->GetWindowManager().ForceActivateWindow(windowID);
4807     else
4808       CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(windowID);
4809 
4810     return true;
4811   }
4812 
4813   return false;
4814 }
4815 
Minimize()4816 void CApplication::Minimize()
4817 {
4818   CServiceBroker::GetWinSystem()->Minimize();
4819 }
4820 
GetCurrentPlayer()4821 std::string CApplication::GetCurrentPlayer()
4822 {
4823   return m_appPlayer.GetCurrentPlayer();
4824 }
4825 
GetAppPlayer()4826 CApplicationPlayer& CApplication::GetAppPlayer()
4827 {
4828   return m_appPlayer;
4829 }
4830 
GetAppStackHelper()4831 CApplicationStackHelper& CApplication::GetAppStackHelper()
4832 {
4833   return m_stackHelper;
4834 }
4835 
UpdateLibraries()4836 void CApplication::UpdateLibraries()
4837 {
4838   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
4839   if (settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_UPDATEONSTARTUP))
4840   {
4841     CLog::LogF(LOGINFO, "Starting video library startup scan");
4842     StartVideoScan("", !settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE));
4843   }
4844 
4845   if (settings->GetBool(CSettings::SETTING_MUSICLIBRARY_UPDATEONSTARTUP))
4846   {
4847     CLog::LogF(LOGINFO, "Starting music library startup scan");
4848     StartMusicScan("", !settings->GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE));
4849   }
4850 }
4851 
UpdateCurrentPlayArt()4852 void CApplication::UpdateCurrentPlayArt()
4853 {
4854   if (!m_appPlayer.IsPlayingAudio())
4855     return;
4856   //Clear and reload the art for the currenty playing item to show updated  art on OSD
4857   m_itemCurrentFile->ClearArt();
4858   CMusicThumbLoader loader;
4859   loader.LoadItem(m_itemCurrentFile.get());
4860   // Mirror changes to GUI item
4861   CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile);
4862 }
4863 
IsVideoScanning() const4864 bool CApplication::IsVideoScanning() const
4865 {
4866   return CVideoLibraryQueue::GetInstance().IsScanningLibrary();
4867 }
4868 
IsMusicScanning() const4869 bool CApplication::IsMusicScanning() const
4870 {
4871   return CMusicLibraryQueue::GetInstance().IsScanningLibrary();
4872 }
4873 
StopVideoScan()4874 void CApplication::StopVideoScan()
4875 {
4876   CVideoLibraryQueue::GetInstance().StopLibraryScanning();
4877 }
4878 
StopMusicScan()4879 void CApplication::StopMusicScan()
4880 {
4881   CMusicLibraryQueue::GetInstance().StopLibraryScanning();
4882 }
4883 
StartVideoCleanup(bool userInitiated,const std::string & content,const std::string & strDirectory)4884 void CApplication::StartVideoCleanup(bool userInitiated /* = true */,
4885                                      const std::string& content /* = "" */,
4886                                      const std::string& strDirectory /* = "" */)
4887 {
4888   if (userInitiated && CVideoLibraryQueue::GetInstance().IsRunning())
4889     return;
4890 
4891   std::set<int> paths;
4892   if (!content.empty() || !strDirectory.empty())
4893   {
4894     CVideoDatabase db;
4895     std::set<std::string> contentPaths;
4896     if (db.Open())
4897     {
4898       if (!strDirectory.empty())
4899         contentPaths.insert(strDirectory);
4900       else
4901         db.GetPaths(contentPaths);
4902       for (const std::string& path : contentPaths)
4903       {
4904         if (db.GetContentForPath(path) == content)
4905         {
4906           paths.insert(db.GetPathId(path));
4907           std::vector<std::pair<int, std::string>> sub;
4908           if (db.GetSubPaths(path, sub))
4909           {
4910             for (const auto& it : sub)
4911               paths.insert(it.first);
4912           }
4913         }
4914       }
4915     }
4916     if (paths.empty())
4917       return;
4918   }
4919   if (userInitiated)
4920     CVideoLibraryQueue::GetInstance().CleanLibraryModal(paths);
4921   else
4922     CVideoLibraryQueue::GetInstance().CleanLibrary(paths, true);
4923 }
4924 
StartVideoScan(const std::string & strDirectory,bool userInitiated,bool scanAll)4925 void CApplication::StartVideoScan(const std::string &strDirectory, bool userInitiated /* = true */, bool scanAll /* = false */)
4926 {
4927   CVideoLibraryQueue::GetInstance().ScanLibrary(strDirectory, scanAll, userInitiated);
4928 }
4929 
StartMusicCleanup(bool userInitiated)4930 void CApplication::StartMusicCleanup(bool userInitiated /* = true */)
4931 {
4932   if (userInitiated && CMusicLibraryQueue::GetInstance().IsRunning())
4933     return;
4934 
4935   if (userInitiated)
4936     /*
4937      CMusicLibraryQueue::GetInstance().CleanLibraryModal();
4938      As cleaning is non-granular and does not offer many opportunities to update progress
4939      dialog rendering, do asynchronously with model dialog
4940     */
4941     CMusicLibraryQueue::GetInstance().CleanLibrary(true);
4942   else
4943     CMusicLibraryQueue::GetInstance().CleanLibrary(false);
4944 }
4945 
StartMusicScan(const std::string & strDirectory,bool userInitiated,int flags)4946 void CApplication::StartMusicScan(const std::string &strDirectory, bool userInitiated /* = true */, int flags /* = 0 */)
4947 {
4948   if (IsMusicScanning())
4949     return;
4950 
4951   // Setup default flags
4952   if (!flags)
4953   { // Online scraping of additional info during scanning
4954     if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
4955       flags |= CMusicInfoScanner::SCAN_ONLINE;
4956   }
4957   if (!userInitiated || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE))
4958     flags |= CMusicInfoScanner::SCAN_BACKGROUND;
4959 
4960   CMusicLibraryQueue::GetInstance().ScanLibrary(strDirectory, flags, !(flags & CMusicInfoScanner::SCAN_BACKGROUND));
4961 }
4962 
StartMusicAlbumScan(const std::string & strDirectory,bool refresh)4963 void CApplication::StartMusicAlbumScan(const std::string& strDirectory, bool refresh)
4964 {
4965   if (IsMusicScanning())
4966     return;
4967 
4968   CMusicLibraryQueue::GetInstance().StartAlbumScan(strDirectory, refresh);
4969 }
4970 
StartMusicArtistScan(const std::string & strDirectory,bool refresh)4971 void CApplication::StartMusicArtistScan(const std::string& strDirectory,
4972                                         bool refresh)
4973 {
4974   if (IsMusicScanning())
4975     return;
4976 
4977   CMusicLibraryQueue::GetInstance().StartArtistScan(strDirectory, refresh);
4978 }
4979 
ProcessAndStartPlaylist(const std::string & strPlayList,CPlayList & playlist,int iPlaylist,int track)4980 bool CApplication::ProcessAndStartPlaylist(const std::string& strPlayList, CPlayList& playlist, int iPlaylist, int track)
4981 {
4982   CLog::Log(LOGDEBUG,"CApplication::ProcessAndStartPlaylist(%s, %i)",strPlayList.c_str(), iPlaylist);
4983 
4984   // initial exit conditions
4985   // no songs in playlist just return
4986   if (playlist.size() == 0)
4987     return false;
4988 
4989   // illegal playlist
4990   if (iPlaylist < PLAYLIST_MUSIC || iPlaylist > PLAYLIST_VIDEO)
4991     return false;
4992 
4993   // setup correct playlist
4994   CServiceBroker::GetPlaylistPlayer().ClearPlaylist(iPlaylist);
4995 
4996   // if the playlist contains an internet stream, this file will be used
4997   // to generate a thumbnail for musicplayer.cover
4998   m_strPlayListFile = strPlayList;
4999 
5000   // add the items to the playlist player
5001   CServiceBroker::GetPlaylistPlayer().Add(iPlaylist, playlist);
5002 
5003   // if we have a playlist
5004   if (CServiceBroker::GetPlaylistPlayer().GetPlaylist(iPlaylist).size())
5005   {
5006     // start playing it
5007     CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlaylist);
5008     CServiceBroker::GetPlaylistPlayer().Reset();
5009     CServiceBroker::GetPlaylistPlayer().Play(track, "");
5010     return true;
5011   }
5012   return false;
5013 }
5014 
IsCurrentThread() const5015 bool CApplication::IsCurrentThread() const
5016 {
5017   return m_threadID == CThread::GetCurrentThreadId();
5018 }
5019 
SetRenderGUI(bool renderGUI)5020 void CApplication::SetRenderGUI(bool renderGUI)
5021 {
5022   if (renderGUI && ! m_renderGUI)
5023   {
5024     CGUIComponent *gui = CServiceBroker::GetGUI();
5025     if (gui)
5026       CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
5027   }
5028   m_renderGUI = renderGUI;
5029 }
5030 
SetLanguage(const std::string & strLanguage)5031 bool CApplication::SetLanguage(const std::string &strLanguage)
5032 {
5033   // nothing to be done if the language hasn't changed
5034   if (strLanguage == CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE))
5035     return true;
5036 
5037   return CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_LOCALE_LANGUAGE, strLanguage);
5038 }
5039 
LoadLanguage(bool reload)5040 bool CApplication::LoadLanguage(bool reload)
5041 {
5042   // load the configured langauge
5043   if (!g_langInfo.SetLanguage("", reload))
5044     return false;
5045 
5046   // set the proper audio and subtitle languages
5047   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
5048   g_langInfo.SetAudioLanguage(settings->GetString(CSettings::SETTING_LOCALE_AUDIOLANGUAGE));
5049   g_langInfo.SetSubtitleLanguage(settings->GetString(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE));
5050 
5051   return true;
5052 }
5053 
SetLoggingIn(bool switchingProfiles)5054 void CApplication::SetLoggingIn(bool switchingProfiles)
5055 {
5056   // don't save skin settings on unloading when logging into another profile
5057   // because in that case we have already loaded the new profile and
5058   // would therefore write the previous skin's settings into the new profile
5059   // instead of into the previous one
5060   m_saveSkinOnUnloading = !switchingProfiles;
5061 }
5062 
CloseNetworkShares()5063 void CApplication::CloseNetworkShares()
5064 {
5065   CLog::Log(LOGDEBUG,"CApplication::CloseNetworkShares: Closing all network shares");
5066 
5067 #if defined(HAS_FILESYSTEM_SMB) && !defined(TARGET_WINDOWS)
5068   smb.Deinit();
5069 #endif
5070 
5071 #ifdef HAS_FILESYSTEM_NFS
5072   gNfsConnection.Deinit();
5073 #endif
5074 
5075   for (const auto& vfsAddon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
5076     vfsAddon->DisconnectAll();
5077 }
5078 
RegisterActionListener(IActionListener * listener)5079 void CApplication::RegisterActionListener(IActionListener *listener)
5080 {
5081   CSingleLock lock(m_critSection);
5082   std::vector<IActionListener *>::iterator it = std::find(m_actionListeners.begin(), m_actionListeners.end(), listener);
5083   if (it == m_actionListeners.end())
5084     m_actionListeners.push_back(listener);
5085 }
5086 
UnregisterActionListener(IActionListener * listener)5087 void CApplication::UnregisterActionListener(IActionListener *listener)
5088 {
5089   CSingleLock lock(m_critSection);
5090   std::vector<IActionListener *>::iterator it = std::find(m_actionListeners.begin(), m_actionListeners.end(), listener);
5091   if (it != m_actionListeners.end())
5092     m_actionListeners.erase(it);
5093 }
5094 
NotifyActionListeners(const CAction & action) const5095 bool CApplication::NotifyActionListeners(const CAction &action) const
5096 {
5097   CSingleLock lock(m_critSection);
5098   for (std::vector<IActionListener *>::const_iterator it = m_actionListeners.begin(); it != m_actionListeners.end(); ++it)
5099   {
5100     if ((*it)->OnAction(action))
5101       return true;
5102   }
5103 
5104   return false;
5105 }
5106