BatteryMonitor
/media/development/cpp/BatteryMonitor/main.cpp
1 
8 #include "main.h"
9 
10 using namespace BatteryMonitor;
11 
14 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
15  LPSTR lpCmdLine, int nCmdShow) {
16 
17  // Do some initialization.
18  strcpy(classname, "BatMonOverlay");
19  instance=hInstance;
20  red = CreateSolidBrush(RGB(0xFF, 0, 0));
21  green = CreateSolidBrush(RGB(0, 0xFF, 0));
22  yellow = CreateSolidBrush(RGB(0xFF, 0xFF, 0));
23  blue = CreateSolidBrush(RGB(0, 0, 0xDF));
24 
25  // Register window class.
26  if (!setupWindowClass()) {
27  errOut();
28  return 0;
29  }
30 
31  // Show window.
32  HWND hwnd=setupWindow(nCmdShow);
33  if (hwnd == NULL) {
34 
35  errOut();
36  return 0;
37  }
38 
39  /* Battery initialization.
40  *
41  * - Sets up battery device path name
42  * - Commits initial update to window
43  * - Starts timer for periodic updates (500 ms)
44  */
45  initDevice();
46  SetTimer(hwnd, UPDATE_BATTERY, 500, NULL);
47  SendMessage(hwnd, WM_TIMER, UPDATE_BATTERY, 0);
48 
49  // Taskbar icon
50  addNotifyIcon(hwnd);
51  WM_TASKBARCREATED = RegisterWindowMessage(TEXT("TaskbarCreated"));
52 
53  // Message loop
54  MSG Msg;
55  while (GetMessage(&Msg, NULL, 0, 0) > 0) {
56  TranslateMessage(&Msg);
57  DispatchMessage(&Msg);
58  }
59 
60  // Finish
61  cleanUp(hwnd);
62  return Msg.wParam;
63 }
64 
65 namespace BatteryMonitor {
66  HWND setupWindow(int nCmdShow){
67  RECT desktop;
68  GetWindowRect(GetDesktopWindow(), &desktop);
69  HWND hwnd;
70  /* Window creation.
71  *
72  * - Position: upper right
73  * - No titlebar, no taskbar button
74  * - Transparent (Opacity 120/255)
75  * - Stay on top
76  * - Click through (WS_EX_TRANSPARENT!)
77  */
78  hwnd = CreateWindowEx(
79  WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TRANSPARENT,
80  classname,
81  "Battery Monitor",
82  WS_POPUP | WS_BORDER,
83  desktop.right - 90, 20, 70, 30,
84  NULL, NULL, instance, NULL);
85  if(!hwnd)return 0;
86  SetLayeredWindowAttributes(hwnd, 0, 120, LWA_ALPHA);
87  ShowWindow(hwnd, nCmdShow);
88  UpdateWindow(hwnd);
89  return hwnd;
90  }
91 
92  void cleanUp(HWND hwnd) {
93  // Delete icon and brushes.
94  NOTIFYICONDATA nfi;
95  nfi.cbSize = sizeof (nfi);
96  nfi.hWnd = hwnd;
97  nfi.uID = 0;
98  Shell_NotifyIcon(NIM_DELETE, &nfi);
99  DeleteObject(red);
100  DeleteObject(yellow);
101  DeleteObject(blue);
102  DeleteObject(green);
103  }
104 
106  WNDCLASSEX wc;
107  wc.cbSize = sizeof (WNDCLASSEX);
108  wc.style = 0;
109  wc.lpfnWndProc = wndProc;
110  wc.cbClsExtra = 0;
111  wc.cbWndExtra = 0;
112  wc.hInstance = instance;
113  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
114  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
115  wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
116  wc.lpszMenuName = NULL;
117  wc.lpszClassName = classname;
118  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
119 
120  return RegisterClassEx(&wc);
121  }
122 
123  void errOut() {
124  char* buf = new char[255];
125  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, buf, 255, NULL);
126 #ifdef DEBUG
127  MessageBox(NULL, buf, "Error",
128  MB_ICONEXCLAMATION | MB_OK);
129 #endif
130  delete[] buf;
131  }
132 
133  LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
134  switch (msg) {
135  case WM_CLOSE:
136  DestroyWindow(hwnd);
137  break;
138  case WM_DESTROY:
139  PostQuitMessage(0);
140  break;
141  case WM_TIMER: // This message is received periodically to update the battery
142  if (wParam == UPDATE_BATTERY) {
143  SYSTEM_POWER_STATUS pwr;
144  GetSystemPowerStatus(&pwr);
145  if (pwr.BatteryFlag == 128 || pwr.BatteryFlag == 255) { // Unknown state or Error
146  battery = 2;
147  } else if (pwr.ACLineStatus) {
148  battery = -(double) pwr.BatteryLifePercent / 100.0;
149  if(shutdown_enabled) {
150  AbortSystemShutdown(NULL);
151  shutdown_enabled=false;
152  }
153  }
154  else {
155  battery = (double) pwr.BatteryLifePercent / 100.0;
156  batterylifetime = pwr.BatteryLifeTime;
157  if (batterylifetime > 0 && batterylifetime < 300)shutdown(); // Shutdown if battery low
158  }
159  }
160  InvalidateRect(hwnd, NULL, true); // Force repaint
161  break;
162  case WM_PAINT: // Paint window
163  {
164  PAINTSTRUCT p;
165  HDC hdc = BeginPaint(hwnd, &p);
166  if (hdc) {
167  RECT r1,rtext;
168  HBRUSH load;
169  if (battery > 1.5) { // Yellow if no information available
170  load = yellow;
171  r1.top = 0;
172  r1.left = 0;
173  r1.right = 70;
174  r1.bottom = 30;
175  } else { // Green if dischargin, blue if charging
176  load = battery > 0 ? green : blue;
177 
178  r1.top = 0;
179  r1.left = 0;
180  r1.right = 70 * battery * (battery > 0 ? 1 : -1);
181  r1.bottom = 30;
182 
183  RECT r2;
184  r2.top = 0;
185  r2.left = r1.right;
186  r2.right = 70;
187  r2.bottom = 30;
188  FillRect(hdc, &r2, red);
189 
190  }
191  FillRect(hdc, &r1, load);
192  SetBkMode(hdc, TRANSPARENT); // Text background transparent
193  SetTextColor(hdc, 0);
194  // Used to center text
195  rtext.bottom = 30;
196  rtext.top = 0;
197  rtext.left = 0;
198  rtext.right = 70;
199  char time[6];
200  batterylifetime /= 60;
201  sprintf(time, "%02ld:%02ld", batterylifetime / 60, batterylifetime % 60);
202  if (batterylifetime >= 0)
203  DrawText(hdc, time, 5, &rtext, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
204  else
205  DrawText(hdc, "N/A", 3, &rtext, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
206 
207  }
208  EndPaint(hwnd, &p);
209  }
210  break;
211  case WM_USER + 1: // Taskbar icon event. Now lParam contains the important window message.
212  if (wParam != 0)break;
213  switch (lParam) {
214  case WM_RBUTTONDBLCLK:
215  PostMessage(hwnd, WM_CLOSE, 0, 0);
216  break;
217  case WM_LBUTTONUP:
218  showBatteryInfo(hwnd);
219  break;
220  default:
221  break;
222  }
223  break;
224  default: // Refresh icon if taskbar was newly created. This can't be handled in a case as WM_TASKBARCREATED is not constant.
225  if (msg == WM_TASKBARCREATED) {
226  addNotifyIcon(hwnd);
227  break;
228  }
229  return DefWindowProc(hwnd, msg, wParam, lParam); // Do default message handling
230  }
231  return 0;
232  }
233 
234  void addNotifyIcon(HWND hwnd) {
235  NOTIFYICONDATA nfi;
236  HICON icon;
237  if (!(icon = LoadIcon(instance, MAKEINTRESOURCE(MAIN_ICON)))) {errOut(); PostMessage(hwnd, WM_CLOSE, 0, 0);} // If icon can't be found, close application.
238  nfi.cbSize = sizeof (nfi);
239  nfi.hWnd = hwnd;
240  nfi.hIcon = icon;
241  nfi.uID = 0;
242  strcpy(nfi.szTip, "BatteryMonitor");
243  nfi.uFlags = NIF_ICON | NIF_TIP | 0x80 | NIF_MESSAGE;
244  nfi.uCallbackMessage = WM_USER + 1;
245 
246  Shell_NotifyIcon(NIM_ADD, &nfi);
247  }
248 
249  void showBatteryInfo(HWND hwnd) {
250  NOTIFYICONDATA nfi;
251  nfi.cbSize = sizeof (nfi);
252  nfi.hWnd = hwnd;
253  nfi.uID = 0;
254  nfi.uFlags = NIF_INFO;
255  strcpy(nfi.szInfoTitle, "Battery Info");
256  nfi.uTimeout = 30000;
257  nfi.dwInfoFlags = NIIF_INFO;
258  BATTERY_INFORMATION info = getBatteryInformation();
259  SYSTEM_POWER_STATUS pwr;
260  GetSystemPowerStatus(&pwr);
261  if (info.DesignedCapacity) {
262  char chem[5];
263  memcpy(chem, info.Chemistry, 4);
264  chem[4] = 0;
265  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);
266  } else {
267  strcpy(nfi.szInfo, "No information available :(");
268  }
269 
270  Shell_NotifyIcon(NIM_MODIFY, &nfi);
271  }
272 
273  void shutdown() {
274  HANDLE hToken;
275  TOKEN_PRIVILEGES tkp;
276 
277  // Get a token for this process.
278 
279  if (!OpenProcessToken(GetCurrentProcess(),
280  TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
281  MessageBox(0, "Low Battery! Shutdown failed.", "Error", MB_ICONERROR);
282 
283  // Get the LUID for the shutdown privilege.
284 
285  LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
286 
287  tkp.PrivilegeCount = 1;
288  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
289 
290  // Get the shutdown privilege.
291 
292  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
293 
294  if (GetLastError() != ERROR_SUCCESS)
295  return;
296  // Shutdown.
297  InitiateSystemShutdown(NULL, "Low battery", 30, FALSE, FALSE);
298  shutdown_enabled=true;
299  }
300 
301  void initDevice() {
302  devicepath = 0; // If something fails, devicepath will be NULL
303  HDEVINFO devinfo = SetupDiGetClassDevs(&GUID_DEVICE_BATTERY, NULL, NULL, DIGCF_DEVICEINTERFACE); // Get battery device information
304  if (INVALID_HANDLE_VALUE != devinfo) {
305  SP_DEVICE_INTERFACE_DATA deviceinfo;
306  deviceinfo.cbSize = sizeof (deviceinfo);
307  if (SetupDiEnumDeviceInterfaces(devinfo, 0, &GUID_DEVICE_BATTERY, 0, &deviceinfo)) { // Get device interface
308  PSP_DEVICE_INTERFACE_DETAIL_DATA devdetails;
309  DWORD reqSize = 0;
310  SetupDiGetDeviceInterfaceDetail(devinfo, &deviceinfo, 0, 0, &reqSize, 0); // Get required size for details
311  if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
312  devdetails = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LPTR, reqSize); // Allocate
313  if (devdetails) {
314  devdetails->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
315  if (SetupDiGetDeviceInterfaceDetail(devinfo, &deviceinfo, devdetails, reqSize, &reqSize, NULL)) { // Get some details on battery device
316  devicepath = new char[strlen(devdetails->DevicePath)]; // There it is, the device path!
317  strcpy(devicepath, devdetails->DevicePath);
318 
319  }
320  LocalFree(devdetails); // Free allocated memory
321  }
322  }
323  }
324  }
325  if (GetLastError() != ERROR_SUCCESS)errOut();
326  }
327 
328  BATTERY_INFORMATION getBatteryInformation() {
329  BATTERY_INFORMATION batteryinformation;
330  if (devicepath) {
331  HANDLE bat = CreateFile(devicepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // Open battery device file
332  if (INVALID_HANDLE_VALUE != bat) {
333  ULONG timeout = 100;
334  ULONG tag;
335  DWORD buf;
336  if (DeviceIoControl(bat, IOCTL_BATTERY_QUERY_TAG, &timeout, sizeof (timeout), &tag, sizeof (tag), &buf, NULL)) { // Get the battery tag.
337  BATTERY_QUERY_INFORMATION infoqueryin;
338  infoqueryin.BatteryTag = tag;
339  infoqueryin.InformationLevel = BatteryInformation;
340  if (DeviceIoControl(bat, IOCTL_BATTERY_QUERY_INFORMATION, &infoqueryin, sizeof (infoqueryin), &batteryinformation, sizeof (batteryinformation), &buf, NULL)) { // Retrieve the wanted information.
341  CloseHandle(bat); // Close battery file handle.
342  return batteryinformation;
343  }
344  }
345  }
346  CloseHandle(bat); // If something fails close it, too.
347  }
348  errOut();
349  batteryinformation.DesignedCapacity = 0; // Error occured
350  return batteryinformation;
351  }
352 
353 }
UINT WM_TASKBARCREATED
Window message for taskbarcreated-event.
Definition: main.h:162
BATTERY_INFORMATION getBatteryInformation()
retrieves battery informations from the system.
Definition: main.cpp:328
void cleanUp(HWND hwnd)
Memory and handler cleanups, taskbar icon removal.
Definition: main.cpp:92
Root namespace.
Definition: main.cpp:65
HBRUSH yellow
Yellow brush.
Definition: main.h:129
void initDevice()
Retrieves the path of the battery device.
Definition: main.cpp:301
void addNotifyIcon(HWND hwnd)
Adds an icon to the taskbar.
Definition: main.cpp:234
void errOut()
Error output.
Definition: main.cpp:123
#define UPDATE_BATTERY
Window message for updating battery status.
Definition: main.h:107
#define MAIN_ICON
The icon of BatteryMonitor.
Definition: mainres.h:13
public funktions and variables
HBRUSH blue
Blue brush.
Definition: main.h:129
HBRUSH green
Green brush.
Definition: main.h:129
double battery
Battery charge.
Definition: main.h:142
bool shutdown_enabled
Indicates, if shutdown was invoked by BatteryMonitor.
Definition: main.h:166
long batterylifetime
Remaining battery lifetime in seconds.
Definition: main.h:148
HINSTANCE instance
Application instance.
Definition: main.h:134
char * devicepath
Path to battery device.
Definition: main.h:115
void showBatteryInfo(HWND hwnd)
Shows a balloon tip with battery information.
Definition: main.cpp:249
long setupWindowClass()
registers the window class stored in classname.
Definition: main.cpp:105
HWND setupWindow(int nCmdShow)
Shows the main window.
Definition: main.cpp:66
LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
The window procedure.
Definition: main.cpp:133
char classname[14]
Name of registered window class.
Definition: main.h:154
void shutdown()
Tries to shut down the computer.
Definition: main.cpp:273
HBRUSH red
Red brush.
Definition: main.h:129