BatteryMonitor

main.cpp

00001 
00008 #include "main.h"
00009 
00010 using namespace BatteryMonitor;
00011 
00014 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
00015         LPSTR lpCmdLine, int nCmdShow) {
00016 
00017     // Do some initialization.
00018     strcpy(classname, "BatMonOverlay");
00019     instance=hInstance;
00020     red = CreateSolidBrush(RGB(0xFF, 0, 0));
00021     green = CreateSolidBrush(RGB(0, 0xFF, 0));
00022     yellow = CreateSolidBrush(RGB(0xFF, 0xFF, 0));
00023     blue = CreateSolidBrush(RGB(0, 0, 0xDF));
00024 
00025     // Register window class.
00026     if (!setupWindowClass()) {
00027         errOut();
00028         return 0;
00029     }
00030 
00031     // Show window.
00032     HWND hwnd=setupWindow(nCmdShow);
00033     if (hwnd == NULL) {
00034 
00035         errOut();
00036         return 0;
00037     }
00038 
00039     /* Battery initialization.
00040      *
00041      * - Sets up battery device path name
00042      * - Commits initial update to window
00043      * - Starts timer for periodic updates (500 ms)
00044      */
00045     initDevice();
00046     SetTimer(hwnd, UPDATE_BATTERY, 500, NULL);
00047     SendMessage(hwnd, WM_TIMER, UPDATE_BATTERY, 0);
00048 
00049     // Taskbar icon
00050     addNotifyIcon(hwnd);
00051     WM_TASKBARCREATED = RegisterWindowMessage(TEXT("TaskbarCreated"));
00052 
00053     // Message loop
00054     MSG Msg;
00055     while (GetMessage(&Msg, NULL, 0, 0) > 0) {
00056         TranslateMessage(&Msg);
00057         DispatchMessage(&Msg);
00058     }
00059 
00060     // Finish
00061     cleanUp(hwnd);
00062     return Msg.wParam;
00063 }
00064 
00065 namespace BatteryMonitor {
00066     HWND setupWindow(int nCmdShow){
00067         RECT desktop;
00068         GetWindowRect(GetDesktopWindow(), &desktop);
00069         HWND hwnd;
00070         /* Window creation.
00071          *
00072          * - Position: upper right
00073          * - No titlebar, no taskbar button
00074          * - Transparent (Opacity 120/255)
00075          * - Stay on top
00076          * - Click through (WS_EX_TRANSPARENT!)
00077          */
00078         hwnd = CreateWindowEx(
00079                 WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TRANSPARENT,
00080                 classname,
00081                 "Battery Monitor",
00082                 WS_POPUP | WS_BORDER,
00083                 desktop.right - 90, 20, 70, 30,
00084                 NULL, NULL, instance, NULL);
00085         if(!hwnd)return 0;
00086         SetLayeredWindowAttributes(hwnd, 0, 120, LWA_ALPHA);
00087         ShowWindow(hwnd, nCmdShow);
00088         UpdateWindow(hwnd);
00089         return hwnd;
00090     }
00091 
00092     void cleanUp(HWND hwnd) {
00093         // Delete icon and brushes.
00094         NOTIFYICONDATA nfi;
00095         nfi.cbSize = sizeof (nfi);
00096         nfi.hWnd = hwnd;
00097         nfi.uID = 0;
00098         Shell_NotifyIcon(NIM_DELETE, &nfi);
00099         DeleteObject(red);
00100         DeleteObject(yellow);
00101         DeleteObject(blue);
00102         DeleteObject(green);
00103     }
00104 
00105     long setupWindowClass() {
00106         WNDCLASSEX wc;
00107         wc.cbSize = sizeof (WNDCLASSEX);
00108         wc.style = 0;
00109         wc.lpfnWndProc = wndProc;
00110         wc.cbClsExtra = 0;
00111         wc.cbWndExtra = 0;
00112         wc.hInstance = instance;
00113         wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
00114         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
00115         wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
00116         wc.lpszMenuName = NULL;
00117         wc.lpszClassName = classname;
00118         wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
00119 
00120         return RegisterClassEx(&wc);
00121     }
00122 
00123     void errOut() {
00124         char* buf = new char[255];
00125         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, buf, 255, NULL);
00126 #ifdef DEBUG
00127         MessageBox(NULL, buf, "Error",
00128                 MB_ICONEXCLAMATION | MB_OK);
00129 #endif
00130         delete[] buf;
00131     }
00132 
00133     LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
00134         switch (msg) {
00135             case WM_CLOSE:
00136                 DestroyWindow(hwnd);
00137                 break;
00138             case WM_DESTROY:
00139                 PostQuitMessage(0);
00140                 break;
00141             case WM_TIMER:  // This message is received periodically to update the battery
00142                 if (wParam == UPDATE_BATTERY) {
00143                     SYSTEM_POWER_STATUS pwr;
00144                     GetSystemPowerStatus(&pwr);
00145                     if (pwr.BatteryFlag == 128 || pwr.BatteryFlag == 255) { // Unknown state or Error
00146                                                 battery = 2;
00147                     } else if (pwr.ACLineStatus) {
00148                         battery = -(double) pwr.BatteryLifePercent / 100.0;
00149                         if(shutdown_enabled) {
00150                             AbortSystemShutdown(NULL);
00151                             shutdown_enabled=false;
00152                         }
00153                     }
00154                     else {
00155                         battery = (double) pwr.BatteryLifePercent / 100.0;
00156                         batterylifetime = pwr.BatteryLifeTime;
00157                         if (batterylifetime > 0 && batterylifetime < 300)shutdown(); // Shutdown if battery low
00158                     }
00159                 }
00160                 InvalidateRect(hwnd, NULL, true);   // Force repaint
00161                 break;
00162             case WM_PAINT:  // Paint window
00163             {
00164                 PAINTSTRUCT p;
00165                 HDC hdc = BeginPaint(hwnd, &p);
00166                 if (hdc) {
00167                     RECT r1,rtext;
00168                     HBRUSH load;
00169                     if (battery > 1.5) {    // Yellow if no information available
00170                         load = yellow;
00171                         r1.top = 0;
00172                         r1.left = 0;
00173                         r1.right = 70;
00174                         r1.bottom = 30;
00175                     } else {    // Green if dischargin, blue if charging
00176                         load = battery > 0 ? green : blue;
00177 
00178                         r1.top = 0;
00179                         r1.left = 0;
00180                         r1.right = 70 * battery * (battery > 0 ? 1 : -1);
00181                         r1.bottom = 30;
00182 
00183                         RECT r2;
00184                         r2.top = 0;
00185                         r2.left = r1.right;
00186                         r2.right = 70;
00187                         r2.bottom = 30;
00188                         FillRect(hdc, &r2, red);
00189 
00190                     }
00191                     FillRect(hdc, &r1, load);
00192                     SetBkMode(hdc, TRANSPARENT); // Text background transparent
00193                     SetTextColor(hdc, 0);
00194                     // Used to center text
00195                     rtext.bottom = 30;
00196                     rtext.top = 0;
00197                     rtext.left = 0;
00198                     rtext.right = 70;
00199                     char time[6];
00200                     batterylifetime /= 60;
00201                                         sprintf(time, "%02ld:%02ld", batterylifetime / 60, batterylifetime % 60);
00202                     if (batterylifetime >= 0)
00203                         DrawText(hdc, time, 5, &rtext, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
00204                     else
00205                         DrawText(hdc, "N/A", 3, &rtext, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
00206 
00207                 }
00208                 EndPaint(hwnd, &p);
00209             }
00210                 break;
00211             case WM_USER + 1:   // Taskbar icon event. Now lParam contains the important window message.
00212                 if (wParam != 0)break;
00213                 switch (lParam) {
00214                     case WM_RBUTTONDBLCLK:
00215                         PostMessage(hwnd, WM_CLOSE, 0, 0);
00216                         break;
00217                     case WM_LBUTTONUP:
00218                         showBatteryInfo(hwnd);
00219                         break;
00220                     default:
00221                         break;
00222                 }
00223                 break;
00224             default:    // Refresh icon if taskbar was newly created. This can't be handled in a case as WM_TASKBARCREATED is not constant.
00225                 if (msg == WM_TASKBARCREATED) {
00226                     addNotifyIcon(hwnd);
00227                     break;
00228                 }
00229                 return DefWindowProc(hwnd, msg, wParam, lParam); // Do default message handling
00230         }
00231         return 0;
00232     }
00233 
00234     void addNotifyIcon(HWND hwnd) {
00235         NOTIFYICONDATA nfi;
00236         HICON icon;
00237         if (!(icon = LoadIcon(instance, MAKEINTRESOURCE(MAIN_ICON)))) {errOut(); PostMessage(hwnd, WM_CLOSE, 0, 0);} // If icon can't be found, close application.
00238         nfi.cbSize = sizeof (nfi);
00239         nfi.hWnd = hwnd;
00240         nfi.hIcon = icon;
00241         nfi.uID = 0;
00242         strcpy(nfi.szTip, "BatteryMonitor");
00243         nfi.uFlags = NIF_ICON | NIF_TIP | 0x80 | NIF_MESSAGE;
00244         nfi.uCallbackMessage = WM_USER + 1;
00245 
00246         Shell_NotifyIcon(NIM_ADD, &nfi);
00247     }
00248 
00249     void showBatteryInfo(HWND hwnd) {
00250         NOTIFYICONDATA nfi;
00251         nfi.cbSize = sizeof (nfi);
00252         nfi.hWnd = hwnd;
00253         nfi.uID = 0;
00254         nfi.uFlags = NIF_INFO;
00255         strcpy(nfi.szInfoTitle, "Battery Info");
00256         nfi.uTimeout = 30000;
00257         nfi.dwInfoFlags = NIIF_INFO;
00258         BATTERY_INFORMATION info = getBatteryInformation();
00259         SYSTEM_POWER_STATUS pwr;
00260         GetSystemPowerStatus(&pwr);
00261         if (info.DesignedCapacity) {
00262             char chem[5];
00263             memcpy(chem, info.Chemistry, 4);
00264             chem[4] = 0;
00265                         sprintf(nfi.szInfo, "Type: %s\nAC: %s\nCharge: %d%%\nWear Level: %lu%%", chem, pwr.ACLineStatus ? "Online" : "Offline", pwr.BatteryLifePercent, 100 - info.FullChargedCapacity * 100 / info.DesignedCapacity);
00266         } else {
00267             strcpy(nfi.szInfo, "No information available :(");
00268         }
00269 
00270         Shell_NotifyIcon(NIM_MODIFY, &nfi);
00271     }
00272 
00273     void shutdown() {
00274         HANDLE hToken;
00275         TOKEN_PRIVILEGES tkp;
00276 
00277         // Get a token for this process.
00278 
00279         if (!OpenProcessToken(GetCurrentProcess(),
00280                 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
00281             MessageBox(0, "Low Battery! Shutdown failed.", "Error", MB_ICONERROR);
00282 
00283         // Get the LUID for the shutdown privilege.
00284 
00285         LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
00286 
00287         tkp.PrivilegeCount = 1;
00288         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
00289 
00290         // Get the shutdown privilege.
00291 
00292         AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
00293 
00294         if (GetLastError() != ERROR_SUCCESS)
00295             return;
00296         // Shutdown.
00297         InitiateSystemShutdown(NULL, "Low battery", 30, FALSE, FALSE);
00298         shutdown_enabled=true;
00299     }
00300 
00301     void initDevice() {
00302         devicepath = 0; // If something fails, devicepath will be NULL
00303         HDEVINFO devinfo = SetupDiGetClassDevs(&GUID_DEVICE_BATTERY, NULL, NULL, DIGCF_DEVICEINTERFACE);    // Get battery device information
00304         if (INVALID_HANDLE_VALUE != devinfo) {
00305             SP_DEVICE_INTERFACE_DATA deviceinfo;
00306             deviceinfo.cbSize = sizeof (deviceinfo);
00307             if (SetupDiEnumDeviceInterfaces(devinfo, 0, &GUID_DEVICE_BATTERY, 0, &deviceinfo)) { // Get device interface
00308                 PSP_DEVICE_INTERFACE_DETAIL_DATA devdetails;
00309                 DWORD reqSize = 0;
00310                 SetupDiGetDeviceInterfaceDetail(devinfo, &deviceinfo, 0, 0, &reqSize, 0);   // Get required size for details
00311                 if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
00312                     devdetails = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LPTR, reqSize); // Allocate
00313                     if (devdetails) {
00314                         devdetails->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
00315                         if (SetupDiGetDeviceInterfaceDetail(devinfo, &deviceinfo, devdetails, reqSize, &reqSize, NULL)) { // Get some details on battery device
00316                             devicepath = new char[strlen(devdetails->DevicePath)]; // There it is, the device path!
00317                             strcpy(devicepath, devdetails->DevicePath);
00318 
00319                         }
00320                         LocalFree(devdetails);  // Free allocated memory
00321                     }
00322                 }
00323             }
00324         }
00325         if (GetLastError() != ERROR_SUCCESS)errOut();
00326     }
00327 
00328     BATTERY_INFORMATION getBatteryInformation() {
00329         BATTERY_INFORMATION batteryinformation;
00330         if (devicepath) {
00331             HANDLE bat = CreateFile(devicepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // Open battery device file
00332             if (INVALID_HANDLE_VALUE != bat) {
00333                 ULONG timeout = 100;
00334                 ULONG tag;
00335                 DWORD buf;
00336                 if (DeviceIoControl(bat, IOCTL_BATTERY_QUERY_TAG, &timeout, sizeof (timeout), &tag, sizeof (tag), &buf, NULL)) { // Get the battery tag.
00337                     BATTERY_QUERY_INFORMATION infoqueryin;
00338                     infoqueryin.BatteryTag = tag;
00339                     infoqueryin.InformationLevel = BatteryInformation;
00340                     if (DeviceIoControl(bat, IOCTL_BATTERY_QUERY_INFORMATION, &infoqueryin, sizeof (infoqueryin), &batteryinformation, sizeof (batteryinformation), &buf, NULL)) { // Retrieve the wanted information.
00341                         CloseHandle(bat);   // Close battery file handle.
00342                         return batteryinformation;
00343                     }
00344                 }
00345             }
00346             CloseHandle(bat); // If something fails close it, too.
00347         }
00348         errOut();
00349         batteryinformation.DesignedCapacity = 0; // Error occured
00350         return batteryinformation;
00351     }
00352 
00353 }