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>
23 #define GBS_HASBATTERY 0x1
24 #define GBS_ONBATTERY 0x2
26 WINE_DEFAULT_DEBUG_CHANNEL(stobject
);
28 int br_icons
[5] = { IDI_BATTCAP0
, IDI_BATTCAP1
, IDI_BATTCAP2
, IDI_BATTCAP3
, IDI_BATTCAP4
}; // battery mode icons.
29 int bc_icons
[5] = { IDI_BATTCHA0
, IDI_BATTCHA1
, IDI_BATTCHA2
, IDI_BATTCHA3
, IDI_BATTCHA4
}; // charging mode icons.
31 typedef struct _PWRSCHEMECONTEXT
36 } PWRSCHEMECONTEXT
, *PPWRSCHEMECONTEXT
;
39 static float g_batCap
= 0;
40 static HICON g_hIconBattery
= NULL
;
41 static BOOL g_IsRunning
= FALSE
;
44 * @name GetBatteryState
46 * Enumerates the available battery devices and provides the remaining capacity.
49 * If no error occurs, then this will contain average remaining capacity.
51 * Helps in making battery type checks.
53 * Returned value includes GBS_HASBATTERY if the system has a non-UPS battery,
54 * and GBS_ONBATTERY if the system is running on a battery.
55 * dwResult & GBS_ONBATTERY means we have not yet found AC power.
56 * dwResult & GBS_HASBATTERY means we have found a non-UPS battery.
59 * @return The error code.
62 static HRESULT
GetBatteryState(float& cap
, DWORD
& dwResult
)
65 dwResult
= GBS_ONBATTERY
;
67 HDEVINFO hdev
= SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY
, 0, 0, DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
);
68 if (INVALID_HANDLE_VALUE
== hdev
)
71 // Limit search to 100 batteries max
72 for (int idev
= 0, count
= 0; idev
< 100; idev
++)
74 SP_DEVICE_INTERFACE_DATA did
= { 0 };
75 did
.cbSize
= sizeof(did
);
77 if (SetupDiEnumDeviceInterfaces(hdev
, 0, &GUID_DEVCLASS_BATTERY
, idev
, &did
))
81 SetupDiGetDeviceInterfaceDetail(hdev
, &did
, 0, 0, &cbRequired
, 0);
82 if (ERROR_INSUFFICIENT_BUFFER
== GetLastError())
84 PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd
= (PSP_DEVICE_INTERFACE_DETAIL_DATA
)LocalAlloc(LPTR
, cbRequired
);
87 pdidd
->cbSize
= sizeof(*pdidd
);
88 if (SetupDiGetDeviceInterfaceDetail(hdev
, &did
, pdidd
, cbRequired
, &cbRequired
, 0))
90 // Enumerated a battery. Ask it for information.
91 HANDLE hBattery
= CreateFile(pdidd
->DevicePath
, GENERIC_READ
| GENERIC_WRITE
,
92 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
94 if (INVALID_HANDLE_VALUE
!= hBattery
)
96 // Ask the battery for its tag.
97 BATTERY_QUERY_INFORMATION bqi
= { 0 };
102 if (DeviceIoControl(hBattery
, IOCTL_BATTERY_QUERY_TAG
, &dwWait
, sizeof(dwWait
), &bqi
.BatteryTag
,
103 sizeof(bqi
.BatteryTag
), &dwOut
, NULL
) && bqi
.BatteryTag
)
105 // With the tag, you can query the battery info.
106 BATTERY_INFORMATION bi
= { 0 };
107 bqi
.InformationLevel
= BatteryInformation
;
109 if (DeviceIoControl(hBattery
, IOCTL_BATTERY_QUERY_INFORMATION
, &bqi
, sizeof(bqi
), &bi
,
110 sizeof(bi
), &dwOut
, NULL
))
112 // Only non-UPS system batteries count
113 if (bi
.Capabilities
& BATTERY_SYSTEM_BATTERY
)
115 if (!(bi
.Capabilities
& BATTERY_IS_SHORT_TERM
))
116 dwResult
|= GBS_HASBATTERY
;
118 // Query the battery status.
119 BATTERY_WAIT_STATUS bws
= { 0 };
120 bws
.BatteryTag
= bqi
.BatteryTag
;
123 if (DeviceIoControl(hBattery
, IOCTL_BATTERY_QUERY_STATUS
, &bws
, sizeof(bws
),
124 &bs
, sizeof(bs
), &dwOut
, NULL
))
126 if (bs
.PowerState
& BATTERY_POWER_ON_LINE
)
127 dwResult
&= ~GBS_ONBATTERY
;
129 // Take average of total capacity of batteries detected!
130 cap
= cap
*(count
)+(float)bs
.Capacity
/ bi
.FullChargedCapacity
* 100;
137 CloseHandle(hBattery
);
144 else if (ERROR_NO_MORE_ITEMS
== GetLastError())
146 break; // Enumeration failed - perhaps we're out of items
149 SetupDiDestroyDeviceInfoList(hdev
);
151 // Final cleanup: If we didn't find a battery, then presume that we
154 if (!(dwResult
& GBS_HASBATTERY
))
155 dwResult
&= ~GBS_ONBATTERY
;
163 * This function quantizes the mentioned quantity to nearest level.
166 * Should be a quantity in percentage.
168 * Quantization level (this excludes base level 0, which will always be present), default is 10.
170 * @return Nearest quantized level, can be directly used as array index based on context.
173 static UINT
Quantize(float p
, UINT lvl
= 10)
176 float f
, q
= (float)100 / lvl
, d
= q
/ 2;
177 for (f
= 0; f
< p
; f
+= q
, i
++);
184 @remarks This function uses centred/symmetric logic for quantization.
185 For the case of lvl = 4, You will get following integer levels if given (p) value falls in between the range partitions:
186 0 <= p < 12.5 : returns 0; (corresponding to 0% centre)
187 12.5 <= p < 37.5 : returns 1; (corresponding to 25% centre)
188 37.5 <= p < 62.5 : returns 2; (corresponding to 50% centre)
189 62.5 <= p < 87.5 : returns 3; (corresponding to 75% centre)
190 87.5 <= p <= 100 : returns 4; (corresponding to 100% centre)
195 * @name DynamicLoadIcon
197 * Returns the respective icon as per the current battery capacity.
198 * It also does the work of setting global parameters of battery capacity and tooltips.
201 * A handle to a instance of the module.
203 * @return The handle to respective battery icon.
206 static HICON
DynamicLoadIcon(HINSTANCE hinst
)
212 HRESULT hr
= GetBatteryState(cap
, dw
);
214 if (!FAILED(hr
) && (dw
& GBS_HASBATTERY
))
216 index
= Quantize(cap
, 4);
222 hBatIcon
= LoadIcon(hinst
, MAKEINTRESOURCE(IDI_BATTCAP_ERR
));
223 g_strTooltip
.LoadStringW(IDS_PWR_UNKNOWN_REMAINING
);
227 if (dw
& GBS_ONBATTERY
)
229 hBatIcon
= LoadIcon(hinst
, MAKEINTRESOURCE(br_icons
[index
]));
230 g_strTooltip
.Format(IDS_PWR_PERCENT_REMAINING
, cap
);
234 hBatIcon
= LoadIcon(hinst
, MAKEINTRESOURCE(bc_icons
[index
]));
235 g_strTooltip
.Format(IDS_PWR_CHARGING
, cap
);
241 HRESULT STDMETHODCALLTYPE
Power_Init(_In_ CSysTray
* pSysTray
)
243 TRACE("Power_Init\n");
244 g_hIconBattery
= DynamicLoadIcon(g_hInstance
);
247 return pSysTray
->NotifyIcon(NIM_ADD
, ID_ICON_POWER
, g_hIconBattery
, g_strTooltip
);
250 HRESULT STDMETHODCALLTYPE
Power_Update(_In_ CSysTray
* pSysTray
)
252 TRACE("Power_Update\n");
253 g_hIconBattery
= DynamicLoadIcon(g_hInstance
);
255 return pSysTray
->NotifyIcon(NIM_MODIFY
, ID_ICON_POWER
, g_hIconBattery
, g_strTooltip
);
258 HRESULT STDMETHODCALLTYPE
Power_Shutdown(_In_ CSysTray
* pSysTray
)
260 TRACE("Power_Shutdown\n");
263 return pSysTray
->NotifyIcon(NIM_DELETE
, ID_ICON_POWER
, NULL
, NULL
);
266 static void _RunPower()
268 ShellExecuteW(NULL
, NULL
, L
"powercfg.cpl", NULL
, NULL
, SW_SHOWNORMAL
);
271 static void _ShowContextMenu(CSysTray
* pSysTray
)
273 CString
strOpen((LPCSTR
)IDS_PWR_PROPERTIES
);
274 HMENU hPopup
= CreatePopupMenu();
275 AppendMenuW(hPopup
, MF_STRING
, IDS_PWR_PROPERTIES
, strOpen
);
277 SetForegroundWindow(pSysTray
->GetHWnd());
278 DWORD flags
= TPM_RETURNCMD
| TPM_NONOTIFY
| TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
;
282 DWORD id
= TrackPopupMenuEx(hPopup
, flags
,
284 pSysTray
->GetHWnd(), NULL
);
288 case IDS_PWR_PROPERTIES
:
298 PowerSchemesEnumProc(
307 PPWRSCHEMECONTEXT PowerSchemeContext
= (PPWRSCHEMECONTEXT
)lParam
;
309 if (AppendMenuW(PowerSchemeContext
->hPopup
, MF_STRING
, uiIndex
+ 1, sName
))
311 if (PowerSchemeContext
->uiFirst
== 0)
312 PowerSchemeContext
->uiFirst
= uiIndex
+ 1;
314 PowerSchemeContext
->uiLast
= uiIndex
+ 1;
322 ShowPowerSchemesPopupMenu(
325 PWRSCHEMECONTEXT PowerSchemeContext
= {NULL
, 0, 0};
329 PowerSchemeContext
.hPopup
= CreatePopupMenu();
330 EnumPwrSchemes(PowerSchemesEnumProc
, (LPARAM
)&PowerSchemeContext
);
332 if (GetActivePwrScheme(&uiActiveScheme
))
334 CheckMenuRadioItem(PowerSchemeContext
.hPopup
,
335 PowerSchemeContext
.uiFirst
,
336 PowerSchemeContext
.uiLast
,
341 SetForegroundWindow(pSysTray
->GetHWnd());
344 id
= TrackPopupMenuEx(PowerSchemeContext
.hPopup
,
345 TPM_RETURNCMD
| TPM_NONOTIFY
| TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
,
351 DestroyMenu(PowerSchemeContext
.hPopup
);
354 SetActivePwrScheme(id
- 1, NULL
, NULL
);
357 HRESULT STDMETHODCALLTYPE
Power_Message(_In_ CSysTray
* pSysTray
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
&lResult
)
359 TRACE("Power_Message uMsg=%d, wParam=%x, lParam=%x\n", uMsg
, wParam
, lParam
);
364 TRACE("Power_Message: WM_USER+220\n");
368 return Power_Init(pSysTray
);
370 return Power_Shutdown(pSysTray
);
375 TRACE("Power_Message: WM_USER+221\n");
378 lResult
= (LRESULT
)g_IsRunning
;
384 Power_Update(pSysTray
);
392 ShowPowerSchemesPopupMenu(pSysTray
);
395 case WM_LBUTTONDBLCLK
:
403 _ShowContextMenu(pSysTray
);
406 case WM_RBUTTONDBLCLK
:
415 TRACE("Power_Message received for unknown ID %d, ignoring.\n");