BatteryMeter.c 8.61 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) {
Hisham Muhammad's avatar
Hisham Muhammad committed
53
   const char batteryPath[] = PROCDIR "/acpi/battery/";
Hisham Muhammad's avatar
Hisham Muhammad committed
54
55
   DIR* batteryDir = opendir(batteryPath);
   if (!batteryDir)
Hisham Muhammad's avatar
Hisham Muhammad committed
56
      return 0;
57
58
59
60
61
62

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

Hisham Muhammad's avatar
Hisham Muhammad committed
63
64
   list *myList = NULL;
   list *newEntry;
65

Hisham Muhammad's avatar
Hisham Muhammad committed
66
   /*
67
68
69
      Some of this is based off of code found in kismet (they claim it came from gkrellm).
      Written for multi battery use...
    */
Hisham Muhammad's avatar
Hisham Muhammad committed
70
71
   for (const struct dirent* dirEntries = readdir((DIR *) batteryDir); dirEntries; dirEntries = readdir((DIR *) batteryDir)) {
      char* entryName = (char *) dirEntries->d_name;
72
73

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

Hisham Muhammad's avatar
Hisham Muhammad committed
76
77
      newEntry = calloc(1, sizeof(list));
      newEntry->next = myList;
78
      newEntry->content = entryName;
Hisham Muhammad's avatar
Hisham Muhammad committed
79
80
81
82
83
84
      myList = newEntry;
   }

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
90
      if ((file = fopen(infoPath, "r")) == NULL) {
91
         closedir(batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
92
93
94
         return 0;
      }

95
96
      for (unsigned short int i = 0; i < lineNum; i++) {
         fgets(line, sizeof line, (FILE *) file);
Hisham Muhammad's avatar
Hisham Muhammad committed
97
      }
98
99
100
101

      fclose((FILE *) file);

      const char *foundNumTmp = String_getToken(line, wordNum);
Hisham Muhammad's avatar
Hisham Muhammad committed
102
      const unsigned long int foundNum = atoi(foundNumTmp);
103
      free((char *) foundNumTmp);
Hisham Muhammad's avatar
Hisham Muhammad committed
104
105
106
107
108
109

      total += foundNum;
   }

   free(myList);
   free(newEntry);
110
   closedir(batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
111
112
113
   return total;
}

114
115
116
117
118
static ACPresence chkIsOnline() {
   FILE *file = NULL;
   ACPresence isOn = AC_ERROR;

   if (access(PROCDIR "/acpi/ac_adapter", F_OK) == 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
119
      const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
120
      DIR *power_supplyDir = opendir(power_supplyPath);
Hisham Muhammad's avatar
Hisham Muhammad committed
121
      if (!power_supplyDir)
122
123
         return AC_ERROR;

Hisham Muhammad's avatar
Hisham Muhammad committed
124
125
      for (const struct dirent *dirEntries = readdir((DIR *) power_supplyDir); dirEntries; dirEntries = readdir((DIR *) power_supplyDir)) {
         char* entryName = (char *) dirEntries->d_name;
126

Hisham Muhammad's avatar
Hisham Muhammad committed
127
         if (entryName[0] != 'A')
128
129
130
            continue;

         char statePath[50];
131
         snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
         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
167
      const char *power_supplyPath = "/sys/class/power_supply";
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

      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
184

185
            char onlinePath[50];
186
            snprintf((char *) onlinePath, sizeof onlinePath, "%s/%s/online", power_supplyPath, entryName);
187
            file = fopen(onlinePath, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
188

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
            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
222
   const unsigned long int totalFull = parseBatInfo("info", 3, 4);
223
224
225
   if (totalFull == 0)
      return 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
226
   const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
227
228
229
230
231
232
233
234
235
   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
236
   const char *power_supplyPath = "/sys/class/power_supply/";
237
   DIR *power_supplyDir = opendir(power_supplyPath);
Hisham Muhammad's avatar
Hisham Muhammad committed
238
   if (!power_supplyDir)
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
      return 0;

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

255
      snprintf((char *) ueventPath, sizeof ueventPath, "%s%s/uevent", power_supplyPath, entryName);
256
257
258
259
260
261
262

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

263
264
265
266
267
      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) {
268
            closedir(power_supplyDir);
269
270
271
272
273
274
275
276
277
278
279
            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) {
280
            closedir(power_supplyDir);
281
282
283
284
285
            fclose(file);
            return 0;
         }
      }

286
287
288
289
290
291
292
293
294
295
      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();
296

297
298
299
300
301
302
303
304
   if (percent == 0) {
      percent = getSysBatData();
      if (percent == 0) {
         snprintf(buffer, len, "n/a");
         return;
      }
   }

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

Hisham Muhammad's avatar
Hisham Muhammad committed
307
   const char *onAcText, *onBatteryText, *unknownText;
308
309

   unknownText = "%.1f%%";
Hisham Muhammad's avatar
Hisham Muhammad committed
310
311
312
313
314
315
316
317
   if (this->mode == TEXT_METERMODE) {
      onAcText = "%.1f%% (Running on A/C)";
      onBatteryText = "%.1f%% (Running on battery)";
   } else {
      onAcText = "%.1f%%(A/C)";
      onBatteryText = "%.1f%%(bat)";
   }

318
319
320
   ACPresence isOnLine = chkIsOnline();

   if (isOnLine == AC_PRESENT) {
Hisham Muhammad's avatar
Hisham Muhammad committed
321
      snprintf(buffer, len, onAcText, percent);
322
323
   } else if (isOnLine == AC_ABSENT) {
      snprintf(buffer, len, onBatteryText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
324
   } else {
325
      snprintf(buffer, len, unknownText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
326
327
328
329
330
   }

   return;
}

331
332
333
334
335
MeterClass BatteryMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
336
   .setValues = BatteryMeter_setValues,
337
   .defaultMode = TEXT_METERMODE,
Hisham Muhammad's avatar
Hisham Muhammad committed
338
339
340
341
342
343
   .total = 100.0,
   .attributes = BatteryMeter_attributes,
   .name = "Battery",
   .uiName = "Battery",
   .caption = "Battery: "
};