зеркало из https://github.com/mozilla/gecko-dev.git
Bug 696045 - Implement Mac backend for Battery API. r=BenWa,mounir
This commit is contained in:
Родитель
841d89c101
Коммит
1e2b24f0e6
|
@ -83,7 +83,7 @@ CPPSRCS += \
|
|||
$(NULL)
|
||||
else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
CPPSRCS += \
|
||||
FallbackBattery.cpp \
|
||||
CocoaBattery.cpp \
|
||||
FallbackVibration.cpp \
|
||||
FallbackPower.cpp \
|
||||
FallbackScreenConfiguration.cpp \
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <IOKit/ps/IOPowerSources.h>
|
||||
#import <IOKit/ps/IOPSKeys.h>
|
||||
|
||||
#include <mozilla/Hal.h>
|
||||
#include <mozilla/dom/battery/Constants.h>
|
||||
#include <mozilla/Services.h>
|
||||
|
||||
#include <nsObserverService.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
|
||||
|
||||
#ifndef kIOPSTimeRemainingUnknown
|
||||
#define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
|
||||
#endif
|
||||
#ifndef kIOPSTimeRemainingUnlimited
|
||||
#define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0)
|
||||
#endif
|
||||
|
||||
using namespace mozilla::dom::battery;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void);
|
||||
|
||||
class MacPowerInformationService
|
||||
{
|
||||
public:
|
||||
static MacPowerInformationService* GetInstance();
|
||||
static void Shutdown();
|
||||
|
||||
void BeginListening();
|
||||
void StopListening();
|
||||
|
||||
static void HandleChange(void *aContext);
|
||||
|
||||
~MacPowerInformationService();
|
||||
|
||||
private:
|
||||
MacPowerInformationService();
|
||||
|
||||
// The reference to the runloop that is notified of power changes.
|
||||
CFRunLoopSourceRef mRunLoopSource;
|
||||
|
||||
double mLevel;
|
||||
bool mCharging;
|
||||
double mRemainingTime;
|
||||
bool mShouldNotify;
|
||||
|
||||
friend void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
|
||||
|
||||
static MacPowerInformationService* sInstance;
|
||||
|
||||
static void* sIOKitFramework;
|
||||
static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate;
|
||||
};
|
||||
|
||||
void* MacPowerInformationService::sIOKitFramework;
|
||||
IOPSGetTimeRemainingEstimateFunc MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
|
||||
|
||||
/*
|
||||
* Implementation of mozilla::hal_impl::EnableBatteryNotifications,
|
||||
* mozilla::hal_impl::DisableBatteryNotifications,
|
||||
* and mozilla::hal_impl::GetCurrentBatteryInformation.
|
||||
*/
|
||||
|
||||
void
|
||||
EnableBatteryNotifications()
|
||||
{
|
||||
MacPowerInformationService::GetInstance()->BeginListening();
|
||||
}
|
||||
|
||||
void
|
||||
DisableBatteryNotifications()
|
||||
{
|
||||
MacPowerInformationService::GetInstance()->StopListening();
|
||||
}
|
||||
|
||||
void
|
||||
GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
|
||||
{
|
||||
MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
|
||||
|
||||
aBatteryInfo->level() = powerService->mLevel;
|
||||
aBatteryInfo->charging() = powerService->mCharging;
|
||||
aBatteryInfo->remainingTime() = powerService->mRemainingTime;
|
||||
}
|
||||
|
||||
/*
|
||||
* Following is the implementation of MacPowerInformationService.
|
||||
*/
|
||||
|
||||
MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
|
||||
|
||||
namespace {
|
||||
struct SingletonDestroyer MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(SingletonDestroyer, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
|
||||
{
|
||||
MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
|
||||
MacPowerInformationService::Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
/* static */ MacPowerInformationService*
|
||||
MacPowerInformationService::GetInstance()
|
||||
{
|
||||
if (sInstance) {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
sInstance = new MacPowerInformationService();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::Shutdown()
|
||||
{
|
||||
delete sInstance;
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
MacPowerInformationService::MacPowerInformationService()
|
||||
: mRunLoopSource(nullptr)
|
||||
, mLevel(kDefaultLevel)
|
||||
, mCharging(kDefaultCharging)
|
||||
, mRemainingTime(kDefaultRemainingTime)
|
||||
, mShouldNotify(false)
|
||||
{
|
||||
// IOPSGetTimeRemainingEstimate (and the related constants) are only available
|
||||
// on 10.7, so we test for their presence at runtime.
|
||||
sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (sIOKitFramework) {
|
||||
sIOPSGetTimeRemainingEstimate =
|
||||
(IOPSGetTimeRemainingEstimateFunc)dlsym(sIOKitFramework, "IOPSGetTimeRemainingEstimate");
|
||||
} else {
|
||||
sIOPSGetTimeRemainingEstimate = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MacPowerInformationService::~MacPowerInformationService()
|
||||
{
|
||||
MOZ_ASSERT(!mRunLoopSource,
|
||||
"The observers have not been correctly removed! "
|
||||
"(StopListening should have been called)");
|
||||
|
||||
if (sIOKitFramework) {
|
||||
dlclose(sIOKitFramework);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::BeginListening()
|
||||
{
|
||||
// Set ourselves up to be notified about changes.
|
||||
MOZ_ASSERT(!mRunLoopSource, "IOPS Notification Loop Source already set up. "
|
||||
"(StopListening should have been called)");
|
||||
|
||||
mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
|
||||
if (mRunLoopSource) {
|
||||
::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
|
||||
// Invoke our callback now so we have data if GetCurrentBatteryInformation is
|
||||
// called before a change happens.
|
||||
HandleChange(this);
|
||||
mShouldNotify = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::StopListening()
|
||||
{
|
||||
MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
|
||||
"(StopListening without BeginListening)");
|
||||
|
||||
::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
mRunLoopSource = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::HandleChange(void* aContext) {
|
||||
MacPowerInformationService* power =
|
||||
static_cast<MacPowerInformationService*>(aContext);
|
||||
|
||||
CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
|
||||
if (!data) {
|
||||
::CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of power sources.
|
||||
CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
|
||||
if (!list) {
|
||||
::CFRelease(list);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default values. These will be used if there are 0 sources or we can't find
|
||||
// better information.
|
||||
double level = kDefaultLevel;
|
||||
double charging = kDefaultCharging;
|
||||
double remainingTime = kDefaultRemainingTime;
|
||||
|
||||
// Look for the first battery power source to give us the information we need.
|
||||
// Usually there's only 1 available, depending on current power source.
|
||||
for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
|
||||
CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
|
||||
CFDictionaryRef currPowerSourceDesc = ::IOPSGetPowerSourceDescription(data, source);
|
||||
if (!currPowerSourceDesc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sIOPSGetTimeRemainingEstimate) {
|
||||
// See if we can get a time estimate.
|
||||
CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
|
||||
if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
|
||||
remainingTime = kUnknownRemainingTime;
|
||||
} else {
|
||||
remainingTime = estimate;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a battery level estimate. This key is required.
|
||||
int currentCapacity = 0;
|
||||
const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
|
||||
::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, ¤tCapacity);
|
||||
|
||||
// This key is also required.
|
||||
int maxCapacity = 0;
|
||||
cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
|
||||
::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
|
||||
|
||||
if (maxCapacity > 0) {
|
||||
level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
|
||||
}
|
||||
|
||||
// Find out if we're charging.
|
||||
// This key is optional, we fallback to kDefaultCharging if the current power
|
||||
// source doesn't have that info.
|
||||
if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
|
||||
charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bool isNewData = level != power->mLevel || charging != power->mCharging ||
|
||||
remainingTime != power->mRemainingTime;
|
||||
|
||||
power->mRemainingTime = remainingTime;
|
||||
power->mCharging = charging;
|
||||
power->mLevel = level;
|
||||
|
||||
// Notify the observers if stuff changed.
|
||||
if (power->mShouldNotify && isNewData) {
|
||||
hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
|
||||
power->mCharging,
|
||||
power->mRemainingTime));
|
||||
}
|
||||
|
||||
::CFRelease(data);
|
||||
::CFRelease(list);
|
||||
}
|
||||
|
||||
} // namespace hal_impl
|
||||
} // namespace mozilla
|
Загрузка…
Ссылка в новой задаче