BatteryMeter.c 8.03 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

59
60
61
   char* batteries[64];
   unsigned int nBatteries = 0;
   memset(batteries, sizeof batteries, sizeof (char*));
62

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

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

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

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

92
      fclose(file);
93

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

      total += foundNum;
   }

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

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

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

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

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

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

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

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

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

141
      fclose(file);
142

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

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

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

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

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

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

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

   return isOn;
}

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

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

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

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

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

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

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

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

      const char ueventPath[50];

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

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

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

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

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

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

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

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

309
310
311
   ACPresence isOnLine = chkIsOnline();

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

   return;
}

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