BatteryMeter.c 8.17 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
   char line[100];
   unsigned long int dValue = 0;
39
   char* saveptr;
40
41
42
43

   while (fgets(line, sizeof line, file)) {
      if (strncmp(line, key, strlen(key)) == 0) {
         char *value;
44
45
         strtok_r(line, "=", &saveptr);
         value = strtok_r(NULL, "=", &saveptr);
46
47
48
49
50
51
52
53
         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
54
   const char batteryPath[] = PROCDIR "/acpi/battery/";
Hisham Muhammad's avatar
Hisham Muhammad committed
55
56
   DIR* batteryDir = opendir(batteryPath);
   if (!batteryDir)
Hisham Muhammad's avatar
Hisham Muhammad committed
57
      return 0;
58

Hisham Muhammad's avatar
Hisham Muhammad committed
59
60
   #define MAX_BATTERIES 64
   char* batteries[MAX_BATTERIES];
61
   unsigned int nBatteries = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
62
   memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
63

64
65
   struct dirent result;
   struct dirent* dirEntry;
Hisham Muhammad's avatar
Hisham Muhammad committed
66
   while (nBatteries < MAX_BATTERIES) {
Hisham Muhammad's avatar
Hisham Muhammad committed
67
68
      int err = readdir_r(batteryDir, &result, &dirEntry);
      if (err || !dirEntry)
69
70
         break;
      char* entryName = dirEntry->d_name;
71
      if (strncmp(entryName, "BAT", 3))
Hisham Muhammad's avatar
Hisham Muhammad committed
72
         continue;
73
74
      batteries[nBatteries] = strdup(entryName);
      nBatteries++;
Hisham Muhammad's avatar
Hisham Muhammad committed
75
   }
76
   closedir(batteryDir);
Hisham Muhammad's avatar
Hisham Muhammad committed
77
78

   unsigned long int total = 0;
79
80
81
   for (unsigned int i = 0; i < nBatteries; i++) {
      char infoPath[30];
      snprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
82

83
84
85
      FILE* file = fopen(infoPath, "r");
      if (!file) {
         break;
Hisham Muhammad's avatar
Hisham Muhammad committed
86
87
      }

Hisham Muhammad's avatar
Hisham Muhammad committed
88
      char line[50] = "";
89
      for (unsigned short int i = 0; i < lineNum; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
90
91
         char* ok = fgets(line, sizeof line, file);
         if (!ok) break;
Hisham Muhammad's avatar
Hisham Muhammad committed
92
      }
93

94
      fclose(file);
95

96
97
98
      char *foundNumStr = String_getToken(line, wordNum);
      const unsigned long int foundNum = atoi(foundNumStr);
      free(foundNumStr);
Hisham Muhammad's avatar
Hisham Muhammad committed
99
100
101
102

      total += foundNum;
   }

103
104
105
106
   for (unsigned int i = 0; i < nBatteries; i++) {
      free(batteries[i]);
   }

Hisham Muhammad's avatar
Hisham Muhammad committed
107
108
109
   return total;
}

110
static ACPresence procAcpiCheck() {
111
   ACPresence isOn = AC_ERROR;
112
113
114
115
116
   const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
   DIR *power_supplyDir = opendir(power_supplyPath);
   if (!power_supplyDir) {
      return AC_ERROR;
   }
117

118
119
120
   struct dirent result;
   struct dirent* dirEntry;
   for (;;) {
Hisham Muhammad's avatar
Hisham Muhammad committed
121
122
      int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
      if (err || !dirEntry)
123
         break;
124

125
      char* entryName = (char *) dirEntry->d_name;
126

127
128
      if (entryName[0] != 'A')
         continue;
129

130
131
132
      char statePath[50];
      snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
      FILE* file = fopen(statePath, "r");
133

134
135
136
      if (!file) {
         isOn = AC_ERROR;
         continue;
137
138
      }

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

143
      fclose(file);
144

145
      const char *isOnline = String_getToken(line, 2);
Hisham Muhammad's avatar
Hisham Muhammad committed
146

147
148
149
150
151
152
153
154
155
156
      if (strcmp(isOnline, "on-line") == 0) {
         isOn = AC_PRESENT;
      } else {
         isOn = AC_ABSENT;
      }
      free((char *) isOnline);
      if (isOn == AC_PRESENT) {
         break;
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
157

158
159
160
161
   if (power_supplyDir)
      closedir(power_supplyDir);
   return isOn;
}
162

163
164
165
166
167
168
169
static ACPresence sysCheck() {
   ACPresence isOn = AC_ERROR;
   const char *power_supplyPath = "/sys/class/power_supply";
   DIR *power_supplyDir = opendir(power_supplyPath);
   if (!power_supplyDir) {
      return AC_ERROR;
   }
170

171
172
173
   struct dirent result;
   struct dirent* dirEntry;
   for (;;) {
Hisham Muhammad's avatar
Hisham Muhammad committed
174
175
      int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
      if (err || !dirEntry)
176
         break;
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
      char* entryName = (char *) dirEntry->d_name;
      if (strncmp(entryName, "A", 1)) {
         continue;
      }
      char onlinePath[50];
      snprintf((char *) onlinePath, sizeof onlinePath, "%s/%s/online", power_supplyPath, entryName);
      FILE* file = fopen(onlinePath, "r");
      if (!file) {
         isOn = AC_ERROR;
      } else {
         isOn = (fgetc(file) - '0');
         fclose(file);
         if (isOn == AC_PRESENT) {
            // If any AC adapter is being used then stop
            break;
193
194
195
196
         }
      }
   }

197
198
   if (power_supplyDir)
      closedir(power_supplyDir);
199
200
201
202

   return isOn;
}

203
204
205
206
207
208
209
210
211
212
static ACPresence chkIsOnline() {
   if (access(PROCDIR "/acpi/ac_adapter", F_OK) == 0) {
      return procAcpiCheck();
   } else if (access("/sys/class/power_supply", F_OK) == 0) {
      return sysCheck();
   } else {
      return AC_ERROR;
   }
}

213
static double getProcBatData() {
Hisham Muhammad's avatar
Hisham Muhammad committed
214
   const unsigned long int totalFull = parseBatInfo("info", 3, 4);
215
216
217
   if (totalFull == 0)
      return 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
218
   const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
219
220
221
   if (totalRemain == 0)
      return 0;

Hisham Muhammad's avatar
Hisham Muhammad committed
222
   return totalRemain * 100.0 / (double) totalFull;
223
224
225
}

static double getSysBatData() {
Hisham Muhammad's avatar
Hisham Muhammad committed
226
   const char *power_supplyPath = "/sys/class/power_supply/";
227
   DIR *power_supplyDir = opendir(power_supplyPath);
Hisham Muhammad's avatar
Hisham Muhammad committed
228
   if (!power_supplyDir)
229
230
231
232
233
      return 0;

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

234
235
236
   struct dirent result;
   struct dirent* dirEntry;
   for (;;) {
Hisham Muhammad's avatar
Hisham Muhammad committed
237
238
      int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
      if (err || !dirEntry)
239
240
         break;
      char* entryName = (char *) dirEntry->d_name;
241
242
243
244
245
246
247

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

      const char ueventPath[50];

248
      snprintf((char *) ueventPath, sizeof ueventPath, "%s%s/uevent", power_supplyPath, entryName);
249
250
251
252
253
254
255

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

256
257
258
259
260
      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) {
261
            closedir(power_supplyDir);
262
263
264
265
266
267
268
269
270
271
272
            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) {
273
            closedir(power_supplyDir);
274
275
276
277
278
            fclose(file);
            return 0;
         }
      }

279
280
281
282
283
284
285
286
287
288
      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();
289

290
291
292
293
294
295
296
297
   if (percent == 0) {
      percent = getSysBatData();
      if (percent == 0) {
         snprintf(buffer, len, "n/a");
         return;
      }
   }

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

Hisham Muhammad's avatar
Hisham Muhammad committed
300
   const char *onAcText, *onBatteryText, *unknownText;
301
302

   unknownText = "%.1f%%";
Hisham Muhammad's avatar
Hisham Muhammad committed
303
304
305
306
307
308
309
310
   if (this->mode == TEXT_METERMODE) {
      onAcText = "%.1f%% (Running on A/C)";
      onBatteryText = "%.1f%% (Running on battery)";
   } else {
      onAcText = "%.1f%%(A/C)";
      onBatteryText = "%.1f%%(bat)";
   }

311
312
313
   ACPresence isOnLine = chkIsOnline();

   if (isOnLine == AC_PRESENT) {
Hisham Muhammad's avatar
Hisham Muhammad committed
314
      snprintf(buffer, len, onAcText, percent);
315
316
   } else if (isOnLine == AC_ABSENT) {
      snprintf(buffer, len, onBatteryText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
317
   } else {
318
      snprintf(buffer, len, unknownText, percent);
Hisham Muhammad's avatar
Hisham Muhammad committed
319
320
321
322
323
   }

   return;
}

324
325
326
327
328
MeterClass BatteryMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
329
   .setValues = BatteryMeter_setValues,
330
   .defaultMode = TEXT_METERMODE,
Hisham Muhammad's avatar
Hisham Muhammad committed
331
332
333
334
335
336
   .total = 100.0,
   .attributes = BatteryMeter_attributes,
   .name = "Battery",
   .uiName = "Battery",
   .caption = "Battery: "
};