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 | |
|
satorux1
2012/06/08 14:59:41
This looks ugly. Why do you need to keep the old i
Mike Mammarella
2012/06/08 18:33:36
We don't, strictly speaking. We had to do this for
| |
| 511 | |
| 512 namespace content { | |
| 513 | |
| 514 class PowerSaveBlocker::Delegate | |
| 515 : public base::RefCountedThreadSafe<PowerSaveBlocker::Delegate> { | |
| 516 public: | |
| 517 // Picks an appropriate DBus API to use based on the desktop environment. | |
| 518 Delegate(PowerSaveBlockerType type, const std::string& reason); | |
| 519 | |
| 520 void ApplyBlock(); | |
| 521 void RemoveBlock(); | |
|
satorux1
2012/06/08 14:59:41
function comments are missing.
Mike Mammarella
2012/06/08 18:33:36
I actually had them in a previous patch set, but I
satorux1
2012/06/08 19:31:50
From function name alone, it's hard to tell what A
Mike Mammarella
2012/06/08 19:50:17
OK, I added comments. (The last patch set had them
| |
| 522 | |
| 523 private: | |
| 524 enum DBusAPI { | |
| 525 kNoAPI, // Disable. No supported API available. | |
| 526 kGnomeAPI, // Use the GNOME API. (Supports more features.) | |
| 527 kFreeDesktopAPI, // Use the FreeDesktop API, for KDE4 and XFCE. | |
| 528 }; | |
| 529 | |
| 530 // Inhibit flags defined in the org.gnome.SessionManager interface. | |
| 531 // Can be OR'd together and passed as argument to the Inhibit() method | |
| 532 // to specify which power management features we want to suspend. | |
| 533 enum GnomeAPIInhibitFlags { | |
| 534 kInhibitLogOut = 1, | |
| 535 kInhibitSwitchUser = 2, | |
| 536 kInhibitSuspendSession = 4, | |
| 537 kInhibitMarkSessionAsIdle = 8 | |
| 538 }; | |
| 539 | |
| 540 static const char kGnomeAPIServiceName[]; | |
| 541 static const char kGnomeAPIInterfaceName[]; | |
| 542 static const char kGnomeAPIObjectPath[]; | |
| 543 static const char kFreeDesktopAPIServiceName[]; | |
| 544 static const char kFreeDesktopAPIInterfaceName[]; | |
| 545 static const char kFreeDesktopAPIObjectPath[]; | |
|
satorux1
2012/06/08 14:59:41
nit: would be simpler to define these constants in
Mike Mammarella
2012/06/08 18:33:36
Done.
| |
| 546 | |
| 547 friend class base::RefCountedThreadSafe<Delegate>; | |
| 548 ~Delegate() {} | |
| 549 | |
| 550 // If DPMS is not enabled, then we don't want to try to disable power saving, | |
|
satorux1
2012/06/08 14:59:41
what does DPMS stand for? Please add some comment
Mike Mammarella
2012/06/08 18:33:36
Improved the comment. It's not particularly releva
| |
| 551 // since on some desktop environments that may enable DPMS with very poor | |
| 552 // default settings (e.g. turning off the display after only 1 second). | |
| 553 static bool DPMSEnabled(); | |
| 554 | |
| 555 // Returns an appropriate DBus API to use based on the desktop environment. | |
|
satorux1
2012/06/08 14:59:41
nit: DBus -> D-Bus
Mike Mammarella
2012/06/08 18:33:36
Done.
| |
| 556 static DBusAPI SelectAPI(); | |
| 557 | |
| 558 const PowerSaveBlockerType type_; | |
| 559 const std::string reason_; | |
| 560 const DBusAPI api_; | |
| 561 | |
| 562 scoped_refptr<dbus::Bus> bus_; | |
| 563 | |
| 564 // The cookie that identifies our inhibit request, | |
| 565 // or 0 if there is no active inhibit request. | |
| 566 uint32 inhibit_cookie_; | |
| 567 | |
| 568 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 569 }; | |
| 570 | |
| 571 const char PowerSaveBlocker::Delegate::kGnomeAPIServiceName[] = | |
| 572 "org.gnome.SessionManager"; | |
| 573 const char PowerSaveBlocker::Delegate::kGnomeAPIInterfaceName[] = | |
| 574 "org.gnome.SessionManager"; | |
| 575 const char PowerSaveBlocker::Delegate::kGnomeAPIObjectPath[] = | |
| 576 "/org/gnome/SessionManager"; | |
| 577 | |
| 578 const char PowerSaveBlocker::Delegate::kFreeDesktopAPIServiceName[] = | |
| 579 "org.freedesktop.PowerManagement"; | |
| 580 const char PowerSaveBlocker::Delegate::kFreeDesktopAPIInterfaceName[] = | |
| 581 "org.freedesktop.PowerManagement.Inhibit"; | |
| 582 const char PowerSaveBlocker::Delegate::kFreeDesktopAPIObjectPath[] = | |
| 583 "/org/freedesktop/PowerManagement/Inhibit"; | |
| 584 | |
| 585 PowerSaveBlocker::Delegate::Delegate(PowerSaveBlockerType type, | |
| 586 const std::string& reason) | |
| 587 : type_(type), reason_(reason), api_(SelectAPI()) { | |
| 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_ == kNoAPI) | |
| 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 // We must provide a valid-looking interface. We'll change it below. | |
|
satorux1
2012/06/08 14:59:41
This looks lie a hack. Rather than replacing it la
Mike Mammarella
2012/06/08 18:33:36
I considered that but thought this seemed a little
satorux1
2012/06/08 19:31:50
I think using a dummy place holder does not look g
Mike Mammarella
2012/06/08 19:50:17
Done. Personally I'm not a fan of the way this cas
| |
| 605 dbus::MethodCall method_call("org.chromium.Placeholder", "Inhibit"); | |
| 606 dbus::MessageWriter message_writer(&method_call); | |
| 607 | |
| 608 switch (api_) { | |
| 609 case kNoAPI: | |
| 610 NOTREACHED(); // We return early above. | |
| 611 return; | |
| 612 case kGnomeAPI: | |
| 613 object_proxy = bus_->GetObjectProxy( | |
| 614 kGnomeAPIServiceName, | |
| 615 dbus::ObjectPath(kGnomeAPIObjectPath)); | |
| 616 method_call.SetInterface(kGnomeAPIInterfaceName); | |
| 617 // The arguments of the method are: | |
| 618 // app_id: The application identifier | |
| 619 // toplevel_xid: The toplevel X window identifier | |
| 620 // reason: The reason for the inhibit | |
| 621 // flags: Flags that spefify what should be inhibited | |
| 622 message_writer.AppendString( | |
| 623 CommandLine::ForCurrentProcess()->GetProgram().value()); | |
| 624 message_writer.AppendUint32(0); // should be toplevel_xid | |
| 625 message_writer.AppendString(reason_.c_str()); | |
|
satorux1
2012/06/08 14:59:41
no need to use .c_str()
Mike Mammarella
2012/06/08 18:33:36
Done.
| |
| 626 { | |
| 627 uint32 flags = 0; | |
| 628 switch (type_) { | |
| 629 case kPowerSaveBlockPreventDisplaySleep: | |
| 630 flags |= kInhibitMarkSessionAsIdle; | |
| 631 flags |= kInhibitSuspendSession; | |
| 632 break; | |
| 633 case kPowerSaveBlockPreventAppSuspension: | |
| 634 flags |= kInhibitSuspendSession; | |
| 635 break; | |
| 636 } | |
| 637 message_writer.AppendUint32(flags); | |
| 638 } | |
| 639 break; | |
| 640 case kFreeDesktopAPI: | |
| 641 object_proxy = bus_->GetObjectProxy( | |
| 642 kFreeDesktopAPIServiceName, | |
| 643 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); | |
| 644 method_call.SetInterface(kFreeDesktopAPIInterfaceName); | |
| 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_.c_str()); | |
| 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, 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 // We must provide a valid-looking interface. We'll change it below. | |
|
satorux1
2012/06/08 14:59:41
see my comment above.
| |
| 679 dbus::MethodCall method_call("org.chromium.Placeholder", "Uninhibit"); | |
| 680 dbus::MessageWriter message_writer(&method_call); | |
| 681 | |
| 682 switch (api_) { | |
| 683 case kNoAPI: | |
| 684 NOTREACHED(); // We return early above. | |
| 685 return; | |
| 686 case kGnomeAPI: | |
| 687 object_proxy = bus_->GetObjectProxy( | |
| 688 kGnomeAPIServiceName, | |
| 689 dbus::ObjectPath(kGnomeAPIObjectPath)); | |
| 690 method_call.SetInterface(kGnomeAPIInterfaceName); | |
| 691 break; | |
| 692 case kFreeDesktopAPI: | |
| 693 object_proxy = bus_->GetObjectProxy( | |
| 694 kFreeDesktopAPIServiceName, | |
| 695 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); | |
| 696 method_call.SetInterface(kFreeDesktopAPIInterfaceName); | |
| 697 break; | |
| 698 } | |
| 699 | |
| 700 message_writer.AppendUint32(inhibit_cookie_); | |
| 701 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( | |
| 702 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
| 703 if (!response.get()) | |
| 704 LOG(ERROR) << "No response to Uninhibit() request!"; | |
| 705 // We don't care about checking the result. We assume it works; we can't | |
| 706 // really do anything about it anyway if it fails. | |
| 707 inhibit_cookie_ = 0; | |
| 708 | |
| 709 bus_->ShutdownAndBlock(); | |
| 710 bus_ = NULL; | |
| 711 } | |
| 712 | |
| 713 // static | |
| 714 bool PowerSaveBlocker::Delegate::DPMSEnabled() { | |
| 715 Display* display = base::MessagePumpForUI::GetDefaultXDisplay(); | |
| 716 BOOL enabled = false; | |
| 717 int dummy; | |
| 718 if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) { | |
| 719 CARD16 state; | |
| 720 DPMSInfo(display, &state, &enabled); | |
| 721 } | |
| 722 return enabled; | |
| 723 } | |
| 724 | |
| 725 // static | |
| 726 PowerSaveBlocker::Delegate::DBusAPI PowerSaveBlocker::Delegate::SelectAPI() { | |
| 727 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
| 728 switch (base::nix::GetDesktopEnvironment(env.get())) { | |
| 729 case base::nix::DESKTOP_ENVIRONMENT_GNOME: | |
| 730 if (DPMSEnabled()) | |
| 731 return kGnomeAPI; | |
| 732 break; | |
| 733 case base::nix::DESKTOP_ENVIRONMENT_XFCE: | |
| 734 case base::nix::DESKTOP_ENVIRONMENT_KDE4: | |
| 735 if (DPMSEnabled()) | |
| 736 return kFreeDesktopAPI; | |
| 737 break; | |
| 738 case base::nix::DESKTOP_ENVIRONMENT_KDE3: | |
| 739 case base::nix::DESKTOP_ENVIRONMENT_OTHER: | |
| 740 // Not supported. | |
| 741 break; | |
| 742 } | |
| 743 return kNoAPI; | |
| 744 } | |
| 745 | |
| 746 PowerSaveBlocker::PowerSaveBlocker( | |
| 747 PowerSaveBlockerType type, const std::string& reason) | |
| 748 : delegate_(new Delegate(type, reason)) { | |
| 749 // We use the FILE thread to service the DBus connection, since we need a | |
| 750 // thread that allows I/O operations. The same thread must be used in the | |
| 751 // destructor below, but other than that we could use any thread. | |
| 752 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
|
satorux1
2012/06/08 14:59:41
FILE thread is now deprecated. Please use BrowserT
Mike Mammarella
2012/06/08 18:33:36
Hmm. As the comment indicates, I need the same thr
satorux1
2012/06/08 19:31:50
Why do they need to run on the same thread? As lon
Mike Mammarella
2012/06/08 19:50:17
They need to be the same because the D-Bus library
satorux1
2012/06/08 20:00:14
Oh, I that explains. You might want to put it as a
Mike Mammarella
2012/06/08 20:28:46
Done.
| |
| 753 base::Bind(&Delegate::ApplyBlock, delegate_)); | |
| 754 } | |
| 755 | |
| 756 PowerSaveBlocker::~PowerSaveBlocker() { | |
| 757 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
| 758 base::Bind(&Delegate::RemoveBlock, delegate_)); | |
| 759 } | |
| 760 | |
| 761 } // namespace content | |
| OLD | NEW |