windows-nt/Source/XPSP1/NT/shell/osshell/cpls/batmeter/batmeter.c
2020-09-26 16:20:57 +08:00

1342 lines
41 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT MICROSOFT CORP., 1997
*
* TITLE: BATMETER.C
*
* VERSION: 2.0
*
* AUTHOR: ReedB
*
* DATE: 17 Oct, 1996
*
* DESCRIPTION:
*
* Implements the battery meter of the PowerCfg or SysTray battery
* meter windows. The battery meter has two display modes, single and
* multi-battery. In single mode, a representation of the total of all battery
* capacity in a system is displayed. In multi-battery mode, battery
* information is displayed for each individual battery as well as the total.
*
* The battery meter parent window receives notification from USER when
* any battery status has changed through the WM_POWERBROADCAST,
* PBT_APMPOWERSTATUSCHANGE message.
*
* ??? We need to add perfmon support: Create and maintain keys/values
* under HKEY_PERFORMANCE_DATA.
*
*******************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <commctrl.h>
#include <dbt.h>
#include <objbase.h>
#include <initguid.h>
#include <ntpoapi.h>
#include <poclass.h>
#include <setupapi.h>
#include <syssetup.h>
#include <setupbat.h>
#include <ccstock.h>
#include <help.h>
#include "powrprofp.h"
#include "batmeter.h"
#include "bmresid.h"
#include "..\powercfg\PwrMn_cs.h"
#include "shfusion.h"
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
// Simulated battery only for debug build.
#ifndef DEBUG
#undef SIM_BATTERY
#endif
// Define some things for debug.h. Required when you include ccstock.h in
// one and only one file.
#define SZ_DEBUGINI "ccshell.ini"
#define SZ_DEBUGSECTION "BATMETER"
#define SZ_MODULE "BATMETER"
#define DECLARE_DEBUG
#include <debug.h>
/*******************************************************************************
*
* G L O B A L D A T A
*
*******************************************************************************/
HINSTANCE g_hInstance; // Global instance handle of this DLL.
HWND g_hwndParent; // Parent of the battery meter.
HWND g_hwndBatMeter; // Battery meter.
// The following constant global array is used to walk through the
// control ID's in the battery metter dialog box. It makes getting
// a control ID from a battery number easy.
#define BAT_ICON 0
#define BAT_STATUS 1
#define BAT_REMAINING 2
#define BAT_NUM 3
#define BAT_LAST BAT_NUM+1
UINT g_iMapBatNumToID [NUM_BAT+1][4]={
{IDC_POWERSTATUSICON, IDC_POWERSTATUSBAR, IDC_REMAINING, IDC_BATNUM0},
{IDC_POWERSTATUSICON1, IDC_STATUS1, IDC_REMAINING1, IDC_BATNUM1},
{IDC_POWERSTATUSICON2, IDC_STATUS2, IDC_REMAINING2, IDC_BATNUM2},
{IDC_POWERSTATUSICON3, IDC_STATUS3, IDC_REMAINING3, IDC_BATNUM3},
{IDC_POWERSTATUSICON4, IDC_STATUS4, IDC_REMAINING4, IDC_BATNUM4},
{IDC_POWERSTATUSICON5, IDC_STATUS5, IDC_REMAINING5, IDC_BATNUM5},
{IDC_POWERSTATUSICON6, IDC_STATUS6, IDC_REMAINING6, IDC_BATNUM6},
{IDC_POWERSTATUSICON7, IDC_STATUS7, IDC_REMAINING7, IDC_BATNUM7},
{IDC_POWERSTATUSICON8, IDC_STATUS8, IDC_REMAINING8, IDC_BATNUM8}
};
// Global battery state list. This list has the composite system battery state
// as it's always present head. individual battery devices are linked to this
// head. Use WalkBatteryState(ALL, ... to walk the entire list, including the
// head. Use WalkBatteryState(DEVICES, ... to walk just the device list. If a
// battery is in this list, it's displayable. g_uiBatCount is the count of
// battery devices in this list. The composite battery is not counted. The
// g_pbs array provides a handy UI battery number to pbs conversion. The
// following three variables are only changed during DeviceChanged.
BATTERY_STATE g_bs;
UINT g_uiBatCount;
PBATTERY_STATE g_pbs[NUM_BAT+1];
LPTSTR g_lpszDriverNames[NUM_BAT];
UINT g_uiDriverCount;
BOOL g_bShowingMulti;
// The following array provides context sensitive help associations between
// resource control identifiers and help resource string identifiers.
const DWORD g_ContextMenuHelpIDs[] =
{
IDC_BATMETERGROUPBOX, IDH_COMM_GROUPBOX,
IDC_BATMETERGROUPBOX1, IDH_COMM_GROUPBOX,
IDC_POWERSTATUSICON, NO_HELP,
IDC_POWERSTATUSICON1, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON2, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON3, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON4, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON5, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON6, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON7, IDH_BATMETER_CHARGING_ICON,
IDC_POWERSTATUSICON8, IDH_BATMETER_CHARGING_ICON,
IDC_BATNUM1, NO_HELP,
IDC_BATNUM2, NO_HELP,
IDC_BATNUM3, NO_HELP,
IDC_BATNUM4, NO_HELP,
IDC_BATNUM5, NO_HELP,
IDC_BATNUM6, NO_HELP,
IDC_BATNUM7, NO_HELP,
IDC_BATNUM8, NO_HELP,
IDC_STATUS1, NO_HELP,
IDC_STATUS2, NO_HELP,
IDC_STATUS3, NO_HELP,
IDC_STATUS4, NO_HELP,
IDC_STATUS5, NO_HELP,
IDC_STATUS6, NO_HELP,
IDC_STATUS7, NO_HELP,
IDC_STATUS8, NO_HELP,
IDC_MOREINFO, NO_HELP,
IDC_CURRENTPOWERSOURCE, IDH_BATMETER_CURPOWERSOURCE,
IDC_BATTERYLEVEL, IDH_BATMETER_CURPOWERSOURCE,
IDC_TOTALBATPWRREMAINING, IDH_BATMETER_TOTALBATPOWER,
IDC_REMAINING, IDH_BATMETER_TOTALBATPOWER,
IDC_POWERSTATUSBAR, IDH_BATMETER_TOTALBATPOWER,
IDC_BARPERCENT, IDH_BATMETER_TOTALBATPOWER,
IDC_TOTALTIME, IDH_BATMETER_TOTALTIME,
IDC_TIMEREMAINING, IDH_BATMETER_TOTALTIME,
IDC_BATTERYNAME, IDH_DETAILED_BATINFO_LABELS,
IDC_DEVNAME, IDH_DETAILED_BATINFO_LABELS,
IDC_UNIQUEID, IDH_DETAILED_BATINFO_LABELS,
IDC_BATID, IDH_DETAILED_BATINFO_LABELS,
IDC_MANUFACTURE, IDH_DETAILED_BATINFO_LABELS,
IDC_BATMANNAME, IDH_DETAILED_BATINFO_LABELS,
IDC_DATEMANUFACTURED, IDH_DETAILED_BATINFO_LABELS,
IDC_BATMANDATE, IDH_DETAILED_BATINFO_LABELS,
IDC_CHEMISTRY, IDH_DETAILED_BATINFO_LABELS,
IDC_CHEM, IDH_DETAILED_BATINFO_LABELS,
IDC_POWERSTATE, IDH_DETAILED_BATINFO_LABELS,
IDC_STATE, IDH_DETAILED_BATINFO_LABELS,
IDC_REFRESH, IDH_DETAILED_BATINFO_REFRESH,
0, 0
};
/*******************************************************************************
*
* P U B L I C E N T R Y P O I N T S
*
*******************************************************************************/
/*******************************************************************************
*
* DllInitialize
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
BOOL DllInitialize(IN PVOID hmod, IN ULONG ulReason, IN PCONTEXT pctx OPTIONAL)
{
UNREFERENCED_PARAMETER(pctx);
switch (ulReason) {
case DLL_PROCESS_ATTACH:
g_hInstance = hmod;
DisableThreadLibraryCalls(g_hInstance);
SHFusionInitializeFromModuleID(hmod, 124);
break;
case DLL_PROCESS_DETACH:
SHFusionUninitialize();
break;
}
return TRUE;
}
/*******************************************************************************
*
* PowerCapabilities
*
* DESCRIPTION:
* This public function is used to determine if the system has any power
* management capabilities which require UI support. Return TRUE if power
* management UI should be displayed.
*
* PARAMETERS:
*
*******************************************************************************/
BOOL PowerCapabilities()
{
SYSTEM_POWER_CAPABILITIES spc;
int dummy;
#ifndef SIM_BATTERY
if (GetPwrCapabilities(&spc)) {
if ((spc.PowerButtonPresent) ||
(spc.SleepButtonPresent) ||
(spc.LidPresent) ||
(spc.SystemS1) ||
(spc.SystemS2) ||
(spc.SystemS3) ||
(spc.SystemS4) ||
(spc.SystemS5) ||
(spc.DiskSpinDown) ||
(spc.SystemBatteriesPresent)) {
return TRUE;
}
else {
if (SystemParametersInfo(SPI_GETLOWPOWERACTIVE, 0, &dummy, 0)) {
return TRUE;
}
}
}
return FALSE;
#else
return TRUE;
#endif
}
/*******************************************************************************
*
* BatMeterCapabilities
*
* DESCRIPTION:
* This public function is used to determine if the battery meter library
* can run on the host machine. Return TRUE on success (battery meter can run).
*
* PARAMETERS:
* ppuiBatCount - Points to a pointer which will be filled in with a pointer
* to the global battery count.
*
*******************************************************************************/
BOOL BatMeterCapabilities(
PUINT *ppuiBatCount
)
{
#ifndef SIM_BATTERY
SYSTEM_POWER_CAPABILITIES spc;
#endif // SIM_BATTERY
if (ppuiBatCount) {
*ppuiBatCount = &g_uiBatCount;
}
g_uiBatCount = 0;
#ifndef SIM_BATTERY
// Make sure we have batteries to query.
if (GetPwrCapabilities(&spc)) {
if (spc.SystemBatteriesPresent) {
g_uiDriverCount = GetBatteryDriverNames(g_lpszDriverNames);
if (g_uiDriverCount != 0) {
g_uiBatCount = g_uiDriverCount;
return TRUE;
}
else {
DebugPrint( "BatMeterCapabilities, no battery drivers found.");
}
}
}
return FALSE;
#else // SIM_BATTERY
g_uiBatCount = g_uiDriverCount = GetBatteryDriverNames(g_lpszDriverNames);
return UpdateDriverList(g_lpszDriverNames, g_uiDriverCount);
#endif // SIM_BATTERY
}
/*******************************************************************************
*
* CreateBatMeter
*
* DESCRIPTION:
* Create, fetch data for and draw the battery meter window. Returns a handle
* to the newly created battery meter window on success, NULL on failure.
*
* PARAMETERS:
* hwndParent - Parent of the battery meter dialog.
* wndFrame - Frame to locate the battery meter dialog.
* bShowMulti - Specifies the display mode (TRUE -> multiple battery).
* pbsComposite - Optional pointer to composite battery state.
*
*******************************************************************************/
HWND CreateBatMeter(
HWND hwndParent,
HWND hwndFrame,
BOOL bShowMulti,
PBATTERY_STATE pbsComposite
)
{
INT iWidth, iHeight;
RECT rFrame = {0};
// Build the battery devices name list if hasn't already been built.
if (!g_uiBatCount)
{
BatMeterCapabilities(NULL);
}
// Remember if we are showing details for each battery
g_bShowingMulti = bShowMulti;
// Make sure we have at least one battery.
if (g_uiBatCount)
{
// Create the battery meter control.
g_hwndParent = hwndParent;
g_hwndBatMeter = CreateDialog(g_hInstance,
MAKEINTRESOURCE(IDD_BATMETER),
hwndParent,
BatMeterDlgProc);
// Place the battery meter in the passed frame window.
if ((g_hwndBatMeter) && (hwndFrame))
{
// Position the BatMeter dialog in the frame.
if (!GetWindowRect(hwndFrame, &rFrame))
{
DebugPrint( "CreateBatMeter, GetWindowRect failed, hwndFrame: %08X", hwndFrame);
}
iWidth = rFrame.right - rFrame.left;
iHeight = rFrame.bottom - rFrame.top;
if (IsBiDiLocalizedSystemEx(NULL))
{
// Whistler #209400: On BIDI systems, ScreenToClient() wants the right
// coord in the left location because everything is flipped.
rFrame.left = rFrame.right;
}
if (!ScreenToClient(hwndParent, (LPPOINT)&rFrame))
{
DebugPrint( "CreateBatMeter, ScreenToClient failed");
}
if (!MoveWindow(g_hwndBatMeter,
rFrame.left,
rFrame.top,
iWidth,
iHeight,
FALSE))
{
DebugPrint( "CreateBatMeter, MoveWindow failed, %d, %d", rFrame.left, rFrame.top);
}
// Build the battery driver data list.
if (!UpdateDriverList(g_lpszDriverNames, g_uiDriverCount))
{
return DestroyBatMeter(g_hwndBatMeter);
}
// Do the first update.
UpdateBatMeter(g_hwndBatMeter, bShowMulti, TRUE, pbsComposite);
ShowWindow(g_hwndBatMeter, SW_SHOWNOACTIVATE);
}
}
return g_hwndBatMeter;
}
/*******************************************************************************
*
* DestroyBatMeter
*
* DESCRIPTION:
*
*******************************************************************************/
HWND DestroyBatMeter(HWND hWnd)
{
SendMessage(hWnd, WM_DESTROYBATMETER, 0, 0);
g_hwndBatMeter = NULL;
return g_hwndBatMeter;
}
/*******************************************************************************
*
* UpdateBatMeter
*
* DESCRIPTION:
* This function should be called when the battery meter parent window
* receives a WM_POWERBROADCAST, PBT_APMPOWERSTATUSCHANGE message, it will
* update the data in the global battery state list. If needed the display
* will also be updated.
*
* PARAMETERS:
* HWND hwndBatMeter, hWnd of the battery meter dialog
* BOOL bShowMulti, Specifies the display mode
* BOOL bForceUpdate, Forces a UI update
* PBATTERY_STATE pbsComposite Optional pointer to composite battery state.
*
*******************************************************************************/
BOOL UpdateBatMeter(
HWND hWnd,
BOOL bShowMulti,
BOOL bForceUpdate,
PBATTERY_STATE pbsComposite
)
{
BOOL bRet = FALSE;
SYSTEM_POWER_STATUS sps;
UINT uIconID;
// Update the composite battery state.
if (GetSystemPowerStatus(&sps) && hWnd) {
if (sps.BatteryLifePercent > 100) {
DebugPrint( "GetSystemPowerStatuse, set BatteryLifePercent: %d", sps.BatteryLifePercent);
}
// Fill in the composite battery state.
SystemPowerStatusToBatteryState(&sps, &g_bs);
// Update the information in the battery state list if we have a battery.
if (g_hwndBatMeter) {
#ifndef SIM_BATTERY
WalkBatteryState(DEVICES,
(WALKENUMPROC)UpdateBatInfoProc,
NULL,
(LPARAM)NULL,
(LPARAM)NULL);
#else
WalkBatteryState(DEVICES,
(WALKENUMPROC)SimUpdateBatInfoProc,
NULL,
(LPARAM)NULL,
(LPARAM)NULL);
#endif
// See if the current display mode matches the requested mode.
if ((g_bShowingMulti != bShowMulti) || (bForceUpdate)) {
g_bShowingMulti = SwitchDisplayMode(hWnd, bShowMulti);
bForceUpdate = TRUE;
}
if (g_bShowingMulti) {
// Walk the bs list, and update all battery displays.
WalkBatteryState(ALL,
(WALKENUMPROC)UpdateBatMeterProc,
hWnd,
(LPARAM)g_bShowingMulti,
(LPARAM)bForceUpdate);
}
else {
// Display only the comosite battery information.
UpdateBatMeterProc(&g_bs,
hWnd,
(LPARAM)g_bShowingMulti,
(LPARAM)bForceUpdate);
}
bRet = TRUE;
}
}
else {
// Fill in default composite info.
g_bs.ulPowerState = BATTERY_POWER_ON_LINE;
g_bs.ulBatLifePercent = (UINT) -1;
g_bs.ulBatLifeTime = (UINT) -1;
uIconID = MapBatInfoToIconID(&g_bs);
g_bs.hIconCache = GetBattIcon(hWnd, uIconID, g_bs.hIconCache, FALSE, 32);
g_bs.hIconCache16 = GetBattIcon(hWnd, uIconID, g_bs.hIconCache16, FALSE, 16);
}
// If a pointer is provided, copy the composite battery state data.
if (pbsComposite) {
if (pbsComposite->ulSize == sizeof(BATTERY_STATE)) {
memcpy(pbsComposite, &g_bs, sizeof(BATTERY_STATE));
}
else {
DebugPrint( "UpdateBatMeter, passed BATTERY_STATE size is invalid");
}
}
return bRet;
}
/*******************************************************************************
*
* P R I V A T E F U N C T I O N S
*
*******************************************************************************/
/*******************************************************************************
*
* LoadDynamicString
*
* DESCRIPTION:
* Wrapper for the FormatMessage function that loads a string from our
* resource table into a dynamically allocated buffer, optionally filling
* it with the variable arguments passed.
*
* PARAMETERS:
* uiStringID - resource identifier of the string to use.
* ... - Optional parameters to use to format the string message.
*
*******************************************************************************/
LPTSTR CDECL LoadDynamicString(UINT uiStringID, ... )
{
va_list Marker;
TCHAR szBuf[256];
LPTSTR lpsz;
int iLen;
// va_start is a macro...it breaks when you use it as an assign...on ALPHA.
va_start(Marker, uiStringID);
iLen = LoadString(g_hInstance, uiStringID, szBuf, ARRAYSIZE(szBuf));
if (iLen == 0) {
DebugPrint( "LoadDynamicString: LoadString on: 0x%X failed", uiStringID);
return NULL;
}
FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
(LPVOID) szBuf, 0, 0, (LPTSTR)&lpsz, 0, &Marker);
return lpsz;
}
/*******************************************************************************
*
* DisplayFreeStr
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
LPTSTR DisplayFreeStr(HWND hWnd, UINT uID, LPTSTR lpsz, BOOL bFree)
{
if (lpsz) {
SetDlgItemText(hWnd, uID, lpsz);
ShowWindow(GetDlgItem(hWnd, uID), SW_SHOWNOACTIVATE);
if (bFree) {
LocalFree(lpsz);
return NULL;
}
}
else {
ShowWindow(GetDlgItem(hWnd, uID), SW_HIDE);
}
return lpsz;
}
/*******************************************************************************
*
* ShowHideItem
* ShowItem
* HideItem
*
* DESCRIPTION:
* Handy helpers to show or hide dialog items in the battery meter dialog.
*
* PARAMETERS:
* hWnd - Battery meter dialog handle.
* uID - Control ID of control to be shown or hidden.
*
*******************************************************************************/
BOOL ShowHideItem(HWND hWnd, UINT uID, BOOL bShow)
{
ShowWindow(GetDlgItem(hWnd, uID), (bShow) ? SW_SHOWNOACTIVATE : SW_HIDE);
return bShow;
}
void ShowItem(HWND hWnd, UINT uID)
{
ShowWindow(GetDlgItem(hWnd, uID), SW_SHOWNOACTIVATE);
}
void HideItem(HWND hWnd, UINT uID)
{
ShowWindow(GetDlgItem(hWnd, uID), SW_HIDE);
}
/*******************************************************************************
*
* SwitchDisplayMode
*
* DESCRIPTION:
* Return TRUE if display is switched to multi battery mode.
*
* PARAMETERS:
*
*******************************************************************************/
BOOL SwitchDisplayMode(HWND hWnd, BOOL bShowMulti)
{
ULONG i, j;
// Override request if multi-battery display is not possible.
if ((bShowMulti) && (!g_uiBatCount)) {
bShowMulti = FALSE;
}
if (!g_uiBatCount) {
//
// Hide all info if no batteries are installed
//
HideItem(hWnd, IDC_POWERSTATUSBAR);
HideItem(hWnd, IDC_BARPERCENT);
HideItem(hWnd, IDC_MOREINFO);
} else if (bShowMulti) {
HideItem(hWnd, IDC_POWERSTATUSBAR);
HideItem(hWnd, IDC_BARPERCENT);
ShowItem(hWnd, IDC_MOREINFO);
for (i = 1; i <= g_uiBatCount; i++) {
for (j = 0; j < BAT_LAST; j++) {
ShowItem(hWnd, g_iMapBatNumToID[i][0]);
}
}
}
else {
for (i = 1; i <= g_uiBatCount; i++) {
for (j = 0; j < BAT_LAST; j++) {
HideItem(hWnd, g_iMapBatNumToID[i][j]);
}
}
ShowItem(hWnd, IDC_POWERSTATUSBAR);
ShowItem(hWnd, IDC_BARPERCENT);
HideItem(hWnd, IDC_MOREINFO);
}
return bShowMulti;
}
/*******************************************************************************
*
* CleanupBatteryData
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
void CleanupBatteryData(void)
{
g_hwndBatMeter = NULL;
// Mark all batteries as missing.
memset(&g_pbs, 0, sizeof(g_pbs));
// Walk the bs list, remove all devices and cleanup.
WalkBatteryState(DEVICES,
(WALKENUMPROC)RemoveMissingProc,
NULL,
(LPARAM)NULL,
(LPARAM)REMOVE_ALL);
// Free any old driver names.
FreeBatteryDriverNames(g_lpszDriverNames);
g_uiBatCount = 0;
}
/*******************************************************************************
*
* BatMeterDlgProc
*
* DESCRIPTION:
* DialogProc for the Battery Meter control. Provide support for more battery
* info.
*
* PARAMETERS:
*
*******************************************************************************/
LRESULT CALLBACK BatMeterDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef WINNT
UINT i, j;
PBATTERY_STATE pbsTemp;
#endif // WINNT
UINT uiBatNum;
switch (uMsg) {
case WM_COMMAND:
if ((HIWORD(wParam) == STN_CLICKED) ||
(HIWORD(wParam) == BN_CLICKED)) {
switch (LOWORD(wParam)) {
case IDC_POWERSTATUSICON1:
case IDC_POWERSTATUSICON2:
case IDC_POWERSTATUSICON3:
case IDC_POWERSTATUSICON4:
case IDC_POWERSTATUSICON5:
case IDC_POWERSTATUSICON6:
case IDC_POWERSTATUSICON7:
case IDC_POWERSTATUSICON8:
uiBatNum = LOWORD(wParam) - IDC_POWERSTATUSICON1 + 1;
// Allow battery details only for present batteries.
if ((g_pbs[uiBatNum]) &&
(g_pbs[uiBatNum]->ulTag != BATTERY_TAG_INVALID)) {
DialogBoxParam(g_hInstance,
MAKEINTRESOURCE(IDD_BATDETAIL),
hWnd,
BatDetailDlgProc,
(LPARAM)g_pbs[uiBatNum]);
}
break;
}
}
break;
case WM_DESTROYBATMETER:
CleanupBatteryData();
EndDialog(hWnd, wParam);
break;
case WM_DESTROY:
CleanupBatteryData();
break;
case WM_DEVICECHANGE:
#ifdef WINNT
if ((wParam == DBT_DEVICEQUERYREMOVE) || (wParam == DBT_DEVICEREMOVECOMPLETE)) {
if ( ((PDEV_BROADCAST_HANDLE)lParam)->dbch_devicetype == DBT_DEVTYP_HANDLE) {
//
// Find Device that got removed
//
pbsTemp = DEVICES;
while (pbsTemp) {
if (pbsTemp->hDevNotify == ((PDEV_BROADCAST_HANDLE)lParam)->dbch_hdevnotify) {
break;
}
pbsTemp = pbsTemp->bsNext;
}
if (!pbsTemp) {
break;
}
//
// Close the handle to this device and release cached data.
//
RemoveBatteryStateDevice (pbsTemp);
g_uiDriverCount--;
g_uiBatCount = g_uiDriverCount;
// Clear and rebuild g_pbs, the handy batttery number to pbs array.
memset(&g_pbs, 0, sizeof(g_pbs));
pbsTemp = &g_bs;
for (i = 0; i <= g_uiBatCount; i++) {
if (pbsTemp) {
g_pbs[i] = pbsTemp;
pbsTemp->ulBatNum = i;
pbsTemp = pbsTemp->bsNext;
}
}
// Refresh display
for (i = 1; i <= NUM_BAT; i++) {
for (j = 0; j < BAT_LAST; j++) {
HideItem(g_hwndBatMeter, g_iMapBatNumToID[i][j]);
}
}
g_bShowingMulti = SwitchDisplayMode (g_hwndBatMeter, g_bShowingMulti);
if (g_bShowingMulti) {
// Walk the bs list, and update all battery displays.
WalkBatteryState(DEVICES,
(WALKENUMPROC)UpdateBatMeterProc,
g_hwndBatMeter,
(LPARAM)g_bShowingMulti,
(LPARAM)TRUE);
}
}
}
#else
if (wParam == DBT_DEVICEQUERYREMOVE) {
if (g_hwndBatMeter) {
// Close all of the batteries.
CleanupBatteryData();
}
}
#endif
return TRUE;
case WM_HELP: // F1
WinHelp(((LPHELPINFO)lParam)->hItemHandle, PWRMANHLP, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)g_ContextMenuHelpIDs);
return TRUE;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND)wParam, PWRMANHLP, HELP_CONTEXTMENU, (ULONG_PTR)(LPTSTR)g_ContextMenuHelpIDs);
return TRUE;
}
return FALSE;
}
/*******************************************************************************
*
* GetBattIcon
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
HICON PASCAL GetBattIcon(
HWND hWnd,
UINT uIconID,
HICON hIconCache,
BOOL bWantBolt,
UINT uiRes)
{
static HIMAGELIST hImgLst32, hImgLst16;
HIMAGELIST hImgLst;
int ImageIndex;
// Destroy the old cached icon.
if (hIconCache) {
DestroyIcon(hIconCache);
}
// Don't put the charging bolt over the top of IDI_BATGONE.
if (uIconID == IDI_BATGONE) {
bWantBolt = FALSE;
}
// Use the transparency color must match that in the bit maps.
if (!hImgLst32 || !hImgLst16) {
hImgLst32 = ImageList_LoadImage(g_hInstance,
MAKEINTRESOURCE(IDB_BATTS),
32, 0, RGB(255, 0, 255), IMAGE_BITMAP, 0);
hImgLst16 = ImageList_LoadImage(g_hInstance,
MAKEINTRESOURCE(IDB_BATTS16),
16, 0, RGB(255, 0, 255), IMAGE_BITMAP, 0);
ImageList_SetOverlayImage(hImgLst32, IDI_CHARGE-FIRST_ICON_IMAGE, 1);
ImageList_SetOverlayImage(hImgLst16, IDI_CHARGE-FIRST_ICON_IMAGE, 1);
}
if (uiRes == 32) {
hImgLst = hImgLst32;
}
else {
hImgLst = hImgLst16;
}
ImageIndex = uIconID - FIRST_ICON_IMAGE;
if (bWantBolt) {
return ImageList_GetIcon(hImgLst, ImageIndex, INDEXTOOVERLAYMASK(1));
}
else {
return ImageList_GetIcon(hImgLst, ImageIndex, ILD_NORMAL);
}
}
/*******************************************************************************
*
* CheckUpdateBatteryState
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
#define UPDATESTATUS_NOUPDATE 0
#define UPDATESTATUS_UPDATE 1
#define UPDATESTATUS_UPDATE_CHARGE 2
UINT CheckUpdateBatteryState(
PBATTERY_STATE pbs,
BOOL bForceUpdate
)
{
UINT uiRetVal = UPDATESTATUS_NOUPDATE;
// Check to see if anything in the battery status has changed
// since last time. If not then we have no work to do!
if ((bForceUpdate) ||
!((pbs->ulTag == pbs->ulLastTag) &&
(pbs->ulBatLifePercent == pbs->ulLastBatLifePercent) &&
(pbs->ulBatLifeTime == pbs->ulLastBatLifeTime) &&
(pbs->ulPowerState == pbs->ulLastPowerState))) {
uiRetVal = UPDATESTATUS_UPDATE;
// Check for the special case where the charging state has changed.
if ((pbs->ulPowerState & BATTERY_CHARGING) !=
(pbs->ulLastPowerState & BATTERY_CHARGING)) {
uiRetVal |= UPDATESTATUS_UPDATE_CHARGE;
}
// Copy current battery state to last.
pbs->ulLastTag = pbs->ulTag;
pbs->ulLastBatLifePercent = pbs->ulBatLifePercent;
pbs->ulLastBatLifeTime = pbs->ulBatLifeTime;
pbs->ulLastPowerState = pbs->ulPowerState;
}
return uiRetVal;
}
/*******************************************************************************
*
* MapBatInfoToIconID
*
* DESCRIPTION:
* Map battery info to an Icon ID.
*
* PARAMETERS:
* ulBatNum - Zero implies composite system state
*
*******************************************************************************/
UINT MapBatInfoToIconID(PBATTERY_STATE pbs)
{
UINT uIconID = IDI_BATDEAD;
if (!pbs->ulBatNum) {
if (pbs->ulPowerState & BATTERY_POWER_ON_LINE) {
return IDI_PLUG;
}
}
else {
if (pbs->ulTag == BATTERY_TAG_INVALID) {
return IDI_BATGONE;
}
}
if (pbs->ulPowerState & BATTERY_CRITICAL) {
return IDI_BATDEAD;
}
if (pbs->ulBatLifePercent > 66) {
uIconID = IDI_BATFULL;
}
else {
if (pbs->ulBatLifePercent > 33) {
uIconID = IDI_BATHALF;
}
else {
if (pbs->ulBatLifePercent > 9) {
uIconID = IDI_BATLOW;
}
}
}
return uIconID;
}
/*******************************************************************************
*
* DisplayIcon
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
void DisplayIcon(
HWND hWnd,
UINT uIconID,
PBATTERY_STATE pbs,
ULONG ulUpdateStatus
)
{
BOOL bBolt;
UINT uiMsg;
// Only redraw the icon if it has changed OR
// if it has gone from charging to not charging.
if ((uIconID != pbs->uiIconIDcache) ||
(ulUpdateStatus != UPDATESTATUS_NOUPDATE)) {
pbs->uiIconIDcache = uIconID;
bBolt = (pbs->ulPowerState & BATTERY_CHARGING);
pbs->hIconCache = GetBattIcon(hWnd, uIconID, pbs->hIconCache, bBolt, 32);
pbs->hIconCache16 = GetBattIcon(hWnd, uIconID, pbs->hIconCache16, bBolt, 16);
if (pbs->ulBatNum) {
uiMsg = BM_SETIMAGE;
}
else {
uiMsg = STM_SETIMAGE;
}
SendDlgItemMessage(hWnd, g_iMapBatNumToID[pbs->ulBatNum][BAT_ICON],
uiMsg, IMAGE_ICON, (LPARAM) pbs->hIconCache);
ShowItem(hWnd, g_iMapBatNumToID[pbs->ulBatNum][BAT_ICON]);
}
}
/*******************************************************************************
*
* UpdateBatMeterProc
*
* DESCRIPTION:
* Updates the System and per battery UI elements if needed.
*
* PARAMETERS:
*
*******************************************************************************/
BOOL UpdateBatMeterProc(
PBATTERY_STATE pbs,
HWND hWnd,
LPARAM bShowMulti,
LPARAM bForceUpdate
)
{
UINT uIconID, uiHour, uiMin;
LPTSTR lpsz, lpszRemaining;
ULONG ulUpdateStatus;
ulUpdateStatus = CheckUpdateBatteryState(pbs, (BOOL) bForceUpdate);
// Make sure there is work to do.
if (ulUpdateStatus == UPDATESTATUS_NOUPDATE) {
return TRUE;
}
// Determine which icon to display.
uIconID = MapBatInfoToIconID(pbs);
DisplayIcon(hWnd, uIconID, pbs, ulUpdateStatus);
// Are we looking for system power status ?
if (!pbs->ulBatNum) {
// Display the Current Power Source text
lpsz = LoadDynamicString(((pbs->ulPowerState & BATTERY_POWER_ON_LINE) ?
IDS_ACLINEONLINE : IDS_BATTERIES));
DisplayFreeStr(hWnd, IDC_BATTERYLEVEL, lpsz, FREE_STR);
if (pbs->ulBatLifePercent <= 100) {
lpsz = LoadDynamicString(IDS_PERCENTREMAININGFORMAT,
pbs->ulBatLifePercent);
}
else {
lpsz = LoadDynamicString(IDS_UNKNOWN);
}
DisplayFreeStr(hWnd, IDC_REMAINING, lpsz, NO_FREE_STR);
ShowHideItem(hWnd, IDC_CHARGING, pbs->ulPowerState & BATTERY_CHARGING);
// Show and Update the PowerStatusBar only if in single battery mode and
// there is al least one battery installed.
if (!bShowMulti && g_uiBatCount) {
SendDlgItemMessage(hWnd, IDC_POWERSTATUSBAR, PBM_SETPOS,
(WPARAM) pbs->ulBatLifePercent, 0);
lpsz = DisplayFreeStr(hWnd, IDC_BARPERCENT, lpsz, FREE_STR);
}
if (lpsz) {
LocalFree(lpsz);
}
if (pbs->ulBatLifeTime != (UINT) -1) {
uiHour = pbs->ulBatLifeTime / 3600;
uiMin = (pbs->ulBatLifeTime % 3600) / 60;
if (uiHour) {
lpsz = LoadDynamicString(IDS_TIMEREMFORMATHOUR, uiHour, uiMin);
}
else {
lpsz = LoadDynamicString(IDS_TIMEREMFORMATMIN, uiMin);
}
DisplayFreeStr(hWnd, IDC_TIMEREMAINING, lpsz, FREE_STR);
ShowHideItem(hWnd, IDC_TOTALTIME, TRUE);
}
else {
ShowHideItem(hWnd, IDC_TOTALTIME, FALSE);
ShowHideItem(hWnd, IDC_TIMEREMAINING, FALSE);
}
}
else {
// Here when getting the power status of each individual battery
// when in multi-battery display mode.
lpsz = LoadDynamicString(IDS_BATNUM, pbs->ulBatNum);
DisplayFreeStr(hWnd, g_iMapBatNumToID[pbs->ulBatNum][BAT_NUM],
lpsz, FREE_STR);
if (pbs->ulTag != BATTERY_TAG_INVALID) {
if (pbs->ulPowerState & BATTERY_CHARGING) {
lpsz = LoadDynamicString(IDS_BATTCHARGING);
}
else {
lpsz = NULL;
}
lpszRemaining = LoadDynamicString(IDS_PERCENTREMAININGFORMAT,
pbs->ulBatLifePercent);
}
else {
lpsz = LoadDynamicString(IDS_NOT_PRESENT);
lpszRemaining = NULL;
}
DisplayFreeStr(hWnd, g_iMapBatNumToID[pbs->ulBatNum][BAT_STATUS],
lpsz, FREE_STR);
DisplayFreeStr(hWnd, g_iMapBatNumToID[pbs->ulBatNum][BAT_REMAINING],
lpszRemaining, FREE_STR);
}
return TRUE;
}
/*******************************************************************************
*
* FreeBatteryDriverNames
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
VOID FreeBatteryDriverNames(LPTSTR *lpszDriverNames)
{
UINT i;
// Free any old driver names.
for (i = 0; i < NUM_BAT; i++) {
if (lpszDriverNames[i]) {
LocalFree(lpszDriverNames[i]);
lpszDriverNames[i] = NULL;
}
}
}
/*******************************************************************************
*
* GetBatteryDriverNames
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
UINT GetBatteryDriverNames(LPTSTR *lpszDriverNames)
{
UINT uiDriverCount, uiIndex;
DWORD dwReqSize;
HDEVINFO hDevInfo;
SP_INTERFACE_DEVICE_DATA InterfaceDevData;
PSP_INTERFACE_DEVICE_DETAIL_DATA pFuncClassDevData;
// Free any old driver names.
FreeBatteryDriverNames(lpszDriverNames);
uiDriverCount = 0;
#ifndef SIM_BATTERY
// Use the SETUPAPI.DLL interface to get the
// possible battery driver names.
hDevInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVICE_BATTERY, NULL, NULL,
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if (hDevInfo != INVALID_HANDLE_VALUE) {
InterfaceDevData.cbSize = sizeof(SP_DEVINFO_DATA);
uiIndex = 0;
while (uiDriverCount < NUM_BAT) {
if (SetupDiEnumInterfaceDevice(hDevInfo,
0,
(LPGUID)&GUID_DEVICE_BATTERY,
uiIndex,
&InterfaceDevData)) {
// Get the required size of the function class device data.
SetupDiGetInterfaceDeviceDetail(hDevInfo,
&InterfaceDevData,
NULL,
0,
&dwReqSize,
NULL);
pFuncClassDevData = LocalAlloc(0, dwReqSize);
if (pFuncClassDevData != NULL) {
pFuncClassDevData->cbSize =
sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if (SetupDiGetInterfaceDeviceDetail(hDevInfo,
&InterfaceDevData,
pFuncClassDevData,
dwReqSize,
&dwReqSize,
NULL)) {
dwReqSize = (lstrlen(pFuncClassDevData->DevicePath) + 1) * sizeof(TCHAR);
lpszDriverNames[uiDriverCount] = LocalAlloc(0, dwReqSize);
if (lpszDriverNames[uiDriverCount]) {
lstrcpy(lpszDriverNames[uiDriverCount],
pFuncClassDevData->DevicePath);
uiDriverCount++;
}
}
else {
DebugPrint("SetupDiGetInterfaceDeviceDetail, failed: %d", GetLastError());
}
LocalFree(pFuncClassDevData);
}
} else {
if (ERROR_NO_MORE_ITEMS == GetLastError()) {
break;
}
else {
DebugPrint("SetupDiEnumInterfaceDevice, failed: %d", GetLastError());
}
}
uiIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
else {
DebugPrint("SetupDiGetClassDevs on GUID_DEVICE_BATTERY, failed: %d", GetLastError());
}
#else
// Simulate batteries.
{
UINT i;
static UINT uiState = 1;
for (i = 0; i <= uiState; i++) {
lpszDriverNames[i] = LocalAlloc(0, STRSIZE(TEXT("SIMULATED_BATTERY_0")));
wsprintf(lpszDriverNames[i], TEXT("SIMULATED_BATTERY_%d"), i);
}
uiState++;
uiDriverCount = uiState;
if (uiState >= NUM_BAT) {
uiState = 0;
}
}
#endif
return uiDriverCount;
}
/*******************************************************************************
*
* UpdateDriverList
*
* DESCRIPTION:
*
* PARAMETERS:
*
*******************************************************************************/
BOOL UpdateDriverList(
LPTSTR *lpszDriverNames,
UINT uiDriverCount
)
{
UINT i;
PBATTERY_STATE pbs;
// Walk the bs list, and remove any devices which aren't in pszDeviceNames.
WalkBatteryState(DEVICES,
(WALKENUMPROC)RemoveMissingProc,
NULL,
(LPARAM)g_lpszDriverNames,
(LPARAM)REMOVE_MISSING);
// Scan the pszDeviceNames list and add any devices which aren't in bs.
for (i = 0; i < uiDriverCount; i++) {
if (WalkBatteryState(DEVICES,
(WALKENUMPROC)FindNameProc,
NULL,
(LPARAM)g_lpszDriverNames[i],
(LPARAM)NULL)) {
#ifndef SIM_BATTERY
if (!AddBatteryStateDevice(g_lpszDriverNames[i], i + 1)) {
// We weren't able get minimal info from driver, dec the
// battery counts. g_uiBatCount should always be > 0.
if (--g_uiDriverCount) {;
g_uiBatCount--;
}
}
#else
SimAddBatteryStateDevice(g_lpszDriverNames[i], i + 1);
#endif
}
}
// Clear and rebuild g_pbs, the handy batttery number to pbs array.
memset(&g_pbs, 0, sizeof(g_pbs));
pbs = &g_bs;
for (i = 0; i <= g_uiBatCount; i++) {
if (pbs) {
g_pbs[i] = pbs;
pbs = pbs->bsNext;
}
}
return TRUE;
}