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.
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()
44 m_shutdownTimer
.StartZero();
46 // screen saver timer is reset only if we're not already in screensaver or
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 if (renderGUI
&& !m_renderGUI
)
72 CGUIComponent
* gui
= CServiceBroker::GetGUI();
74 CServiceBroker::GetGUI()->GetWindowManager().MarkDirty();
76 m_renderGUI
= renderGUI
;
79 void CApplicationPowerHandling::StopScreenSaverTimer()
81 m_screenSaverTimer
.Stop();
84 bool CApplicationPowerHandling::ToggleDPMS(bool manual
)
86 auto winSystem
= CServiceBroker::GetWinSystem();
90 std::shared_ptr
<CDPMSSupport
> dpms
= winSystem
->GetDPMSManager();
94 if (manual
|| (m_dpmsIsManual
== manual
))
98 m_dpmsIsActive
= false;
99 m_dpmsIsManual
= false;
101 CheckOSScreenSaverInhibitionSetting();
102 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI
, "OnDPMSDeactivated");
103 return dpms
->DisablePowerSaving();
107 if (dpms
->EnablePowerSaving(dpms
->GetSupportedModes()[0]))
109 m_dpmsIsActive
= true;
110 m_dpmsIsManual
= manual
;
112 CheckOSScreenSaverInhibitionSetting();
113 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI
, "OnDPMSActivated");
121 bool CApplicationPowerHandling::WakeUpScreenSaverAndDPMS(bool bPowerOffKeyPressed
/* = false */)
125 // First reset DPMS, if active
130 //! @todo if screensaver lock is specified but screensaver is not active
131 //! (DPMS came first), activate screensaver now.
133 ResetScreenSaverTimer();
134 result
= !m_screensaverActive
|| WakeUpScreenSaver(bPowerOffKeyPressed
);
136 else if (m_screensaverActive
)
137 result
= WakeUpScreenSaver(bPowerOffKeyPressed
);
141 // allow listeners to ignore the deactivation if it precedes a powerdown/suspend etc
142 CVariant
data(CVariant::VariantTypeObject
);
143 data
["shuttingdown"] = bPowerOffKeyPressed
;
144 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI
,
145 "OnScreensaverDeactivated", data
);
151 bool CApplicationPowerHandling::WakeUpScreenSaver(bool bPowerOffKeyPressed
/* = false */)
153 if (m_iScreenSaveLock
== 2)
156 // if Screen saver is active
157 if (m_screensaverActive
&& !m_screensaverIdInUse
.empty())
159 if (m_iScreenSaveLock
== 0)
161 const std::shared_ptr
<CProfileManager
> profileManager
=
162 CServiceBroker::GetSettingsComponent()->GetProfileManager();
163 if (profileManager
->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE
&&
164 (profileManager
->UsingLoginScreen() ||
165 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
166 CSettings::SETTING_MASTERLOCK_STARTUPLOCK
)) &&
167 profileManager
->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE
&&
168 m_screensaverIdInUse
!= "screensaver.xbmc.builtin.dim" &&
169 m_screensaverIdInUse
!= "screensaver.xbmc.builtin.black" &&
170 m_screensaverIdInUse
!= "visualization")
172 m_iScreenSaveLock
= 2;
173 CGUIMessage
msg(GUI_MSG_CHECK_LOCK
, 0, 0);
175 CGUIWindow
* pWindow
=
176 CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_SCREENSAVER
);
178 pWindow
->OnMessage(msg
);
181 if (m_iScreenSaveLock
== -1)
183 m_iScreenSaveLock
= 0;
187 // disable screensaver
188 m_screensaverActive
= false;
189 m_iScreenSaveLock
= 0;
190 ResetScreenSaverTimer();
192 if (m_screensaverIdInUse
== "visualization")
194 // we can just continue as usual from vis mode
197 else if (m_screensaverIdInUse
== "screensaver.xbmc.builtin.dim" ||
198 m_screensaverIdInUse
== "screensaver.xbmc.builtin.black" ||
199 m_screensaverIdInUse
.empty())
204 { // we're in screensaver window
205 if (m_pythonScreenSaver
)
207 // What sound does a python screensaver make?
208 #define SCRIPT_ALARM "sssssscreensaver"
209 #define SCRIPT_TIMEOUT 15 // seconds
211 /* FIXME: This is a hack but a proper fix is non-trivial. Basically this code
212 * makes sure the addon gets terminated after we've moved out of the screensaver window.
213 * If we don't do this, we may simply lockup.
215 g_alarmClock
.Start(SCRIPT_ALARM
, SCRIPT_TIMEOUT
,
216 "StopScript(" + m_pythonScreenSaver
->LibPath() + ")", true, false);
217 m_pythonScreenSaver
.reset();
219 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SCREENSAVER
)
220 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow(); // show the previous window
221 else if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW
)
222 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION
, WINDOW_SLIDESHOW
, -1,
223 static_cast<void*>(new CAction(ACTION_STOP
)));
231 void CApplicationPowerHandling::CheckOSScreenSaverInhibitionSetting()
233 // Kodi screen saver overrides OS one: always inhibit OS screen saver then
234 // except when DPMS is active (inhibiting the screen saver then might also
235 // disable DPMS again)
236 if (!m_dpmsIsActive
&&
237 !CServiceBroker::GetSettingsComponent()
239 ->GetString(CSettings::SETTING_SCREENSAVER_MODE
)
241 CServiceBroker::GetWinSystem()->GetOSScreenSaver())
243 if (!m_globalScreensaverInhibitor
)
245 m_globalScreensaverInhibitor
=
246 CServiceBroker::GetWinSystem()->GetOSScreenSaver()->CreateInhibitor();
249 else if (m_globalScreensaverInhibitor
)
251 m_globalScreensaverInhibitor
.Release();
255 void CApplicationPowerHandling::CheckScreenSaverAndDPMS()
257 bool maybeScreensaver
= true;
259 maybeScreensaver
= false;
260 else if (m_screensaverActive
)
261 maybeScreensaver
= false;
262 else if (CServiceBroker::GetSettingsComponent()
264 ->GetString(CSettings::SETTING_SCREENSAVER_MODE
)
266 maybeScreensaver
= false;
268 auto winSystem
= CServiceBroker::GetWinSystem();
272 std::shared_ptr
<CDPMSSupport
> dpms
= winSystem
->GetDPMSManager();
274 bool maybeDPMS
= true;
277 else if (!dpms
|| !dpms
->IsSupported())
279 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
280 CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF
) <= 0)
283 // whether the current state of the application should be regarded as active even when there is no
284 // explicit user activity such as input
285 bool haveIdleActivity
= false;
287 if (m_bResetScreenSaver
)
289 m_bResetScreenSaver
= false;
290 haveIdleActivity
= true;
293 // When inhibit screensaver is enabled prevent screensaver from kicking in
294 if (m_bInhibitScreenSaver
)
295 haveIdleActivity
= true;
297 // Are we playing a video and it is not paused?
298 const auto& components
= CServiceBroker::GetAppComponents();
299 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
300 if (appPlayer
&& appPlayer
->IsPlayingVideo() && !appPlayer
->IsPaused())
301 haveIdleActivity
= true;
303 // Are we playing audio and screensaver is disabled globally for audio?
304 else if (appPlayer
&& appPlayer
->IsPlayingAudio() &&
305 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
306 CSettings::SETTING_SCREENSAVER_DISABLEFORAUDIO
))
308 haveIdleActivity
= true;
311 // Handle OS screen saver state
312 if (haveIdleActivity
&& CServiceBroker::GetWinSystem()->GetOSScreenSaver())
314 // Always inhibit OS screen saver during these kinds of activities
315 if (!m_screensaverInhibitor
)
317 m_screensaverInhibitor
=
318 CServiceBroker::GetWinSystem()->GetOSScreenSaver()->CreateInhibitor();
321 else if (m_screensaverInhibitor
)
323 m_screensaverInhibitor
.Release();
326 // Has the screen saver window become active?
327 if (maybeScreensaver
&&
328 CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_SCREENSAVER
))
330 m_screensaverActive
= true;
331 maybeScreensaver
= false;
334 if (m_screensaverActive
&& haveIdleActivity
)
336 WakeUpScreenSaverAndDPMS();
340 if (!maybeScreensaver
&& !maybeDPMS
)
341 return; // Nothing to do.
343 // See if we need to reset timer.
344 if (haveIdleActivity
)
346 ResetScreenSaverTimer();
350 float elapsed
= m_screenSaverTimer
.IsRunning() ? m_screenSaverTimer
.GetElapsedSeconds() : 0.f
;
352 // DPMS has priority (it makes the screensaver not needed)
353 if (maybeDPMS
&& elapsed
> CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
354 CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF
) *
360 else if (maybeScreensaver
&&
361 elapsed
> CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
362 CSettings::SETTING_SCREENSAVER_TIME
) *
365 ActivateScreenSaver();
369 // activate the screensaver.
370 // if forceType is true, we ignore the various conditions that can alter
371 // the type of screensaver displayed
372 void CApplicationPowerHandling::ActivateScreenSaver(bool forceType
/*= false */)
374 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
375 const auto& components
= CServiceBroker::GetAppComponents();
376 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
378 m_screensaverActive
= true;
379 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI
, "OnScreensaverActivated");
381 // disable screensaver lock from the login screen
383 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN
? 1 : 0;
385 m_screensaverIdInUse
= settings
->GetString(CSettings::SETTING_SCREENSAVER_MODE
);
389 if (m_screensaverIdInUse
== "screensaver.xbmc.builtin.dim" ||
390 m_screensaverIdInUse
== "screensaver.xbmc.builtin.black" || m_screensaverIdInUse
.empty())
395 // Enforce Dim for special cases.
396 bool bUseDim
= false;
397 if (appPlayer
&& appPlayer
->IsPlayingVideo() &&
398 settings
->GetBool(CSettings::SETTING_SCREENSAVER_USEDIMONPAUSE
))
400 else if (CServiceBroker::GetPVRManager().Get
<PVR::GUI::Channels
>().IsRunningChannelScan())
404 m_screensaverIdInUse
= "screensaver.xbmc.builtin.dim";
407 if (m_screensaverIdInUse
== "screensaver.xbmc.builtin.dim" ||
408 m_screensaverIdInUse
== "screensaver.xbmc.builtin.black" || m_screensaverIdInUse
.empty())
412 else if (CServiceBroker::GetAddonMgr().GetAddon(m_screensaverIdInUse
, m_pythonScreenSaver
,
413 ADDON::AddonType::SCREENSAVER
,
414 ADDON::OnlyEnabled::CHOICE_YES
))
416 std::string libPath
= m_pythonScreenSaver
->LibPath();
417 if (CScriptInvocationManager::GetInstance().HasLanguageInvoker(libPath
))
419 CLog::Log(LOGDEBUG
, "using python screensaver add-on {}", m_screensaverIdInUse
);
421 // Don't allow a previously-scheduled alarm to kill our new screensaver
422 g_alarmClock
.Stop(SCRIPT_ALARM
, true);
424 if (!CScriptInvocationManager::GetInstance().Stop(libPath
))
425 CScriptInvocationManager::GetInstance().ExecuteAsync(
427 ADDON::AddonPtr(new ADDON::CAddon(dynamic_cast<ADDON::CAddon
&>(*m_pythonScreenSaver
))));
430 m_pythonScreenSaver
.reset();
433 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SCREENSAVER
);
436 void CApplicationPowerHandling::InhibitScreenSaver(bool inhibit
)
438 m_bInhibitScreenSaver
= inhibit
;
441 bool CApplicationPowerHandling::IsScreenSaverInhibited() const
443 return m_bInhibitScreenSaver
;
446 // Global Idle Time in Seconds
447 // idle time will be reset if on any OnKey()
448 // int return: system Idle time in seconds! 0 is no idle!
449 int CApplicationPowerHandling::GlobalIdleTime()
451 if (!m_idleTimer
.IsRunning())
452 m_idleTimer
.StartZero();
453 return (int)m_idleTimer
.GetElapsedSeconds();
456 float CApplicationPowerHandling::NavigationIdleTime()
458 if (!m_navigationTimer
.IsRunning())
459 m_navigationTimer
.StartZero();
460 return m_navigationTimer
.GetElapsedSeconds();
463 void CApplicationPowerHandling::StopShutdownTimer()
465 m_shutdownTimer
.Stop();
468 void CApplicationPowerHandling::ResetShutdownTimers()
470 // reset system shutdown timer
471 m_shutdownTimer
.StartZero();
473 // delete custom shutdown timer
474 if (g_alarmClock
.HasAlarm("shutdowntimer"))
475 g_alarmClock
.Stop("shutdowntimer", true);
478 void CApplicationPowerHandling::HandleShutdownMessage()
480 switch (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
481 CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE
))
483 case POWERSTATE_SHUTDOWN
:
484 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_POWERDOWN
);
487 case POWERSTATE_SUSPEND
:
488 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SUSPEND
);
491 case POWERSTATE_HIBERNATE
:
492 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_HIBERNATE
);
495 case POWERSTATE_QUIT
:
496 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT
);
499 case POWERSTATE_MINIMIZE
:
500 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MINIMIZE
);
504 CLog::Log(LOGERROR
, "{}: No valid shutdownstate matched", __FUNCTION__
);
509 void CApplicationPowerHandling::CheckShutdown()
511 // first check if we should reset the timer
512 const auto& components
= CServiceBroker::GetAppComponents();
513 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
517 if (m_bInhibitIdleShutdown
|| appPlayer
->IsPlaying() ||
518 appPlayer
->IsPausedPlayback() // is something playing?
519 || CMusicLibraryQueue::GetInstance().IsRunning() ||
520 CVideoLibraryQueue::GetInstance().IsRunning() ||
521 CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(
522 WINDOW_DIALOG_PROGRESS
) // progress dialog is onscreen
524 !CServiceBroker::GetPVRManager().Get
<PVR::GUI::PowerManagement
>().CanSystemPowerdown(false))
526 m_shutdownTimer
.StartZero();
530 float elapsed
= m_shutdownTimer
.IsRunning() ? m_shutdownTimer
.GetElapsedSeconds() : 0.f
;
531 if (elapsed
> CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
532 CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME
) *
535 // Since it is a sleep instead of a shutdown, let's set everything to reset when we wake up.
536 m_shutdownTimer
.Stop();
539 CLog::LogF(LOGDEBUG
, "Timer is over (shutdown function: {})",
540 CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
541 CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE
));
542 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SHUTDOWN
);
546 void CApplicationPowerHandling::InhibitIdleShutdown(bool inhibit
)
548 m_bInhibitIdleShutdown
= inhibit
;
551 bool CApplicationPowerHandling::IsIdleShutdownInhibited() const
553 return m_bInhibitIdleShutdown
;
556 bool CApplicationPowerHandling::OnSettingChanged(const CSetting
& setting
)
558 const std::string
& settingId
= setting
.GetId();
560 if (settingId
== CSettings::SETTING_SCREENSAVER_MODE
)
562 CheckOSScreenSaverInhibitionSetting();
570 bool CApplicationPowerHandling::OnSettingAction(const CSetting
& setting
)
572 const std::string
& settingId
= setting
.GetId();
574 if (settingId
== CSettings::SETTING_SCREENSAVER_PREVIEW
)
575 ActivateScreenSaver(true);
576 else if (settingId
== CSettings::SETTING_SCREENSAVER_SETTINGS
)
578 ADDON::AddonPtr addon
;
579 if (CServiceBroker::GetAddonMgr().GetAddon(
580 CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
581 CSettings::SETTING_SCREENSAVER_MODE
),
582 addon
, ADDON::AddonType::SCREENSAVER
, ADDON::OnlyEnabled::CHOICE_YES
))
583 CGUIDialogAddonSettings::ShowForAddon(addon
);