2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/stobject/power.cpp
5 * PURPOSE: Power notification icon handler
6 * PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org>
7 Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
8 * David Quintana <gigaherz@gmail.com>
19 #define GBS_HASBATTERY 0x1
20 #define GBS_ONBATTERY 0x2
22 int br_icons
[5] = { IDI_BATTCAP0
, IDI_BATTCAP1
, IDI_BATTCAP2
, IDI_BATTCAP3
, IDI_BATTCAP4
}; // battery mode icons.
23 int bc_icons
[5] = { IDI_BATTCHA0
, IDI_BATTCHA1
, IDI_BATTCHA2
, IDI_BATTCHA3
, IDI_BATTCHA4
}; // charging mode icons.
25 typedef struct _PWRSCHEMECONTEXT
30 } PWRSCHEMECONTEXT
, *PPWRSCHEMECONTEXT
;
33 static float g_batCap
= 0;
34 static HICON g_hIconBattery
= NULL
;
35 static BOOL g_IsRunning
= FALSE
;
38 * @name GetBatteryState
40 * Enumerates the available battery devices and provides the remaining capacity.
43 * If no error occurs, then this will contain average remaining capacity.
45 * Helps in making battery type checks.
47 * Returned value includes GBS_HASBATTERY if the system has a non-UPS battery,
48 * and GBS_ONBATTERY if the system is running on a battery.
49 * dwResult & GBS_ONBATTERY means we have not yet found AC power.
50 * dwResult & GBS_HASBATTERY means we have found a non-UPS battery.
53 * @return The error code.
56 static HRESULT
GetBatteryState(float& cap
, DWORD
& dwResult
)
59 dwResult
= GBS_ONBATTERY
;
61 HDEVINFO hdev
= SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY
, 0, 0, DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
);
62 if (INVALID_HANDLE_VALUE
== hdev
)
65 // Limit search to 100 batteries max
66 for (int idev
= 0, count
= 0; idev
< 100; idev
++)
68 SP_DEVICE_INTERFACE_DATA did
= { 0 };
69 did
.cbSize
= sizeof(did
);
71 if (SetupDiEnumDeviceInterfaces(hdev
, 0, &GUID_DEVCLASS_BATTERY
, idev
, &did
))
75 SetupDiGetDeviceInterfaceDetail(hdev
, &did
, 0, 0, &cbRequired
, 0);
76 if (ERROR_INSUFFICIENT_BUFFER
== GetLastError())
78 PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd
= (PSP_DEVICE_INTERFACE_DETAIL_DATA
)LocalAlloc(LPTR
, cbRequired
);
81 pdidd
->cbSize
= sizeof(*pdidd
);
82 if (SetupDiGetDeviceInterfaceDetail(hdev
, &did
, pdidd
, cbRequired
, &cbRequired
, 0))
84 // Enumerated a battery. Ask it for information.
85 HANDLE hBattery
= CreateFile(pdidd
->DevicePath
, GENERIC_READ
| GENERIC_WRITE
,
86 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
88 if (INVALID_HANDLE_VALUE
!= hBattery
)
90 // Ask the battery for its tag.
91 BATTERY_QUERY_INFORMATION bqi
= { 0 };
96 if (DeviceIoControl(hBattery
, IOCTL_BATTERY_QUERY_TAG
, &dwWait
, sizeof(dwWait
), &bqi
.BatteryTag
,
97 sizeof(bqi
.BatteryTag
), &dwOut
, NULL
) && bqi
.BatteryTag
)
99 // With the tag, you can query the battery info.
100 BATTERY_INFORMATION bi
= { 0 };
101 bqi
.InformationLevel
= BatteryInformation
;
103 if (DeviceIoControl(hBattery
, IOCTL_BATTERY_QUERY_INFORMATION
, &bqi
, sizeof(bqi
), &bi
,
104 sizeof(bi
), &dwOut
, NULL
))
106 // Only non-UPS system batteries count
107 if (bi
.Capabilities
& BATTERY_SYSTEM_BATTERY
)
109 if (!(bi
.Capabilities
& BATTERY_IS_SHORT_TERM
))
110 dwResult
|= GBS_HASBATTERY
;
112 // Query the battery status.
113 BATTERY_WAIT_STATUS bws
= { 0 };
114 bws
.BatteryTag
= bqi
.BatteryTag
;
117 if (DeviceIoControl(hBattery
, IOCTL_BATTERY_QUERY_STATUS
, &bws
, sizeof(bws
),
118 &bs
, sizeof(bs
), &dwOut
, NULL
))
120 if (bs
.PowerState
& BATTERY_POWER_ON_LINE
)
121 dwResult
&= ~GBS_ONBATTERY
;
123 // Take average of total capacity of batteries detected!
124 cap
= cap
*(count
)+(float)bs
.Capacity
/ bi
.FullChargedCapacity
* 100;
131 CloseHandle(hBattery
);
138 else if (ERROR_NO_MORE_ITEMS
== GetLastError())
140 break; // Enumeration failed - perhaps we're out of items
143 SetupDiDestroyDeviceInfoList(hdev
);
145 // Final cleanup: If we didn't find a battery, then presume that we
148 if (!(dwResult
& GBS_HASBATTERY
))
149 dwResult
&= ~GBS_ONBATTERY
;
157 * This function quantizes the mentioned quantity to nearest level.
160 * Should be a quantity in percentage.
162 * Quantization level (this excludes base level 0, which will always be present), default is 10.
164 * @return Nearest quantized level, can be directly used as array index based on context.
167 static UINT
Quantize(float p
, UINT lvl
= 10)
170 float f
, q
= (float)100 / lvl
, d
= q
/ 2;
171 for (f
= 0; f
< p
; f
+= q
, i
++);
178 @remarks This function uses centred/symmetric logic for quantization.
179 For the case of lvl = 4, You will get following integer levels if given (p) value falls in between the range partitions:
180 0 <= p < 12.5 : returns 0; (corresponding to 0% centre)
181 12.5 <= p < 37.5 : returns 1; (corresponding to 25% centre)
182 37.5 <= p < 62.5 : returns 2; (corresponding to 50% centre)
183 62.5 <= p < 87.5 : returns 3; (corresponding to 75% centre)
184 87.5 <= p <= 100 : returns 4; (corresponding to 100% centre)
189 * @name DynamicLoadIcon
191 * Returns the respective icon as per the current battery capacity.
192 * It also does the work of setting global parameters of battery capacity and tooltips.
195 * A handle to a instance of the module.
197 * @return The handle to respective battery icon.
200 static HICON
DynamicLoadIcon(HINSTANCE hinst
)
206 HRESULT hr
= GetBatteryState(cap
, dw
);
208 if (!FAILED(hr
) && (dw
& GBS_HASBATTERY
))
210 index
= Quantize(cap
, 4);
216 hBatIcon
= LoadIcon(hinst
, MAKEINTRESOURCE(IDI_BATTCAP_ERR
));
217 g_strTooltip
.LoadStringW(IDS_PWR_UNKNOWN_REMAINING
);
221 if (dw
& GBS_ONBATTERY
)
223 hBatIcon
= LoadIcon(hinst
, MAKEINTRESOURCE(br_icons
[index
]));
224 g_strTooltip
.Format(IDS_PWR_PERCENT_REMAINING
, cap
);
228 hBatIcon
= LoadIcon(hinst
, MAKEINTRESOURCE(bc_icons
[index
]));
229 g_strTooltip
.Format(IDS_PWR_CHARGING
, cap
);
235 HRESULT STDMETHODCALLTYPE
Power_Init(_In_ CSysTray
* pSysTray
)
237 TRACE("Power_Init\n");
238 g_hIconBattery
= DynamicLoadIcon(g_hInstance
);
241 return pSysTray
->NotifyIcon(NIM_ADD
, ID_ICON_POWER
, g_hIconBattery
, g_strTooltip
);
244 HRESULT STDMETHODCALLTYPE
Power_Update(_In_ CSysTray
* pSysTray
)
246 TRACE("Power_Update\n");
247 g_hIconBattery
= DynamicLoadIcon(g_hInstance
);
249 return pSysTray
->NotifyIcon(NIM_MODIFY
, ID_ICON_POWER
, g_hIconBattery
, g_strTooltip
);
252 HRESULT STDMETHODCALLTYPE
Power_Shutdown(_In_ CSysTray
* pSysTray
)
254 TRACE("Power_Shutdown\n");
257 return pSysTray
->NotifyIcon(NIM_DELETE
, ID_ICON_POWER
, NULL
, NULL
);
260 static void _RunPower()
262 ShellExecuteW(NULL
, NULL
, L
"powercfg.cpl", NULL
, NULL
, SW_SHOWNORMAL
);
265 static void _ShowContextMenu(CSysTray
* pSysTray
)
267 CString
strOpen((LPCSTR
)IDS_PWR_PROPERTIES
);
268 HMENU hPopup
= CreatePopupMenu();
269 AppendMenuW(hPopup
, MF_STRING
, IDS_PWR_PROPERTIES
, strOpen
);
271 SetForegroundWindow(pSysTray
->GetHWnd());
272 DWORD flags
= TPM_RETURNCMD
| TPM_NONOTIFY
| TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
;
276 DWORD id
= TrackPopupMenuEx(hPopup
, flags
,
278 pSysTray
->GetHWnd(), NULL
);
282 case IDS_PWR_PROPERTIES
:
292 PowerSchemesEnumProc(
301 PPWRSCHEMECONTEXT PowerSchemeContext
= (PPWRSCHEMECONTEXT
)lParam
;
303 if (AppendMenuW(PowerSchemeContext
->hPopup
, MF_STRING
, uiIndex
+ 1, sName
))
305 if (PowerSchemeContext
->uiFirst
== 0)
306 PowerSchemeContext
->uiFirst
= uiIndex
+ 1;
308 PowerSchemeContext
->uiLast
= uiIndex
+ 1;
316 ShowPowerSchemesPopupMenu(
319 PWRSCHEMECONTEXT PowerSchemeContext
= {NULL
, 0, 0};
323 PowerSchemeContext
.hPopup
= CreatePopupMenu();
324 EnumPwrSchemes(PowerSchemesEnumProc
, (LPARAM
)&PowerSchemeContext
);
326 if (GetActivePwrScheme(&uiActiveScheme
))
328 CheckMenuRadioItem(PowerSchemeContext
.hPopup
,
329 PowerSchemeContext
.uiFirst
,
330 PowerSchemeContext
.uiLast
,
335 SetForegroundWindow(pSysTray
->GetHWnd());
338 id
= TrackPopupMenuEx(PowerSchemeContext
.hPopup
,
339 TPM_RETURNCMD
| TPM_NONOTIFY
| TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
,
345 DestroyMenu(PowerSchemeContext
.hPopup
);
348 SetActivePwrScheme(id
- 1, NULL
, NULL
);
351 HRESULT STDMETHODCALLTYPE
Power_Message(_In_ CSysTray
* pSysTray
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
&lResult
)
353 TRACE("Power_Message uMsg=%d, wParam=%x, lParam=%x\n", uMsg
, wParam
, lParam
);
358 TRACE("Power_Message: WM_USER+220\n");
362 return Power_Init(pSysTray
);
364 return Power_Shutdown(pSysTray
);
369 TRACE("Power_Message: WM_USER+221\n");
372 lResult
= (LRESULT
)g_IsRunning
;
378 Power_Update(pSysTray
);
386 ShowPowerSchemesPopupMenu(pSysTray
);
389 case WM_LBUTTONDBLCLK
:
397 _ShowContextMenu(pSysTray
);
400 case WM_RBUTTONDBLCLK
:
409 TRACE("Power_Message received for unknown ID %d, ignoring.\n");