BatteryMeter.c 8.69 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
#include "ProcessList.h"
#include "CRT.h"
#include "String.h"

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

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

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

}*/

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

Hisham Muhammad's avatar
Hisham Muhammad committed
36
static unsigned long int parseUevent(FILE * file, const char *key) {
37
38
39
40
41
42
   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
43
         strtok(line, "=");
44
45
46
47
48
49
50
51
52
         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) {
53
   DIR* batteryDir;
54
55
   const struct dirent *dirEntries;

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

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

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
72
   /*
73
74
75
76
77
78
79
      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
80
         continue;
81

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

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

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

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

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

      fclose((FILE *) file);

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

      total += foundNum;
   }

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

120
121
122
123
124
125
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
126
      const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
      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];
143
         snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
144
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
         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
179
      const char *power_supplyPath = "/sys/class/power_supply";
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

      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
196

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

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
            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
234
   const unsigned long int totalFull = parseBatInfo("info", 3, 4);
235
236
237
   if (totalFull == 0)
      return 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
238
   const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
239
240
241
242
243
244
245
246
247
   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
248
   const char *power_supplyPath = "/sys/class/power_supply/";
249
250
251
252
253
   DIR *power_supplyDir = opendir(power_supplyPath);


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

   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];

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

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

278
279
280
281
282
      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) {
283
            closedir(power_supplyDir);
284
285
286
287
288
289
290
291
292
293
294
            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) {
295
            closedir(power_supplyDir);
296
297
298
299
300
            fclose(file);
            return 0;
         }
      }

301
302
303
304
305
306
307
308
309
310
      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();
311

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

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

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

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

333
334
335
   ACPresence isOnLine = chkIsOnline();

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

   return;
}

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