1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/hal/windows/WindowsBattery.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,190 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "Hal.h" 1.10 +#include "HalImpl.h" 1.11 +#include "nsITimer.h" 1.12 +#include "mozilla/Preferences.h" 1.13 +#include "mozilla/dom/battery/Constants.h" 1.14 +#include "nsComponentManagerUtils.h" 1.15 + 1.16 +#include <windows.h> 1.17 +#include "mozilla/WindowsVersion.h" 1.18 + 1.19 +using namespace mozilla::dom::battery; 1.20 + 1.21 +namespace mozilla { 1.22 +namespace hal_impl { 1.23 + 1.24 +static nsCOMPtr<nsITimer> sUpdateTimer; 1.25 + 1.26 +/* Power Event API is Vista or later */ 1.27 +static decltype(RegisterPowerSettingNotification)* sRegisterPowerSettingNotification = nullptr; 1.28 +static decltype(UnregisterPowerSettingNotification)* sUnregisterPowerSettingNotification = nullptr; 1.29 +static HPOWERNOTIFY sPowerHandle = nullptr; 1.30 +static HPOWERNOTIFY sCapacityHandle = nullptr; 1.31 +static HWND sHWnd = nullptr; 1.32 + 1.33 +static void 1.34 +UpdateHandler(nsITimer* aTimer, void* aClosure) { 1.35 + NS_ASSERTION(!IsVistaOrLater(), 1.36 + "We shouldn't call this function for Vista or later version!"); 1.37 + 1.38 + static hal::BatteryInformation sLastInfo; 1.39 + hal::BatteryInformation currentInfo; 1.40 + 1.41 + hal_impl::GetCurrentBatteryInformation(¤tInfo); 1.42 + if (sLastInfo.level() != currentInfo.level() || 1.43 + sLastInfo.charging() != currentInfo.charging() || 1.44 + sLastInfo.remainingTime() != currentInfo.remainingTime()) { 1.45 + hal::NotifyBatteryChange(currentInfo); 1.46 + sLastInfo = currentInfo; 1.47 + } 1.48 +} 1.49 + 1.50 +static 1.51 +LRESULT CALLBACK 1.52 +BatteryWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { 1.53 + if (msg != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE) { 1.54 + return DefWindowProc(hwnd, msg, wParam, lParam); 1.55 + } 1.56 + 1.57 + hal::BatteryInformation currentInfo; 1.58 + 1.59 + // Since we need update remainingTime, we cannot use LPARAM. 1.60 + hal_impl::GetCurrentBatteryInformation(¤tInfo); 1.61 + 1.62 + hal::NotifyBatteryChange(currentInfo); 1.63 + return TRUE; 1.64 +} 1.65 + 1.66 +void 1.67 +EnableBatteryNotifications() 1.68 +{ 1.69 + if (IsVistaOrLater()) { 1.70 + // RegisterPowerSettingNotification is from Vista or later. 1.71 + // Use this API if available. 1.72 + HMODULE hUser32 = GetModuleHandleW(L"USER32.DLL"); 1.73 + if (!sRegisterPowerSettingNotification) 1.74 + sRegisterPowerSettingNotification = (decltype(RegisterPowerSettingNotification)*) 1.75 + GetProcAddress(hUser32, "RegisterPowerSettingNotification"); 1.76 + if (!sUnregisterPowerSettingNotification) 1.77 + sUnregisterPowerSettingNotification = (decltype(UnregisterPowerSettingNotification)*) 1.78 + GetProcAddress(hUser32, "UnregisterPowerSettingNotification"); 1.79 + 1.80 + if (!sRegisterPowerSettingNotification || 1.81 + !sUnregisterPowerSettingNotification) { 1.82 + NS_ASSERTION(false, "Canot find PowerSettingNotification functions."); 1.83 + return; 1.84 + } 1.85 + 1.86 + // Create custom window to watch battery event 1.87 + // If we can get Gecko's window handle, this is unnecessary. 1.88 + 1.89 + if (sHWnd == nullptr) { 1.90 + WNDCLASSW wc; 1.91 + HMODULE hSelf = GetModuleHandle(nullptr); 1.92 + 1.93 + if (!GetClassInfoW(hSelf, L"MozillaBatteryClass", &wc)) { 1.94 + ZeroMemory(&wc, sizeof(WNDCLASSW)); 1.95 + wc.hInstance = hSelf; 1.96 + wc.lpfnWndProc = BatteryWindowProc; 1.97 + wc.lpszClassName = L"MozillaBatteryClass"; 1.98 + RegisterClassW(&wc); 1.99 + } 1.100 + 1.101 + sHWnd = CreateWindowW(L"MozillaBatteryClass", L"Battery Watcher", 1.102 + 0, 0, 0, 0, 0, 1.103 + nullptr, nullptr, hSelf, nullptr); 1.104 + } 1.105 + 1.106 + if (sHWnd == nullptr) { 1.107 + return; 1.108 + } 1.109 + 1.110 + sPowerHandle = 1.111 + sRegisterPowerSettingNotification(sHWnd, 1.112 + &GUID_ACDC_POWER_SOURCE, 1.113 + DEVICE_NOTIFY_WINDOW_HANDLE); 1.114 + sCapacityHandle = 1.115 + sRegisterPowerSettingNotification(sHWnd, 1.116 + &GUID_BATTERY_PERCENTAGE_REMAINING, 1.117 + DEVICE_NOTIFY_WINDOW_HANDLE); 1.118 + } else 1.119 + { 1.120 + // for Windows XP. If we remove Windows XP support, 1.121 + // we should remove timer-based power notification 1.122 + sUpdateTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.123 + if (sUpdateTimer) { 1.124 + sUpdateTimer->InitWithFuncCallback(UpdateHandler, 1.125 + nullptr, 1.126 + Preferences::GetInt("dom.battery.timer", 1.127 + 30000 /* 30s */), 1.128 + nsITimer::TYPE_REPEATING_SLACK); 1.129 + } 1.130 + } 1.131 +} 1.132 + 1.133 +void 1.134 +DisableBatteryNotifications() 1.135 +{ 1.136 + if (IsVistaOrLater()) { 1.137 + if (sPowerHandle) { 1.138 + sUnregisterPowerSettingNotification(sPowerHandle); 1.139 + sPowerHandle = nullptr; 1.140 + } 1.141 + 1.142 + if (sCapacityHandle) { 1.143 + sUnregisterPowerSettingNotification(sCapacityHandle); 1.144 + sCapacityHandle = nullptr; 1.145 + } 1.146 + 1.147 + if (sHWnd) { 1.148 + DestroyWindow(sHWnd); 1.149 + sHWnd = nullptr; 1.150 + } 1.151 + } else 1.152 + { 1.153 + if (sUpdateTimer) { 1.154 + sUpdateTimer->Cancel(); 1.155 + sUpdateTimer = nullptr; 1.156 + } 1.157 + } 1.158 +} 1.159 + 1.160 +void 1.161 +GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) 1.162 +{ 1.163 + SYSTEM_POWER_STATUS status; 1.164 + if (!GetSystemPowerStatus(&status)) { 1.165 + aBatteryInfo->level() = kDefaultLevel; 1.166 + aBatteryInfo->charging() = kDefaultCharging; 1.167 + aBatteryInfo->remainingTime() = kDefaultRemainingTime; 1.168 + return; 1.169 + } 1.170 + 1.171 + aBatteryInfo->level() = 1.172 + status.BatteryLifePercent == 255 ? kDefaultLevel 1.173 + : ((double)status.BatteryLifePercent) / 100.0; 1.174 + aBatteryInfo->charging() = (status.ACLineStatus != 0); 1.175 + if (status.ACLineStatus != 0) { 1.176 + if (aBatteryInfo->level() == 1.0) { 1.177 + // GetSystemPowerStatus API may returns -1 for BatteryFullLifeTime. 1.178 + // So, if battery is 100%, set kDefaultRemainingTime at force. 1.179 + aBatteryInfo->remainingTime() = kDefaultRemainingTime; 1.180 + } else { 1.181 + aBatteryInfo->remainingTime() = 1.182 + status.BatteryFullLifeTime == (DWORD)-1 ? kUnknownRemainingTime 1.183 + : status.BatteryFullLifeTime; 1.184 + } 1.185 + } else { 1.186 + aBatteryInfo->remainingTime() = 1.187 + status.BatteryLifeTime == (DWORD)-1 ? kUnknownRemainingTime 1.188 + : status.BatteryLifeTime; 1.189 + } 1.190 +} 1.191 + 1.192 +} // hal_impl 1.193 +} // mozilla