summarylogtreecommitdiffstats
path: root/x11-ozone-fix-X11-screensaver-suspension.patch
blob: 4336d6a7a0a4b6775a10a2b2773de99de75ceb78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
From 8c1ebea5f601b0b5247535dcdfd01755f3e6e1a6 Mon Sep 17 00:00:00 2001
From: Andrew Wolfers <aswolfers@chromium.org>
Date: Tue, 19 Jul 2022 15:01:25 +0000
Subject: [PATCH] [x11][ozone] Fix X11 screensaver suspension.

X11 screensaver suspension was broken by https://crrev.com/c/3507472,
in which usage patterns were migrated to a non-stacking paradigm.

"Non-stacking" screensaver suspension describes an overriding behavior,
such that the last suspending or un-suspending call defines the current
state. Conversely, a "stacking" screensaver suspension paradigm allows
for parallel suspension, such that suspending calls are expected to be
matched by an equal number of un-suspending calls.

Documentation for `PlatformScreen::SetScreenSaverSuspended` (inherited
by `X11ScreenOzone`) explains that it should be used in a non-stacking
manner [1], which contradicts the child class's underlying
implementation [2].

> If XScreenSaverSuspend is called multiple times with suspend set to
> 'True', it must be called an equal number of times with suspend set
> to 'False' in order for the screensaver timer to be restarted.

This change updates the documentation/API of the `PlatformScreen`
parent class to correctly describe the stacking behavior of child class
`X11ScreenOzone`. This change also updates the implementation of
`WaylandScreen` to a stacking version. Lastly, this change updates the
call sites of `PlatformScreen` according to the API change.

[1] https://crsrc.org/c/ui/ozone/public/platform_screen.h;l=96
[2] https://linux.die.net/man/3/xscreensaverunsetattributes

Bug: b:193670013
Bug: b:196213351
Bug: 1329573
Bug: 1339361
Change-Id: I60975c8da9f86a0f26f3c32cf49c4a7eeeea6a12
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3759067
Commit-Queue: Andrew Wolfers <aswolfers@chromium.org>
Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1025717}

(cherry picked from commit e61f08f8dbf1ec7cead427f3c497934e9d0db35f)
---
 ui/aura/screen_ozone.cc                       | 14 ++++++--
 ui/aura/screen_ozone.h                        | 29 ++++++++++++----
 ui/base/x/x11_util.h                          |  4 ++-
 ui/display/screen.cc                          | 21 ++----------
 ui/display/screen.h                           | 34 ++++++-------------
 .../platform/wayland/host/wayland_screen.cc   | 31 +++++++++++++++++
 .../platform/wayland/host/wayland_screen.h    | 30 +++++++++++++++-
 ui/ozone/platform/x11/x11_screen_ozone.cc     | 27 +++++++++++++--
 ui/ozone/platform/x11/x11_screen_ozone.h      | 19 ++++++++++-
 ui/ozone/public/platform_screen.cc            |  8 +++--
 ui/ozone/public/platform_screen.h             | 26 +++++++++++---
 11 files changed, 182 insertions(+), 61 deletions(-)

diff --git a/ui/aura/screen_ozone.cc b/ui/aura/screen_ozone.cc
index a78a6a48f1..09f62dc982 100644
--- a/ui/aura/screen_ozone.cc
+++ b/ui/aura/screen_ozone.cc
@@ -4,6 +4,8 @@
 
 #include "ui/aura/screen_ozone.h"
 
+#include <memory>
+
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -108,8 +110,16 @@ display::Display ScreenOzone::GetPrimaryDisplay() const {
 }
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
-bool ScreenOzone::SetScreenSaverSuspended(bool suspend) {
-  return platform_screen_->SetScreenSaverSuspended(suspend);
+ScreenOzone::ScreenSaverSuspenderOzone::ScreenSaverSuspenderOzone(
+    std::unique_ptr<ui::PlatformScreen::PlatformScreenSaverSuspender> suspender)
+    : suspender_(std::move(suspender)) {}
+
+ScreenOzone::ScreenSaverSuspenderOzone::~ScreenSaverSuspenderOzone() = default;
+
+std::unique_ptr<display::Screen::ScreenSaverSuspender>
+ScreenOzone::SuspendScreenSaver() {
+  return std::make_unique<ScreenSaverSuspenderOzone>(
+      platform_screen_->SuspendScreenSaver());
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 
diff --git a/ui/aura/screen_ozone.h b/ui/aura/screen_ozone.h
index 2970a0e0e7..d033abf366 100644
--- a/ui/aura/screen_ozone.h
+++ b/ui/aura/screen_ozone.h
@@ -11,10 +11,7 @@
 #include "build/chromeos_buildflags.h"
 #include "ui/aura/aura_export.h"
 #include "ui/display/screen.h"
-
-namespace ui {
-class PlatformScreen;
-}
+#include "ui/ozone/public/platform_screen.h"
 
 namespace aura {
 
@@ -48,6 +45,10 @@ class AURA_EXPORT ScreenOzone : public display::Screen {
   display::Display GetDisplayMatching(
       const gfx::Rect& match_rect) const override;
   display::Display GetPrimaryDisplay() const override;
+#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
+  std::unique_ptr<display::Screen::ScreenSaverSuspender> SuspendScreenSaver()
+      override;
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
   bool IsScreenSaverActive() const override;
   base::TimeDelta CalculateIdleTime() const override;
   void AddObserver(display::DisplayObserver* observer) override;
@@ -65,11 +66,27 @@ class AURA_EXPORT ScreenOzone : public display::Screen {
  protected:
   ui::PlatformScreen* platform_screen() { return platform_screen_.get(); }
 
+ private:
 #if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
-  bool SetScreenSaverSuspended(bool suspend) override;
+  class ScreenSaverSuspenderOzone
+      : public display::Screen::ScreenSaverSuspender {
+   public:
+    explicit ScreenSaverSuspenderOzone(
+        std::unique_ptr<ui::PlatformScreen::PlatformScreenSaverSuspender>
+            suspender);
+
+    ScreenSaverSuspenderOzone(const ScreenSaverSuspenderOzone&) = delete;
+    ScreenSaverSuspenderOzone& operator=(const ScreenSaverSuspenderOzone&) =
+        delete;
+
+    ~ScreenSaverSuspenderOzone() override;
+
+   private:
+    std::unique_ptr<ui::PlatformScreen::PlatformScreenSaverSuspender>
+        suspender_;
+  };
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 
- private:
   gfx::AcceleratedWidget GetAcceleratedWidgetForWindow(
       aura::Window* window) const;
 
diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h
index bf36efe170..0692571582 100644
--- a/ui/base/x/x11_util.h
+++ b/ui/base/x/x11_util.h
@@ -337,7 +337,9 @@ COMPONENT_EXPORT(UI_BASE_X) bool IsCompositingManagerPresent();
 COMPONENT_EXPORT(UI_BASE_X) bool IsX11WindowFullScreen(x11::Window window);
 
 // Suspends or resumes the X screen saver, and returns whether the operation was
-// successful.  Must be called on the UI thread.
+// successful.  Must be called on the UI thread. If called multiple times with
+// |suspend| set to true, the screen saver is not un-suspended until this method
+// is called an equal number of times with |suspend| set to false.
 COMPONENT_EXPORT(UI_BASE_X) bool SuspendX11ScreenSaver(bool suspend);
 
 // Returns true if the window manager supports the given hint.
diff --git a/ui/display/screen.cc b/ui/display/screen.cc
index b9723889ce..70dc0a9f5c 100644
--- a/ui/display/screen.cc
+++ b/ui/display/screen.cc
@@ -85,26 +85,11 @@ void Screen::SetDisplayForNewWindows(int64_t display_id) {
 }
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
-std::unique_ptr<Screen::ScreenSaverSuspender> Screen::SuspendScreenSaver() {
-  SetScreenSaverSuspended(true);
-  screen_saver_suspension_count_++;
-  return base::WrapUnique(new Screen::ScreenSaverSuspender(this));
-}
-
-Screen::ScreenSaverSuspender::~ScreenSaverSuspender() {
-  // Check that this suspender still refers to the active screen. Particularly
-  // in tests, the screen might be destructed before the suspender.
-  if (screen_ == GetScreen()) {
-    screen_->screen_saver_suspension_count_--;
-    if (screen_->screen_saver_suspension_count_ == 0) {
-      screen_->SetScreenSaverSuspended(false);
-    }
-  }
-}
+Screen::ScreenSaverSuspender::~ScreenSaverSuspender() = default;
 
-bool Screen::SetScreenSaverSuspended(bool suspend) {
+std::unique_ptr<Screen::ScreenSaverSuspender> Screen::SuspendScreenSaver() {
   NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  return nullptr;
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 
diff --git a/ui/display/screen.h b/ui/display/screen.h
index a86c5b63cc..d04534006f 100644
--- a/ui/display/screen.h
+++ b/ui/display/screen.h
@@ -133,28 +133,22 @@ class DISPLAY_EXPORT Screen {
   // its existence.
   class ScreenSaverSuspender {
    public:
-    ScreenSaverSuspender(const Screen&) = delete;
-    ScreenSaverSuspender& operator=(const Screen&) = delete;
+    ScreenSaverSuspender() = default;
 
-    // Notifies |screen_| that this instance is being destructed, and causes its
-    // platform-specific screensaver to be un-suspended if this is the last such
-    // remaining instance.
-    ~ScreenSaverSuspender();
+    ScreenSaverSuspender(const ScreenSaverSuspender&) = delete;
+    ScreenSaverSuspender& operator=(const ScreenSaverSuspender&) = delete;
 
-   private:
-    friend class Screen;
-
-    explicit ScreenSaverSuspender(Screen* screen) : screen_(screen) {}
-
-    Screen* screen_;
+    // Causes the platform-specific screensaver to be un-suspended iff this is
+    // the last remaining instance.
+    virtual ~ScreenSaverSuspender() = 0;
   };
 
   // Suspends the platform-specific screensaver until the returned
-  // |ScreenSaverSuspender| is destructed. This method allows stacking multiple
-  // overlapping calls, such that the platform-specific screensaver will not be
-  // un-suspended until all returned |SreenSaverSuspender| instances have been
-  // destructed.
-  std::unique_ptr<ScreenSaverSuspender> SuspendScreenSaver();
+  // |ScreenSaverSuspender| is destructed, or returns nullptr if suspension
+  // failed. This method allows stacking multiple overlapping calls, such that
+  // the platform-specific screensaver will not be un-suspended until all
+  // returned |ScreenSaverSuspender| instances have been destructed.
+  virtual std::unique_ptr<ScreenSaverSuspender> SuspendScreenSaver();
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 
   // Returns whether the screensaver is currently running.
@@ -200,12 +194,6 @@ class DISPLAY_EXPORT Screen {
       const gfx::GpuExtraInfo& gpu_extra_info);
 
  protected:
-#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
-  // Suspends or un-suspends the platform-specific screensaver, and returns
-  // whether the operation was successful.
-  virtual bool SetScreenSaverSuspended(bool suspend);
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
-
   void set_shutdown(bool shutdown) { shutdown_ = shutdown; }
 
  private:
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.cc b/ui/ozone/platform/wayland/host/wayland_screen.cc
index 0c7dc5c02b..18cd81b472 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -327,6 +327,37 @@ display::Display WaylandScreen::GetDisplayMatching(
   return display_matching ? *display_matching : GetPrimaryDisplay();
 }
 
+std::unique_ptr<WaylandScreen::WaylandScreenSaverSuspender>
+WaylandScreen::WaylandScreenSaverSuspender::Create(WaylandScreen& screen) {
+  auto suspender = base::WrapUnique(new WaylandScreenSaverSuspender(screen));
+  if (suspender->is_suspending_) {
+    screen.screen_saver_suspension_count_++;
+    return suspender;
+  }
+
+  return nullptr;
+}
+
+WaylandScreen::WaylandScreenSaverSuspender::WaylandScreenSaverSuspender(
+    WaylandScreen& screen)
+    : screen_(screen.GetWeakPtr()) {
+  is_suspending_ = screen.SetScreenSaverSuspended(true);
+}
+
+WaylandScreen::WaylandScreenSaverSuspender::~WaylandScreenSaverSuspender() {
+  if (screen_ && is_suspending_) {
+    screen_->screen_saver_suspension_count_--;
+    if (screen_->screen_saver_suspension_count_ == 0) {
+      screen_->SetScreenSaverSuspended(false);
+    }
+  }
+}
+
+std::unique_ptr<PlatformScreen::PlatformScreenSaverSuspender>
+WaylandScreen::SuspendScreenSaver() {
+  return WaylandScreenSaverSuspender::Create(*this);
+}
+
 bool WaylandScreen::SetScreenSaverSuspended(bool suspend) {
   if (!connection_->zwp_idle_inhibit_manager())
     return false;
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.h b/ui/ozone/platform/wayland/host/wayland_screen.h
index 87358f4f06..8e5515104a 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.h
+++ b/ui/ozone/platform/wayland/host/wayland_screen.h
@@ -68,7 +68,8 @@ class WaylandScreen : public PlatformScreen {
       const gfx::Point& point) const override;
   display::Display GetDisplayMatching(
       const gfx::Rect& match_rect) const override;
-  bool SetScreenSaverSuspended(bool suspend) override;
+  std::unique_ptr<PlatformScreen::PlatformScreenSaverSuspender>
+  SuspendScreenSaver() override;
   bool IsScreenSaverActive() const override;
   base::TimeDelta CalculateIdleTime() const override;
   void AddObserver(display::DisplayObserver* observer) override;
@@ -76,7 +77,33 @@ class WaylandScreen : public PlatformScreen {
   std::vector<base::Value> GetGpuExtraInfo(
       const gfx::GpuExtraInfo& gpu_extra_info) override;
 
+ protected:
+  // Suspends or un-suspends the platform-specific screensaver, and returns
+  // whether the operation was successful. Can be called more than once with the
+  // same value for |suspend|, but those states should not stack: the first
+  // alternating value should toggle the state of the suspend.
+  bool SetScreenSaverSuspended(bool suspend);
+
  private:
+  class WaylandScreenSaverSuspender
+      : public PlatformScreen::PlatformScreenSaverSuspender {
+   public:
+    WaylandScreenSaverSuspender(const WaylandScreenSaverSuspender&) = delete;
+    WaylandScreenSaverSuspender& operator=(const WaylandScreenSaverSuspender&) =
+        delete;
+
+    ~WaylandScreenSaverSuspender() override;
+
+    static std::unique_ptr<WaylandScreenSaverSuspender> Create(
+        WaylandScreen& screen);
+
+   private:
+    explicit WaylandScreenSaverSuspender(WaylandScreen& screen);
+
+    base::WeakPtr<WaylandScreen> screen_;
+    bool is_suspending_ = false;
+  };
+
   // All parameters are in DIP screen coordinates/units except |physical_size|,
   // which is in physical pixels.
   void AddOrUpdateDisplay(uint32_t output_id,
@@ -103,6 +130,7 @@ class WaylandScreen : public PlatformScreen {
 #endif
 
   wl::Object<zwp_idle_inhibitor_v1> idle_inhibitor_;
+  uint32_t screen_saver_suspension_count_ = 0;
 
   base::WeakPtrFactory<WaylandScreen> weak_factory_;
 };
diff --git a/ui/ozone/platform/x11/x11_screen_ozone.cc b/ui/ozone/platform/x11/x11_screen_ozone.cc
index 53265ab58a..b450df9c83 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone.cc
+++ b/ui/ozone/platform/x11/x11_screen_ozone.cc
@@ -4,6 +4,8 @@
 
 #include "ui/ozone/platform/x11/x11_screen_ozone.h"
 
+#include <memory>
+
 #include "base/containers/flat_set.h"
 #include "ui/base/linux/linux_desktop.h"
 #include "ui/base/x/x11_idle_query.h"
@@ -131,8 +133,29 @@ display::Display X11ScreenOzone::GetDisplayMatching(
   return matching_display ? *matching_display : GetPrimaryDisplay();
 }
 
-bool X11ScreenOzone::SetScreenSaverSuspended(bool suspend) {
-  return SuspendX11ScreenSaver(suspend);
+X11ScreenOzone::X11ScreenSaverSuspender::X11ScreenSaverSuspender() {
+  is_suspending_ = SuspendX11ScreenSaver(true);
+}
+
+std::unique_ptr<X11ScreenOzone::X11ScreenSaverSuspender>
+X11ScreenOzone::X11ScreenSaverSuspender::Create() {
+  auto suspender = base::WrapUnique(new X11ScreenSaverSuspender());
+  if (suspender->is_suspending_) {
+    return suspender;
+  }
+
+  return nullptr;
+}
+
+X11ScreenOzone::X11ScreenSaverSuspender::~X11ScreenSaverSuspender() {
+  if (is_suspending_) {
+    SuspendX11ScreenSaver(false);
+  }
+}
+
+std::unique_ptr<PlatformScreen::PlatformScreenSaverSuspender>
+X11ScreenOzone::SuspendScreenSaver() {
+  return X11ScreenSaverSuspender::Create();
 }
 
 bool X11ScreenOzone::IsScreenSaverActive() const {
diff --git a/ui/ozone/platform/x11/x11_screen_ozone.h b/ui/ozone/platform/x11/x11_screen_ozone.h
index d86acae9aa..81e0fd13d8 100644
--- a/ui/ozone/platform/x11/x11_screen_ozone.h
+++ b/ui/ozone/platform/x11/x11_screen_ozone.h
@@ -50,7 +50,8 @@ class X11ScreenOzone : public PlatformScreen,
       const gfx::Point& point) const override;
   display::Display GetDisplayMatching(
       const gfx::Rect& match_rect) const override;
-  bool SetScreenSaverSuspended(bool suspend) override;
+  std::unique_ptr<PlatformScreen::PlatformScreenSaverSuspender>
+  SuspendScreenSaver() override;
   bool IsScreenSaverActive() const override;
   base::TimeDelta CalculateIdleTime() const override;
   void AddObserver(display::DisplayObserver* observer) override;
@@ -66,6 +67,22 @@ class X11ScreenOzone : public PlatformScreen,
  private:
   friend class X11ScreenOzoneTest;
 
+  class X11ScreenSaverSuspender
+      : public PlatformScreen::PlatformScreenSaverSuspender {
+   public:
+    X11ScreenSaverSuspender(const X11ScreenSaverSuspender&) = delete;
+    X11ScreenSaverSuspender& operator=(const X11ScreenSaverSuspender&) = delete;
+
+    ~X11ScreenSaverSuspender() override;
+
+    static std::unique_ptr<X11ScreenSaverSuspender> Create();
+
+   private:
+    X11ScreenSaverSuspender();
+
+    bool is_suspending_ = false;
+  };
+
   // Overridden from ui::XDisplayManager::Delegate:
   void OnXDisplayListUpdated() override;
   float GetXDisplayScaleFactor() const override;
diff --git a/ui/ozone/public/platform_screen.cc b/ui/ozone/public/platform_screen.cc
index 98f599aa41..2353208396 100644
--- a/ui/ozone/public/platform_screen.cc
+++ b/ui/ozone/public/platform_screen.cc
@@ -30,9 +30,13 @@ std::string PlatformScreen::GetCurrentWorkspace() {
   return {};
 }
 
-bool PlatformScreen::SetScreenSaverSuspended(bool suspend) {
+PlatformScreen::PlatformScreenSaverSuspender::~PlatformScreenSaverSuspender() =
+    default;
+
+std::unique_ptr<PlatformScreen::PlatformScreenSaverSuspender>
+PlatformScreen::SuspendScreenSaver() {
   NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  return nullptr;
 }
 
 bool PlatformScreen::IsScreenSaverActive() const {
diff --git a/ui/ozone/public/platform_screen.h b/ui/ozone/public/platform_screen.h
index 091220a99f..e4adfafce3 100644
--- a/ui/ozone/public/platform_screen.h
+++ b/ui/ozone/public/platform_screen.h
@@ -89,11 +89,27 @@ class COMPONENT_EXPORT(OZONE_BASE) PlatformScreen {
   virtual display::Display GetDisplayMatching(
       const gfx::Rect& match_rect) const = 0;
 
-  // Suspends or un-suspends the platform-specific screensaver, and returns
-  // whether the operation was successful. Can be called more than once with the
-  // same value for |suspend|, but those states should not stack: the first
-  // alternating value should toggle the state of the suspend.
-  virtual bool SetScreenSaverSuspended(bool suspend);
+  // Object which suspends the platform-specific screensaver for the duration of
+  // its existence.
+  class PlatformScreenSaverSuspender {
+   public:
+    PlatformScreenSaverSuspender() = default;
+
+    PlatformScreenSaverSuspender(const PlatformScreenSaverSuspender&) = delete;
+    PlatformScreenSaverSuspender& operator=(
+        const PlatformScreenSaverSuspender&) = delete;
+
+    // Causes the platform-specific screensaver to be un-suspended iff this is
+    // the last remaining instance.
+    virtual ~PlatformScreenSaverSuspender() = 0;
+  };
+
+  // Suspends the platform-specific screensaver until the returned
+  // |PlatformScreenSaverSuspender| is destructed, or returns nullptr if
+  // suspension failed. This method allows stacking multiple overlapping calls,
+  // such that the platform-specific screensaver will not be un-suspended until
+  // all returned |PlatformScreenSaverSuspender| instances have been destructed.
+  virtual std::unique_ptr<PlatformScreenSaverSuspender> SuspendScreenSaver();
 
   // Returns whether the screensaver is currently running.
   virtual bool IsScreenSaverActive() const;