Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Unified Diff: content/browser/power_save_blocker_x11.cc

Issue 2075973002: Revert of Move content/browser/power_save_blocker to //device/power_save_blocker (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@power-save-next-2
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/power_save_blocker_win.cc ('k') | content/browser/renderer_host/render_widget_host_impl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/power_save_blocker_x11.cc
diff --git a/content/browser/power_save_blocker_x11.cc b/content/browser/power_save_blocker_x11.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a177e392ce1466f2469fe0f76908cd1c09959d7d
--- /dev/null
+++ b/content/browser/power_save_blocker_x11.cc
@@ -0,0 +1,508 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <X11/Xlib.h>
+#include <X11/extensions/dpms.h>
+#include <X11/extensions/scrnsaver.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "content/browser/power_save_blocker_impl.h"
+// Xlib #defines Status, but we can't have that for some of our headers.
+#ifdef Status
+#undef Status
+#endif
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/nix/xdg_util.h"
+#include "base/synchronization/lock.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace {
+
+enum DBusAPI {
+ NO_API, // Disable. No supported API available.
+ GNOME_API, // Use the GNOME API. (Supports more features.)
+ FREEDESKTOP_API, // Use the FreeDesktop API, for KDE4, KDE5, and XFCE.
+};
+
+// Inhibit flags defined in the org.gnome.SessionManager interface.
+// Can be OR'd together and passed as argument to the Inhibit() method
+// to specify which power management features we want to suspend.
+enum GnomeAPIInhibitFlags {
+ INHIBIT_LOGOUT = 1,
+ INHIBIT_SWITCH_USER = 2,
+ INHIBIT_SUSPEND_SESSION = 4,
+ INHIBIT_MARK_SESSION_IDLE = 8
+};
+
+const char kGnomeAPIServiceName[] = "org.gnome.SessionManager";
+const char kGnomeAPIInterfaceName[] = "org.gnome.SessionManager";
+const char kGnomeAPIObjectPath[] = "/org/gnome/SessionManager";
+
+const char kFreeDesktopAPIPowerServiceName[] =
+ "org.freedesktop.PowerManagement";
+const char kFreeDesktopAPIPowerInterfaceName[] =
+ "org.freedesktop.PowerManagement.Inhibit";
+const char kFreeDesktopAPIPowerObjectPath[] =
+ "/org/freedesktop/PowerManagement/Inhibit";
+
+const char kFreeDesktopAPIScreenServiceName[] = "org.freedesktop.ScreenSaver";
+const char kFreeDesktopAPIScreenInterfaceName[] = "org.freedesktop.ScreenSaver";
+const char kFreeDesktopAPIScreenObjectPath[] = "/org/freedesktop/ScreenSaver";
+
+} // namespace
+
+namespace content {
+
+class PowerSaveBlockerImpl::Delegate
+ : public base::RefCountedThreadSafe<PowerSaveBlockerImpl::Delegate> {
+ public:
+ // Picks an appropriate D-Bus API to use based on the desktop environment.
+ Delegate(PowerSaveBlockerType type,
+ const std::string& description,
+ bool freedesktop_only,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner);
+
+ // Post a task to initialize the delegate on the UI thread, which will itself
+ // then post a task to apply the power save block on the FILE thread.
+ void Init();
+
+ // Post a task to remove the power save block on the FILE thread, unless it
+ // hasn't yet been applied, in which case we just prevent it from applying.
+ void CleanUp();
+
+ private:
+ friend class base::RefCountedThreadSafe<Delegate>;
+ ~Delegate() {}
+
+ // Selects an appropriate D-Bus API to use for this object. Must be called on
+ // the UI thread. Checks enqueue_apply_ once an API has been selected, and
+ // enqueues a call back to ApplyBlock() if it is true. See the comments for
+ // enqueue_apply_ below.
+ void InitOnUIThread();
+
+ // Returns true if ApplyBlock() / RemoveBlock() should be called.
+ bool ShouldBlock() const;
+
+ // Apply or remove the power save block, respectively. These methods should be
+ // called once each, on the same thread, per instance. They block waiting for
+ // the action to complete (with a timeout); the thread must thus allow I/O.
+ void ApplyBlock();
+ void RemoveBlock();
+
+ // Asynchronous callback functions for ApplyBlock and RemoveBlock.
+ // Functions do not receive ownership of |response|.
+ void ApplyBlockFinished(dbus::Response* response);
+ void RemoveBlockFinished(dbus::Response* response);
+
+ // Wrapper for XScreenSaverSuspend. Checks whether the X11 Screen Saver
+ // Extension is available first. If it isn't, this is a no-op.
+ // Must be called on the UI thread.
+ void XSSSuspendSet(bool suspend);
+
+ // If DPMS (the power saving system in X11) is not enabled, then we don't want
+ // to try to disable power saving, since on some desktop environments that may
+ // enable DPMS with very poor default settings (e.g. turning off the display
+ // after only 1 second). Must be called on the UI thread.
+ bool DPMSEnabled();
+
+ // If no other method is available (i.e. not running under a Desktop
+ // Environment) check whether the X11 Screen Saver Extension can be used
+ // to disable the screen saver. Must be called on the UI thread.
+ bool XSSAvailable();
+
+ // Returns an appropriate D-Bus API to use based on the desktop environment.
+ // Must be called on the UI thread, as it may call DPMSEnabled() above.
+ DBusAPI SelectAPI();
+
+ const PowerSaveBlockerType type_;
+ const std::string description_;
+ const bool freedesktop_only_;
+
+ // Initially, we post a message to the UI thread to select an API. When it
+ // finishes, it will post a message to the FILE thread to perform the actual
+ // application of the block, unless enqueue_apply_ is false. We set it to
+ // false when we post that message, or when RemoveBlock() is called before
+ // ApplyBlock() has run. Both api_ and enqueue_apply_ are guarded by lock_.
+ DBusAPI api_;
+ bool enqueue_apply_;
+ base::Lock lock_;
+
+ // Indicates that a D-Bus power save blocking request is in flight.
+ bool block_inflight_;
+ // Used to detect erronous redundant calls to RemoveBlock().
+ bool unblock_inflight_;
+ // Indicates that RemoveBlock() is called before ApplyBlock() has finished.
+ // If it's true, then the RemoveBlock() call will be processed immediately
+ // after ApplyBlock() has finished.
+ bool enqueue_unblock_;
+
+ scoped_refptr<dbus::Bus> bus_;
+
+ // The cookie that identifies our inhibit request,
+ // or 0 if there is no active inhibit request.
+ uint32_t inhibit_cookie_;
+
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+};
+
+PowerSaveBlockerImpl::Delegate::Delegate(
+ PowerSaveBlockerType type,
+ const std::string& description,
+ bool freedesktop_only,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+ : type_(type),
+ description_(description),
+ freedesktop_only_(freedesktop_only),
+ api_(NO_API),
+ enqueue_apply_(false),
+ inhibit_cookie_(0),
+ ui_task_runner_(ui_task_runner),
+ blocking_task_runner_(blocking_task_runner) {
+ // We're on the client's thread here, so we don't allocate the dbus::Bus
+ // object yet. We'll do it later in ApplyBlock(), on the FILE thread.
+}
+
+void PowerSaveBlockerImpl::Delegate::Init() {
+ base::AutoLock lock(lock_);
+ DCHECK(!enqueue_apply_);
+ enqueue_apply_ = true;
+ block_inflight_ = false;
+ unblock_inflight_ = false;
+ enqueue_unblock_ = false;
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Delegate::InitOnUIThread, this));
+}
+
+void PowerSaveBlockerImpl::Delegate::CleanUp() {
+ base::AutoLock lock(lock_);
+ if (enqueue_apply_) {
+ // If a call to ApplyBlock() has not yet been enqueued because we are still
+ // initializing on the UI thread, then just cancel it. We don't need to
+ // remove the block because we haven't even applied it yet.
+ enqueue_apply_ = false;
+ } else {
+ if (ShouldBlock()) {
+ blocking_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Delegate::RemoveBlock, this));
+ }
+
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&Delegate::XSSSuspendSet, this, false));
+ }
+}
+
+void PowerSaveBlockerImpl::Delegate::InitOnUIThread() {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ base::AutoLock lock(lock_);
+ api_ = SelectAPI();
+
+ if (enqueue_apply_) {
+ if (ShouldBlock()) {
+ // The thread we use here becomes the origin and D-Bus thread for the
+ // D-Bus library, so we need to use the same thread above for
+ // RemoveBlock(). It must be a thread that allows I/O operations, so we
+ // use the FILE thread.
+ blocking_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Delegate::ApplyBlock, this));
+ }
+ XSSSuspendSet(true);
+ }
+ enqueue_apply_ = false;
+}
+
+bool PowerSaveBlockerImpl::Delegate::ShouldBlock() const {
+ return freedesktop_only_ ? api_ == FREEDESKTOP_API : api_ != NO_API;
+}
+
+void PowerSaveBlockerImpl::Delegate::ApplyBlock() {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!bus_); // ApplyBlock() should only be called once.
+ DCHECK(!block_inflight_);
+
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SESSION;
+ options.connection_type = dbus::Bus::PRIVATE;
+ bus_ = new dbus::Bus(options);
+
+ scoped_refptr<dbus::ObjectProxy> object_proxy;
+ std::unique_ptr<dbus::MethodCall> method_call;
+ std::unique_ptr<dbus::MessageWriter> message_writer;
+
+ switch (api_) {
+ case NO_API:
+ NOTREACHED(); // We should never call this method with this value.
+ return;
+ case GNOME_API:
+ object_proxy = bus_->GetObjectProxy(
+ kGnomeAPIServiceName,
+ dbus::ObjectPath(kGnomeAPIObjectPath));
+ method_call.reset(
+ new dbus::MethodCall(kGnomeAPIInterfaceName, "Inhibit"));
+ message_writer.reset(new dbus::MessageWriter(method_call.get()));
+ // The arguments of the method are:
+ // app_id: The application identifier
+ // toplevel_xid: The toplevel X window identifier
+ // reason: The reason for the inhibit
+ // flags: Flags that spefify what should be inhibited
+ message_writer->AppendString(
+ base::CommandLine::ForCurrentProcess()->GetProgram().value());
+ message_writer->AppendUint32(0); // should be toplevel_xid
+ message_writer->AppendString(description_);
+ {
+ uint32_t flags = 0;
+ switch (type_) {
+ case kPowerSaveBlockPreventDisplaySleep:
+ flags |= INHIBIT_MARK_SESSION_IDLE;
+ flags |= INHIBIT_SUSPEND_SESSION;
+ break;
+ case kPowerSaveBlockPreventAppSuspension:
+ flags |= INHIBIT_SUSPEND_SESSION;
+ break;
+ }
+ message_writer->AppendUint32(flags);
+ }
+ break;
+ case FREEDESKTOP_API:
+ switch (type_) {
+ case kPowerSaveBlockPreventDisplaySleep:
+ object_proxy = bus_->GetObjectProxy(
+ kFreeDesktopAPIScreenServiceName,
+ dbus::ObjectPath(kFreeDesktopAPIScreenObjectPath));
+ method_call.reset(new dbus::MethodCall(
+ kFreeDesktopAPIScreenInterfaceName, "Inhibit"));
+ break;
+ case kPowerSaveBlockPreventAppSuspension:
+ object_proxy = bus_->GetObjectProxy(
+ kFreeDesktopAPIPowerServiceName,
+ dbus::ObjectPath(kFreeDesktopAPIPowerObjectPath));
+ method_call.reset(new dbus::MethodCall(
+ kFreeDesktopAPIPowerInterfaceName, "Inhibit"));
+ break;
+ }
+ message_writer.reset(new dbus::MessageWriter(method_call.get()));
+ // The arguments of the method are:
+ // app_id: The application identifier
+ // reason: The reason for the inhibit
+ message_writer->AppendString(
+ base::CommandLine::ForCurrentProcess()->GetProgram().value());
+ message_writer->AppendString(description_);
+ break;
+ }
+
+ block_inflight_ = true;
+ object_proxy->CallMethod(
+ method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&PowerSaveBlockerImpl::Delegate::ApplyBlockFinished, this));
+}
+
+void PowerSaveBlockerImpl::Delegate::ApplyBlockFinished(
+ dbus::Response* response) {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(bus_);
+ DCHECK(block_inflight_);
+ block_inflight_ = false;
+
+ if (response) {
+ // The method returns an inhibit_cookie, used to uniquely identify
+ // this request. It should be used as an argument to Uninhibit()
+ // in order to remove the request.
+ dbus::MessageReader message_reader(response);
+ if (!message_reader.PopUint32(&inhibit_cookie_))
+ LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString();
+ } else {
+ LOG(ERROR) << "No response to Inhibit() request!";
+ }
+
+ if (enqueue_unblock_) {
+ enqueue_unblock_ = false;
+ // RemoveBlock() was called while the Inhibit operation was in flight,
+ // so go ahead and remove the block now.
+ blocking_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Delegate::RemoveBlock, this));
+ }
+}
+
+void PowerSaveBlockerImpl::Delegate::RemoveBlock() {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(bus_); // RemoveBlock() should only be called once.
+ DCHECK(!unblock_inflight_);
+
+ if (block_inflight_) {
+ DCHECK(!enqueue_unblock_);
+ // Can't call RemoveBlock until ApplyBlock's async operation has
+ // finished. Enqueue it for execution once ApplyBlock is done.
+ enqueue_unblock_ = true;
+ return;
+ }
+
+ scoped_refptr<dbus::ObjectProxy> object_proxy;
+ std::unique_ptr<dbus::MethodCall> method_call;
+
+ switch (api_) {
+ case NO_API:
+ NOTREACHED(); // We should never call this method with this value.
+ return;
+ case GNOME_API:
+ object_proxy = bus_->GetObjectProxy(
+ kGnomeAPIServiceName,
+ dbus::ObjectPath(kGnomeAPIObjectPath));
+ method_call.reset(
+ new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit"));
+ break;
+ case FREEDESKTOP_API:
+ switch (type_) {
+ case kPowerSaveBlockPreventDisplaySleep:
+ object_proxy = bus_->GetObjectProxy(
+ kFreeDesktopAPIScreenServiceName,
+ dbus::ObjectPath(kFreeDesktopAPIScreenObjectPath));
+ method_call.reset(new dbus::MethodCall(
+ kFreeDesktopAPIScreenInterfaceName, "UnInhibit"));
+ break;
+ case kPowerSaveBlockPreventAppSuspension:
+ object_proxy = bus_->GetObjectProxy(
+ kFreeDesktopAPIPowerServiceName,
+ dbus::ObjectPath(kFreeDesktopAPIPowerObjectPath));
+ method_call.reset(new dbus::MethodCall(
+ kFreeDesktopAPIPowerInterfaceName, "UnInhibit"));
+ break;
+ }
+ break;
+ }
+
+ dbus::MessageWriter message_writer(method_call.get());
+ message_writer.AppendUint32(inhibit_cookie_);
+ unblock_inflight_ = true;
+ object_proxy->CallMethod(
+ method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&PowerSaveBlockerImpl::Delegate::RemoveBlockFinished, this));
+}
+
+void PowerSaveBlockerImpl::Delegate::RemoveBlockFinished(
+ dbus::Response* response) {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(bus_);
+ unblock_inflight_ = false;
+
+ if (!response)
+ LOG(ERROR) << "No response to Uninhibit() request!";
+ // We don't care about checking the result. We assume it works; we can't
+ // really do anything about it anyway if it fails.
+ inhibit_cookie_ = 0;
+
+ bus_->ShutdownAndBlock();
+ bus_ = nullptr;
+}
+
+void PowerSaveBlockerImpl::Delegate::XSSSuspendSet(bool suspend) {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ if (!XSSAvailable())
+ return;
+
+ XDisplay* display = gfx::GetXDisplay();
+ XScreenSaverSuspend(display, suspend);
+}
+
+bool PowerSaveBlockerImpl::Delegate::DPMSEnabled() {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ XDisplay* display = gfx::GetXDisplay();
+ BOOL enabled = false;
+ int dummy;
+ if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) {
+ CARD16 state;
+ DPMSInfo(display, &state, &enabled);
+ }
+ return enabled;
+}
+
+bool PowerSaveBlockerImpl::Delegate::XSSAvailable() {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ XDisplay* display = gfx::GetXDisplay();
+ int dummy;
+ int major;
+ int minor;
+
+ if (!XScreenSaverQueryExtension(display, &dummy, &dummy))
+ return false;
+
+ if (!XScreenSaverQueryVersion(display, &major, &minor))
+ return false;
+
+ return major > 1 || (major == 1 && minor >= 1);
+}
+
+DBusAPI PowerSaveBlockerImpl::Delegate::SelectAPI() {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+ switch (base::nix::GetDesktopEnvironment(env.get())) {
+ case base::nix::DESKTOP_ENVIRONMENT_GNOME:
+ case base::nix::DESKTOP_ENVIRONMENT_UNITY:
+ if (DPMSEnabled())
+ return GNOME_API;
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_XFCE:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE5:
+ if (DPMSEnabled())
+ return FREEDESKTOP_API;
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_KDE3:
+ case base::nix::DESKTOP_ENVIRONMENT_OTHER:
+ // Not supported.
+ break;
+ }
+ return NO_API;
+}
+
+PowerSaveBlockerImpl::PowerSaveBlockerImpl(
+ PowerSaveBlockerType type,
+ Reason reason,
+ const std::string& description,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> blocking_task_runner)
+ : delegate_(new Delegate(type,
+ description,
+ false /* freedesktop_only */,
+ ui_task_runner,
+ blocking_task_runner)),
+ ui_task_runner_(ui_task_runner),
+ blocking_task_runner_(blocking_task_runner) {
+ delegate_->Init();
+
+ if (type == kPowerSaveBlockPreventDisplaySleep) {
+ freedesktop_suspend_delegate_ = new Delegate(
+ kPowerSaveBlockPreventAppSuspension, description,
+ true /* freedesktop_only */, ui_task_runner, blocking_task_runner);
+ freedesktop_suspend_delegate_->Init();
+ }
+}
+
+PowerSaveBlockerImpl::~PowerSaveBlockerImpl() {
+ delegate_->CleanUp();
+ if (freedesktop_suspend_delegate_)
+ freedesktop_suspend_delegate_->CleanUp();
+}
+
+} // namespace content
« no previous file with comments | « content/browser/power_save_blocker_win.cc ('k') | content/browser/renderer_host/render_widget_host_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698