OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/battery_status/battery_status_manager.h" | |
6 | |
7 #include <windows.h> | |
8 #include "base/memory/ref_counted.h" | |
9 #include "base/win/windows_version.h" | |
10 #include "base/win/wrapped_window_proc.h" | |
11 #include "content/public/browser/browser_thread.h" | |
12 #include "third_party/WebKit/public/platform/WebBatteryStatus.h" | |
13 | |
14 namespace content { | |
15 | |
16 namespace { | |
17 | |
18 typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback; | |
19 | |
20 const wchar_t kWindowClassName[] = L"BatteryStatusMessageWindow"; | |
21 | |
22 void ComputeWebBatteryStatus(const SYSTEM_POWER_STATUS& win_status, | |
23 blink::WebBatteryStatus& status) { | |
24 bool on_battery = win_status.ACLineStatus == 0; | |
25 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.
| |
26 // Set level if available. Otherwise keep the default value which is 1. | |
27 if (win_status.BatteryLifePercent != 255) { | |
28 // Convert percentage to a value between 0 and 1 with 3 significant digits. | |
29 status.level = round(win_status.BatteryLifePercent * 10) / 1000.f; | |
30 } | |
31 if (on_battery) { | |
32 // Set dischargingTime if available otherwise keep the default value, | |
33 // which is +Infinity. | |
34 if (win_status.BatteryLifeTime != -1) | |
35 status.dischargingTime = win_status.BatteryLifeTime; | |
36 status.chargingTime = std::numeric_limits<double>::infinity(); | |
37 } else { | |
38 // Set chargingTime to +Infinity if not fully charged, otherwise leave the | |
39 // default value, which is 0. | |
40 if (status.level < 1) | |
41 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
| |
42 } | |
43 } | |
44 | |
45 // Message-only window for handling battery changes on Windows. | |
46 class BatteryStatusObserver | |
47 : public base::RefCountedThreadSafe<BatteryStatusObserver> { | |
48 public: | |
49 explicit BatteryStatusObserver(const BatteryCallback& callback) | |
50 : register_func_(NULL), | |
51 unregister_func_(NULL), | |
52 instance_(NULL), | |
53 message_hwnd_(NULL), | |
54 power_handle_(NULL), | |
55 battery_change_handle_(NULL), | |
56 callback_(callback) { | |
57 InitPowerSettingFunctions(); | |
58 } | |
59 | |
60 virtual ~BatteryStatusObserver() { DCHECK(!message_hwnd_); } | |
61 | |
62 void Start() { | |
63 // Need to start on the UI thread to receive battery status notifications. | |
64 BrowserThread::PostTask( | |
65 BrowserThread::UI, | |
66 FROM_HERE, | |
67 base::Bind(&BatteryStatusObserver::StartOnUI, this)); | |
68 } | |
69 | |
70 void Stop() { | |
71 BrowserThread::PostTask( | |
72 BrowserThread::UI, | |
73 FROM_HERE, | |
74 base::Bind(&BatteryStatusObserver::StopOnUI, this)); | |
75 } | |
76 | |
77 private: | |
78 | |
79 void StartOnUI() { | |
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
81 if (message_hwnd_) | |
82 return; | |
83 | |
84 if (CreateMessageWindow()) { | |
85 BatteryChanged(); | |
86 // RegisterPowerSettingNotification function work from Windows Vista | |
87 // onwards. However even without them we will receive notifications, | |
88 // e.g. when a power source is connected. | |
89 power_handle_ = | |
90 RegisterPowerSettingNotification(&GUID_ACDC_POWER_SOURCE); | |
91 battery_change_handle_ = | |
92 RegisterPowerSettingNotification(&GUID_BATTERY_PERCENTAGE_REMAINING); | |
93 } else { | |
94 // Could not create a message window, execute callback with the default | |
95 // values. | |
96 callback_.Run(blink::WebBatteryStatus()); | |
97 } | |
98 | |
99 } | |
100 | |
101 void StopOnUI() { | |
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
103 if (!message_hwnd_) | |
104 return; | |
105 | |
106 if (power_handle_) | |
107 UnregisterPowerSettingNotification(power_handle_); | |
108 if (battery_change_handle_) | |
109 UnregisterPowerSettingNotification(battery_change_handle_); | |
110 DestroyWindow(message_hwnd_); | |
111 UnregisterClass(kWindowClassName, instance_); | |
112 message_hwnd_ = NULL; | |
113 } | |
114 | |
115 static LRESULT CALLBACK WndProcThunk(HWND hwnd, | |
116 UINT message, | |
117 WPARAM wparam, | |
118 LPARAM lparam) { | |
119 BatteryStatusObserver* msg_wnd = reinterpret_cast<BatteryStatusObserver*>( | |
120 GetWindowLongPtr(hwnd, GWLP_USERDATA)); | |
121 switch(message) { | |
122 case WM_POWERBROADCAST: | |
123 if (wparam == PBT_APMPOWERSTATUSCHANGE || | |
124 wparam == PBT_POWERSETTINGCHANGE) { | |
125 msg_wnd->BatteryChanged(); | |
126 } | |
127 return true; | |
128 default: | |
129 return ::DefWindowProc(hwnd, message, wparam, lparam); | |
130 } | |
131 } | |
132 | |
133 void BatteryChanged() { | |
134 blink::WebBatteryStatus status; | |
135 SYSTEM_POWER_STATUS win_status; | |
136 if (GetSystemPowerStatus(&win_status)) | |
137 ComputeWebBatteryStatus(win_status, status); | |
138 | |
139 callback_.Run(status); | |
140 } | |
141 | |
142 HPOWERNOTIFY RegisterPowerSettingNotification(LPCGUID power_setting) { | |
143 return register_func_ ? register_func_(message_hwnd_, power_setting, | |
144 DEVICE_NOTIFY_WINDOW_HANDLE) | |
145 : NULL; | |
146 } | |
147 | |
148 BOOL UnregisterPowerSettingNotification(HPOWERNOTIFY handle) { | |
149 return unregister_func_ ? unregister_func_(handle) : FALSE; | |
150 } | |
151 | |
152 void InitPowerSettingFunctions() { | |
153 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
154 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.
| |
155 | |
156 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.
| |
157 register_func_ = reinterpret_cast<RegisterPowerSettingNotificationFunc>( | |
158 GetProcAddress(user32, "RegisterPowerSettingNotification")); | |
159 unregister_func_ = reinterpret_cast<UnregisterPowerSettingNotificationFunc>( | |
160 GetProcAddress(user32, "UnregisterPowerSettingNotification")); | |
161 DCHECK(register_func_ && unregister_func_); | |
162 } | |
163 | |
164 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.
| |
165 WNDCLASSEX window_class; | |
166 base::win::InitializeWindowClass(kWindowClassName, | |
167 &base::win::WrappedWindowProc<BatteryStatusObserver::WndProcThunk>, | |
168 0, 0, 0, NULL, NULL, NULL, NULL, NULL, &window_class); | |
169 instance_ = window_class.hInstance; | |
170 ATOM clazz = RegisterClassEx(&window_class); | |
171 DCHECK(clazz); | |
172 message_hwnd_ = CreateWindowEx(WS_EX_NOACTIVATE, kWindowClassName, | |
173 NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, instance_, NULL); | |
174 if (!message_hwnd_) | |
175 return false; | |
176 SetWindowLongPtr(message_hwnd_, GWLP_USERDATA, | |
177 reinterpret_cast<LONG_PTR>(this)); | |
178 return true; | |
179 } | |
180 | |
181 typedef HPOWERNOTIFY (WINAPI* RegisterPowerSettingNotificationFunc) | |
182 (HANDLE recipient, LPCGUID id, DWORD flags); | |
183 RegisterPowerSettingNotificationFunc register_func_; | |
184 | |
185 typedef BOOL (WINAPI* UnregisterPowerSettingNotificationFunc) | |
186 (HPOWERNOTIFY handle); | |
187 UnregisterPowerSettingNotificationFunc unregister_func_; | |
188 | |
189 HMODULE instance_; | |
190 HWND message_hwnd_; | |
191 HPOWERNOTIFY power_handle_; | |
192 HPOWERNOTIFY battery_change_handle_; | |
193 BatteryCallback callback_; | |
194 | |
195 DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); | |
196 }; | |
197 | |
198 class BatteryStatusManagerWin : public BatteryStatusManager { | |
199 public: | |
200 explicit BatteryStatusManagerWin(const BatteryCallback& callback) | |
201 : battery_observer_(new BatteryStatusObserver(callback)) {} | |
202 virtual ~BatteryStatusManagerWin() { battery_observer_->Stop(); } | |
203 | |
204 public: | |
205 // BatteryStatusManager: | |
206 virtual bool StartListeningBatteryChange() OVERRIDE { | |
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
208 battery_observer_->Start(); | |
209 return true; | |
210 } | |
211 | |
212 virtual void StopListeningBatteryChange() OVERRIDE { | |
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
214 battery_observer_->Stop(); | |
215 } | |
216 | |
217 private: | |
218 scoped_refptr<BatteryStatusObserver> battery_observer_; | |
219 | |
220 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerWin); | |
221 }; | |
222 | |
223 } // namespace | |
224 | |
225 // static | |
226 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
227 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
228 return scoped_ptr<BatteryStatusManager>( | |
229 new BatteryStatusManagerWin(callback)); | |
230 } | |
231 | |
232 } // namespace content | |
OLD | NEW |