| 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 NO_API, // Disable. No supported API available. |
| 516 GNOME_API, // Use the GNOME API. (Supports more features.) |
| 517 FREEDESKTOP_API, // 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 INHIBIT_LOGOUT = 1, |
| 525 INHIBIT_SWITCH_USER = 2, |
| 526 INHIBIT_SUSPEND_SESSION = 4, |
| 527 INHIBIT_MARK_SESSION_IDLE = 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), |
| 585 reason_(reason), |
| 586 api_(SelectAPI()), |
| 587 inhibit_cookie_(0) { |
| 588 // We're on the client's thread here, so we don't allocate the dbus::Bus |
| 589 // object yet. We'll do it below in ApplyBlock(), on the FILE thread. |
| 590 } |
| 591 |
| 592 void PowerSaveBlocker::Delegate::ApplyBlock() { |
| 593 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 594 DCHECK(!bus_.get()); // ApplyBlock() should only be called once. |
| 595 if (api_ == NO_API) |
| 596 return; |
| 597 |
| 598 dbus::Bus::Options options; |
| 599 options.bus_type = dbus::Bus::SESSION; |
| 600 options.connection_type = dbus::Bus::PRIVATE; |
| 601 bus_ = new dbus::Bus(options); |
| 602 |
| 603 scoped_refptr<dbus::ObjectProxy> object_proxy; |
| 604 scoped_ptr<dbus::MethodCall> method_call; |
| 605 scoped_ptr<dbus::MessageWriter> message_writer; |
| 606 |
| 607 switch (api_) { |
| 608 case NO_API: |
| 609 NOTREACHED(); // We return early above. |
| 610 return; |
| 611 case GNOME_API: |
| 612 object_proxy = bus_->GetObjectProxy( |
| 613 kGnomeAPIServiceName, |
| 614 dbus::ObjectPath(kGnomeAPIObjectPath)); |
| 615 method_call.reset( |
| 616 new dbus::MethodCall(kGnomeAPIInterfaceName, "Inhibit")); |
| 617 message_writer.reset(new dbus::MessageWriter(method_call.get())); |
| 618 // The arguments of the method are: |
| 619 // app_id: The application identifier |
| 620 // toplevel_xid: The toplevel X window identifier |
| 621 // reason: The reason for the inhibit |
| 622 // flags: Flags that spefify what should be inhibited |
| 623 message_writer->AppendString( |
| 624 CommandLine::ForCurrentProcess()->GetProgram().value()); |
| 625 message_writer->AppendUint32(0); // should be toplevel_xid |
| 626 message_writer->AppendString(reason_); |
| 627 { |
| 628 uint32 flags = 0; |
| 629 switch (type_) { |
| 630 case kPowerSaveBlockPreventDisplaySleep: |
| 631 flags |= INHIBIT_MARK_SESSION_IDLE; |
| 632 flags |= INHIBIT_SUSPEND_SESSION; |
| 633 break; |
| 634 case kPowerSaveBlockPreventAppSuspension: |
| 635 flags |= INHIBIT_SUSPEND_SESSION; |
| 636 break; |
| 637 } |
| 638 message_writer->AppendUint32(flags); |
| 639 } |
| 640 break; |
| 641 case FREEDESKTOP_API: |
| 642 object_proxy = bus_->GetObjectProxy( |
| 643 kFreeDesktopAPIServiceName, |
| 644 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); |
| 645 method_call.reset( |
| 646 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "Inhibit")); |
| 647 message_writer.reset(new dbus::MessageWriter(method_call.get())); |
| 648 // The arguments of the method are: |
| 649 // app_id: The application identifier |
| 650 // reason: The reason for the inhibit |
| 651 message_writer->AppendString( |
| 652 CommandLine::ForCurrentProcess()->GetProgram().value()); |
| 653 message_writer->AppendString(reason_); |
| 654 break; |
| 655 } |
| 656 |
| 657 // We could do this method call asynchronously, but if we did, we'd need to |
| 658 // handle the case where we want to cancel the block before we get a reply. |
| 659 // We're on the FILE thread so it should be OK to block briefly here. |
| 660 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( |
| 661 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| 662 if (response.get()) { |
| 663 // The method returns an inhibit_cookie, used to uniquely identify |
| 664 // this request. It should be used as an argument to Uninhibit() |
| 665 // in order to remove the request. |
| 666 dbus::MessageReader message_reader(response.get()); |
| 667 if (!message_reader.PopUint32(&inhibit_cookie_)) |
| 668 LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString(); |
| 669 } else { |
| 670 LOG(ERROR) << "No response to Inhibit() request!"; |
| 671 } |
| 672 } |
| 673 |
| 674 void PowerSaveBlocker::Delegate::RemoveBlock() { |
| 675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 676 if (api_ == NO_API) |
| 677 return; |
| 678 DCHECK(bus_.get()); // RemoveBlock() should only be called once. |
| 679 |
| 680 scoped_refptr<dbus::ObjectProxy> object_proxy; |
| 681 scoped_ptr<dbus::MethodCall> method_call; |
| 682 |
| 683 switch (api_) { |
| 684 case NO_API: |
| 685 NOTREACHED(); // We return early above. |
| 686 return; |
| 687 case GNOME_API: |
| 688 object_proxy = bus_->GetObjectProxy( |
| 689 kGnomeAPIServiceName, |
| 690 dbus::ObjectPath(kGnomeAPIObjectPath)); |
| 691 method_call.reset( |
| 692 new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); |
| 693 break; |
| 694 case FREEDESKTOP_API: |
| 695 object_proxy = bus_->GetObjectProxy( |
| 696 kFreeDesktopAPIServiceName, |
| 697 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); |
| 698 method_call.reset( |
| 699 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "Uninhibit")); |
| 700 break; |
| 701 } |
| 702 |
| 703 dbus::MessageWriter message_writer(method_call.get()); |
| 704 message_writer.AppendUint32(inhibit_cookie_); |
| 705 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( |
| 706 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| 707 if (!response.get()) |
| 708 LOG(ERROR) << "No response to Uninhibit() request!"; |
| 709 // We don't care about checking the result. We assume it works; we can't |
| 710 // really do anything about it anyway if it fails. |
| 711 inhibit_cookie_ = 0; |
| 712 |
| 713 bus_->ShutdownAndBlock(); |
| 714 bus_ = NULL; |
| 715 } |
| 716 |
| 717 // static |
| 718 bool PowerSaveBlocker::Delegate::DPMSEnabled() { |
| 719 Display* display = base::MessagePumpForUI::GetDefaultXDisplay(); |
| 720 BOOL enabled = false; |
| 721 int dummy; |
| 722 if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) { |
| 723 CARD16 state; |
| 724 DPMSInfo(display, &state, &enabled); |
| 725 } |
| 726 return enabled; |
| 727 } |
| 728 |
| 729 // static |
| 730 DBusAPI PowerSaveBlocker::Delegate::SelectAPI() { |
| 731 scoped_ptr<base::Environment> env(base::Environment::Create()); |
| 732 switch (base::nix::GetDesktopEnvironment(env.get())) { |
| 733 case base::nix::DESKTOP_ENVIRONMENT_GNOME: |
| 734 if (DPMSEnabled()) |
| 735 return GNOME_API; |
| 736 break; |
| 737 case base::nix::DESKTOP_ENVIRONMENT_XFCE: |
| 738 case base::nix::DESKTOP_ENVIRONMENT_KDE4: |
| 739 if (DPMSEnabled()) |
| 740 return FREEDESKTOP_API; |
| 741 break; |
| 742 case base::nix::DESKTOP_ENVIRONMENT_KDE3: |
| 743 case base::nix::DESKTOP_ENVIRONMENT_OTHER: |
| 744 // Not supported. |
| 745 break; |
| 746 } |
| 747 return NO_API; |
| 748 } |
| 749 |
| 750 PowerSaveBlocker::PowerSaveBlocker( |
| 751 PowerSaveBlockerType type, const std::string& reason) |
| 752 : delegate_(new Delegate(type, reason)) { |
| 753 // The thread we use here becomes the origin and D-Bus thread for the D-Bus |
| 754 // library, so we need to use the same thread below in the destructor. It must |
| 755 // be a thread that allows I/O operations. |
| 756 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| 757 base::Bind(&Delegate::ApplyBlock, delegate_)); |
| 758 } |
| 759 |
| 760 PowerSaveBlocker::~PowerSaveBlocker() { |
| 761 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| 762 base::Bind(&Delegate::RemoveBlock, delegate_)); |
| 763 } |
| 764 |
| 765 } // namespace content |
| OLD | NEW |