Bug 1193610 - Also use SystemParametersInfo to block screen saver on Windows, so it works on WinXP and Vista. r=jimm
authorChris Pearce <cpearce@mozilla.com>
Tue, 27 Oct 2015 11:58:15 +1300
changeset 304809 96eeff9fec807670d5e39102498469fdf613c91a
parent 304808 08493e40a4ad44fb3d05d4c19ab8fd923a8de737
child 304810 f9cd7aa25d6f212d0cf6c48318ff47f2c4ebab88
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1193610
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1193610 - Also use SystemParametersInfo to block screen saver on Windows, so it works on WinXP and Vista. r=jimm
widget/windows/nsAppShell.cpp
--- a/widget/windows/nsAppShell.cpp
+++ b/widget/windows/nsAppShell.cpp
@@ -17,48 +17,127 @@
 #include "mozilla/widget/AudioSession.h"
 #include "mozilla/HangMonitor.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "mozilla/StaticPtr.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "GeckoProfiler.h"
+#include "nsComponentManagerUtils.h"
+#include "nsITimer.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
+#define WAKE_LOCK_LOG(...) MOZ_LOG(GetWinWakeLockLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+PRLogModuleInfo* GetWinWakeLockLog() {
+  static PRLogModuleInfo* log = nullptr;
+  if (!log) {
+    log = PR_NewLogModule("WinWakeLock");
+  }
+  return log;
+}
+
 // A wake lock listener that disables screen saver when requested by
 // Gecko. For example when we're playing video in a foreground tab we
 // don't want the screen saver to turn on.
-class WinWakeLockListener final : public nsIDOMMozWakeLockListener {
+class WinWakeLockListener final : public nsIDOMMozWakeLockListener
+                                , public nsITimerCallback {
 public:
   NS_DECL_ISUPPORTS;
 
+  NS_IMETHODIMP Notify(nsITimer *timer) override {
+    WAKE_LOCK_LOG("WinWakeLock: periodic timer fired");
+    ResetScreenSaverTimeout();
+    return NS_OK;
+  }
 private:
   ~WinWakeLockListener() {}
 
   NS_IMETHOD Callback(const nsAString& aTopic, const nsAString& aState) {
     if (!aTopic.EqualsASCII("screen")) {
       return NS_OK;
     }
     // Note the wake lock code ensures that we're not sent duplicate
     // "locked-foreground" notifications when multipe wake locks are held.
     if (aState.EqualsASCII("locked-foreground")) {
-      // Prevent screen saver.
+      WAKE_LOCK_LOG("WinWakeLock: Blocking screen saver");
+      // We block the screen saver by periodically resetting the screen
+      // saver timeout.
+      StartTimer();
+      // Prevent the display turning off. On Win7 and later this also
+      // blocks the screen saver, but we need the timer started above
+      // to block on Win XP and Vista.
       SetThreadExecutionState(ES_DISPLAY_REQUIRED|ES_CONTINUOUS);
     } else {
+      WAKE_LOCK_LOG("WinWakeLock: Unblocking screen saver");
       // Re-enable screen saver.
+      StopTimer();
+      // Unblock display turning off.
       SetThreadExecutionState(ES_CONTINUOUS);
     }
     return NS_OK;
   }
+
+  void StartTimer() {
+    ResetScreenSaverTimeout();
+    MOZ_ASSERT(!mTimer);
+    if (mTimer) {
+      return;
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to create screen saver timeout reset timer");
+      return;
+    }
+    // The minimum screensaver timeout that can be specified with Windows' UI
+    // is 60 seconds. We set a timer to re-jig the screen saver 10 seconds
+    // before we expect the timer to run out, but always at least in 1 second
+    // intervals. We reset the timer at a max of 50 seconds, so that if the
+    // user changes the timeout using the UI, we won't be caught out.
+    int32_t timeout = std::max(std::min(50, (int32_t)mScreenSaverTimeout - 10), 1);
+    uint32_t timeoutMs = (uint32_t)timeout * 1000;
+    WAKE_LOCK_LOG("WinWakeLock: Setting periodic timer for %d ms", timeoutMs);
+    rv = timer->InitWithCallback(this,
+                                 timeoutMs,
+                                 nsITimer::TYPE_REPEATING_SLACK);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to initialize screen saver timeout reset timer");
+      return;
+    }
+
+    mTimer = timer.forget();
+  }
+
+  void StopTimer() {
+    WAKE_LOCK_LOG("WinWakeLock: StopTimer()");
+    if (!mTimer) {
+      return;
+    }
+    mTimer->Cancel();
+    mTimer = nullptr;
+  }
+
+  // Resets the operating system's timeout for when to disable the screen.
+  // Called periodically to keep the screensaver off.
+  void ResetScreenSaverTimeout() {
+    if (SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &mScreenSaverTimeout, 0)) {
+      SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, mScreenSaverTimeout, NULL, 0);
+    }
+    WAKE_LOCK_LOG("WinWakeLock: ResetScreenSaverTimeout() mScreenSaverTimeout=%d", mScreenSaverTimeout);
+  }
+
+  UINT mScreenSaverTimeout = 60;
+  nsCOMPtr<nsITimer> mTimer;
 };
 
-NS_IMPL_ISUPPORTS(WinWakeLockListener, nsIDOMMozWakeLockListener)
+NS_IMPL_ISUPPORTS(WinWakeLockListener, nsIDOMMozWakeLockListener, nsITimerCallback)
 StaticRefPtr<WinWakeLockListener> sWakeLockListener;
 
 static void
 AddScreenWakeLockListener()
 {
   nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
   if (sPowerManagerService) {
     sWakeLockListener = new WinWakeLockListener();