xref: /reactos/dll/shellext/stobject/power.cpp (revision d6eebaa4)
1 /*
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>
9  */
10 
11 #include "precomp.h"
12 
13 #include <devguid.h>
14 #include <winioctl.h>
15 #include <powrprof.h>
16 #include <windows.h>
17 #include <batclass.h>
18 
19 int br_icons[5] = { IDI_BATTCAP0, IDI_BATTCAP1, IDI_BATTCAP2, IDI_BATTCAP3, IDI_BATTCAP4 }; // battery mode icons.
20 int bc_icons[5] = { IDI_BATTCHA0, IDI_BATTCHA1, IDI_BATTCHA2, IDI_BATTCHA3, IDI_BATTCHA4 }; // charging mode icons.
21 
22 typedef struct _PWRSCHEMECONTEXT
23 {
24     HMENU hPopup;
25     UINT uiFirst;
26     UINT uiLast;
27 } PWRSCHEMECONTEXT, *PPWRSCHEMECONTEXT;
28 
29 CString  g_strTooltip;
30 static HICON g_hIconBattery = NULL;
31 
32 
33 /*++
34 * @name Quantize
35 *
36 * This function quantizes the mentioned quantity to nearest level.
37 *
38 * @param p
39 *        Should be a quantity in percentage.
40 *
41 * @return Nearest quantized level, can be directly used as array index based on context.
42 *
43  @remarks This function uses centred/symmetric logic for quantization.
44  For the case of lvl = 4, You will get following integer levels if given (p) value falls in between the range partitions:
45      0    <= p <  12.5 : returns 0; (corresponding to 0% centre)
46      12.5 <= p <  37.5 : returns 1; (corresponding to 25% centre)
47      37.5 <= p <  62.5 : returns 2; (corresponding to 50% centre)
48      62.5 <= p <  87.5 : returns 3; (corresponding to 75% centre)
49      87.5 <= p <= 100  : returns 4; (corresponding to 100% centre)
50  *--*/
51 static UINT Quantize(BYTE p)
52 {
53     if (p <= 12)
54         return 0;
55     else if (p > 12 && p <= 37)
56         return 1;
57     else if (p > 37 && p <= 62)
58         return 2;
59     else if (p > 62 && p <= 87)
60         return 3;
61     else
62         return 4;
63 }
64 
65 /*++
66 * @name DynamicLoadIcon
67 *
68 * Returns the respective icon as per the current battery capacity.
69 * It also does the work of setting global parameters of battery capacity and tooltips.
70 *
71 * @param hinst
72 *        A handle to a instance of the module.
73 *
74 * @return The handle to respective battery icon.
75 *
76 *--*/
77 static HICON DynamicLoadIcon(HINSTANCE hinst)
78 {
79     SYSTEM_POWER_STATUS PowerStatus;
80     HICON hBatIcon;
81     UINT index = -1;
82 
83     if (!GetSystemPowerStatus(&PowerStatus) ||
84         PowerStatus.ACLineStatus == AC_LINE_UNKNOWN ||
85         PowerStatus.BatteryFlag == BATTERY_FLAG_UNKNOWN)
86     {
87         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_BATTCAP_ERR));
88         g_strTooltip.LoadStringW(IDS_PWR_UNKNOWN_REMAINING);
89         return hBatIcon;
90     }
91 
92     if (((PowerStatus.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0) &&
93         (PowerStatus.BatteryLifePercent == BATTERY_PERCENTAGE_UNKNOWN))
94     {
95         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_BATTCAP_ERR));
96         g_strTooltip.LoadStringW(IDS_PWR_UNKNOWN_REMAINING);
97     }
98     else if (((PowerStatus.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0) &&
99         ((PowerStatus.BatteryFlag & BATTERY_FLAG_CHARGING) == BATTERY_FLAG_CHARGING))
100     {
101         index = Quantize(PowerStatus.BatteryLifePercent);
102         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(bc_icons[index]));
103         g_strTooltip.Format(IDS_PWR_CHARGING, PowerStatus.BatteryLifePercent);
104     }
105     else if (((PowerStatus.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0) &&
106              ((PowerStatus.BatteryFlag & BATTERY_FLAG_CHARGING) == 0))
107     {
108         index = Quantize(PowerStatus.BatteryLifePercent);
109         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(br_icons[index]));
110         g_strTooltip.Format(IDS_PWR_PERCENT_REMAINING, PowerStatus.BatteryLifePercent);
111     }
112     else
113     {
114         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_POWER_AC));
115         g_strTooltip.LoadStringW(IDS_PWR_AC);
116     }
117 
118     return hBatIcon;
119 }
120 
121 HRESULT STDMETHODCALLTYPE Power_Init(_In_ CSysTray * pSysTray)
122 {
123     TRACE("Power_Init\n");
124     g_hIconBattery = DynamicLoadIcon(g_hInstance);
125 
126     return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
127 }
128 
129 HRESULT STDMETHODCALLTYPE Power_Update(_In_ CSysTray * pSysTray)
130 {
131     TRACE("Power_Update\n");
132     g_hIconBattery = DynamicLoadIcon(g_hInstance);
133 
134     return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
135 }
136 
137 HRESULT STDMETHODCALLTYPE Power_Shutdown(_In_ CSysTray * pSysTray)
138 {
139     TRACE("Power_Shutdown\n");
140 
141     return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_POWER, NULL, NULL);
142 }
143 
144 static void _RunPower()
145 {
146     ShellExecuteW(NULL, NULL, L"powercfg.cpl", NULL, NULL, SW_SHOWNORMAL);
147 }
148 
149 static void _ShowContextMenu(CSysTray * pSysTray)
150 {
151     CString strOpen((LPCSTR)IDS_PWR_PROPERTIES);
152     HMENU hPopup = CreatePopupMenu();
153     AppendMenuW(hPopup, MF_STRING, IDS_PWR_PROPERTIES, strOpen);
154     SetMenuDefaultItem(hPopup, IDS_PWR_PROPERTIES, FALSE);
155 
156     SetForegroundWindow(pSysTray->GetHWnd());
157     DWORD flags = TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN;
158     POINT pt;
159     GetCursorPos(&pt);
160 
161     DWORD id = TrackPopupMenuEx(hPopup, flags,
162         pt.x, pt.y,
163         pSysTray->GetHWnd(), NULL);
164 
165     switch (id)
166     {
167         case IDS_PWR_PROPERTIES:
168             _RunPower();
169             break;
170     }
171     DestroyMenu(hPopup);
172 }
173 
174 static
175 BOOLEAN
176 CALLBACK
177 PowerSchemesEnumProc(
178     UINT uiIndex,
179     DWORD dwName,
180     LPWSTR sName,
181     DWORD dwDesc,
182     LPWSTR sDesc,
183     PPOWER_POLICY pp,
184     LPARAM lParam)
185 {
186     PPWRSCHEMECONTEXT PowerSchemeContext = (PPWRSCHEMECONTEXT)lParam;
187 
188     if (AppendMenuW(PowerSchemeContext->hPopup, MF_STRING, uiIndex + 1, sName))
189     {
190         if (PowerSchemeContext->uiFirst == 0)
191             PowerSchemeContext->uiFirst = uiIndex + 1;
192 
193         PowerSchemeContext->uiLast = uiIndex + 1;
194     }
195 
196     return TRUE;
197 }
198 
199 static
200 VOID
201 ShowPowerSchemesPopupMenu(
202     CSysTray *pSysTray)
203 {
204     PWRSCHEMECONTEXT PowerSchemeContext = {NULL, 0, 0};
205     UINT uiActiveScheme;
206     DWORD id;
207     POINT pt;
208     PowerSchemeContext.hPopup = CreatePopupMenu();
209     EnumPwrSchemes(PowerSchemesEnumProc, (LPARAM)&PowerSchemeContext);
210 
211     if (GetActivePwrScheme(&uiActiveScheme))
212     {
213         CheckMenuRadioItem(PowerSchemeContext.hPopup,
214                            PowerSchemeContext.uiFirst,
215                            PowerSchemeContext.uiLast,
216                            uiActiveScheme + 1,
217                            MF_BYCOMMAND);
218     }
219 
220     SetForegroundWindow(pSysTray->GetHWnd());
221     GetCursorPos(&pt);
222 
223     id = TrackPopupMenuEx(PowerSchemeContext.hPopup,
224                           TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN,
225                           pt.x,
226                           pt.y,
227                           pSysTray->GetHWnd(),
228                           NULL);
229 
230     DestroyMenu(PowerSchemeContext.hPopup);
231 
232     if (id != 0)
233         SetActivePwrScheme(id - 1, NULL, NULL);
234 }
235 
236 HRESULT STDMETHODCALLTYPE Power_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
237 {
238     TRACE("Power_Message uMsg=%d, wParam=%x, lParam=%x\n", uMsg, wParam, lParam);
239 
240     switch (uMsg)
241     {
242         case WM_USER + 220:
243             TRACE("Power_Message: WM_USER+220\n");
244             if (wParam == POWER_SERVICE_FLAG)
245             {
246                 if (lParam)
247                 {
248                     pSysTray->EnableService(POWER_SERVICE_FLAG, TRUE);
249                     return Power_Init(pSysTray);
250                 }
251                 else
252                 {
253                     pSysTray->EnableService(POWER_SERVICE_FLAG, FALSE);
254                     return Power_Shutdown(pSysTray);
255                 }
256             }
257             return S_FALSE;
258 
259         case WM_USER + 221:
260             TRACE("Power_Message: WM_USER+221\n");
261             if (wParam == POWER_SERVICE_FLAG)
262             {
263                 lResult = (LRESULT)pSysTray->IsServiceEnabled(POWER_SERVICE_FLAG);
264                 return S_OK;
265             }
266             return S_FALSE;
267 
268         case WM_TIMER:
269             if (wParam == POWER_TIMER_ID)
270             {
271                 KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
272                 ShowPowerSchemesPopupMenu(pSysTray);
273             }
274             break;
275 
276         case ID_ICON_POWER:
277             Power_Update(pSysTray);
278 
279             switch (lParam)
280             {
281                 case WM_LBUTTONDOWN:
282                     SetTimer(pSysTray->GetHWnd(), POWER_TIMER_ID, GetDoubleClickTime(), NULL);
283                     break;
284 
285                 case WM_LBUTTONUP:
286                     break;
287 
288                 case WM_LBUTTONDBLCLK:
289                     KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
290                     _RunPower();
291                     break;
292 
293                 case WM_RBUTTONDOWN:
294                     break;
295 
296                 case WM_RBUTTONUP:
297                     _ShowContextMenu(pSysTray);
298                     break;
299 
300                 case WM_RBUTTONDBLCLK:
301                     break;
302 
303                 case WM_MOUSEMOVE:
304                     break;
305             }
306             return S_OK;
307 
308         default:
309             TRACE("Power_Message received for unknown ID %d, ignoring.\n");
310             return S_FALSE;
311     }
312 
313     return S_FALSE;
314 }
315