Chromium Code Reviews| 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> | 7 #include <X11/Xlib.h> |
| 8 #include <X11/extensions/dpms.h> | 8 #include <X11/extensions/dpms.h> |
| 9 // Xlib #defines Status, but we can't have that for some of our headers. | 9 // Xlib #defines Status, but we can't have that for some of our headers. |
| 10 #ifdef Status | 10 #ifdef Status |
| (...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 497 } | 497 } |
| 498 | 498 |
| 499 } // namespace | 499 } // namespace |
| 500 | 500 |
| 501 // Called only from UI thread. | 501 // Called only from UI thread. |
| 502 // static | 502 // static |
| 503 void PowerSaveBlocker::ApplyBlock(PowerSaveBlockerType type) { | 503 void PowerSaveBlocker::ApplyBlock(PowerSaveBlockerType type) { |
| 504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 505 DBusPowerSaveBlocker::GetInstance()->ApplyBlock(type); | 505 DBusPowerSaveBlocker::GetInstance()->ApplyBlock(type); |
| 506 } | 506 } |
| 507 | |
| 508 // TODO(mdm): remove from the beginning of the file up to (and including) this | |
| 509 // line to switch to the new implementation. | |
| 510 #define PowerSaveBlocker PowerSaveBlocker2 | |
| 511 | |
| 512 namespace { | |
| 513 | |
| 514 enum DBusAPI { | |
| 515 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
| |
| 516 kGnomeAPI, // Use the GNOME API. (Supports more features.) | |
| 517 kFreeDesktopAPI, // Use the FreeDesktop API, for KDE4 and XFCE. | |
| 518 }; | |
| 519 | |
| 520 // Inhibit flags defined in the org.gnome.SessionManager interface. | |
| 521 // Can be OR'd together and passed as argument to the Inhibit() method | |
| 522 // to specify which power management features we want to suspend. | |
| 523 enum GnomeAPIInhibitFlags { | |
| 524 kInhibitLogOut = 1, | |
| 525 kInhibitSwitchUser = 2, | |
| 526 kInhibitSuspendSession = 4, | |
| 527 kInhibitMarkSessionAsIdle = 8 | |
| 528 }; | |
| 529 | |
| 530 const char kGnomeAPIServiceName[] = "org.gnome.SessionManager"; | |
| 531 const char kGnomeAPIInterfaceName[] = "org.gnome.SessionManager"; | |
| 532 const char kGnomeAPIObjectPath[] = "/org/gnome/SessionManager"; | |
| 533 | |
| 534 const char kFreeDesktopAPIServiceName[] = "org.freedesktop.PowerManagement"; | |
| 535 const char kFreeDesktopAPIInterfaceName[] = | |
| 536 "org.freedesktop.PowerManagement.Inhibit"; | |
| 537 const char kFreeDesktopAPIObjectPath[] = | |
| 538 "/org/freedesktop/PowerManagement/Inhibit"; | |
| 539 | |
| 540 } // anonymous namespace | |
| 541 | |
| 542 namespace content { | |
| 543 | |
| 544 class PowerSaveBlocker::Delegate | |
| 545 : public base::RefCountedThreadSafe<PowerSaveBlocker::Delegate> { | |
| 546 public: | |
| 547 // Picks an appropriate D-Bus API to use based on the desktop environment. | |
| 548 Delegate(PowerSaveBlockerType type, const std::string& reason); | |
| 549 | |
| 550 // Apply or remove the power save block, respectively. These methods should be | |
| 551 // called once each, on the same thread, per instance. They block waiting for | |
| 552 // the action to complete (with a timeout); the thread must thus allow IO. | |
| 553 void ApplyBlock(); | |
| 554 void RemoveBlock(); | |
| 555 | |
| 556 private: | |
| 557 friend class base::RefCountedThreadSafe<Delegate>; | |
| 558 ~Delegate() {} | |
| 559 | |
| 560 // If DPMS (the power saving system in X11) is not enabled, then we don't want | |
| 561 // to try to disable power saving, since on some desktop environments that may | |
| 562 // enable DPMS with very poor default settings (e.g. turning off the display | |
| 563 // after only 1 second). | |
| 564 static bool DPMSEnabled(); | |
| 565 | |
| 566 // Returns an appropriate D-Bus API to use based on the desktop environment. | |
| 567 static DBusAPI SelectAPI(); | |
| 568 | |
| 569 const PowerSaveBlockerType type_; | |
| 570 const std::string reason_; | |
| 571 const DBusAPI api_; | |
| 572 | |
| 573 scoped_refptr<dbus::Bus> bus_; | |
| 574 | |
| 575 // The cookie that identifies our inhibit request, | |
| 576 // or 0 if there is no active inhibit request. | |
| 577 uint32 inhibit_cookie_; | |
| 578 | |
| 579 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 580 }; | |
| 581 | |
| 582 PowerSaveBlocker::Delegate::Delegate(PowerSaveBlockerType type, | |
| 583 const std::string& reason) | |
| 584 : 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!)
| |
| 585 // We're on the client's thread here, so we don't allocate the dbus::Bus | |
| 586 // object yet. We'll do it below in ApplyBlock(), on the FILE thread. | |
| 587 } | |
| 588 | |
| 589 void PowerSaveBlocker::Delegate::ApplyBlock() { | |
| 590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 591 DCHECK(!bus_.get()); // ApplyBlock() should only be called once. | |
| 592 if (api_ == kNoAPI) | |
| 593 return; | |
| 594 | |
| 595 dbus::Bus::Options options; | |
| 596 options.bus_type = dbus::Bus::SESSION; | |
| 597 options.connection_type = dbus::Bus::PRIVATE; | |
| 598 bus_ = new dbus::Bus(options); | |
| 599 | |
| 600 scoped_refptr<dbus::ObjectProxy> object_proxy; | |
| 601 scoped_ptr<dbus::MethodCall> method_call; | |
| 602 scoped_ptr<dbus::MessageWriter> message_writer; | |
| 603 | |
| 604 switch (api_) { | |
| 605 case kNoAPI: | |
| 606 NOTREACHED(); // We return early above. | |
| 607 return; | |
| 608 case kGnomeAPI: | |
| 609 object_proxy = bus_->GetObjectProxy( | |
| 610 kGnomeAPIServiceName, | |
| 611 dbus::ObjectPath(kGnomeAPIObjectPath)); | |
| 612 method_call.reset( | |
| 613 new dbus::MethodCall(kGnomeAPIInterfaceName, "Inhibit")); | |
| 614 message_writer.reset(new dbus::MessageWriter(method_call.get())); | |
| 615 // The arguments of the method are: | |
| 616 // app_id: The application identifier | |
| 617 // toplevel_xid: The toplevel X window identifier | |
| 618 // reason: The reason for the inhibit | |
| 619 // flags: Flags that spefify what should be inhibited | |
| 620 message_writer->AppendString( | |
| 621 CommandLine::ForCurrentProcess()->GetProgram().value()); | |
| 622 message_writer->AppendUint32(0); // should be toplevel_xid | |
| 623 message_writer->AppendString(reason_); | |
| 624 { | |
| 625 uint32 flags = 0; | |
| 626 switch (type_) { | |
| 627 case kPowerSaveBlockPreventDisplaySleep: | |
| 628 flags |= kInhibitMarkSessionAsIdle; | |
| 629 flags |= kInhibitSuspendSession; | |
| 630 break; | |
| 631 case kPowerSaveBlockPreventAppSuspension: | |
| 632 flags |= kInhibitSuspendSession; | |
| 633 break; | |
| 634 } | |
| 635 message_writer->AppendUint32(flags); | |
| 636 } | |
| 637 break; | |
| 638 case kFreeDesktopAPI: | |
| 639 object_proxy = bus_->GetObjectProxy( | |
| 640 kFreeDesktopAPIServiceName, | |
| 641 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); | |
| 642 method_call.reset( | |
| 643 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "Inhibit")); | |
| 644 message_writer.reset(new dbus::MessageWriter(method_call.get())); | |
| 645 // The arguments of the method are: | |
| 646 // app_id: The application identifier | |
| 647 // reason: The reason for the inhibit | |
| 648 message_writer->AppendString( | |
| 649 CommandLine::ForCurrentProcess()->GetProgram().value()); | |
| 650 message_writer->AppendString(reason_); | |
| 651 break; | |
| 652 } | |
| 653 | |
| 654 // We could do this method call asynchronously, but if we did, we'd need to | |
| 655 // handle the case where we want to cancel the block before we get a reply. | |
| 656 // We're on the FILE thread so it should be OK to block briefly here. | |
| 657 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( | |
| 658 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
| 659 if (response.get()) { | |
| 660 // The method returns an inhibit_cookie, used to uniquely identify | |
| 661 // this request. It should be used as an argument to Uninhibit() | |
| 662 // in order to remove the request. | |
| 663 dbus::MessageReader message_reader(response.get()); | |
| 664 if (!message_reader.PopUint32(&inhibit_cookie_)) | |
| 665 LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString(); | |
| 666 } else { | |
| 667 LOG(ERROR) << "No response to Inhibit() request!"; | |
| 668 } | |
| 669 } | |
| 670 | |
| 671 void PowerSaveBlocker::Delegate::RemoveBlock() { | |
| 672 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 673 if (api_ == kNoAPI) | |
| 674 return; | |
| 675 DCHECK(bus_.get()); // RemoveBlock() should only be called once. | |
| 676 | |
| 677 scoped_refptr<dbus::ObjectProxy> object_proxy; | |
| 678 scoped_ptr<dbus::MethodCall> method_call; | |
| 679 | |
| 680 switch (api_) { | |
| 681 case kNoAPI: | |
| 682 NOTREACHED(); // We return early above. | |
| 683 return; | |
| 684 case kGnomeAPI: | |
| 685 object_proxy = bus_->GetObjectProxy( | |
| 686 kGnomeAPIServiceName, | |
| 687 dbus::ObjectPath(kGnomeAPIObjectPath)); | |
| 688 method_call.reset( | |
| 689 new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); | |
| 690 break; | |
| 691 case kFreeDesktopAPI: | |
| 692 object_proxy = bus_->GetObjectProxy( | |
| 693 kFreeDesktopAPIServiceName, | |
| 694 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); | |
| 695 method_call.reset( | |
| 696 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "Uninhibit")); | |
| 697 break; | |
| 698 } | |
| 699 | |
| 700 dbus::MessageWriter message_writer(method_call.get()); | |
| 701 message_writer.AppendUint32(inhibit_cookie_); | |
| 702 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( | |
| 703 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
| 704 if (!response.get()) | |
| 705 LOG(ERROR) << "No response to Uninhibit() request!"; | |
| 706 // We don't care about checking the result. We assume it works; we can't | |
| 707 // really do anything about it anyway if it fails. | |
| 708 inhibit_cookie_ = 0; | |
| 709 | |
| 710 bus_->ShutdownAndBlock(); | |
| 711 bus_ = NULL; | |
| 712 } | |
| 713 | |
| 714 // static | |
| 715 bool PowerSaveBlocker::Delegate::DPMSEnabled() { | |
| 716 Display* display = base::MessagePumpForUI::GetDefaultXDisplay(); | |
| 717 BOOL enabled = false; | |
| 718 int dummy; | |
| 719 if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) { | |
| 720 CARD16 state; | |
| 721 DPMSInfo(display, &state, &enabled); | |
| 722 } | |
| 723 return enabled; | |
| 724 } | |
| 725 | |
| 726 // static | |
| 727 DBusAPI PowerSaveBlocker::Delegate::SelectAPI() { | |
| 728 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
| 729 switch (base::nix::GetDesktopEnvironment(env.get())) { | |
| 730 case base::nix::DESKTOP_ENVIRONMENT_GNOME: | |
| 731 if (DPMSEnabled()) | |
| 732 return kGnomeAPI; | |
| 733 break; | |
| 734 case base::nix::DESKTOP_ENVIRONMENT_XFCE: | |
| 735 case base::nix::DESKTOP_ENVIRONMENT_KDE4: | |
| 736 if (DPMSEnabled()) | |
| 737 return kFreeDesktopAPI; | |
| 738 break; | |
| 739 case base::nix::DESKTOP_ENVIRONMENT_KDE3: | |
| 740 case base::nix::DESKTOP_ENVIRONMENT_OTHER: | |
| 741 // Not supported. | |
| 742 break; | |
| 743 } | |
| 744 return kNoAPI; | |
| 745 } | |
| 746 | |
| 747 PowerSaveBlocker::PowerSaveBlocker( | |
| 748 PowerSaveBlockerType type, const std::string& reason) | |
| 749 : delegate_(new Delegate(type, reason)) { | |
| 750 // The thread we use here becomes the origin and D-Bus thread for the D-Bus | |
| 751 // library, so we need to use the same thread below in the destructor. It must | |
| 752 // be a thread that allows I/O operations. | |
| 753 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 754 base::Bind(&Delegate::ApplyBlock, delegate_)); | |
| 755 } | |
| 756 | |
| 757 PowerSaveBlocker::~PowerSaveBlocker() { | |
| 758 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 759 base::Bind(&Delegate::RemoveBlock, delegate_)); | |
| 760 } | |
| 761 | |
| 762 } // namespace content | |
| OLD | NEW |