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 m_renderGUI
= renderGUI
;
73 void CApplicationPowerHandling::StopScreenSaverTimer()
75 m_screenSaverTimer
.Stop();
78 bool CApplicationPowerHandling::ToggleDPMS(bool manual
)
80 auto winSystem
= CServiceBroker::GetWinSystem();
84 std::shared_ptr
<CDPMSSupport
> dpms
= winSystem
->GetDPMSManager();
88 if (manual
|| (m_dpmsIsManual
== manual
))
92 m_dpmsIsActive
= false;
93 m_dpmsIsManual
= false;
95 CheckOSScreenSaverInhibitionSetting();
96 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI
, "OnDPMSDeactivated");
97 return dpms
->DisablePowerSaving();
101 if (dpms
->EnablePowerSaving(dpms
->GetSupportedModes()[0]))
103 m_dpmsIsActive
= true;
104 m_dpmsIsManual
= manual
;
106 CheckOSScreenSaverInhibitionSetting();
107 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::GUI
, "OnDPMSActivated");
115 bool CApplicationPowerHandling::WakeUpScreenSaverAndDPMS(bool bPowerOffKeyPressed
/* = false */)
119 // First reset DPMS, if active
124 //! @todo if screensaver lock is specified but screensaver is not active
125 //! (DPMS came first), activate screensaver now.
127 ResetScreenSaverTimer();
128 result
= !m_screensaverActive
|| WakeUpScreenSaver(bPowerOffKeyPressed
);
130 else if (m_screensaverActive
)
131 result
= WakeUpScreenSaver(bPowerOffKeyPressed
);
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
);
145 bool CApplicationPowerHandling::WakeUpScreenSaver(bool bPowerOffKeyPressed
/* = false */)
147 if (m_iScreenSaveLock
== 2)
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
);
172 pWindow
->OnMessage(msg
);
175 if (m_iScreenSaveLock
== -1)
177 m_iScreenSaveLock
= 0;
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
191 else if (m_screensaverIdInUse
== "screensaver.xbmc.builtin.dim" ||
192 m_screensaverIdInUse
== "screensaver.xbmc.builtin.black" ||
193 m_screensaverIdInUse
.empty())
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
)));
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()
233 ->GetString(CSettings::SETTING_SCREENSAVER_MODE
)
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;
253 maybeScreensaver
= false;
254 else if (m_screensaverActive
)
255 maybeScreensaver
= false;
256 else if (CServiceBroker::GetSettingsComponent()
258 ->GetString(CSettings::SETTING_SCREENSAVER_MODE
)
260 maybeScreensaver
= false;
262 auto winSystem
= CServiceBroker::GetWinSystem();
266 std::shared_ptr
<CDPMSSupport
> dpms
= winSystem
->GetDPMSManager();
268 bool maybeDPMS
= true;
271 else if (!dpms
|| !dpms
->IsSupported())
273 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
274 CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF
) <= 0)
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();
334 if (!maybeScreensaver
&& !maybeDPMS
)
335 return; // Nothing to do.
337 // See if we need to reset timer.
338 if (haveIdleActivity
)
340 ResetScreenSaverTimer();
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
) *
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
377 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_LOGIN_SCREEN
? 1 : 0;
379 m_screensaverIdInUse
= settings
->GetString(CSettings::SETTING_SCREENSAVER_MODE
);
383 if (m_screensaverIdInUse
== "screensaver.xbmc.builtin.dim" ||
384 m_screensaverIdInUse
== "screensaver.xbmc.builtin.black" || m_screensaverIdInUse
.empty())
389 // Enforce Dim for special cases.
390 bool bUseDim
= false;
391 if (appPlayer
&& appPlayer
->IsPlayingVideo() &&
392 settings
->GetBool(CSettings::SETTING_SCREENSAVER_USEDIMONPAUSE
))
394 else if (CServiceBroker::GetPVRManager().Get
<PVR::GUI::Channels
>().IsRunningChannelScan())
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())
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(
421 ADDON::AddonPtr(new ADDON::CAddon(dynamic_cast<ADDON::CAddon
&>(*m_pythonScreenSaver
))));
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
);
481 case POWERSTATE_SUSPEND
:
482 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SUSPEND
);
485 case POWERSTATE_HIBERNATE
:
486 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_HIBERNATE
);
489 case POWERSTATE_QUIT
:
490 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT
);
493 case POWERSTATE_MINIMIZE
:
494 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MINIMIZE
);
498 CLog::Log(LOGERROR
, "{}: No valid shutdownstate matched", __FUNCTION__
);
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
>();
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();
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();
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();
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
);