|
|
/*******************************************************************************
* * (C) COPYRIGHT MICROSOFT CORP., 1993-1995 * * TITLE: POWER.C * * VERSION: 2.0 * * AUTHOR: TCS/RAL * * DATE: 08 Feb 1994 * ******************************************************************************** * * CHANGE LOG: * * DATE REV DESCRIPTION * ----------- --- ------------------------------------------------------------- * 08 Feb 1994 TCS Original implementation. * 11 Nov 1994 RAL Converted from batmeter to systray * 11 Aug 1995 JEM Split batmeter functions into power.c & minor enahncements * 23 Oct 1995 Shawnb UNICODE Enabled * 24 Jan 1997 Reedb ACPI power management, common battery meter code. * *******************************************************************************/
#include "stdafx.h"
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <initguid.h>
#include <ntpoapi.h>
#include <poclass.h>
#include "systray.h"
#include "batmeter.h"
#include "powrprof.h"
#include "powercfp.h"
#define UPDATE_REGISTRY TRUE
#define NO_REGISTRY_UPDATE FALSE
// Structure to manage the power profile enum proc parameters.
typedef struct _POWER_PROFILE_ENUM_PROC_PARAMS { UINT uiCurActiveIndex; HMENU hMenu; UINT uiCurActiveID; } POWER_PROFILE_ENUM_PROC_PARAMS, *PPOWER_PROFILE_ENUM_PROC_PARAMS;
// G L O B A L D A T A -------------------------------------------------------
BOOL g_bPowerEnabled; // Tracks the power service state.
UINT g_uiPowerSchemeCount; // Number of power schemes, left context menu.
HMENU g_hMenu[2]; // Context menus.
// BatMeter creation parameters.
HWND g_hwndBatMeter; BOOL g_bShowMulti; HWND g_hwndBatMeterFrame;
GLOBAL_POWER_POLICY g_gpp;
// Context sensitive help must be added to the windows.hlp file,
// for now we will use this dummy array define. Remove when windows.hlp updated.
#define IDH_POWERCFG_ENABLEMULTI IDH_POWERCFG_POWERSTATUSBAR
const DWORD g_ContextMenuHelpIDs[] = { IDC_POWERSTATUSGROUPBOX, IDH_COMM_GROUPBOX, IDC_ENABLEMETER, IDH_POWERCFG_ENABLEMETER, IDC_ENABLEMULTI, IDH_POWERCFG_ENABLEMULTI, 0, 0 };
// Used to track registration for WM_DEVICECHANGED message.
HDEVNOTIFY g_hDevNotify;
/*******************************************************************************
* * RunningOffLine * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/
BOOLEAN RunningOffLine(void) { SYSTEM_POWER_STATUS sps; BOOLEAN bRet = FALSE;
if (GetSystemPowerStatus(&sps)) { if (sps.ACLineStatus == 0) { bRet = TRUE; } } return bRet; }
/*----------------------------------------------------------------------------
* Power_OnCommand * * Process WM_COMMAND msgs for the battery meter dialog. * *----------------------------------------------------------------------------*/
void Power_OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) { BOOL Checked; DWORD dwMask; UINT uiCommandID = GET_WM_COMMAND_ID(wParam, lParam);
switch (uiCommandID) {
case IDC_ENABLEMETER: dwMask = EnableSysTrayBatteryMeter; goto DoUpdateFlags;
case IDC_ENABLEMULTI: dwMask = EnableMultiBatteryDisplay; goto DoUpdateFlags;
DoUpdateFlags: Checked = (IsDlgButtonChecked(hWnd, uiCommandID) == BST_CHECKED); Update_PowerFlags(dwMask, Checked); if (uiCommandID == IDC_ENABLEMETER) { PowerCfg_Notify(); SysTray_EnableService(STSERVICE_POWER, g_gpp.user.GlobalFlags & EnableSysTrayBatteryMeter); } else { g_bShowMulti = Checked; Power_UpdateStatus(hWnd, NIM_MODIFY, TRUE); } break;
case IDCANCEL: EndDialog(hWnd, wParam); break;
default: // Notify battery meter of enter key events.
if (HIWORD(wParam) == BN_CLICKED) { SendMessage(g_hwndBatMeter, WM_COMMAND, wParam, lParam); } } }
/*******************************************************************************
* * Power_OnPowerBroadcast * * DESCRIPTION: * Process WM_POWERBROADCAS message for the battery meter dialog. * * PARAMETERS: * *******************************************************************************/
void Power_OnPowerBroadcast(HWND hWnd, WPARAM wParam, LPARAM lParam) { if (wParam == PBT_APMPOWERSTATUSCHANGE) {
// If the power icon is not showing (power service disabled) and
// we are running on batteries, enable the systray power service.
if (!g_bPowerEnabled && RunningOffLine()) { PostMessage(hWnd, STWM_ENABLESERVICE, STSERVICE_POWER, TRUE); } else
// If the power icon is showing (power service enabled) and
// we are not running on batteries, disable the systray power service.
if (g_bPowerEnabled && !RunningOffLine()) { PostMessage(hWnd, STWM_ENABLESERVICE, STSERVICE_POWER, FALSE); }
// Don't change the state of the power service, just update the icon.
Power_UpdateStatus(hWnd, NIM_MODIFY, FALSE); } }
/*******************************************************************************
* * Power_OnDeviceChange * * DESCRIPTION: * Process WM_DEVICECHANGE message for the battery meter dialog. * * PARAMETERS: * *******************************************************************************/
void Power_OnDeviceChange(HWND hWnd, WPARAM wParam, LPARAM lParam) { //
// Only listen to the WM_DEVICECHANGE if it is for GUID_DEVICE_BATTERY and
// it is a DBT_DEVICEARRIVAL, DBT_DEVICEREMOVECOMPLETE, or DBT_DEVICEQUERYREMOVEFAILED.
//
if (((wParam == DBT_DEVICEARRIVAL) || (wParam == DBT_DEVICEREMOVECOMPLETE) || (wParam == DBT_DEVICEQUERYREMOVEFAILED)) && (lParam) && (((PDEV_BROADCAST_DEVICEINTERFACE)lParam)->dbcc_devicetype == DBT_DEVTYP_DEVICEINTERFACE) && (IsEqualGUID(&((PDEV_BROADCAST_DEVICEINTERFACE)lParam)->dbcc_classguid, &GUID_DEVICE_BATTERY))) {
// Make sure BatMeter has been initialized.
if (g_hwndBatMeterFrame) { if (g_hwndBatMeter) { g_hwndBatMeter = DestroyBatMeter(g_hwndBatMeter); } g_hwndBatMeter = CreateBatMeter(hWnd, g_hwndBatMeterFrame, g_bShowMulti, NULL); InvalidateRect(hWnd, NULL, TRUE); } } }
/*******************************************************************************
* * Power_OnActivate * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/
BOOLEAN Power_OnActivate(HWND hWnd, WPARAM wParam, LPARAM lParam) { if (g_hwndBatMeter) { SendMessage(g_hwndBatMeter, WM_ACTIVATE, wParam, lParam); return TRUE; } return FALSE; }
/*******************************************************************************
* * PowerProfileEnumProc * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/
#define POWERMENU_SCHEME 300
BOOLEAN CALLBACK PowerProfileEnumProc( UINT uiID, DWORD dwNameSize, LPTSTR lpszName, DWORD dwDescSize, LPTSTR lpszDesc, PPOWER_POLICY ppp, LPARAM lParam ) { PPOWER_PROFILE_ENUM_PROC_PARAMS pppepp; MENUITEMINFO mii;
if ((pppepp = (PPOWER_PROFILE_ENUM_PROC_PARAMS) lParam) == NULL) { return FALSE; }
AppendMenu(pppepp->hMenu, MF_STRING, POWERMENU_SCHEME + g_uiPowerSchemeCount, lpszName);
// Store the power scheme ID in the menu info.
mii.cbSize = sizeof(mii); mii.fMask = MIIM_DATA; mii.dwItemData = uiID; SetMenuItemInfo(pppepp->hMenu, POWERMENU_SCHEME + g_uiPowerSchemeCount, FALSE, &mii);
if (uiID == pppepp->uiCurActiveID) { pppepp->uiCurActiveIndex = POWERMENU_SCHEME + g_uiPowerSchemeCount; }
g_uiPowerSchemeCount++; return TRUE; }
/*----------------------------------------------------------------------------
* GetPowerMenu() * * Build a menu containing battery meter/power selections. * *----------------------------------------------------------------------------*/
#define POWERMENU_OPEN 100
#define POWERMENU_PROPERTIES 101
#define POWERMENU_ENABLEWARN 200
#define POWERMENU_SHOWTIME 201
#define POWERMENU_SHOWPERCENT 202
HMENU GetPowerMenu(LONG l) { LPTSTR lpszMenu; UINT uiCurActiveID;
POWER_PROFILE_ENUM_PROC_PARAMS ppepp;
if (l > 0) { // Right button menu -- can change, rebuild each time.
if (g_hMenu[0]) { DestroyMenu(g_hMenu[0]); }
g_hMenu[1] = CreatePopupMenu();
// Properties for Power, PowerCfg.
if ((lpszMenu = LoadDynamicString(IDS_PROPFORPOWER)) != NULL) { AppendMenu(g_hMenu[1], MF_STRING, POWERMENU_PROPERTIES, lpszMenu); DeleteDynamicString(lpszMenu); }
// If we have a battery meter, add it's menu item and set as default.
if (g_hwndBatMeter) { if ((lpszMenu = LoadDynamicString(IDS_OPEN)) != NULL) { AppendMenu(g_hMenu[1], MF_STRING, POWERMENU_OPEN, lpszMenu); DeleteDynamicString(lpszMenu); } // Open BatMeter is default (double click action)
SetMenuDefaultItem(g_hMenu[1], POWERMENU_OPEN, FALSE); } else { // Use open PowerCfg as default (double click action)
SetMenuDefaultItem(g_hMenu[1], POWERMENU_PROPERTIES, FALSE); } }
// Left button menu -- can change, rebuild each time.
if (g_hMenu[0]) { DestroyMenu(g_hMenu[0]); }
g_hMenu[0] = CreatePopupMenu();
// Get the currently active power policies.
if (GetActivePwrScheme(&uiCurActiveID)) { g_uiPowerSchemeCount = 0; ppepp.hMenu = g_hMenu[0]; ppepp.uiCurActiveID = uiCurActiveID; EnumPwrSchemes(PowerProfileEnumProc, (LPARAM)&ppepp);
// Check the currently active menu item.
CheckMenuRadioItem(g_hMenu[0], POWERMENU_SCHEME, POWERMENU_SCHEME + g_uiPowerSchemeCount - 1, ppepp.uiCurActiveIndex, MF_BYCOMMAND); } return g_hMenu[l]; }
/*----------------------------------------------------------------------------
* Power_Open * * Update and display the battery meter dialog * *----------------------------------------------------------------------------*/
void Power_Open(HWND hWnd) { if (g_hwndBatMeter) { SetFocus(GetDlgItem(hWnd, IDC_ENABLEMETER)); CheckDlgButton(hWnd, IDC_ENABLEMULTI, (g_gpp.user.GlobalFlags & EnableMultiBatteryDisplay) ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hWnd, IDC_ENABLEMETER, (g_gpp.user.GlobalFlags & EnableSysTrayBatteryMeter) ? BST_CHECKED : BST_UNCHECKED);
Power_UpdateStatus(hWnd, NIM_MODIFY, FALSE); // show current info
ShowWindow(hWnd, SW_SHOW); SetForegroundWindow(hWnd); } else { SysTray_RunProperties(IDS_RUNPOWERPROPERTIES); } }
/*----------------------------------------------------------------------------
* DoPowerMenu * * Create and process a right or left button menu. * *----------------------------------------------------------------------------*/
void DoPowerMenu(HWND hwnd, UINT uMenuNum, UINT uButton) { POINT pt; UINT iCmd; MENUITEMINFO mii;
SetForegroundWindow(hwnd); GetCursorPos(&pt);
iCmd = (UINT)TrackPopupMenu(GetPowerMenu(uMenuNum), uButton | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, 0, hwnd, NULL);
if (iCmd >= POWERMENU_SCHEME) { mii.cbSize = sizeof(mii); mii.fMask = MIIM_DATA; if (GetMenuItemInfo(g_hMenu[uMenuNum], iCmd, FALSE, &mii)) { SetActivePwrScheme((UINT)mii.dwItemData, NULL, NULL); PowerCfg_Notify(); } } else { switch (iCmd) {
case POWERMENU_OPEN: Power_Open(hwnd); break;
case POWERMENU_PROPERTIES: SysTray_RunProperties(IDS_RUNPOWERPROPERTIES); break;
case 0: // The user cancelled the menu without choosing.
SetIconFocus(hwnd, STWM_NOTIFYPOWER); break; } } }
/*----------------------------------------------------------------------------
* Power_Notify * * Handle a notification from the power tray icon. * *----------------------------------------------------------------------------*/
#define PN_TIMER_CLEAR 0
#define PN_TIMER_SET 1
#define PN_DBLCLK 2
UINT g_uiTimerSet = PN_TIMER_CLEAR; LARGE_INTEGER g_liHoverUpdateTime = {0,0};
void Power_Notify(HWND hWnd, WPARAM wParam, LPARAM lParam) { LARGE_INTEGER liPerformanceFrequency; LARGE_INTEGER liPerformanceCount;
switch (lParam) { case WM_RBUTTONUP: DoPowerMenu(hWnd, 1, TPM_RIGHTBUTTON); // right button menu
break;
case WM_LBUTTONUP: // start timing for left button menu
if (g_uiTimerSet == PN_TIMER_CLEAR) { SetTimer(hWnd, POWER_TIMER_ID, GetDoubleClickTime()+100, NULL); g_uiTimerSet = PN_TIMER_SET; } break;
case WM_LBUTTONDBLCLK: g_uiTimerSet = PN_DBLCLK; Power_Open(hWnd); // show battery meter dialog
break;
case WM_MOUSEMOVE: if (QueryPerformanceFrequency (&liPerformanceFrequency)) { if (QueryPerformanceCounter (&liPerformanceCount)) { // Update no more than once a second
if ((liPerformanceCount.QuadPart - g_liHoverUpdateTime.QuadPart) > liPerformanceFrequency.QuadPart) { g_liHoverUpdateTime = liPerformanceCount; Power_UpdateStatus(hWnd, NIM_MODIFY, FALSE); } } } break;
} }
/*-----------------------------------------------------------------------------
* Power_Timer * * Execute the left button menu on WM_LBUTTONDOWN time-out. * *----------------------------------------------------------------------------*/
void Power_Timer(HWND hwnd) { KillTimer(hwnd, POWER_TIMER_ID); if (g_uiTimerSet != PN_DBLCLK) { DoPowerMenu(hwnd, 0, TPM_LEFTBUTTON); } g_uiTimerSet = PN_TIMER_CLEAR; }
/*----------------------------------------------------------------------------
* Update_PowerFlags * * Set power flags using powrprof.dll API's. * *----------------------------------------------------------------------------*/
void Update_PowerFlags(DWORD dwMask, BOOL bEnable) { if (bEnable) { g_gpp.user.GlobalFlags |= dwMask; } else { g_gpp.user.GlobalFlags &= ~dwMask; } WriteGlobalPwrPolicy(&g_gpp); }
/*----------------------------------------------------------------------------
* Get_PowerFlags * * Get power flags using powrprof.dll API's. * *----------------------------------------------------------------------------*/
DWORD Get_PowerFlags(void) { ReadGlobalPwrPolicy(&g_gpp); return g_gpp.user.GlobalFlags; }
/*******************************************************************************
* * BatteryMeterInit * * DESCRIPTION: * NOTE: Can be called multiple times. Simply re-init. * * PARAMETERS: * (returns), TRUE if the Battery Meter could be enabled * *******************************************************************************/
BOOL PASCAL BatteryMeterInit(HWND hWnd) { PUINT puiBatCount = NULL;
if (!BatMeterCapabilities(&puiBatCount)) { return FALSE; }
if (!g_hwndBatMeter) { g_hwndBatMeterFrame = GetDlgItem(hWnd, IDC_STATIC_FRAME_BATMETER); g_bShowMulti = g_gpp.user.GlobalFlags & EnableMultiBatteryDisplay; g_hwndBatMeter = CreateBatMeter(hWnd, g_hwndBatMeterFrame, g_bShowMulti, NULL); } return TRUE; }
/*******************************************************************************
* * Power_UpdateStatus * * DESCRIPTION: * * PARAMETERS: * *******************************************************************************/
VOID PASCAL Power_UpdateStatus( HWND hWnd, DWORD NotifyIconMessage, BOOL bForceUpdate ) { static TCHAR szTipCache[64]; static HICON hIconCache;
TCHAR szTip[64]; LPTSTR lpsz; BATTERY_STATE bs; UINT uiHour, uiMin;
*szTip = 0;
bs.ulSize = sizeof(BATTERY_STATE); UpdateBatMeter(g_hwndBatMeter, g_bShowMulti, bForceUpdate, &bs);
// Build up a new tool tip.
if (g_hwndBatMeter && !(((bs.ulPowerState & BATTERY_POWER_ON_LINE) && !(bs.ulPowerState & BATTERY_CHARGING)))) {
if (bs.ulBatLifePercent <= 100) { if (bs.ulBatLifeTime != (UINT) -1) { uiHour = bs.ulBatLifeTime / 3600; uiMin = (bs.ulBatLifeTime % 3600) / 60; if (uiHour) { lpsz = LoadDynamicString(IDS_TIMEREMFORMATHOUR, uiHour, uiMin, bs.ulBatLifePercent); } else { lpsz = LoadDynamicString(IDS_TIMEREMFORMATMIN, uiMin, bs.ulBatLifePercent); } if (lpsz) { StrCpyN(szTip, lpsz, ARRAYSIZE(szTip)); LocalFree(lpsz); if (bs.ulPowerState & BATTERY_CHARGING) { if ((lpsz = LoadDynamicString(IDS_CHARGING)) != NULL) { StrCatBuff(szTip, lpsz, ARRAYSIZE(szTip)); LocalFree(lpsz); } } } } else { if ((lpsz = LoadDynamicString(IDS_REMAINING, bs.ulBatLifePercent)) != NULL) { StrCpyN(szTip, lpsz, ARRAYSIZE(szTip)); LocalFree(lpsz); if (bs.ulPowerState & BATTERY_CHARGING) { if ((lpsz = LoadDynamicString(IDS_CHARGING)) != NULL) { StrCatBuff(szTip, lpsz, ARRAYSIZE(szTip)); LocalFree(lpsz); } } } } } else { lpsz = LoadDynamicString(IDS_UNKNOWN); StrCpyN(szTip, lpsz, ARRAYSIZE(szTip)); LocalFree(lpsz); } } else { lpsz = LoadDynamicString(IDS_ACPOWER); StrCpyN(szTip, lpsz, ARRAYSIZE(szTip)); LocalFree(lpsz); }
if ((NotifyIconMessage == NIM_ADD) || (hIconCache != bs.hIconCache16) || (lstrcmp(szTip, szTipCache))) {
hIconCache = bs.hIconCache16; StrCpyN(szTipCache, szTip, ARRAYSIZE(szTipCache));
SysTray_NotifyIcon(hWnd, STWM_NOTIFYPOWER, NotifyIconMessage, hIconCache, szTipCache); } }
/*******************************************************************************
* * RegisterForDeviceNotification * * DESCRIPTION: * Do onetime registration for WM_DEVICECHANGED. * * PARAMETERS: * *******************************************************************************/
BOOL RegisterForDeviceNotification(HWND hWnd) { DEV_BROADCAST_DEVICEINTERFACE dbc;
memset(&dbc, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE)); dbc.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); dbc.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; dbc.dbcc_classguid = GUID_DEVICE_BATTERY; g_hDevNotify = RegisterDeviceNotification(hWnd, &dbc, DEVICE_NOTIFY_WINDOW_HANDLE); if (!g_hDevNotify) { return FALSE; } return TRUE; }
/*******************************************************************************
* * Power_WmDestroy * * DESCRIPTION: * * * PARAMETERS: * *******************************************************************************/
void Power_WmDestroy(HWND hWnd) { if (g_hDevNotify) { UnregisterDeviceNotification(g_hDevNotify); g_hDevNotify = NULL; } }
/*******************************************************************************
* * Power_CheckEnable * * DESCRIPTION: * Return TRUE if the power service icon was enabled. * Can be called multiple times. Simply re-init. * * PARAMETERS: * bSvcEnabled - Request to enable/disable power service on tray. * *******************************************************************************/
BOOL Power_CheckEnable(HWND hWnd, BOOL bSvcEnable) { static BOOL bRegisteredForDC = FALSE;
// Is there any reason to display the systray power icon?
if (!PowerCapabilities()) { return FALSE; }
// Do onetime registration for WM_DEVICECHANGED.
if (!bRegisteredForDC) { bRegisteredForDC = RegisterForDeviceNotification(hWnd); }
// Get current battery meter flags from the registry
Get_PowerFlags();
// Are we running on battery power or has the user set
// the systray power icon to always on? If so, force enable.
if ((g_gpp.user.GlobalFlags & EnableSysTrayBatteryMeter) || (RunningOffLine())) { bSvcEnable = TRUE; } else { bSvcEnable = FALSE; }
// Set the power service state.
if (bSvcEnable) { if (g_bPowerEnabled) { Power_UpdateStatus(hWnd, NIM_MODIFY, FALSE); } else { BatteryMeterInit(hWnd); Power_UpdateStatus(hWnd, NIM_ADD, FALSE); } g_bPowerEnabled = TRUE; } else { SysTray_NotifyIcon(hWnd, STWM_NOTIFYPOWER, NIM_DELETE, NULL, NULL); g_bPowerEnabled = FALSE; } return g_bPowerEnabled; }
|