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