Chromium Code Reviews| Index: content/browser/power_save_blocker_linux.cc |
| =================================================================== |
| --- content/browser/power_save_blocker_linux.cc (revision 141149) |
| +++ content/browser/power_save_blocker_linux.cc (working copy) |
| @@ -504,3 +504,259 @@ |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DBusPowerSaveBlocker::GetInstance()->ApplyBlock(type); |
| } |
| + |
| +// TODO(mdm): remove from the beginning of the file up to (and including) this |
| +// line to switch to the new implementation. |
| +#define PowerSaveBlocker PowerSaveBlocker2 |
| + |
| +namespace { |
| + |
| +enum DBusAPI { |
| + kNoAPI, // Disable. No supported API available. |
|
sky
2012/06/08 20:44:41
enum style in chrome is ALL_CAPS.
Mike Mammarella
2012/06/08 20:52:34
Ah yes, I always forget this difference between th
|
| + kGnomeAPI, // Use the GNOME API. (Supports more features.) |
| + kFreeDesktopAPI, // Use the FreeDesktop API, for KDE4 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 { |
| + kInhibitLogOut = 1, |
| + kInhibitSwitchUser = 2, |
| + kInhibitSuspendSession = 4, |
| + kInhibitMarkSessionAsIdle = 8 |
| +}; |
| + |
| +const char kGnomeAPIServiceName[] = "org.gnome.SessionManager"; |
| +const char kGnomeAPIInterfaceName[] = "org.gnome.SessionManager"; |
| +const char kGnomeAPIObjectPath[] = "/org/gnome/SessionManager"; |
| + |
| +const char kFreeDesktopAPIServiceName[] = "org.freedesktop.PowerManagement"; |
| +const char kFreeDesktopAPIInterfaceName[] = |
| + "org.freedesktop.PowerManagement.Inhibit"; |
| +const char kFreeDesktopAPIObjectPath[] = |
| + "/org/freedesktop/PowerManagement/Inhibit"; |
| + |
| +} // anonymous namespace |
| + |
| +namespace content { |
| + |
| +class PowerSaveBlocker::Delegate |
| + : public base::RefCountedThreadSafe<PowerSaveBlocker::Delegate> { |
| + public: |
| + // Picks an appropriate D-Bus API to use based on the desktop environment. |
| + Delegate(PowerSaveBlockerType type, const std::string& reason); |
| + |
| + // 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 IO. |
| + void ApplyBlock(); |
| + void RemoveBlock(); |
| + |
| + private: |
| + friend class base::RefCountedThreadSafe<Delegate>; |
| + ~Delegate() {} |
| + |
| + // 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). |
| + static bool DPMSEnabled(); |
| + |
| + // Returns an appropriate D-Bus API to use based on the desktop environment. |
| + static DBusAPI SelectAPI(); |
| + |
| + const PowerSaveBlockerType type_; |
| + const std::string reason_; |
| + const DBusAPI api_; |
| + |
| + scoped_refptr<dbus::Bus> bus_; |
| + |
| + // The cookie that identifies our inhibit request, |
| + // or 0 if there is no active inhibit request. |
| + uint32 inhibit_cookie_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Delegate); |
| +}; |
| + |
| +PowerSaveBlocker::Delegate::Delegate(PowerSaveBlockerType type, |
| + const std::string& reason) |
| + : type_(type), reason_(reason), api_(SelectAPI()) { |
|
sky
2012/06/08 20:44:41
each on its own line, and initialize inhibit_cooki
Mike Mammarella
2012/06/08 20:52:34
Done. (Another difference in the style guides!)
|
| + // We're on the client's thread here, so we don't allocate the dbus::Bus |
| + // object yet. We'll do it below in ApplyBlock(), on the FILE thread. |
| +} |
| + |
| +void PowerSaveBlocker::Delegate::ApplyBlock() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK(!bus_.get()); // ApplyBlock() should only be called once. |
| + if (api_ == kNoAPI) |
| + return; |
| + |
| + 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; |
| + scoped_ptr<dbus::MethodCall> method_call; |
| + scoped_ptr<dbus::MessageWriter> message_writer; |
| + |
| + switch (api_) { |
| + case kNoAPI: |
| + NOTREACHED(); // We return early above. |
| + return; |
| + case kGnomeAPI: |
| + 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( |
| + CommandLine::ForCurrentProcess()->GetProgram().value()); |
| + message_writer->AppendUint32(0); // should be toplevel_xid |
| + message_writer->AppendString(reason_); |
| + { |
| + uint32 flags = 0; |
| + switch (type_) { |
| + case kPowerSaveBlockPreventDisplaySleep: |
| + flags |= kInhibitMarkSessionAsIdle; |
| + flags |= kInhibitSuspendSession; |
| + break; |
| + case kPowerSaveBlockPreventAppSuspension: |
| + flags |= kInhibitSuspendSession; |
| + break; |
| + } |
| + message_writer->AppendUint32(flags); |
| + } |
| + break; |
| + case kFreeDesktopAPI: |
| + object_proxy = bus_->GetObjectProxy( |
| + kFreeDesktopAPIServiceName, |
| + dbus::ObjectPath(kFreeDesktopAPIObjectPath)); |
| + method_call.reset( |
| + new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "Inhibit")); |
| + 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( |
| + CommandLine::ForCurrentProcess()->GetProgram().value()); |
| + message_writer->AppendString(reason_); |
| + break; |
| + } |
| + |
| + // We could do this method call asynchronously, but if we did, we'd need to |
| + // handle the case where we want to cancel the block before we get a reply. |
| + // We're on the FILE thread so it should be OK to block briefly here. |
| + scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( |
| + method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| + if (response.get()) { |
| + // 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.get()); |
| + if (!message_reader.PopUint32(&inhibit_cookie_)) |
| + LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString(); |
| + } else { |
| + LOG(ERROR) << "No response to Inhibit() request!"; |
| + } |
| +} |
| + |
| +void PowerSaveBlocker::Delegate::RemoveBlock() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + if (api_ == kNoAPI) |
| + return; |
| + DCHECK(bus_.get()); // RemoveBlock() should only be called once. |
| + |
| + scoped_refptr<dbus::ObjectProxy> object_proxy; |
| + scoped_ptr<dbus::MethodCall> method_call; |
| + |
| + switch (api_) { |
| + case kNoAPI: |
| + NOTREACHED(); // We return early above. |
| + return; |
| + case kGnomeAPI: |
| + object_proxy = bus_->GetObjectProxy( |
| + kGnomeAPIServiceName, |
| + dbus::ObjectPath(kGnomeAPIObjectPath)); |
| + method_call.reset( |
| + new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); |
| + break; |
| + case kFreeDesktopAPI: |
| + object_proxy = bus_->GetObjectProxy( |
| + kFreeDesktopAPIServiceName, |
| + dbus::ObjectPath(kFreeDesktopAPIObjectPath)); |
| + method_call.reset( |
| + new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "Uninhibit")); |
| + break; |
| + } |
| + |
| + dbus::MessageWriter message_writer(method_call.get()); |
| + message_writer.AppendUint32(inhibit_cookie_); |
| + scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( |
| + method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| + if (!response.get()) |
| + 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_ = NULL; |
| +} |
| + |
| +// static |
| +bool PowerSaveBlocker::Delegate::DPMSEnabled() { |
| + Display* display = base::MessagePumpForUI::GetDefaultXDisplay(); |
| + BOOL enabled = false; |
| + int dummy; |
| + if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) { |
| + CARD16 state; |
| + DPMSInfo(display, &state, &enabled); |
| + } |
| + return enabled; |
| +} |
| + |
| +// static |
| +DBusAPI PowerSaveBlocker::Delegate::SelectAPI() { |
| + scoped_ptr<base::Environment> env(base::Environment::Create()); |
| + switch (base::nix::GetDesktopEnvironment(env.get())) { |
| + case base::nix::DESKTOP_ENVIRONMENT_GNOME: |
| + if (DPMSEnabled()) |
| + return kGnomeAPI; |
| + break; |
| + case base::nix::DESKTOP_ENVIRONMENT_XFCE: |
| + case base::nix::DESKTOP_ENVIRONMENT_KDE4: |
| + if (DPMSEnabled()) |
| + return kFreeDesktopAPI; |
| + break; |
| + case base::nix::DESKTOP_ENVIRONMENT_KDE3: |
| + case base::nix::DESKTOP_ENVIRONMENT_OTHER: |
| + // Not supported. |
| + break; |
| + } |
| + return kNoAPI; |
| +} |
| + |
| +PowerSaveBlocker::PowerSaveBlocker( |
| + PowerSaveBlockerType type, const std::string& reason) |
| + : delegate_(new Delegate(type, reason)) { |
| + // 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 below in the destructor. It must |
| + // be a thread that allows I/O operations. |
| + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&Delegate::ApplyBlock, delegate_)); |
| +} |
| + |
| +PowerSaveBlocker::~PowerSaveBlocker() { |
| + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&Delegate::RemoveBlock, delegate_)); |
| +} |
| + |
| +} // namespace content |