BatteryMeter.c 8.62 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
/*
  htop
Hisham Muhammad's avatar
Hisham Muhammad committed
3
  (C) 2004-2011 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) {
48
   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
      if ((file = fopen(infoPath, "r")) == NULL) {
92
         closedir(batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
93
94
95
         return 0;
      }

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

      fclose((FILE *) file);

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

      total += foundNum;
   }

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

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

      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
191

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

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


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

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

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

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

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

296
297
298
299
300
301
302
303
304
305
      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();
306

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
317
   const char *onAcText, *onBatteryText, *unknownText;
318
319

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

328
329
330
   ACPresence isOnLine = chkIsOnline();

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

   return;
}

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