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