diff --git a/BatteryMeter.c b/BatteryMeter.c index 977a1919..b997e81c 100644 --- a/BatteryMeter.c +++ b/BatteryMeter.c @@ -4,7 +4,7 @@ Released under the GNU GPL, see the COPYING file in the source distribution for its full text. - This "Meter" written by Ian P. Hands (iphands@gmail.com). + This "Meter" written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). */ #include "BatteryMeter.h" @@ -14,102 +14,289 @@ #include "String.h" #include "debug.h" +/*{ + +typedef enum ACPresence_ { + AC_ABSENT, + AC_PRESENT, + AC_ERROR +} ACPresence; + +}*/ + int BatteryMeter_attributes[] = { BATTERY }; -static unsigned long int parseBatInfo(const char * fileName, const unsigned short int lineNum, const unsigned short int wordNum) { +static unsigned long int parseUevent(FILE * file, char *key) { + char line[100]; + unsigned long int dValue = 0; + + while (fgets(line, sizeof line, file)) { + if (strncmp(line, key, strlen(key)) == 0) { + char *value; + value = strtok(line, "="); + 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) { const DIR *batteryDir; - const struct dirent *pDirEnt; - + const struct dirent *dirEntries; + const char batteryPath[] = PROCDIR "/acpi/battery/"; batteryDir = opendir(batteryPath); - + if (batteryDir == NULL) { return 0; } - - char * string; - typedef struct listLbl { char* content; struct listLbl* next; } list; - + + char *entryName; + typedef struct listLbl { + char *content; + struct listLbl *next; + } list; + list *myList = NULL; list *newEntry; - + /* - Some of this is based off of code found in kismet (they claim it came from gkrellm). - Written for multi battery use... - */ - for (pDirEnt= readdir((DIR*)batteryDir); pDirEnt; pDirEnt = readdir((DIR*)batteryDir)) { - string = (char*)pDirEnt->d_name; - if(!strcmp(string, ".") || !strcmp(string, "..")) + 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)) continue; - + newEntry = calloc(1, sizeof(list)); newEntry->next = myList; - newEntry->content = string; + newEntry->content = entryName; myList = newEntry; } unsigned long int total = 0; for (newEntry = myList; newEntry; newEntry = newEntry->next) { const char infoPath[30]; - const FILE * file; - char line[50]; - - sprintf((char*)infoPath, "%s%s/%s", batteryPath, newEntry->content, fileName); - + const FILE *file; + char line[50]; + + sprintf((char *) infoPath, "%s%s/%s", batteryPath, newEntry->content, fileName); + if ((file = fopen(infoPath, "r")) == NULL) { return 0; } - for (unsigned short int i = 0; i < lineNum; i++){ - fgets(line, sizeof line, (FILE*)file); + for (unsigned short int i = 0; i < lineNum; i++) { + fgets(line, sizeof line, (FILE *) file); } - - fclose((FILE*)file); - - const char * foundNumTmp = String_getToken(line, wordNum); + + fclose((FILE *) file); + + const char *foundNumTmp = String_getToken(line, wordNum); const unsigned long int foundNum = atoi(foundNumTmp); - free((char*)foundNumTmp); + free((char *) foundNumTmp); total += foundNum; } free(myList); free(newEntry); - closedir((DIR*)batteryDir); + closedir((DIR *) batteryDir); return total; } -static void BatteryMeter_setValues(Meter* this, char* buffer, int len) { - FILE* file = fopen(PROCDIR "/acpi/ac_adapter/AC/state", "r"); - if (!file) - file = fopen(PROCDIR "/acpi/ac_adapter/ADP1/state", "r"); - if (!file) { - snprintf(buffer, len, "n/a"); - return; - } +static ACPresence chkIsOnline() { + FILE *file = NULL; + ACPresence isOn = AC_ERROR; - char line [100]; - fgets(line, sizeof line, file); - line[sizeof(line) - 1] = '\0'; - fclose(file); + if (access(PROCDIR "/acpi/ac_adapter", F_OK) == 0) { + const struct dirent *dirEntries; + char *power_supplyPath = PROCDIR "/acpi/ac_adapter"; + DIR *power_supplyDir = opendir(power_supplyPath); + char *entryName; - const unsigned long int totalFull = parseBatInfo("info", 3, 4); - const unsigned long int totalRemain = parseBatInfo("state", 5, 3); - const double percent = totalFull > 0 ? ((double)totalRemain * 100) / (double)totalFull : 0; - - if (totalFull == 0) { - snprintf(buffer, len, "n/a"); - return; + if (!power_supplyDir) { + closedir(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]; + sprintf((char *) statePath, "%s/%s/state", power_supplyPath, entryName); + 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 { + + char *power_supplyPath = "/sys/class/power_supply"; + + 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; + } + + char onlinePath[50]; + sprintf((char *) onlinePath, "%s/%s/online", power_supplyPath, entryName); + file = fopen(onlinePath, "r"); + + 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() { + const unsigned long int totalFull = parseBatInfo("info", 3, 4); + if (totalFull == 0) + return 0; + + const unsigned long int totalRemain = parseBatInfo("state", 5, 3); + if (totalRemain == 0) + return 0; + + double percent = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0; + + return percent; +} + +static double getSysBatData() { + const struct dirent *dirEntries; + char *power_supplyPath = "/sys/class/power_supply/"; + DIR *power_supplyDir = opendir(power_supplyPath); + + + if (!power_supplyDir) { + closedir(power_supplyDir); + return 0; + } + + 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]; + + sprintf((char *) ueventPath, "%s%s/uevent", power_supplyPath, entryName); + + FILE *file; + if ((file = fopen(ueventPath, "r")) == NULL) { + closedir(power_supplyDir); + return 0; + } + + totalFull += parseUevent(file, "POWER_SUPPLY_ENERGY_FULL="); + totalRemain += parseUevent(file, "POWER_SUPPLY_ENERGY_NOW="); + 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(); + if (percent == 0) { + percent = getSysBatData(); + if (percent == 0) { + snprintf(buffer, len, "n/a"); + return; + } + } + this->values[0] = percent; - const char* isOnline = String_getToken(line, 2); - - char *onAcText, *onBatteryText; - + char *onAcText, *onBatteryText, *unknownText; + + unknownText = "%.1f%%"; if (this->mode == TEXT_METERMODE) { onAcText = "%.1f%% (Running on A/C)"; onBatteryText = "%.1f%% (Running on battery)"; @@ -118,18 +305,21 @@ static void BatteryMeter_setValues(Meter* this, char* buffer, int len) { onBatteryText = "%.1f%%(bat)"; } - if (strcmp(String_getToken(line, 2),"on-line") == 0) { + ACPresence isOnLine = chkIsOnline(); + + if (isOnLine == AC_PRESENT) { snprintf(buffer, len, onAcText, percent); + } else if (isOnLine == AC_ABSENT) { + snprintf(buffer, len, onBatteryText, percent); } else { - snprintf(buffer, len, onBatteryText, percent); + snprintf(buffer, len, unknownText, percent); } - free((char*)isOnline); return; } MeterType BatteryMeter = { - .setValues = BatteryMeter_setValues, + .setValues = BatteryMeter_setValues, .display = NULL, .mode = TEXT_METERMODE, .items = 1,