michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "Hal.h" michael@0: #include "HalImpl.h" michael@0: #include "nsITimer.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/battery/Constants.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: michael@0: #include michael@0: #include "mozilla/WindowsVersion.h" michael@0: michael@0: using namespace mozilla::dom::battery; michael@0: michael@0: namespace mozilla { michael@0: namespace hal_impl { michael@0: michael@0: static nsCOMPtr sUpdateTimer; michael@0: michael@0: /* Power Event API is Vista or later */ michael@0: static decltype(RegisterPowerSettingNotification)* sRegisterPowerSettingNotification = nullptr; michael@0: static decltype(UnregisterPowerSettingNotification)* sUnregisterPowerSettingNotification = nullptr; michael@0: static HPOWERNOTIFY sPowerHandle = nullptr; michael@0: static HPOWERNOTIFY sCapacityHandle = nullptr; michael@0: static HWND sHWnd = nullptr; michael@0: michael@0: static void michael@0: UpdateHandler(nsITimer* aTimer, void* aClosure) { michael@0: NS_ASSERTION(!IsVistaOrLater(), michael@0: "We shouldn't call this function for Vista or later version!"); michael@0: michael@0: static hal::BatteryInformation sLastInfo; michael@0: hal::BatteryInformation currentInfo; michael@0: michael@0: hal_impl::GetCurrentBatteryInformation(¤tInfo); michael@0: if (sLastInfo.level() != currentInfo.level() || michael@0: sLastInfo.charging() != currentInfo.charging() || michael@0: sLastInfo.remainingTime() != currentInfo.remainingTime()) { michael@0: hal::NotifyBatteryChange(currentInfo); michael@0: sLastInfo = currentInfo; michael@0: } michael@0: } michael@0: michael@0: static michael@0: LRESULT CALLBACK michael@0: BatteryWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { michael@0: if (msg != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE) { michael@0: return DefWindowProc(hwnd, msg, wParam, lParam); michael@0: } michael@0: michael@0: hal::BatteryInformation currentInfo; michael@0: michael@0: // Since we need update remainingTime, we cannot use LPARAM. michael@0: hal_impl::GetCurrentBatteryInformation(¤tInfo); michael@0: michael@0: hal::NotifyBatteryChange(currentInfo); michael@0: return TRUE; michael@0: } michael@0: michael@0: void michael@0: EnableBatteryNotifications() michael@0: { michael@0: if (IsVistaOrLater()) { michael@0: // RegisterPowerSettingNotification is from Vista or later. michael@0: // Use this API if available. michael@0: HMODULE hUser32 = GetModuleHandleW(L"USER32.DLL"); michael@0: if (!sRegisterPowerSettingNotification) michael@0: sRegisterPowerSettingNotification = (decltype(RegisterPowerSettingNotification)*) michael@0: GetProcAddress(hUser32, "RegisterPowerSettingNotification"); michael@0: if (!sUnregisterPowerSettingNotification) michael@0: sUnregisterPowerSettingNotification = (decltype(UnregisterPowerSettingNotification)*) michael@0: GetProcAddress(hUser32, "UnregisterPowerSettingNotification"); michael@0: michael@0: if (!sRegisterPowerSettingNotification || michael@0: !sUnregisterPowerSettingNotification) { michael@0: NS_ASSERTION(false, "Canot find PowerSettingNotification functions."); michael@0: return; michael@0: } michael@0: michael@0: // Create custom window to watch battery event michael@0: // If we can get Gecko's window handle, this is unnecessary. michael@0: michael@0: if (sHWnd == nullptr) { michael@0: WNDCLASSW wc; michael@0: HMODULE hSelf = GetModuleHandle(nullptr); michael@0: michael@0: if (!GetClassInfoW(hSelf, L"MozillaBatteryClass", &wc)) { michael@0: ZeroMemory(&wc, sizeof(WNDCLASSW)); michael@0: wc.hInstance = hSelf; michael@0: wc.lpfnWndProc = BatteryWindowProc; michael@0: wc.lpszClassName = L"MozillaBatteryClass"; michael@0: RegisterClassW(&wc); michael@0: } michael@0: michael@0: sHWnd = CreateWindowW(L"MozillaBatteryClass", L"Battery Watcher", michael@0: 0, 0, 0, 0, 0, michael@0: nullptr, nullptr, hSelf, nullptr); michael@0: } michael@0: michael@0: if (sHWnd == nullptr) { michael@0: return; michael@0: } michael@0: michael@0: sPowerHandle = michael@0: sRegisterPowerSettingNotification(sHWnd, michael@0: &GUID_ACDC_POWER_SOURCE, michael@0: DEVICE_NOTIFY_WINDOW_HANDLE); michael@0: sCapacityHandle = michael@0: sRegisterPowerSettingNotification(sHWnd, michael@0: &GUID_BATTERY_PERCENTAGE_REMAINING, michael@0: DEVICE_NOTIFY_WINDOW_HANDLE); michael@0: } else michael@0: { michael@0: // for Windows XP. If we remove Windows XP support, michael@0: // we should remove timer-based power notification michael@0: sUpdateTimer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: if (sUpdateTimer) { michael@0: sUpdateTimer->InitWithFuncCallback(UpdateHandler, michael@0: nullptr, michael@0: Preferences::GetInt("dom.battery.timer", michael@0: 30000 /* 30s */), michael@0: nsITimer::TYPE_REPEATING_SLACK); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DisableBatteryNotifications() michael@0: { michael@0: if (IsVistaOrLater()) { michael@0: if (sPowerHandle) { michael@0: sUnregisterPowerSettingNotification(sPowerHandle); michael@0: sPowerHandle = nullptr; michael@0: } michael@0: michael@0: if (sCapacityHandle) { michael@0: sUnregisterPowerSettingNotification(sCapacityHandle); michael@0: sCapacityHandle = nullptr; michael@0: } michael@0: michael@0: if (sHWnd) { michael@0: DestroyWindow(sHWnd); michael@0: sHWnd = nullptr; michael@0: } michael@0: } else michael@0: { michael@0: if (sUpdateTimer) { michael@0: sUpdateTimer->Cancel(); michael@0: sUpdateTimer = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) michael@0: { michael@0: SYSTEM_POWER_STATUS status; michael@0: if (!GetSystemPowerStatus(&status)) { michael@0: aBatteryInfo->level() = kDefaultLevel; michael@0: aBatteryInfo->charging() = kDefaultCharging; michael@0: aBatteryInfo->remainingTime() = kDefaultRemainingTime; michael@0: return; michael@0: } michael@0: michael@0: aBatteryInfo->level() = michael@0: status.BatteryLifePercent == 255 ? kDefaultLevel michael@0: : ((double)status.BatteryLifePercent) / 100.0; michael@0: aBatteryInfo->charging() = (status.ACLineStatus != 0); michael@0: if (status.ACLineStatus != 0) { michael@0: if (aBatteryInfo->level() == 1.0) { michael@0: // GetSystemPowerStatus API may returns -1 for BatteryFullLifeTime. michael@0: // So, if battery is 100%, set kDefaultRemainingTime at force. michael@0: aBatteryInfo->remainingTime() = kDefaultRemainingTime; michael@0: } else { michael@0: aBatteryInfo->remainingTime() = michael@0: status.BatteryFullLifeTime == (DWORD)-1 ? kUnknownRemainingTime michael@0: : status.BatteryFullLifeTime; michael@0: } michael@0: } else { michael@0: aBatteryInfo->remainingTime() = michael@0: status.BatteryLifeTime == (DWORD)-1 ? kUnknownRemainingTime michael@0: : status.BatteryLifeTime; michael@0: } michael@0: } michael@0: michael@0: } // hal_impl michael@0: } // mozilla