BatteryMeter.c 8.06 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) {
67
68
69
70
      readdir_r(batteryDir, &result, &dirEntry);
      if (!dirEntry)
         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
      }

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

93
      fclose(file);
94

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

      total += foundNum;
   }

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

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

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

117
118
119
120
121
122
   struct dirent result;
   struct dirent* dirEntry;
   for (;;) {
      readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
      if (!dirEntry)
         break;
123

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

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

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

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

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

142
      fclose(file);
143

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

146
147
148
149
150
151
152
153
154
155
      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
156

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

162
163
164
165
166
167
168
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;
   }
169

170
171
172
173
174
175
   struct dirent result;
   struct dirent* dirEntry;
   for (;;) {
      readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
      if (!dirEntry)
         break;
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
      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;
192
193
194
195
         }
      }
   }

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

   return isOn;
}

202
203
204
205
206
207
208
209
210
211
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;
   }
}

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

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

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

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

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

233
234
235
236
237
238
239
   struct dirent result;
   struct dirent* dirEntry;
   for (;;) {
      readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
      if (!dirEntry)
         break;
      char* entryName = (char *) dirEntry->d_name;
240
241
242
243
244
245
246

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

      const char ueventPath[50];

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

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

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

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

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

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

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

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

310
311
312
   ACPresence isOnLine = chkIsOnline();

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

   return;
}

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