Chromium Code Reviews| Index: content/browser/battery_status/battery_status_manager_win.cc |
| diff --git a/content/browser/battery_status/battery_status_manager_win.cc b/content/browser/battery_status/battery_status_manager_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d64b80d1efbb3c254726cd818d69e97b7a569885 |
| --- /dev/null |
| +++ b/content/browser/battery_status/battery_status_manager_win.cc |
| @@ -0,0 +1,232 @@ |
| +// Copyright 2014 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 "content/browser/battery_status/battery_status_manager.h" |
| + |
| +#include <windows.h> |
| +#include "base/memory/ref_counted.h" |
| +#include "base/win/windows_version.h" |
| +#include "base/win/wrapped_window_proc.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "third_party/WebKit/public/platform/WebBatteryStatus.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback; |
| + |
| +const wchar_t kWindowClassName[] = L"BatteryStatusMessageWindow"; |
| + |
| +void ComputeWebBatteryStatus(const SYSTEM_POWER_STATUS& win_status, |
| + blink::WebBatteryStatus& status) { |
| + bool on_battery = win_status.ACLineStatus == 0; |
| + status.charging = !on_battery; // not offline |
|
mlamouri (slow - plz ping)
2014/08/07 16:40:47
Couldn't you do:
status.charging = win_status.ACL
timvolodine
2014/08/11 19:43:41
Done.
|
| + // Set level if available. Otherwise keep the default value which is 1. |
| + if (win_status.BatteryLifePercent != 255) { |
| + // Convert percentage to a value between 0 and 1 with 3 significant digits. |
| + status.level = round(win_status.BatteryLifePercent * 10) / 1000.f; |
| + } |
| + if (on_battery) { |
| + // Set dischargingTime if available otherwise keep the default value, |
| + // which is +Infinity. |
| + if (win_status.BatteryLifeTime != -1) |
| + status.dischargingTime = win_status.BatteryLifeTime; |
| + status.chargingTime = std::numeric_limits<double>::infinity(); |
| + } else { |
| + // Set chargingTime to +Infinity if not fully charged, otherwise leave the |
| + // default value, which is 0. |
| + if (status.level < 1) |
| + status.chargingTime = std::numeric_limits<double>::infinity(); |
|
mlamouri (slow - plz ping)
2014/08/07 16:40:47
Can't you use status.BatteryFullLifeTime?
timvolodine
2014/08/11 19:43:41
according to the documentation BatteryFullLifeTime
|
| + } |
| +} |
| + |
| +// Message-only window for handling battery changes on Windows. |
| +class BatteryStatusObserver |
| + : public base::RefCountedThreadSafe<BatteryStatusObserver> { |
| + public: |
| + explicit BatteryStatusObserver(const BatteryCallback& callback) |
| + : register_func_(NULL), |
| + unregister_func_(NULL), |
| + instance_(NULL), |
| + message_hwnd_(NULL), |
| + power_handle_(NULL), |
| + battery_change_handle_(NULL), |
| + callback_(callback) { |
| + InitPowerSettingFunctions(); |
| + } |
| + |
| + virtual ~BatteryStatusObserver() { DCHECK(!message_hwnd_); } |
| + |
| + void Start() { |
| + // Need to start on the UI thread to receive battery status notifications. |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&BatteryStatusObserver::StartOnUI, this)); |
| + } |
| + |
| + void Stop() { |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&BatteryStatusObserver::StopOnUI, this)); |
| + } |
| + |
| + private: |
| + |
| + void StartOnUI() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (message_hwnd_) |
| + return; |
| + |
| + if (CreateMessageWindow()) { |
| + BatteryChanged(); |
| + // RegisterPowerSettingNotification function work from Windows Vista |
| + // onwards. However even without them we will receive notifications, |
| + // e.g. when a power source is connected. |
| + power_handle_ = |
| + RegisterPowerSettingNotification(&GUID_ACDC_POWER_SOURCE); |
| + battery_change_handle_ = |
| + RegisterPowerSettingNotification(&GUID_BATTERY_PERCENTAGE_REMAINING); |
| + } else { |
| + // Could not create a message window, execute callback with the default |
| + // values. |
| + callback_.Run(blink::WebBatteryStatus()); |
| + } |
| + |
| + } |
| + |
| + void StopOnUI() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!message_hwnd_) |
| + return; |
| + |
| + if (power_handle_) |
| + UnregisterPowerSettingNotification(power_handle_); |
| + if (battery_change_handle_) |
| + UnregisterPowerSettingNotification(battery_change_handle_); |
| + DestroyWindow(message_hwnd_); |
| + UnregisterClass(kWindowClassName, instance_); |
| + message_hwnd_ = NULL; |
| + } |
| + |
| + static LRESULT CALLBACK WndProcThunk(HWND hwnd, |
| + UINT message, |
| + WPARAM wparam, |
| + LPARAM lparam) { |
| + BatteryStatusObserver* msg_wnd = reinterpret_cast<BatteryStatusObserver*>( |
| + GetWindowLongPtr(hwnd, GWLP_USERDATA)); |
| + switch(message) { |
| + case WM_POWERBROADCAST: |
| + if (wparam == PBT_APMPOWERSTATUSCHANGE || |
| + wparam == PBT_POWERSETTINGCHANGE) { |
| + msg_wnd->BatteryChanged(); |
| + } |
| + return true; |
| + default: |
| + return ::DefWindowProc(hwnd, message, wparam, lparam); |
| + } |
| + } |
| + |
| + void BatteryChanged() { |
| + blink::WebBatteryStatus status; |
| + SYSTEM_POWER_STATUS win_status; |
| + if (GetSystemPowerStatus(&win_status)) |
| + ComputeWebBatteryStatus(win_status, status); |
| + |
| + callback_.Run(status); |
| + } |
| + |
| + HPOWERNOTIFY RegisterPowerSettingNotification(LPCGUID power_setting) { |
| + return register_func_ ? register_func_(message_hwnd_, power_setting, |
| + DEVICE_NOTIFY_WINDOW_HANDLE) |
| + : NULL; |
| + } |
| + |
| + BOOL UnregisterPowerSettingNotification(HPOWERNOTIFY handle) { |
| + return unregister_func_ ? unregister_func_(handle) : FALSE; |
| + } |
| + |
| + void InitPowerSettingFunctions() { |
| + if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| + return; |
|
mlamouri (slow - plz ping)
2014/08/07 16:40:47
If that means that we don't get any event before V
timvolodine
2014/08/11 19:43:41
Done. added a crbug.
|
| + |
| + HMODULE user32 = GetModuleHandleA("user32.dll"); |
|
scottmg
2014/08/08 21:32:19
I believe user32 is delayload, so you can just run
timvolodine
2014/08/11 19:43:41
Done.
|
| + register_func_ = reinterpret_cast<RegisterPowerSettingNotificationFunc>( |
| + GetProcAddress(user32, "RegisterPowerSettingNotification")); |
| + unregister_func_ = reinterpret_cast<UnregisterPowerSettingNotificationFunc>( |
| + GetProcAddress(user32, "UnregisterPowerSettingNotification")); |
| + DCHECK(register_func_ && unregister_func_); |
| + } |
| + |
| + bool CreateMessageWindow() { |
|
scottmg
2014/08/08 21:32:19
can we use base::win::MessageWindow here?
timvolodine
2014/08/11 19:43:41
yes. thanks for noticing this, done.
|
| + WNDCLASSEX window_class; |
| + base::win::InitializeWindowClass(kWindowClassName, |
| + &base::win::WrappedWindowProc<BatteryStatusObserver::WndProcThunk>, |
| + 0, 0, 0, NULL, NULL, NULL, NULL, NULL, &window_class); |
| + instance_ = window_class.hInstance; |
| + ATOM clazz = RegisterClassEx(&window_class); |
| + DCHECK(clazz); |
| + message_hwnd_ = CreateWindowEx(WS_EX_NOACTIVATE, kWindowClassName, |
| + NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, instance_, NULL); |
| + if (!message_hwnd_) |
| + return false; |
| + SetWindowLongPtr(message_hwnd_, GWLP_USERDATA, |
| + reinterpret_cast<LONG_PTR>(this)); |
| + return true; |
| + } |
| + |
| + typedef HPOWERNOTIFY (WINAPI* RegisterPowerSettingNotificationFunc) |
| + (HANDLE recipient, LPCGUID id, DWORD flags); |
| + RegisterPowerSettingNotificationFunc register_func_; |
| + |
| + typedef BOOL (WINAPI* UnregisterPowerSettingNotificationFunc) |
| + (HPOWERNOTIFY handle); |
| + UnregisterPowerSettingNotificationFunc unregister_func_; |
| + |
| + HMODULE instance_; |
| + HWND message_hwnd_; |
| + HPOWERNOTIFY power_handle_; |
| + HPOWERNOTIFY battery_change_handle_; |
| + BatteryCallback callback_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); |
| +}; |
| + |
| +class BatteryStatusManagerWin : public BatteryStatusManager { |
| + public: |
| + explicit BatteryStatusManagerWin(const BatteryCallback& callback) |
| + : battery_observer_(new BatteryStatusObserver(callback)) {} |
| + virtual ~BatteryStatusManagerWin() { battery_observer_->Stop(); } |
| + |
| + public: |
| + // BatteryStatusManager: |
| + virtual bool StartListeningBatteryChange() OVERRIDE { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + battery_observer_->Start(); |
| + return true; |
| + } |
| + |
| + virtual void StopListeningBatteryChange() OVERRIDE { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + battery_observer_->Stop(); |
| + } |
| + |
| + private: |
| + scoped_refptr<BatteryStatusObserver> battery_observer_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerWin); |
| +}; |
| + |
| +} // namespace |
| + |
| +// static |
| +scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( |
| + const BatteryStatusService::BatteryUpdateCallback& callback) { |
| + return scoped_ptr<BatteryStatusManager>( |
| + new BatteryStatusManagerWin(callback)); |
| +} |
| + |
| +} // namespace content |