Bug 1193610 - Also use SystemParametersInfo to block screen saver on Windows, so it works on WinXP and Vista. r=jimm,a=lizzard
authorChris Pearce <cpearce@mozilla.com>
Tue, 27 Oct 2015 11:58:15 +1300
changeset 296685 fabe9ce33dc2
parent 296684 74993763ef07
child 296686 26ef30e81468
push id5288
push usercpearce@mozilla.com
push date2015-11-10 22:49 +0000
treeherdermozilla-beta@fabe9ce33dc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, lizzard
bugs1193610
milestone43.0
Bug 1193610 - Also use SystemParametersInfo to block screen saver on Windows, so it works on WinXP and Vista. r=jimm,a=lizzard
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();