OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/power_save_blocker.h" | 5 #include "content/browser/power_save_blocker.h" |
6 | 6 |
| 7 #include <X11/Xlib.h> |
| 8 #include <X11/extensions/dpms.h> |
| 9 // Xlib #defines Status, but we can't have that for some of our headers. |
| 10 #ifdef Status |
| 11 #undef Status |
| 12 #endif |
| 13 |
7 #include "base/basictypes.h" | 14 #include "base/basictypes.h" |
8 #include "base/bind.h" | 15 #include "base/bind.h" |
9 #include "base/callback.h" | 16 #include "base/callback.h" |
10 #include "base/command_line.h" | 17 #include "base/command_line.h" |
11 #include "base/environment.h" | 18 #include "base/environment.h" |
12 #include "base/file_path.h" | 19 #include "base/file_path.h" |
13 #include "base/logging.h" | 20 #include "base/logging.h" |
14 #include "base/memory/ref_counted.h" | 21 #include "base/memory/ref_counted.h" |
15 #include "base/memory/scoped_ptr.h" | 22 #include "base/memory/scoped_ptr.h" |
16 #include "base/memory/singleton.h" | 23 #include "base/memory/singleton.h" |
17 #include "base/message_loop_proxy.h" | 24 #include "base/message_loop_proxy.h" |
| 25 #if defined(TOOLKIT_GTK) |
| 26 #include "base/message_pump_gtk.h" |
| 27 #else |
| 28 #include "base/message_pump_x.h" |
| 29 #endif |
18 #include "base/nix/xdg_util.h" | 30 #include "base/nix/xdg_util.h" |
19 #include "content/public/browser/browser_thread.h" | 31 #include "content/public/browser/browser_thread.h" |
20 #include "dbus/bus.h" | 32 #include "dbus/bus.h" |
21 #include "dbus/message.h" | 33 #include "dbus/message.h" |
22 #include "dbus/object_path.h" | 34 #include "dbus/object_path.h" |
23 #include "dbus/object_proxy.h" | 35 #include "dbus/object_proxy.h" |
24 | 36 |
25 using content::BrowserThread; | 37 using content::BrowserThread; |
26 | 38 |
27 namespace { | 39 namespace { |
28 | 40 |
29 // This class is used to inhibit Power Management on Linux systems | 41 // This class is used to inhibit Power Management on Linux systems using D-Bus |
30 // using D-Bus interfaces. Mainly, there are two interfaces that | 42 // interfaces. Mainly, there are two interfaces that make this possible. |
31 // make this possible. | 43 // org.freedesktop.PowerManagement[.Inhibit] is considered to be the |
32 // org.freedesktop.PowerManagement[.Inhibit] is considered to be | 44 // desktop-agnostic solution. However, it is only used by KDE4 and XFCE. |
33 // the desktop-agnostic solution. However, it is only used by | 45 // |
34 // KDE4 and XFCE. | 46 // org.gnome.SessionManager is the Power Management interface available on GNOME |
35 // org.gnome.SessionManager is the Power Management interface | 47 // desktops. Given that there is no generic solution to this problem, this class |
36 // available on Gnome desktops. | 48 // delegates the task of calling specific D-Bus APIs, to a |
37 // Given that there is no generic solution to this problem, | 49 // DBusPowerSaveBlock::Delegate object. |
38 // this class delegates the task of calling specific D-Bus APIs, | 50 // |
39 // to a DBusPowerSaveBlock::Delegate object. | 51 // This class is a Singleton and the delegate will be instantiated internally, |
40 // This class is a Singleton and the delegate will be instantiated | 52 // when the singleton instance is created, based on the desktop environment in |
41 // internally, when the singleton instance is created, based on | 53 // which the application is running. When the class is instantiated, if it runs |
42 // the desktop environment in which the application is running. | 54 // under a supported desktop environment it creates the Bus object and the |
43 // When the class is instantiated, if it runs under a supported | 55 // delegate. Otherwise, no object is created and the ApplyBlock method will not |
44 // desktop environment it creates the Bus object and the | 56 // do anything. |
45 // delegate. Otherwise, no object is created and the ApplyBlock | |
46 // method will not do anything. | |
47 class DBusPowerSaveBlocker { | 57 class DBusPowerSaveBlocker { |
48 public: | 58 public: |
49 // String passed to D-Bus APIs as the reason for which | 59 // String passed to D-Bus APIs as the reason for which |
50 // the power management features are temporarily disabled. | 60 // the power management features are temporarily disabled. |
51 static const char kPowerSaveReason[]; | 61 static const char kPowerSaveReason[]; |
52 | 62 |
53 // This delegate interface represents a concrete | 63 // This delegate interface represents a concrete implementation for a specific |
54 // implementation for a specific D-Bus interface. | 64 // D-Bus interface. It is responsible for obtaining specific object proxies, |
55 // It is responsible for obtaining specific object proxies, | |
56 // making D-Bus method calls and handling D-Bus responses. | 65 // making D-Bus method calls and handling D-Bus responses. |
57 // When a new DBusPowerBlocker is created, only a specific | 66 // |
58 // implementation of the delegate is instantiated. See the | 67 // When a new DBusPowerBlocker is created, only a specific implementation of |
59 // DBusPowerSaveBlocker constructor for more details. | 68 // the delegate is instantiated. See the DBusPowerSaveBlocker constructor for |
60 // This is ref_counted to make sure that the callbacks | 69 // more details. This is ref_counted to make sure that the callbacks stay |
61 // stay alive even after the DBusPowerSaveBlocker object | 70 // alive even after the DBusPowerSaveBlocker object is deleted. |
62 // is deleted. | |
63 class Delegate : public base::RefCountedThreadSafe<Delegate> { | 71 class Delegate : public base::RefCountedThreadSafe<Delegate> { |
64 public: | 72 public: |
65 Delegate() {} | 73 Delegate() {} |
66 virtual ~Delegate() {} | 74 virtual ~Delegate() {} |
67 virtual void ApplyBlock(PowerSaveBlocker::PowerSaveBlockerType type) = 0; | 75 virtual void ApplyBlock(PowerSaveBlocker::PowerSaveBlockerType type) = 0; |
68 private: | 76 private: |
69 DISALLOW_COPY_AND_ASSIGN(Delegate); | 77 DISALLOW_COPY_AND_ASSIGN(Delegate); |
70 }; | 78 }; |
71 | 79 |
72 // Returns a pointer to the sole instance of this class | 80 // Returns a pointer to the sole instance of this class |
73 static DBusPowerSaveBlocker* GetInstance(); | 81 static DBusPowerSaveBlocker* GetInstance(); |
74 | 82 |
75 // Forwards a power save block request to the concrete implementation | 83 // Forwards a power save block request to the concrete implementation of the |
76 // of the Delegate interface. | 84 // Delegate interface. If |delegate_| is NULL, the application runs under an |
77 // If |delegate_| is NULL, the application runs under an unsupported | 85 // unsupported desktop environment. In this case, the method does nothing. |
78 // desktop environment. In this case, the method doesn't do anything. | |
79 void ApplyBlock(PowerSaveBlocker::PowerSaveBlockerType type) { | 86 void ApplyBlock(PowerSaveBlocker::PowerSaveBlockerType type) { |
80 if (delegate_) | 87 if (delegate_) |
81 delegate_->ApplyBlock(type); | 88 delegate_->ApplyBlock(type); |
82 } | 89 } |
83 | 90 |
84 // Getter for the Bus object. Used by the Delegates to obtain object proxies. | 91 // Getter for the Bus object. Used by the Delegates to obtain object proxies. |
85 scoped_refptr<dbus::Bus> bus() const { return bus_; } | 92 scoped_refptr<dbus::Bus> bus() const { return bus_; } |
86 | 93 |
87 private: | 94 private: |
88 DBusPowerSaveBlocker(); | 95 DBusPowerSaveBlocker(); |
89 virtual ~DBusPowerSaveBlocker(); | 96 virtual ~DBusPowerSaveBlocker(); |
90 | 97 |
| 98 // If DPMS is not enabled, then we don't want to try to disable power saving, |
| 99 // since on some desktop environments that may enable DPMS with very poor |
| 100 // default settings (e.g. turning off the display after only 1 second). |
| 101 static bool DPMSEnabled(); |
| 102 |
91 // The D-Bus connection. | 103 // The D-Bus connection. |
92 scoped_refptr<dbus::Bus> bus_; | 104 scoped_refptr<dbus::Bus> bus_; |
93 | 105 |
94 // Concrete implementation of the Delegate interface. | 106 // Concrete implementation of the Delegate interface. |
95 scoped_refptr<Delegate> delegate_; | 107 scoped_refptr<Delegate> delegate_; |
96 | 108 |
97 friend struct DefaultSingletonTraits<DBusPowerSaveBlocker>; | 109 friend struct DefaultSingletonTraits<DBusPowerSaveBlocker>; |
98 | 110 |
99 DISALLOW_COPY_AND_ASSIGN(DBusPowerSaveBlocker); | 111 DISALLOW_COPY_AND_ASSIGN(DBusPowerSaveBlocker); |
100 }; | 112 }; |
101 | 113 |
102 // Delegate implementation for KDE4. | 114 // Delegate implementation for KDE4. It uses the |
103 // It uses the org.freedesktop.PowerManagement interface. | 115 // org.freedesktop.PowerManagement interface. It works on XFCE4, too. |
104 // Works on XFCE4, too. | |
105 class KDEPowerSaveBlocker : public DBusPowerSaveBlocker::Delegate { | 116 class KDEPowerSaveBlocker : public DBusPowerSaveBlocker::Delegate { |
106 public: | 117 public: |
107 KDEPowerSaveBlocker() | 118 KDEPowerSaveBlocker() |
108 : inhibit_cookie_(0), | 119 : inhibit_cookie_(0), |
109 pending_inhibit_call_(false), | 120 pending_inhibit_call_(false), |
110 postponed_uninhibit_call_(false) {} | 121 postponed_uninhibit_call_(false) { |
| 122 } |
111 ~KDEPowerSaveBlocker() {} | 123 ~KDEPowerSaveBlocker() {} |
112 | 124 |
113 virtual void ApplyBlock( | 125 virtual void ApplyBlock( |
114 PowerSaveBlocker::PowerSaveBlockerType type) OVERRIDE { | 126 PowerSaveBlocker::PowerSaveBlockerType type) OVERRIDE { |
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
116 DCHECK(pending_inhibit_call_ || !postponed_uninhibit_call_); | 128 DCHECK(pending_inhibit_call_ || !postponed_uninhibit_call_); |
117 | 129 |
118 // If we have a pending inhibit call, we add a postponed uninhibit | 130 // If we have a pending inhibit call, we add a postponed uninhibit |
119 // request, such that it will be canceled as soon as the response arrives. | 131 // request, such that it will be canceled as soon as the response arrives. |
120 // If we have an active inhibit request and receive a new one, | 132 // If we have an active inhibit request and receive a new one, |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 : inhibit_cookie_(0), | 258 : inhibit_cookie_(0), |
247 pending_inhibit_calls_(0), | 259 pending_inhibit_calls_(0), |
248 postponed_uninhibit_calls_(0) {} | 260 postponed_uninhibit_calls_(0) {} |
249 ~GnomePowerSaveBlocker() {} | 261 ~GnomePowerSaveBlocker() {} |
250 | 262 |
251 virtual void ApplyBlock( | 263 virtual void ApplyBlock( |
252 PowerSaveBlocker::PowerSaveBlockerType type) OVERRIDE { | 264 PowerSaveBlocker::PowerSaveBlockerType type) OVERRIDE { |
253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
254 DCHECK(postponed_uninhibit_calls_ <= pending_inhibit_calls_); | 266 DCHECK(postponed_uninhibit_calls_ <= pending_inhibit_calls_); |
255 | 267 |
256 // If we have a pending inhibit call, we add a postponed uninhibit | 268 // If we have a pending inhibit call, we add a postponed uninhibit request, |
257 // request, such that it will be canceled as soon as the response arrives. | 269 // such that it will be canceled as soon as the response arrives. We want to |
258 // We want to cancel the current inhibit request whether |type| is | 270 // cancel the current inhibit request whether |type| is |
259 // kPowerSaveBlockPreventNone or not. If |type| represents an inhibit | 271 // kPowerSaveBlockPreventNone or not. If |type| represents an inhibit |
260 // request, we are dealing with the same case as below, just that the | 272 // request, we are dealing with the same case as below, just that the reply |
261 // reply to the previous inhibit request did not arrive yet, so we have | 273 // to the previous inhibit request did not arrive yet, so we have to wait |
262 // to wait for the cookie in order to cancel it. | 274 // for the cookie in order to cancel it. Meanwhile, we can still make the |
263 // Meanwhile, we can still make the new request. | 275 // new request. |
264 // We also have to check that | 276 // |
265 // postponed_uninhibit_calls_ < pending_inhibit_calls_. | 277 // We also have to check that postponed_uninhibit_calls_ < |
266 // If this is not the case, then all the pending requests were already | 278 // pending_inhibit_calls_. If this is not the case, then all the pending |
267 // canceled and we should not increment the number of postponed uninhibit | 279 // requests were already canceled and we should not increment the number of |
268 // requests; otherwise we will cancel unwanted future inhibits, | 280 // postponed uninhibit requests; otherwise we will cancel unwanted future |
269 // that will be made after this call. | 281 // inhibits, that will be made after this call. |
270 // NOTE: The implementation is based on the fact that we receive | 282 // |
271 // the D-Bus replies in the same order in which the requests are made. | 283 // NOTE: The implementation is based on the fact that we receive the D-Bus |
| 284 // replies in the same order in which the requests are made. |
272 if (pending_inhibit_calls_ > 0 && | 285 if (pending_inhibit_calls_ > 0 && |
273 postponed_uninhibit_calls_ < pending_inhibit_calls_) { | 286 postponed_uninhibit_calls_ < pending_inhibit_calls_) { |
274 ++postponed_uninhibit_calls_; | 287 ++postponed_uninhibit_calls_; |
275 // If the call was an Uninhibit, then we are done for the moment. | 288 // If the call was an Uninhibit, then we are done for the moment. |
276 if (type == PowerSaveBlocker::kPowerSaveBlockPreventNone) | 289 if (type == PowerSaveBlocker::kPowerSaveBlockPreventNone) |
277 return; | 290 return; |
278 } | 291 } |
279 | 292 |
280 // If we have an active inhibit request and no pending inhibit calls, | 293 // If we have an active inhibit request and no pending inhibit calls, |
281 // we make an uninhibit request to cancel it now. | 294 // we make an uninhibit request to cancel it now. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 // org.gnome.SessionManager.Uninhibit(). | 351 // org.gnome.SessionManager.Uninhibit(). |
339 // It takes only one argument, the cookie that identifies | 352 // It takes only one argument, the cookie that identifies |
340 // the request we want to cancel. | 353 // the request we want to cancel. |
341 method_call.SetMember("Uninhibit"); | 354 method_call.SetMember("Uninhibit"); |
342 message_writer.AppendUint32(inhibit_cookie_); | 355 message_writer.AppendUint32(inhibit_cookie_); |
343 bus_callback = base::Bind(&GnomePowerSaveBlocker::OnUnInhibitResponse, | 356 bus_callback = base::Bind(&GnomePowerSaveBlocker::OnUnInhibitResponse, |
344 this); | 357 this); |
345 ++pending_inhibit_calls_; | 358 ++pending_inhibit_calls_; |
346 break; | 359 break; |
347 case PowerSaveBlocker::kPowerSaveBlockPreventStateCount: | 360 case PowerSaveBlocker::kPowerSaveBlockPreventStateCount: |
348 // This is an invalid argument; | 361 // This is an invalid argument. |
349 NOTREACHED(); | 362 NOTREACHED(); |
350 break; | 363 break; |
351 } | 364 } |
352 | 365 |
353 object_proxy->CallMethod(&method_call, | 366 object_proxy->CallMethod(&method_call, |
354 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | 367 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
355 bus_callback); | 368 bus_callback); |
356 } | 369 } |
357 | 370 |
358 private: | 371 private: |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 | 421 |
409 // Initialize the DBusPowerSaveBlocker instance: | 422 // Initialize the DBusPowerSaveBlocker instance: |
410 // 1. Instantiate a concrete delegate based on the current desktop environment, | 423 // 1. Instantiate a concrete delegate based on the current desktop environment, |
411 // 2. Instantiate the D-Bus object | 424 // 2. Instantiate the D-Bus object |
412 DBusPowerSaveBlocker::DBusPowerSaveBlocker() { | 425 DBusPowerSaveBlocker::DBusPowerSaveBlocker() { |
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
414 | 427 |
415 scoped_ptr<base::Environment> env(base::Environment::Create()); | 428 scoped_ptr<base::Environment> env(base::Environment::Create()); |
416 switch (base::nix::GetDesktopEnvironment(env.get())) { | 429 switch (base::nix::GetDesktopEnvironment(env.get())) { |
417 case base::nix::DESKTOP_ENVIRONMENT_GNOME: | 430 case base::nix::DESKTOP_ENVIRONMENT_GNOME: |
418 delegate_ = new GnomePowerSaveBlocker(); | 431 if (DPMSEnabled()) |
| 432 delegate_ = new GnomePowerSaveBlocker(); |
419 break; | 433 break; |
420 case base::nix::DESKTOP_ENVIRONMENT_XFCE: | 434 case base::nix::DESKTOP_ENVIRONMENT_XFCE: |
421 case base::nix::DESKTOP_ENVIRONMENT_KDE4: | 435 case base::nix::DESKTOP_ENVIRONMENT_KDE4: |
422 delegate_ = new KDEPowerSaveBlocker(); | 436 if (DPMSEnabled()) |
| 437 delegate_ = new KDEPowerSaveBlocker(); |
423 break; | 438 break; |
424 case base::nix::DESKTOP_ENVIRONMENT_KDE3: | 439 case base::nix::DESKTOP_ENVIRONMENT_KDE3: |
425 case base::nix::DESKTOP_ENVIRONMENT_OTHER: | 440 case base::nix::DESKTOP_ENVIRONMENT_OTHER: |
426 // Not supported, so we exit. | 441 // Not supported, so we exit. |
427 // We don't create D-Bus objects. | 442 // We don't create D-Bus objects. |
428 break; | 443 break; |
429 } | 444 } |
430 | 445 |
431 if (delegate_) { | 446 if (delegate_) { |
432 dbus::Bus::Options options; | 447 dbus::Bus::Options options; |
(...skipping 16 matching lines...) Expand all Loading... |
449 // we are at the very end of the shutting down phase. | 464 // we are at the very end of the shutting down phase. |
450 // Connection to D-Bus is just a Unix domain socket, which is not | 465 // Connection to D-Bus is just a Unix domain socket, which is not |
451 // a persistent resource, hence the operating system will take care | 466 // a persistent resource, hence the operating system will take care |
452 // of closing it when the process terminates. | 467 // of closing it when the process terminates. |
453 if (BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { | 468 if (BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) { |
454 bus_->ShutdownOnDBusThreadAndBlock(); | 469 bus_->ShutdownOnDBusThreadAndBlock(); |
455 } | 470 } |
456 } | 471 } |
457 | 472 |
458 // static | 473 // static |
| 474 bool DBusPowerSaveBlocker::DPMSEnabled() { |
| 475 #if defined(TOOLKIT_GTK) |
| 476 Display* display = base::MessagePumpGtk::GetDefaultXDisplay(); |
| 477 #else |
| 478 Display* display = base::MessagePumpX::GetDefaultXDisplay(); |
| 479 #endif |
| 480 BOOL enabled = false; |
| 481 int dummy; |
| 482 if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) { |
| 483 CARD16 state; |
| 484 DPMSInfo(display, &state, &enabled); |
| 485 } |
| 486 return enabled; |
| 487 } |
| 488 |
| 489 // static |
459 DBusPowerSaveBlocker* DBusPowerSaveBlocker::GetInstance() { | 490 DBusPowerSaveBlocker* DBusPowerSaveBlocker::GetInstance() { |
460 return Singleton<DBusPowerSaveBlocker>::get(); | 491 return Singleton<DBusPowerSaveBlocker>::get(); |
461 } | 492 } |
462 | 493 |
463 } // namespace | 494 } // namespace |
464 | 495 |
465 // Called only from UI thread. | 496 // Called only from UI thread. |
466 // static | 497 // static |
467 void PowerSaveBlocker::ApplyBlock(PowerSaveBlockerType type) { | 498 void PowerSaveBlocker::ApplyBlock(PowerSaveBlockerType type) { |
468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
469 DBusPowerSaveBlocker::GetInstance()->ApplyBlock(type); | 500 DBusPowerSaveBlocker::GetInstance()->ApplyBlock(type); |
470 } | 501 } |
OLD | NEW |