Merge pull request #26350 from jjd-uk/estuary_media_align
[xbmc.git] / xbmc / application / ApplicationPowerHandling.cpp
blob3b9c2e080aa5e52cd4e25c18a6f65151ee7dbd5d
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "ApplicationPowerHandling.h"
11 #include "GUIUserMessages.h"
12 #include "ServiceBroker.h"
13 #include "addons/Addon.h"
14 #include "addons/AddonManager.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "addons/gui/GUIDialogAddonSettings.h"
17 #include "application/ApplicationComponents.h"
18 #include "application/ApplicationPlayer.h"
19 #include "guilib/GUIComponent.h"
20 #include "guilib/GUIMessage.h"
21 #include "guilib/GUIWindowManager.h"
22 #include "input/actions/Action.h"
23 #include "input/actions/ActionIDs.h"
24 #include "interfaces/AnnouncementManager.h"
25 #include "interfaces/generic/ScriptInvocationManager.h"
26 #include "messaging/ApplicationMessenger.h"
27 #include "music/MusicLibraryQueue.h"
28 #include "powermanagement/DPMSSupport.h"
29 #include "powermanagement/PowerTypes.h"
30 #include "profiles/ProfileManager.h"
31 #include "pvr/PVRManager.h"
32 #include "pvr/guilib/PVRGUIActionsChannels.h"
33 #include "pvr/guilib/PVRGUIActionsPowerManagement.h"
34 #include "settings/Settings.h"
35 #include "settings/SettingsComponent.h"
36 #include "utils/AlarmClock.h"
37 #include "utils/log.h"
38 #include "video/VideoLibraryQueue.h"
39 #include "windowing/WinSystem.h"
41 void CApplicationPowerHandling::ResetScreenSaver()
43 // reset our timers
44 m_shutdownTimer.StartZero();
46 // screen saver timer is reset only if we're not already in screensaver or
47 // DPMS mode
48 if ((!m_screensaverActive && m_iScreenSaveLock == 0) && !m_dpmsIsActive)
49 ResetScreenSaverTimer();
52 void CApplicationPowerHandling::ResetScreenSaverTimer()
54 m_screenSaverTimer.StartZero();
57 void CApplicationPowerHandling::ResetSystemIdleTimer()
59 // reset system idle timer
60 m_idleTimer.StartZero();
63 void CApplicationPowerHandling::ResetNavigationTimer()
65 m_navigationTimer.StartZero();
68 void CApplicationPowerHandling::SetRenderGUI(bool renderGUI)
70 m_renderGUI = renderGUI;
73 void CApplicationPowerHandling::StopScreenSaverTimer()
75 m_screenSaverTimer.Stop();
78 bool CApplicationPowerHandling::ToggleDPMS(bool manual)
80 auto winSystem = CServiceBroker::GetWinSystem();
81 if (!winSystem)
82 return false;
84 std::shared_ptr<CDPMSSupport> dpms = winSystem->GetDPMSManager();
85 if (!dpms)
86 return false;
88 if (manual || (m_dpmsIsManual == manual))
90 if (m_dpmsIsActive)
92 m_dpmsIsActive = false;
93 m_dpmsIsManual = false;
94 SetRenderGUI(true);
95 CheckOSScreenSaverInhibitionSetting();
96 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI, "OnDPMSDeactivated");
97 return dpms->DisablePowerSaving();
99 else
101 if (dpms->EnablePowerSaving(dpms->GetSupportedModes()[0]))
103 m_dpmsIsActive = true;
104 m_dpmsIsManual = manual;
105 SetRenderGUI(false);
106 CheckOSScreenSaverInhibitionSetting();
107 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI, "OnDPMSActivated");
108 return true;
112 return false;
115 bool CApplicationPowerHandling::WakeUpScreenSaverAndDPMS(bool bPowerOffKeyPressed /* = false */)
117 bool result = false;
119 // First reset DPMS, if active
120 if (m_dpmsIsActive)
122 if (m_dpmsIsManual)
123 return false;
124 //! @todo if screensaver lock is specified but screensaver is not active
125 //! (DPMS came first), activate screensaver now.
126 ToggleDPMS(false);
127 ResetScreenSaverTimer();
128 result = !m_screensaverActive || WakeUpScreenSaver(bPowerOffKeyPressed);
130 else if (m_screensaverActive)
131 result = WakeUpScreenSaver(bPowerOffKeyPressed);
133 if (result)
135 // allow listeners to ignore the deactivation if it precedes a powerdown/suspend etc
136 CVariant data(CVariant::VariantTypeObject);
137 data["shuttingdown"] = bPowerOffKeyPressed;
138 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI,
139 "OnScreensaverDeactivated", data);
142 return result;
145 bool CApplicationPowerHandling::WakeUpScreenSaver(bool bPowerOffKeyPressed /* = false */)
147 if (m_iScreenSaveLock == 2)
148 return false;
150 // if Screen saver is active
151 if (m_screensaverActive && !m_screensaverIdInUse.empty())
153 if (m_iScreenSaveLock == 0)
155 const std::shared_ptr<CProfileManager> profileManager =
156 CServiceBroker::GetSettingsComponent()->GetProfileManager();
157 if (profileManager->GetMasterProfile().getLockMode() != LockMode::EVERYONE &&
158 (profileManager->UsingLoginScreen() ||
159 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
160 CSettings::SETTING_MASTERLOCK_STARTUPLOCK)) &&
161 profileManager->GetCurrentProfile().getLockMode() != LockMode::EVERYONE &&
162 m_screensaverIdInUse != "screensaver.xbmc.builtin.dim" &&
163 m_screensaverIdInUse != "screensaver.xbmc.builtin.black" &&
164 m_screensaverIdInUse != "visualization")
166 m_iScreenSaveLock = 2;
167 CGUIMessage msg(GUI_MSG_CHECK_LOCK, 0, 0);
169 CGUIWindow* pWindow =
170 CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_SCREENSAVER);
171 if (pWindow)
172 pWindow->OnMessage(msg);
175 if (m_iScreenSaveLock == -1)
177 m_iScreenSaveLock = 0;
178 return true;
181 // disable screensaver
182 m_screensaverActive = false;
183 m_iScreenSaveLock = 0;
184 ResetScreenSaverTimer();
186 if (m_screensaverIdInUse == "visualization")
188 // we can just continue as usual from vis mode
189 return false;
191 else if (m_screensaverIdInUse == "screensaver.xbmc.builtin.dim" ||
192 m_screensaverIdInUse == "screensaver.xbmc.builtin.black" ||
193 m_screensaverIdInUse.empty())
195 return true;
197 else
198 { // we're in screensaver window
199 if (m_pythonScreenSaver)
201 // What sound does a python screensaver make?
202 #define SCRIPT_ALARM "sssssscreensaver"
203 #define SCRIPT_TIMEOUT 15 // seconds
205 /* FIXME: This is a hack but a proper fix is non-trivial. Basically this code
206 * makes sure the addon gets terminated after we've moved out of the screensaver window.
207 * If we don't do this, we may simply lockup.
209 g_alarmClock.Start(SCRIPT_ALARM, SCRIPT_TIMEOUT,
210 "StopScript(" + m_pythonScreenSaver->LibPath() + ")", true, false);
211 m_pythonScreenSaver.reset();
213 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SCREENSAVER)
214 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow(); // show the previous window
215 else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW)
216 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, WINDOW_SLIDESHOW, -1,
217 static_cast<void*>(new CAction(ACTION_STOP)));
219 return true;
221 else
222 return false;
225 void CApplicationPowerHandling::CheckOSScreenSaverInhibitionSetting()
227 // Kodi screen saver overrides OS one: always inhibit OS screen saver then
228 // except when DPMS is active (inhibiting the screen saver then might also
229 // disable DPMS again)
230 if (!m_dpmsIsActive &&
231 !CServiceBroker::GetSettingsComponent()
232 ->GetSettings()
233 ->GetString(CSettings::SETTING_SCREENSAVER_MODE)
234 .empty() &&
235 CServiceBroker::GetWinSystem()->GetOSScreenSaver())
237 if (!m_globalScreensaverInhibitor)
239 m_globalScreensaverInhibitor =
240 CServiceBroker::GetWinSystem()->GetOSScreenSaver()->CreateInhibitor();
243 else if (m_globalScreensaverInhibitor)
245 m_globalScreensaverInhibitor.Release();
249 void CApplicationPowerHandling::CheckScreenSaverAndDPMS()
251 bool maybeScreensaver = true;
252 if (m_dpmsIsActive)
253 maybeScreensaver = false;
254 else if (m_screensaverActive)
255 maybeScreensaver = false;
256 else if (CServiceBroker::GetSettingsComponent()
257 ->GetSettings()
258 ->GetString(CSettings::SETTING_SCREENSAVER_MODE)
259 .empty())
260 maybeScreensaver = false;
262 auto winSystem = CServiceBroker::GetWinSystem();
263 if (!winSystem)
264 return;
266 std::shared_ptr<CDPMSSupport> dpms = winSystem->GetDPMSManager();
268 bool maybeDPMS = true;
269 if (m_dpmsIsActive)
270 maybeDPMS = false;
271 else if (!dpms || !dpms->IsSupported())
272 maybeDPMS = false;
273 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
274 CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) <= 0)
275 maybeDPMS = false;
277 // whether the current state of the application should be regarded as active even when there is no
278 // explicit user activity such as input
279 bool haveIdleActivity = false;
281 if (m_bResetScreenSaver)
283 m_bResetScreenSaver = false;
284 haveIdleActivity = true;
287 // When inhibit screensaver is enabled prevent screensaver from kicking in
288 if (m_bInhibitScreenSaver)
289 haveIdleActivity = true;
291 // Are we playing a video and it is not paused?
292 const auto& components = CServiceBroker::GetAppComponents();
293 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
294 if (appPlayer && appPlayer->IsPlayingVideo() && !appPlayer->IsPaused())
295 haveIdleActivity = true;
297 // Are we playing audio and screensaver is disabled globally for audio?
298 else if (appPlayer && appPlayer->IsPlayingAudio() &&
299 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
300 CSettings::SETTING_SCREENSAVER_DISABLEFORAUDIO))
302 haveIdleActivity = true;
305 // Handle OS screen saver state
306 if (haveIdleActivity && CServiceBroker::GetWinSystem()->GetOSScreenSaver())
308 // Always inhibit OS screen saver during these kinds of activities
309 if (!m_screensaverInhibitor)
311 m_screensaverInhibitor =
312 CServiceBroker::GetWinSystem()->GetOSScreenSaver()->CreateInhibitor();
315 else if (m_screensaverInhibitor)
317 m_screensaverInhibitor.Release();
320 // Has the screen saver window become active?
321 if (maybeScreensaver &&
322 CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_SCREENSAVER))
324 m_screensaverActive = true;
325 maybeScreensaver = false;
328 if (m_screensaverActive && haveIdleActivity)
330 WakeUpScreenSaverAndDPMS();
331 return;
334 if (!maybeScreensaver && !maybeDPMS)
335 return; // Nothing to do.
337 // See if we need to reset timer.
338 if (haveIdleActivity)
340 ResetScreenSaverTimer();
341 return;
344 float elapsed = m_screenSaverTimer.IsRunning() ? m_screenSaverTimer.GetElapsedSeconds() : 0.f;
346 // DPMS has priority (it makes the screensaver not needed)
347 if (maybeDPMS && elapsed > CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
348 CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF) *
351 ToggleDPMS(false);
352 WakeUpScreenSaver();
354 else if (maybeScreensaver &&
355 elapsed > CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
356 CSettings::SETTING_SCREENSAVER_TIME) *
359 ActivateScreenSaver();
363 // activate the screensaver.
364 // if forceType is true, we ignore the various conditions that can alter
365 // the type of screensaver displayed
366 void CApplicationPowerHandling::ActivateScreenSaver(bool forceType /*= false */)
368 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
369 const auto& components = CServiceBroker::GetAppComponents();
370 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
372 m_screensaverActive = true;
373 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI, "OnScreensaverActivated");
375 // disable screensaver lock from the login screen
376 m_iScreenSaveLock =
377 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN ? 1 : 0;
379 m_screensaverIdInUse = settings->GetString(CSettings::SETTING_SCREENSAVER_MODE);
381 if (!forceType)
383 if (m_screensaverIdInUse == "screensaver.xbmc.builtin.dim" ||
384 m_screensaverIdInUse == "screensaver.xbmc.builtin.black" || m_screensaverIdInUse.empty())
386 return;
389 // Enforce Dim for special cases.
390 bool bUseDim = false;
391 if (appPlayer && appPlayer->IsPlayingVideo() &&
392 settings->GetBool(CSettings::SETTING_SCREENSAVER_USEDIMONPAUSE))
393 bUseDim = true;
394 else if (CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().IsRunningChannelScan())
395 bUseDim = true;
397 if (bUseDim)
398 m_screensaverIdInUse = "screensaver.xbmc.builtin.dim";
401 if (m_screensaverIdInUse == "screensaver.xbmc.builtin.dim" ||
402 m_screensaverIdInUse == "screensaver.xbmc.builtin.black" || m_screensaverIdInUse.empty())
404 return;
406 else if (CServiceBroker::GetAddonMgr().GetAddon(m_screensaverIdInUse, m_pythonScreenSaver,
407 ADDON::AddonType::SCREENSAVER,
408 ADDON::OnlyEnabled::CHOICE_YES))
410 std::string libPath = m_pythonScreenSaver->LibPath();
411 if (CScriptInvocationManager::GetInstance().HasLanguageInvoker(libPath))
413 CLog::Log(LOGDEBUG, "using python screensaver add-on {}", m_screensaverIdInUse);
415 // Don't allow a previously-scheduled alarm to kill our new screensaver
416 g_alarmClock.Stop(SCRIPT_ALARM, true);
418 if (!CScriptInvocationManager::GetInstance().Stop(libPath))
419 CScriptInvocationManager::GetInstance().ExecuteAsync(
420 libPath,
421 ADDON::AddonPtr(new ADDON::CAddon(dynamic_cast<ADDON::CAddon&>(*m_pythonScreenSaver))));
422 return;
424 m_pythonScreenSaver.reset();
427 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SCREENSAVER);
430 void CApplicationPowerHandling::InhibitScreenSaver(bool inhibit)
432 m_bInhibitScreenSaver = inhibit;
435 bool CApplicationPowerHandling::IsScreenSaverInhibited() const
437 return m_bInhibitScreenSaver;
440 // Global Idle Time in Seconds
441 // idle time will be reset if on any OnKey()
442 // int return: system Idle time in seconds! 0 is no idle!
443 int CApplicationPowerHandling::GlobalIdleTime()
445 if (!m_idleTimer.IsRunning())
446 m_idleTimer.StartZero();
447 return (int)m_idleTimer.GetElapsedSeconds();
450 float CApplicationPowerHandling::NavigationIdleTime()
452 if (!m_navigationTimer.IsRunning())
453 m_navigationTimer.StartZero();
454 return m_navigationTimer.GetElapsedSeconds();
457 void CApplicationPowerHandling::StopShutdownTimer()
459 m_shutdownTimer.Stop();
462 void CApplicationPowerHandling::ResetShutdownTimers()
464 // reset system shutdown timer
465 m_shutdownTimer.StartZero();
467 // delete custom shutdown timer
468 if (g_alarmClock.HasAlarm("shutdowntimer"))
469 g_alarmClock.Stop("shutdowntimer", true);
472 void CApplicationPowerHandling::HandleShutdownMessage()
474 switch (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
475 CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE))
477 case POWERSTATE_SHUTDOWN:
478 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_POWERDOWN);
479 break;
481 case POWERSTATE_SUSPEND:
482 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SUSPEND);
483 break;
485 case POWERSTATE_HIBERNATE:
486 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_HIBERNATE);
487 break;
489 case POWERSTATE_QUIT:
490 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
491 break;
493 case POWERSTATE_MINIMIZE:
494 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MINIMIZE);
495 break;
497 default:
498 CLog::Log(LOGERROR, "{}: No valid shutdownstate matched", __FUNCTION__);
499 break;
503 void CApplicationPowerHandling::CheckShutdown()
505 // first check if we should reset the timer
506 const auto& components = CServiceBroker::GetAppComponents();
507 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
508 if (!appPlayer)
509 return;
511 if (m_bInhibitIdleShutdown || appPlayer->IsPlaying() ||
512 appPlayer->IsPausedPlayback() // is something playing?
513 || CMusicLibraryQueue::GetInstance().IsRunning() ||
514 CVideoLibraryQueue::GetInstance().IsRunning() ||
515 CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(
516 WINDOW_DIALOG_PROGRESS) // progress dialog is onscreen
518 !CServiceBroker::GetPVRManager().Get<PVR::GUI::PowerManagement>().CanSystemPowerdown(false))
520 m_shutdownTimer.StartZero();
521 return;
524 float elapsed = m_shutdownTimer.IsRunning() ? m_shutdownTimer.GetElapsedSeconds() : 0.f;
525 if (elapsed > CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
526 CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME) *
529 // Since it is a sleep instead of a shutdown, let's set everything to reset when we wake up.
530 m_shutdownTimer.Stop();
532 // Sleep the box
533 CLog::LogF(LOGDEBUG, "Timer is over (shutdown function: {})",
534 CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
535 CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE));
536 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SHUTDOWN);
540 void CApplicationPowerHandling::InhibitIdleShutdown(bool inhibit)
542 m_bInhibitIdleShutdown = inhibit;
545 bool CApplicationPowerHandling::IsIdleShutdownInhibited() const
547 return m_bInhibitIdleShutdown;
550 bool CApplicationPowerHandling::OnSettingChanged(const CSetting& setting)
552 const std::string& settingId = setting.GetId();
554 if (settingId == CSettings::SETTING_SCREENSAVER_MODE)
556 CheckOSScreenSaverInhibitionSetting();
558 else
559 return false;
561 return true;
564 bool CApplicationPowerHandling::OnSettingAction(const CSetting& setting)
566 const std::string& settingId = setting.GetId();
568 if (settingId == CSettings::SETTING_SCREENSAVER_PREVIEW)
569 ActivateScreenSaver(true);
570 else if (settingId == CSettings::SETTING_SCREENSAVER_SETTINGS)
572 ADDON::AddonPtr addon;
573 if (CServiceBroker::GetAddonMgr().GetAddon(
574 CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
575 CSettings::SETTING_SCREENSAVER_MODE),
576 addon, ADDON::AddonType::SCREENSAVER, ADDON::OnlyEnabled::CHOICE_YES))
577 CGUIDialogAddonSettings::ShowForAddon(addon);
579 else
580 return false;
582 return true;