BatteryMeter.c 8.71 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
/*
Hisham Muhammad's avatar
Hisham Muhammad committed
2
3
4
5
htop - BatteryMeter.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
Hisham Muhammad's avatar
Hisham Muhammad committed
6

Hisham Muhammad's avatar
Hisham Muhammad committed
7
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
Hisham Muhammad's avatar
Hisham Muhammad committed
8
9
10
*/

#include "BatteryMeter.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
11

Hisham Muhammad's avatar
Hisham Muhammad committed
12
13
14
15
16
#include "ProcessList.h"
#include "CRT.h"
#include "String.h"
#include "debug.h"

Hisham Muhammad's avatar
Hisham Muhammad committed
17
18
19
20
21
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>

22
/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
23
#include "Meter.h"
24
25
26
27
28
29
30
31
32

typedef enum ACPresence_ {
   AC_ABSENT,
   AC_PRESENT,
   AC_ERROR
} ACPresence;

}*/

Hisham Muhammad's avatar
Hisham Muhammad committed
33
34
35
36
int BatteryMeter_attributes[] = {
   BATTERY
};

Hisham Muhammad's avatar
Hisham Muhammad committed
37
static unsigned long int parseUevent(FILE * file, const char *key) {
38
39
40
41
42
43
   char line[100];
   unsigned long int dValue = 0;

   while (fgets(line, sizeof line, file)) {
      if (strncmp(line, key, strlen(key)) == 0) {
         char *value;
Hisham Muhammad's avatar
Hisham Muhammad committed
44
         strtok(line, "=");
45
46
47
48
49
50
51
52
53
         value = strtok(NULL, "=");
         dValue = atoi(value);
         break;
      }
   }
   return dValue;
}

static unsigned long int parseBatInfo(const char *fileName, const unsigned short int lineNum, const unsigned short int wordNum) {
54
   DIR* batteryDir;
55
56
   const struct dirent *dirEntries;

Hisham Muhammad's avatar
Hisham Muhammad committed
57
58
   const char batteryPath[] = PROCDIR "/acpi/battery/";
   batteryDir = opendir(batteryPath);
59

Hisham Muhammad's avatar
Hisham Muhammad committed
60
61
62
   if (batteryDir == NULL) {
      return 0;
   }
63
64
65
66
67
68
69

   char *entryName;
   typedef struct listLbl {
      char *content;
      struct listLbl *next;
   } list;

Hisham Muhammad's avatar
Hisham Muhammad committed
70
71
   list *myList = NULL;
   list *newEntry;
72

Hisham Muhammad's avatar
Hisham Muhammad committed
73
   /*
74
75
76
77
78
79
80
      Some of this is based off of code found in kismet (they claim it came from gkrellm).
      Written for multi battery use...
    */
   for (dirEntries = readdir((DIR *) batteryDir); dirEntries; dirEntries = readdir((DIR *) batteryDir)) {
      entryName = (char *) dirEntries->d_name;

      if (strncmp(entryName, "BAT", 3))
Hisham Muhammad's avatar
Hisham Muhammad committed
81
         continue;
82

Hisham Muhammad's avatar
Hisham Muhammad committed
83
84
      newEntry = calloc(1, sizeof(list));
      newEntry->next = myList;
85
      newEntry->content = entryName;
Hisham Muhammad's avatar
Hisham Muhammad committed
86
87
88
89
90
91
      myList = newEntry;
   }

   unsigned long int total = 0;
   for (newEntry = myList; newEntry; newEntry = newEntry->next) {
      const char infoPath[30];
92
93
94
      const FILE *file;
      char line[50];

95
      snprintf((char *) infoPath, sizeof infoPath, "%s%s/%s", batteryPath, newEntry->content, fileName);
96

Hisham Muhammad's avatar
Hisham Muhammad committed
97
      if ((file = fopen(infoPath, "r")) == NULL) {
98
         closedir(batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
99
100
101
         return 0;
      }

102
103
      for (unsigned short int i = 0; i < lineNum; i++) {
         fgets(line, sizeof line, (FILE *) file);
Hisham Muhammad's avatar
Hisham Muhammad committed
104
      }
105
106
107
108

      fclose((FILE *) file);

      const char *foundNumTmp = String_getToken(line, wordNum);
Hisham Muhammad's avatar
Hisham Muhammad committed
109
      const unsigned long int foundNum = atoi(foundNumTmp);
110
      free((char *) foundNumTmp);
Hisham Muhammad's avatar
Hisham Muhammad committed
111
112
113
114
115
116

      total += foundNum;
   }

   free(myList);
   free(newEntry);
117
   closedir(batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
118
119
120
   return total;
}

121
122
123
124
125
126
static ACPresence chkIsOnline() {
   FILE *file = NULL;
   ACPresence isOn = AC_ERROR;

   if (access(PROCDIR "/acpi/ac_adapter", F_OK) == 0) {
      const struct dirent *dirEntries;
Hisham Muhammad's avatar
Hisham Muhammad committed
127
      const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
      DIR *power_supplyDir = opendir(power_supplyPath);
      char *entryName;

      if (!power_supplyDir) {
         return AC_ERROR;
      }

      for (dirEntries = readdir((DIR *) power_supplyDir); dirEntries; dirEntries = readdir((DIR *) power_supplyDir)) {
         entryName = (char *) dirEntries->d_name;

         if (strncmp(entryName, "A", 1)) {
            continue;
         }


         char statePath[50];
144
         snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
         file = fopen(statePath, "r");

         if (!file) {
            isOn = AC_ERROR;
            continue;
         }

         char line[100];
         fgets(line, sizeof line, file);
         line[sizeof(line) - 1] = '\0';

         if (file) {
            fclose(file);
            file = NULL;
         }

         const char *isOnline = String_getToken(line, 2);

         if (strcmp(isOnline, "on-line") == 0) {
            free((char *) isOnline);
            isOn = AC_PRESENT;
            // If any AC adapter is being used then stop
            break;

         } else {
            isOn = AC_ABSENT;
         }
         free((char *) isOnline);
      }

      if (power_supplyDir)
         closedir(power_supplyDir);

   } else {

Hisham Muhammad's avatar
Hisham Muhammad committed
180
      const char *power_supplyPath = "/sys/class/power_supply";
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

      if (access("/sys/class/power_supply", F_OK) == 0) {
         const struct dirent *dirEntries;
         DIR *power_supplyDir = opendir(power_supplyPath);
         char *entryName;

         if (!power_supplyDir) {
            return AC_ERROR;
         }

         for (dirEntries = readdir((DIR *) power_supplyDir); dirEntries; dirEntries = readdir((DIR *) power_supplyDir)) {
            entryName = (char *) dirEntries->d_name;

            if (strncmp(entryName, "A", 1)) {
               continue;
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
197

198
            char onlinePath[50];
199
            snprintf((char *) onlinePath, sizeof onlinePath, "%s/%s/online", power_supplyPath, entryName);
200
            file = fopen(onlinePath, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
            if (!file) {
               isOn = AC_ERROR;
               continue;
            }

            isOn = (fgetc(file) - '0');

            if (file) {
               fclose(file);
               file = NULL;
            }

            if (isOn == AC_PRESENT) {
               // If any AC adapter is being used then stop
               break;
            } else {
               continue;
            }
         }

         if (power_supplyDir)
            closedir(power_supplyDir);
      }
   }

   // Just in case :-)
   if (file)
      fclose(file);

   return isOn;
}

static double getProcBatData() {
Hisham Muhammad's avatar
Hisham Muhammad committed
235
   const unsigned long int totalFull = parseBatInfo("info", 3, 4);
236
237
238
   if (totalFull == 0)
      return 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
239
   const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
240
241
242
243
244
245
246
247
248
   if (totalRemain == 0)
      return 0;

   double percent = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0;
   return percent;
}

static double getSysBatData() {
   const struct dirent *dirEntries;
Hisham Muhammad's avatar
Hisham Muhammad committed
249
   const char *power_supplyPath = "/sys/class/power_supply/";
250
251
252
253
254
   DIR *power_supplyDir = opendir(power_supplyPath);


   if (!power_supplyDir) {
      return 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
255
   }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

   char *entryName;

   unsigned long int totalFull = 0;
   unsigned long int totalRemain = 0;

   for (dirEntries = readdir((DIR *) power_supplyDir); dirEntries; dirEntries = readdir((DIR *) power_supplyDir)) {
      entryName = (char *) dirEntries->d_name;

      if (strncmp(entryName, "BAT", 3)) {
         continue;
      }

      const char ueventPath[50];

271
      snprintf((char *) ueventPath, sizeof ueventPath, "%s%s/uevent", power_supplyPath, entryName);
272
273
274
275
276
277
278

      FILE *file;
      if ((file = fopen(ueventPath, "r")) == NULL) {
         closedir(power_supplyDir);
         return 0;
      }

279
280
281
282
283
      if ((totalFull += parseUevent(file, "POWER_SUPPLY_ENERGY_FULL="))) {
         totalRemain += parseUevent(file, "POWER_SUPPLY_ENERGY_NOW=");
      } else {
         //reset file pointer
         if (fseek(file, 0, SEEK_SET) < 0) {
284
            closedir(power_supplyDir);
285
286
287
288
289
290
291
292
293
294
295
            fclose(file);
            return 0;
         }
      }

      //Some systems have it as CHARGE instead of ENERGY.
      if ((totalFull += parseUevent(file, "POWER_SUPPLY_CHARGE_FULL="))) {
         totalRemain += parseUevent(file, "POWER_SUPPLY_CHARGE_NOW=");
      } else {
        //reset file pointer
         if (fseek(file, 0, SEEK_SET) < 0) {
296
            closedir(power_supplyDir);
297
298
299
300
301
            fclose(file);
            return 0;
         }
      }

302
303
304
305
306
307
308
309
310
311
      fclose(file);
   }

   const double percent = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0;
   closedir(power_supplyDir);
   return percent;
}

static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
   double percent = getProcBatData();
312

313
314
315
316
317
318
319
320
   if (percent == 0) {
      percent = getSysBatData();
      if (percent == 0) {
         snprintf(buffer, len, "n/a");
         return;
      }
   }

Hisham Muhammad's avatar
Hisham Muhammad committed
321
322
   this->values[0] = percent;

Hisham Muhammad's avatar
Hisham Muhammad committed
323
   const char *onAcText, *onBatteryText, *unknownText;
324
325

   unknownText = "%.1f%%";
Hisham Muhammad's avatar
Hisham Muhammad committed
326
327
328
329
330
331
332
333
   if (this->mode == TEXT_METERMODE) {
      onAcText = "%.1f%% (Running on A/C)";
      onBatteryText = "%.1f%% (Running on battery)";
   } else {
      onAcText = "%.1f%%(A/C)";
      onBatteryText = "%.1f%%(bat)";
   }

334
335
336
   ACPresence isOnLine = chkIsOnline();

   if (isOnLine == AC_PRESENT) {
Hisham Muhammad's avatar
Hisham Muhammad committed
337
      snprintf(buffer, len, onAcText, percent);
338
339
   } else if (isOnLine == AC_ABSENT) {
      snprintf(buffer, len, onBatteryText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
340
   } else {
341
      snprintf(buffer, len, unknownText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
342
343
344
345
346
347
   }

   return;
}

MeterType BatteryMeter = {
348
   .setValues = BatteryMeter_setValues,
Hisham Muhammad's avatar
Hisham Muhammad committed
349
350
351
352
353
354
355
356
357
   .display = NULL,
   .mode = TEXT_METERMODE,
   .items = 1,
   .total = 100.0,
   .attributes = BatteryMeter_attributes,
   .name = "Battery",
   .uiName = "Battery",
   .caption = "Battery: "
};