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