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 ¶ms)
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