[NTDLL] Allow shimdata to override the process manifest.
[reactos.git] / dll / shellext / stobject / power.cpp
index 24b5feb..24d5fd8 100644 (file)
@@ -4,13 +4,23 @@
  * FILE:        dll/shellext/stobject/power.cpp
  * PURPOSE:     Power notification icon handler
  * PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org>
+                Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
  *              David Quintana <gigaherz@gmail.com>
  */
 
 #include "precomp.h"
-#include "powrprof.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(stobject);
+#include <devguid.h>
+#include <winioctl.h>
+#include <powrprof.h>
+#include <windows.h>
+#include <batclass.h>
+
+#define GBS_HASBATTERY 0x1
+#define GBS_ONBATTERY  0x2
+
+int br_icons[5] = { IDI_BATTCAP0, IDI_BATTCAP1, IDI_BATTCAP2, IDI_BATTCAP3, IDI_BATTCAP4 }; // battery mode icons.
+int bc_icons[5] = { IDI_BATTCHA0, IDI_BATTCHA1, IDI_BATTCHA2, IDI_BATTCHA3, IDI_BATTCHA4 }; // charging mode icons.
 
 typedef struct _PWRSCHEMECONTEXT
 {
@@ -19,71 +29,261 @@ typedef struct _PWRSCHEMECONTEXT
     UINT uiLast;
 } PWRSCHEMECONTEXT, *PPWRSCHEMECONTEXT;
 
+CString  g_strTooltip;
+static float g_batCap = 0;
 static HICON g_hIconBattery = NULL;
 static BOOL g_IsRunning = FALSE;
 
+/*++
+* @name GetBatteryState
+*
+* Enumerates the available battery devices and provides the remaining capacity.
+*
+* @param cap
+*        If no error occurs, then this will contain average remaining capacity.
+* @param dwResult
+*        Helps in making battery type checks.
+*       {
+*           Returned value includes GBS_HASBATTERY if the system has a non-UPS battery,
+*           and GBS_ONBATTERY if the system is running on a battery.
+*           dwResult & GBS_ONBATTERY means we have not yet found AC power.
+*           dwResult & GBS_HASBATTERY means we have found a non-UPS battery.
+*       }
+*
+* @return The error code.
+*
+*--*/
+static HRESULT GetBatteryState(float& cap, DWORD& dwResult)
+{
+    cap = 0;
+    dwResult = GBS_ONBATTERY;
 
-HRESULT STDMETHODCALLTYPE Power_Init(_In_ CSysTray * pSysTray)
+    HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+    if (INVALID_HANDLE_VALUE == hdev)
+        return E_HANDLE;
+
+    // Limit search to 100 batteries max
+    for (int idev = 0, count = 0; idev < 100; idev++)
+    {
+        SP_DEVICE_INTERFACE_DATA did = { 0 };
+        did.cbSize = sizeof(did);
+
+        if (SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVCLASS_BATTERY, idev, &did))
+        {
+            DWORD cbRequired = 0;
+
+            SetupDiGetDeviceInterfaceDetail(hdev, &did, 0, 0, &cbRequired, 0);
+            if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
+            {
+                PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
+                if (pdidd)
+                {
+                    pdidd->cbSize = sizeof(*pdidd);
+                    if (SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, 0))
+                    {
+                        // Enumerated a battery.  Ask it for information.
+                        HANDLE hBattery = CreateFile(pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE,
+                            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+                        if (INVALID_HANDLE_VALUE != hBattery)
+                        {
+                            // Ask the battery for its tag.
+                            BATTERY_QUERY_INFORMATION bqi = { 0 };
+
+                            DWORD dwWait = 0;
+                            DWORD dwOut;
+
+                            if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG, &dwWait, sizeof(dwWait), &bqi.BatteryTag,
+                                sizeof(bqi.BatteryTag), &dwOut, NULL) && bqi.BatteryTag)
+                            {
+                                // With the tag, you can query the battery info.
+                                BATTERY_INFORMATION bi = { 0 };
+                                bqi.InformationLevel = BatteryInformation;
+
+                                if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi,
+                                    sizeof(bi), &dwOut, NULL))
+                                {
+                                    // Only non-UPS system batteries count
+                                    if (bi.Capabilities & BATTERY_SYSTEM_BATTERY)
+                                    {
+                                        if (!(bi.Capabilities & BATTERY_IS_SHORT_TERM))
+                                            dwResult |= GBS_HASBATTERY;
+
+                                        // Query the battery status.
+                                        BATTERY_WAIT_STATUS bws = { 0 };
+                                        bws.BatteryTag = bqi.BatteryTag;
+
+                                        BATTERY_STATUS bs;
+                                        if (DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws),
+                                            &bs, sizeof(bs), &dwOut, NULL))
+                                        {
+                                            if (bs.PowerState & BATTERY_POWER_ON_LINE)
+                                                dwResult &= ~GBS_ONBATTERY;
+
+                                            // Take average of total capacity of batteries detected!
+                                            cap = cap*(count)+(float)bs.Capacity / bi.FullChargedCapacity * 100;
+                                            cap /= count + 1;
+                                            count++;
+                                        }
+                                    }
+                                }
+                            }
+                            CloseHandle(hBattery);
+                        }
+                    }
+                    LocalFree(pdidd);
+                }
+            }
+        }
+        else  if (ERROR_NO_MORE_ITEMS == GetLastError())
+        {
+            break;  // Enumeration failed - perhaps we're out of items
+        }
+    }
+    SetupDiDestroyDeviceInfoList(hdev);
+
+    //  Final cleanup:  If we didn't find a battery, then presume that we
+    //  are on AC power.
+
+    if (!(dwResult & GBS_HASBATTERY))
+        dwResult &= ~GBS_ONBATTERY;
+
+    return S_OK;
+}
+
+/*++
+* @name Quantize
+* 
+* This function quantizes the mentioned quantity to nearest level.
+* 
+* @param p
+*        Should be a quantity in percentage.
+* @param lvl
+*        Quantization level (this excludes base level 0, which will always be present), default is 10.
+*
+* @return Nearest quantized level, can be directly used as array index based on context.
+*
+*--*/
+static UINT Quantize(float p, UINT lvl = 10)
 {
-    WCHAR strTooltip[128];
+    int i = 0;
+    float f, q = (float)100 / lvl, d = q / 2;
+    for (f = 0; f < p; f += q, i++);
+
+    if ((f - d) <= p)
+        return i;
+    else
+        return i - 1;
+/* 
+ @remarks This function uses centred/symmetric logic for quantization.
+ For the case of lvl = 4, You will get following integer levels if given (p) value falls in between the range partitions:
+     0    <= p <  12.5 : returns 0; (corresponding to 0% centre)
+     12.5 <= p <  37.5 : returns 1; (corresponding to 25% centre)
+     37.5 <= p <  62.5 : returns 2; (corresponding to 50% centre)
+     62.5 <= p <  87.5 : returns 3; (corresponding to 75% centre)
+     87.5 <= p <= 100  : returns 4; (corresponding to 100% centre)
+*/
+}
 
-    TRACE("Power_Init\n");
+/*++
+* @name DynamicLoadIcon
+*
+* Returns the respective icon as per the current battery capacity.
+* It also does the work of setting global parameters of battery capacity and tooltips.
+*
+* @param hinst
+*        A handle to a instance of the module.
+*
+* @return The handle to respective battery icon.
+*
+*--*/
+static HICON DynamicLoadIcon(HINSTANCE hinst)
+{    
+    HICON hBatIcon;
+    float cap = 0;
+    DWORD dw = 0;
+    UINT index = -1;
+    HRESULT hr = GetBatteryState(cap, dw);
+
+    if (!FAILED(hr) && (dw & GBS_HASBATTERY))
+    {
+        index = Quantize(cap, 4);
+        g_batCap = cap;
+    }
+    else
+    {
+        g_batCap = 0;
+        hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_BATTCAP_ERR));
+        g_strTooltip.LoadStringW(IDS_PWR_UNKNOWN_REMAINING);
+        return hBatIcon;
+    }
 
-    g_hIconBattery = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_BATTERY));
+    if (dw & GBS_ONBATTERY)
+    {
+        hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(br_icons[index]));
+        g_strTooltip.Format(IDS_PWR_PERCENT_REMAINING, cap);
+    }
+    else
+    {
+        hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(bc_icons[index])); 
+        g_strTooltip.Format(IDS_PWR_CHARGING, cap);
+    }
 
-    LoadStringW(g_hInstance, IDS_PWR_AC, strTooltip, _countof(strTooltip));
+    return hBatIcon;
+}
 
+HRESULT STDMETHODCALLTYPE Power_Init(_In_ CSysTray * pSysTray)
+{ 
+    TRACE("Power_Init\n");
+    g_hIconBattery = DynamicLoadIcon(g_hInstance);
     g_IsRunning = TRUE;
 
-    return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_POWER, g_hIconBattery, strTooltip);
+    return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
 }
 
 HRESULT STDMETHODCALLTYPE Power_Update(_In_ CSysTray * pSysTray)
 {
     TRACE("Power_Update\n");
-    return S_OK;
+    g_hIconBattery = DynamicLoadIcon(g_hInstance);
+
+    return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
 }
 
 HRESULT STDMETHODCALLTYPE Power_Shutdown(_In_ CSysTray * pSysTray)
 {
     TRACE("Power_Shutdown\n");
-
     g_IsRunning = FALSE;
 
     return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_POWER, NULL, NULL);
 }
 
-static void RunPower()
+static void _RunPower()
 {
-    ShellExecuteW(NULL, NULL, L"rundll32.exe", L"shell32.dll,Control_RunDLL powercfg.cpl", NULL, SW_SHOWNORMAL);
+    ShellExecuteW(NULL, NULL, L"powercfg.cpl", NULL, NULL, SW_SHOWNORMAL);
 }
 
-static void ShowContextMenu(CSysTray * pSysTray)
+static void _ShowContextMenu(CSysTray * pSysTray)
 {
-    WCHAR szBuffer[128];
-    DWORD id, msgPos;
-    HMENU hPopup;
-
-    LoadStringW(g_hInstance, IDS_PWR_PROPERTIES, szBuffer, _countof(szBuffer));
-
-    hPopup = CreatePopupMenu();
-    AppendMenuW(hPopup, MF_STRING, IDS_PWR_PROPERTIES, szBuffer);
-    SetMenuDefaultItem(hPopup, IDS_PWR_PROPERTIES, FALSE);
-
-    msgPos = GetMessagePos();
+    CString strOpen((LPCSTR)IDS_PWR_PROPERTIES);
+    HMENU hPopup = CreatePopupMenu();
+    AppendMenuW(hPopup, MF_STRING, IDS_PWR_PROPERTIES, strOpen);
 
     SetForegroundWindow(pSysTray->GetHWnd());
-    id = TrackPopupMenuEx(hPopup,
-                          TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN,
-                          GET_X_LPARAM(msgPos),
-                          GET_Y_LPARAM(msgPos),
-                          pSysTray->GetHWnd(),
-                          NULL);
+    DWORD flags = TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN;
+    POINT pt;
+    GetCursorPos(&pt);
 
-    DestroyMenu(hPopup);
+    DWORD id = TrackPopupMenuEx(hPopup, flags,
+        pt.x, pt.y,
+        pSysTray->GetHWnd(), NULL);
 
-    if (id == IDS_PWR_PROPERTIES)
-        RunPower();
+    switch (id)
+    {
+        case IDS_PWR_PROPERTIES:
+            _RunPower();
+            break;
+    }
+    DestroyMenu(hPopup);
 }
 
 static
@@ -114,12 +314,12 @@ PowerSchemesEnumProc(
 static
 VOID
 ShowPowerSchemesPopupMenu(
-    HWND hWnd)
+    CSysTray *pSysTray)
 {
     PWRSCHEMECONTEXT PowerSchemeContext = {NULL, 0, 0};
     UINT uiActiveScheme;
-    DWORD id, msgPos;
-
+    DWORD id;
+    POINT pt;
     PowerSchemeContext.hPopup = CreatePopupMenu();
     EnumPwrSchemes(PowerSchemesEnumProc, (LPARAM)&PowerSchemeContext);
 
@@ -132,14 +332,14 @@ ShowPowerSchemesPopupMenu(
                            MF_BYCOMMAND);
     }
 
-    msgPos = GetMessagePos();
-
-    SetForegroundWindow(hWnd);
+    SetForegroundWindow(pSysTray->GetHWnd());
+    GetCursorPos(&pt);
+    
     id = TrackPopupMenuEx(PowerSchemeContext.hPopup,
                           TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN,
-                          GET_X_LPARAM(msgPos),
-                          GET_Y_LPARAM(msgPos),
-                          hWnd,
+                          pt.x,
+                          pt.y,
+                          pSysTray->GetHWnd(),
                           NULL);
 
     DestroyMenu(PowerSchemeContext.hPopup);
@@ -174,13 +374,21 @@ HRESULT STDMETHODCALLTYPE Power_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPA
             }
             return S_FALSE;
 
+        case WM_TIMER:
+            if (wParam == POWER_TIMER_ID)
+            {
+                KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
+                ShowPowerSchemesPopupMenu(pSysTray);
+            }
+            break;
+
         case ID_ICON_POWER:
             Power_Update(pSysTray);
 
             switch (lParam)
             {
                 case WM_LBUTTONDOWN:
-                    SetTimer(pSysTray->GetHWnd(), POWER_TIMER_ID, 500, NULL);
+                    SetTimer(pSysTray->GetHWnd(), POWER_TIMER_ID, GetDoubleClickTime(), NULL);
                     break;
 
                 case WM_LBUTTONUP:
@@ -188,14 +396,14 @@ HRESULT STDMETHODCALLTYPE Power_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPA
 
                 case WM_LBUTTONDBLCLK:
                     KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
-                    RunPower();
+                    _RunPower();
                     break;
 
                 case WM_RBUTTONDOWN:
                     break;
 
                 case WM_RBUTTONUP:
-                    ShowContextMenu(pSysTray);
+                    _ShowContextMenu(pSysTray);
                     break;
 
                 case WM_RBUTTONDBLCLK:
@@ -213,11 +421,3 @@ HRESULT STDMETHODCALLTYPE Power_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPA
 
     return S_FALSE;
 }
-
-VOID
-Power_OnTimer(HWND hWnd)
-{
-    TRACE("Power_OnTimer\n!");
-    KillTimer(hWnd, POWER_TIMER_ID);
-    ShowPowerSchemesPopupMenu(hWnd);
-}