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

7
  This "Meter" written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
Hisham Muhammad's avatar
Hisham Muhammad committed
8
9
10
11
12
13
14
15
16
*/

#include "BatteryMeter.h"
#include "Meter.h"
#include "ProcessList.h"
#include "CRT.h"
#include "String.h"
#include "debug.h"

17
18
19
20
21
22
23
24
25
26
/*{

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

}*/

Hisham Muhammad's avatar
Hisham Muhammad committed
27
28
29
30
int BatteryMeter_attributes[] = {
   BATTERY
};

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

Hisham Muhammad's avatar
Hisham Muhammad committed
51
52
   const char batteryPath[] = PROCDIR "/acpi/battery/";
   batteryDir = opendir(batteryPath);
53

Hisham Muhammad's avatar
Hisham Muhammad committed
54
55
56
   if (batteryDir == NULL) {
      return 0;
   }
57
58
59
60
61
62
63

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
67
   /*
68
69
70
71
72
73
74
      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
75
         continue;
76

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

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
91
92
93
94
      if ((file = fopen(infoPath, "r")) == NULL) {
         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((DIR *) batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
111
112
113
   return total;
}

114
115
116
117
118
119
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
120
      const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
      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];
137
         snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
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
167
168
169
170
171
172
         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
173
      const char *power_supplyPath = "/sys/class/power_supply";
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

      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
190

191
            char onlinePath[50];
192
            snprintf((char *) onlinePath, sizeof onlinePath, "%s/%s/online", power_supplyPath, entryName);
193
            file = fopen(onlinePath, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
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
222
223
224
225
226
227
            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
228
   const unsigned long int totalFull = parseBatInfo("info", 3, 4);
229
230
231
   if (totalFull == 0)
      return 0;

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


   if (!power_supplyDir) {
      return 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
248
   }
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

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

264
      snprintf((char *) ueventPath, sizeof ueventPath, "%s%s/uevent", power_supplyPath, entryName);
265
266
267
268
269
270
271

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

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
      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) {
            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) {
            fclose(file);
            return 0;
         }
      }

293
294
295
296
297
298
299
300
301
302
      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();
303

304
305
306
307
308
309
310
311
   if (percent == 0) {
      percent = getSysBatData();
      if (percent == 0) {
         snprintf(buffer, len, "n/a");
         return;
      }
   }

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

Hisham Muhammad's avatar
Hisham Muhammad committed
314
   const char *onAcText, *onBatteryText, *unknownText;
315
316

   unknownText = "%.1f%%";
Hisham Muhammad's avatar
Hisham Muhammad committed
317
318
319
320
321
322
323
324
   if (this->mode == TEXT_METERMODE) {
      onAcText = "%.1f%% (Running on A/C)";
      onBatteryText = "%.1f%% (Running on battery)";
   } else {
      onAcText = "%.1f%%(A/C)";
      onBatteryText = "%.1f%%(bat)";
   }

325
326
327
   ACPresence isOnLine = chkIsOnline();

   if (isOnLine == AC_PRESENT) {
Hisham Muhammad's avatar
Hisham Muhammad committed
328
      snprintf(buffer, len, onAcText, percent);
329
330
   } else if (isOnLine == AC_ABSENT) {
      snprintf(buffer, len, onBatteryText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
331
   } else {
332
      snprintf(buffer, len, unknownText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
333
334
335
336
337
338
   }

   return;
}

MeterType BatteryMeter = {
339
   .setValues = BatteryMeter_setValues,
Hisham Muhammad's avatar
Hisham Muhammad committed
340
341
342
343
344
345
346
347
348
   .display = NULL,
   .mode = TEXT_METERMODE,
   .items = 1,
   .total = 100.0,
   .attributes = BatteryMeter_attributes,
   .name = "Battery",
   .uiName = "Battery",
   .caption = "Battery: "
};