]> git.sur5r.net Git - i3/i3status/blobdiff - src/print_battery_info.c
Move time and ratio computations out of slurp_battery_info. (#151)
[i3/i3status] / src / print_battery_info.c
index f548dfaec4088833ba429fbee8ae239498215cc6..c7948d05a1c072a56dbd53bfcd00de121bd40a0d 100644 (file)
 #endif
 
 struct battery_info {
+    int full_design;
+    int full_last;
+    int remaining;
+
     int present_rate;
     int seconds_remaining;
     float percentage_remaining;
     charging_status_t status;
 };
 
-/*
- * Estimate the number of seconds remaining in state 'status'.
- *
- * Assumes a constant (dis)charge rate.
- */
-static int seconds_remaining_from_rate(charging_status_t status, float full_design, float remaining, float present_rate) {
-    if (status == CS_CHARGING)
-        return 3600.0 * (full_design - remaining) / present_rate;
-    else if (status == CS_DISCHARGING)
-        return 3600.0 * remaining / present_rate;
-    else
-        return 0;
-}
+static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen, char *buffer, int number, const char *path, const char *format_down) {
+    char *outwalk = buffer;
 
-static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen, char *buffer, int number, const char *path, const char *format_down, bool last_full_capacity) {
+#if defined(LINUX)
     char buf[1024];
     const char *walk, *last;
-    char *outwalk = buffer;
     bool watt_as_unit = false;
-    int full_design = -1,
-        remaining = -1,
-        voltage = -1;
-
-#if defined(LINUX)
+    int voltage = -1;
     char batpath[512];
     sprintf(batpath, path, number);
     INSTANCE(batpath);
@@ -79,10 +67,10 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
 
         if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_NOW")) {
             watt_as_unit = true;
-            remaining = atoi(walk + 1);
+            batt_info->remaining = atoi(walk + 1);
         } else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_NOW")) {
             watt_as_unit = false;
-            remaining = atoi(walk + 1);
+            batt_info->remaining = atoi(walk + 1);
         } else if (BEGINS_WITH(last, "POWER_SUPPLY_CURRENT_NOW"))
             batt_info->present_rate = abs(atoi(walk + 1));
         else if (BEGINS_WITH(last, "POWER_SUPPLY_VOLTAGE_NOW"))
@@ -101,52 +89,22 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
             batt_info->status = CS_DISCHARGING;
         else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS="))
             batt_info->status = CS_UNKNOWN;
-        else {
-            /* The only thing left is the full capacity */
-            if (last_full_capacity) {
-                if (!BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") &&
-                    !BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL"))
-                    continue;
-            } else {
-                if (!BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") &&
-                    !BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN"))
-                    continue;
-            }
-
-            full_design = atoi(walk + 1);
-        }
+        else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") ||
+                 BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN"))
+            batt_info->full_design = atoi(walk + 1);
+        else if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") ||
+                 BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL"))
+            batt_info->full_last = atoi(walk + 1);
     }
 
     /* the difference between POWER_SUPPLY_ENERGY_NOW and
      * POWER_SUPPLY_CHARGE_NOW is the unit of measurement. The energy is
      * given in mWh, the charge in mAh. So calculate every value given in
      * ampere to watt */
-    if (!watt_as_unit) {
+    if (!watt_as_unit && voltage != -1) {
         batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0));
-
-        if (voltage != -1) {
-            remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
-            full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
-        }
-    }
-
-    if ((full_design == -1) || (remaining == -1)) {
-        OUTPUT_FULL_TEXT(format_down);
-        return false;
-    }
-
-    batt_info->percentage_remaining = (((float)remaining / (float)full_design) * 100);
-    /* Some batteries report POWER_SUPPLY_CHARGE_NOW=<full_design> when fully
-     * charged, even though that’s plainly wrong. For people who chose to see
-     * the percentage calculated based on the last full capacity, we clamp the
-     * value to 100%, as that makes more sense.
-     * See http://bugs.debian.org/785398 */
-    if (last_full_capacity && batt_info->percentage_remaining > 100) {
-        batt_info->percentage_remaining = 100;
-    }
-
-    if (batt_info->present_rate > 0 && batt_info->status != CS_FULL) {
-        batt_info->seconds_remaining = seconds_remaining_from_rate(batt_info->status, full_design, remaining, batt_info->present_rate);
+        batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0));
+        batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0));
     }
 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
     int state;
@@ -177,8 +135,6 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
         batt_info->status = CS_CHARGING;
     else
         batt_info->status = CS_DISCHARGING;
-
-    full_design = sysctl_rslt;
 #elif defined(__OpenBSD__)
     /*
         * We're using apm(4) here, which is the interface to acpi(4) on amd64/i386 and
@@ -228,7 +184,9 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
     /*
      * Using envsys(4) via sysmon(4).
      */
-    int fd, rval, last_full_cap;
+    bool watt_as_unit = false;
+    int voltage = -1;
+    int fd, rval;
     bool is_found = false;
     char *sensor_desc;
     bool is_full = false;
@@ -268,10 +226,8 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
     /* iterate over the dictionary returned by the kernel */
     while ((obj = prop_object_iterator_next(iter)) != NULL) {
         /* skip this dict if it's not what we're looking for */
-        if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) == strlen(sensor_desc)) &&
-            (strncmp(sensor_desc,
-                     prop_dictionary_keysym_cstring_nocopy(obj),
-                     strlen(sensor_desc)) != 0))
+        if (strcmp(sensor_desc,
+                   prop_dictionary_keysym_cstring_nocopy(obj)) != 0)
             continue;
 
         is_found = true;
@@ -296,74 +252,43 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
         while ((obj2 = prop_object_iterator_next(iter2)) != NULL) {
             obj3 = prop_dictionary_get(obj2, "description");
 
-            if (obj3 &&
-                strlen(prop_string_cstring_nocopy(obj3)) == 8 &&
-                strncmp("charging",
-                        prop_string_cstring_nocopy(obj3),
-                        8) == 0) {
+            if (obj3 == NULL)
+                continue;
+
+            if (strcmp("charging", prop_string_cstring_nocopy(obj3)) == 0) {
                 obj3 = prop_dictionary_get(obj2, "cur-value");
 
                 if (prop_number_integer_value(obj3))
                     batt_info->status = CS_CHARGING;
                 else
                     batt_info->status = CS_DISCHARGING;
-
-                continue;
-            }
-
-            if (obj3 &&
-                strlen(prop_string_cstring_nocopy(obj3)) == 6 &&
-                strncmp("charge",
-                        prop_string_cstring_nocopy(obj3),
-                        6) == 0) {
+            } else if (strcmp("charge", prop_string_cstring_nocopy(obj3)) == 0) {
                 obj3 = prop_dictionary_get(obj2, "cur-value");
                 obj4 = prop_dictionary_get(obj2, "max-value");
                 obj5 = prop_dictionary_get(obj2, "type");
 
-                remaining = prop_number_integer_value(obj3);
-                full_design = prop_number_integer_value(obj4);
+                batt_info->remaining = prop_number_integer_value(obj3);
+                batt_info->full_design = prop_number_integer_value(obj4);
 
-                if (remaining == full_design)
+                if (batt_info->remaining == batt_info->full_design)
                     is_full = true;
 
-                if (strncmp("Ampere hour",
-                            prop_string_cstring_nocopy(obj5),
-                            11) == 0)
+                if (strcmp("Ampere hour", prop_string_cstring_nocopy(obj5)) == 0)
                     watt_as_unit = false;
                 else
                     watt_as_unit = true;
-
-                continue;
-            }
-
-            if (obj3 &&
-                strlen(prop_string_cstring_nocopy(obj3)) == 14 &&
-                strncmp("discharge rate",
-                        prop_string_cstring_nocopy(obj3),
-                        14) == 0) {
+            } else if (strcmp("discharge rate", prop_string_cstring_nocopy(obj3)) == 0) {
                 obj3 = prop_dictionary_get(obj2, "cur-value");
                 batt_info->present_rate = prop_number_integer_value(obj3);
-                continue;
-            }
-
-            if (obj3 &&
-                strlen(prop_string_cstring_nocopy(obj3)) == 13 &&
-                strncmp("last full cap",
-                        prop_string_cstring_nocopy(obj3),
-                        13) == 0) {
+            } else if (strcmp("charge rate", prop_string_cstring_nocopy(obj3)) == 0) {
                 obj3 = prop_dictionary_get(obj2, "cur-value");
-                last_full_cap = prop_number_integer_value(obj3);
-                continue;
-            }
-
-            if (obj3 &&
-                strlen(prop_string_cstring_nocopy(obj3)) == 7 &&
-                strncmp("voltage",
-                        prop_string_cstring_nocopy(obj3),
-                        7) == 0) {
+                batt_info->present_rate = prop_number_integer_value(obj3);
+            } else if (strcmp("last full cap", prop_string_cstring_nocopy(obj3)) == 0) {
+                obj3 = prop_dictionary_get(obj2, "cur-value");
+                batt_info->full_last = prop_number_integer_value(obj3);
+            } else if (strcmp("voltage", prop_string_cstring_nocopy(obj3)) == 0) {
                 obj3 = prop_dictionary_get(obj2, "cur-value");
                 voltage = prop_number_integer_value(obj3);
-                continue;
             }
         }
         prop_object_iterator_release(iter2);
@@ -378,26 +303,14 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
         return false;
     }
 
-    if (last_full_capacity)
-        full_design = last_full_cap;
-
-    if (!watt_as_unit) {
+    if (!watt_as_unit && voltage != -1) {
         batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0));
-        remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
-        full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
+        batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0));
+        batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0));
     }
 
-    batt_info->percentage_remaining =
-        (((float)remaining / (float)full_design) * 100);
-
     if (is_full)
         batt_info->status = CS_FULL;
-
-    /*
-     * The envsys(4) ACPI routines do not appear to provide a 'time
-     * remaining' figure, so we must deduce it.
-     */
-    batt_info->seconds_remaining = seconds_remaining_from_rate(batt_info->status, full_design, remaining, batt_info->present_rate);
 #endif
 
     return true;
@@ -407,6 +320,8 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
     const char *walk;
     char *outwalk = buffer;
     struct battery_info batt_info = {
+        .full_design = -1,
+        .full_last = -1,
         .present_rate = -1,
         .seconds_remaining = -1,
         .percentage_remaining = -1,
@@ -423,8 +338,37 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
     hide_seconds = true;
 #endif
 
-    if (!slurp_battery_info(&batt_info, json_gen, buffer, number, path, format_down, last_full_capacity))
+    if (!slurp_battery_info(&batt_info, json_gen, buffer, number, path, format_down))
+        return;
+
+    int full = (last_full_capacity ? batt_info.full_last : batt_info.full_design);
+    if (full < 0 && batt_info.percentage_remaining < 0) {
+        /* We have no physical measurements and no estimates. Nothing
+         * much we can report, then. */
+        OUTPUT_FULL_TEXT(format_down);
         return;
+    }
+
+    if (batt_info.percentage_remaining < 0) {
+        batt_info.percentage_remaining = (((float)batt_info.remaining / (float)full) * 100);
+        /* Some batteries report POWER_SUPPLY_CHARGE_NOW=<full_design> when fully
+         * charged, even though that’s plainly wrong. For people who chose to see
+         * the percentage calculated based on the last full capacity, we clamp the
+         * value to 100%, as that makes more sense.
+         * See http://bugs.debian.org/785398 */
+        if (last_full_capacity && batt_info.percentage_remaining > 100) {
+            batt_info.percentage_remaining = 100;
+        }
+    }
+
+    if (batt_info.seconds_remaining < 0 && batt_info.present_rate > 0 && batt_info.status != CS_FULL) {
+        if (batt_info.status == CS_CHARGING)
+            batt_info.seconds_remaining = 3600.0 * (full - batt_info.remaining) / batt_info.present_rate;
+        else if (batt_info.status == CS_DISCHARGING)
+            batt_info.seconds_remaining = 3600.0 * batt_info.remaining / batt_info.present_rate;
+        else
+            batt_info.seconds_remaining = 0;
+    }
 
     if (batt_info.status == CS_DISCHARGING && low_threshold > 0) {
         if (batt_info.percentage_remaining >= 0 && strcasecmp(threshold_type, "percentage") == 0 && batt_info.percentage_remaining < low_threshold) {
@@ -436,14 +380,14 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
         }
     }
 
-#define EAT_SPACE_FROM_OUTPUT_IF_NO_OUTPUT()              \
-    do {                                                  \
-        if (outwalk == prevoutwalk) {                     \
-            if (outwalk > buffer && isspace(outwalk[-1])) \
-                outwalk--;                                \
-            else if (isspace(*(walk + 1)))                \
-                walk++;                                   \
-        }                                                 \
+#define EAT_SPACE_FROM_OUTPUT_IF_NO_OUTPUT()                   \
+    do {                                                       \
+        if (outwalk == prevoutwalk) {                          \
+            if (outwalk > buffer && isspace((int)outwalk[-1])) \
+                outwalk--;                                     \
+            else if (isspace((int)*(walk + 1)))                \
+                walk++;                                        \
+        }                                                      \
     } while (0)
 
     for (walk = format; *walk != '\0'; walk++) {