xref: /reactos/dll/shellext/stobject/power.cpp (revision 9d33a205)
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.BatteryFlag & BATTERY_FLAG_CHARGING) == BATTERY_FLAG_CHARGING))
94     {
95         index = Quantize(PowerStatus.BatteryLifePercent);
96         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(bc_icons[index]));
97         g_strTooltip.Format(IDS_PWR_CHARGING, PowerStatus.BatteryLifePercent);
98     }
99     else if (((PowerStatus.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0) &&
100              ((PowerStatus.BatteryFlag & BATTERY_FLAG_CHARGING) == 0))
101     {
102         index = Quantize(PowerStatus.BatteryLifePercent);
103         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(br_icons[index]));
104         g_strTooltip.Format(IDS_PWR_PERCENT_REMAINING, PowerStatus.BatteryLifePercent);
105     }
106     else
107     {
108         hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_POWER_AC));
109         g_strTooltip.LoadStringW(IDS_PWR_AC);
110     }
111 
112     return hBatIcon;
113 }
114 
115 HRESULT STDMETHODCALLTYPE Power_Init(_In_ CSysTray * pSysTray)
116 {
117     TRACE("Power_Init\n");
118     g_hIconBattery = DynamicLoadIcon(g_hInstance);
119 
120     return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
121 }
122 
123 HRESULT STDMETHODCALLTYPE Power_Update(_In_ CSysTray * pSysTray)
124 {
125     TRACE("Power_Update\n");
126     g_hIconBattery = DynamicLoadIcon(g_hInstance);
127 
128     return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
129 }
130 
131 HRESULT STDMETHODCALLTYPE Power_Shutdown(_In_ CSysTray * pSysTray)
132 {
133     TRACE("Power_Shutdown\n");
134 
135     return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_POWER, NULL, NULL);
136 }
137 
138 static void _RunPower()
139 {
140     ShellExecuteW(NULL, NULL, L"powercfg.cpl", NULL, NULL, SW_SHOWNORMAL);
141 }
142 
143 static void _ShowContextMenu(CSysTray * pSysTray)
144 {
145     CString strOpen((LPCSTR)IDS_PWR_PROPERTIES);
146     HMENU hPopup = CreatePopupMenu();
147     AppendMenuW(hPopup, MF_STRING, IDS_PWR_PROPERTIES, strOpen);
148     SetMenuDefaultItem(hPopup, IDS_PWR_PROPERTIES, FALSE);
149 
150     SetForegroundWindow(pSysTray->GetHWnd());
151     DWORD flags = TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN;
152     POINT pt;
153     GetCursorPos(&pt);
154 
155     DWORD id = TrackPopupMenuEx(hPopup, flags,
156         pt.x, pt.y,
157         pSysTray->GetHWnd(), NULL);
158 
159     switch (id)
160     {
161         case IDS_PWR_PROPERTIES:
162             _RunPower();
163             break;
164     }
165     DestroyMenu(hPopup);
166 }
167 
168 static
169 BOOLEAN
170 CALLBACK
171 PowerSchemesEnumProc(
172     UINT uiIndex,
173     DWORD dwName,
174     LPWSTR sName,
175     DWORD dwDesc,
176     LPWSTR sDesc,
177     PPOWER_POLICY pp,
178     LPARAM lParam)
179 {
180     PPWRSCHEMECONTEXT PowerSchemeContext = (PPWRSCHEMECONTEXT)lParam;
181 
182     if (AppendMenuW(PowerSchemeContext->hPopup, MF_STRING, uiIndex + 1, sName))
183     {
184         if (PowerSchemeContext->uiFirst == 0)
185             PowerSchemeContext->uiFirst = uiIndex + 1;
186 
187         PowerSchemeContext->uiLast = uiIndex + 1;
188     }
189 
190     return TRUE;
191 }
192 
193 static
194 VOID
195 ShowPowerSchemesPopupMenu(
196     CSysTray *pSysTray)
197 {
198     PWRSCHEMECONTEXT PowerSchemeContext = {NULL, 0, 0};
199     UINT uiActiveScheme;
200     DWORD id;
201     POINT pt;
202     PowerSchemeContext.hPopup = CreatePopupMenu();
203     EnumPwrSchemes(PowerSchemesEnumProc, (LPARAM)&PowerSchemeContext);
204 
205     if (GetActivePwrScheme(&uiActiveScheme))
206     {
207         CheckMenuRadioItem(PowerSchemeContext.hPopup,
208                            PowerSchemeContext.uiFirst,
209                            PowerSchemeContext.uiLast,
210                            uiActiveScheme + 1,
211                            MF_BYCOMMAND);
212     }
213 
214     SetForegroundWindow(pSysTray->GetHWnd());
215     GetCursorPos(&pt);
216 
217     id = TrackPopupMenuEx(PowerSchemeContext.hPopup,
218                           TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN,
219                           pt.x,
220                           pt.y,
221                           pSysTray->GetHWnd(),
222                           NULL);
223 
224     DestroyMenu(PowerSchemeContext.hPopup);
225 
226     if (id != 0)
227         SetActivePwrScheme(id - 1, NULL, NULL);
228 }
229 
230 HRESULT STDMETHODCALLTYPE Power_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
231 {
232     TRACE("Power_Message uMsg=%d, wParam=%x, lParam=%x\n", uMsg, wParam, lParam);
233 
234     switch (uMsg)
235     {
236         case WM_USER + 220:
237             TRACE("Power_Message: WM_USER+220\n");
238             if (wParam == POWER_SERVICE_FLAG)
239             {
240                 if (lParam)
241                 {
242                     pSysTray->EnableService(POWER_SERVICE_FLAG, TRUE);
243                     return Power_Init(pSysTray);
244                 }
245                 else
246                 {
247                     pSysTray->EnableService(POWER_SERVICE_FLAG, FALSE);
248                     return Power_Shutdown(pSysTray);
249                 }
250             }
251             return S_FALSE;
252 
253         case WM_USER + 221:
254             TRACE("Power_Message: WM_USER+221\n");
255             if (wParam == POWER_SERVICE_FLAG)
256             {
257                 lResult = (LRESULT)pSysTray->IsServiceEnabled(POWER_SERVICE_FLAG);
258                 return S_OK;
259             }
260             return S_FALSE;
261 
262         case WM_TIMER:
263             if (wParam == POWER_TIMER_ID)
264             {
265                 KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
266                 ShowPowerSchemesPopupMenu(pSysTray);
267             }
268             break;
269 
270         case ID_ICON_POWER:
271             Power_Update(pSysTray);
272 
273             switch (lParam)
274             {
275                 case WM_LBUTTONDOWN:
276                     SetTimer(pSysTray->GetHWnd(), POWER_TIMER_ID, GetDoubleClickTime(), NULL);
277                     break;
278 
279                 case WM_LBUTTONUP:
280                     break;
281 
282                 case WM_LBUTTONDBLCLK:
283                     KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
284                     _RunPower();
285                     break;
286 
287                 case WM_RBUTTONDOWN:
288                     break;
289 
290                 case WM_RBUTTONUP:
291                     _ShowContextMenu(pSysTray);
292                     break;
293 
294                 case WM_RBUTTONDBLCLK:
295                     break;
296 
297                 case WM_MOUSEMOVE:
298                     break;
299             }
300             return S_OK;
301 
302         default:
303             TRACE("Power_Message received for unknown ID %d, ignoring.\n");
304             return S_FALSE;
305     }
306 
307     return S_FALSE;
308 }
309