summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--README.md12
-rw-r--r--i3status.c8
-rw-r--r--include/i3status.h2
-rw-r--r--man/i3status.man24
-rw-r--r--src/print_battery_info.c33
-rw-r--r--src/print_eth_info.c54
-rw-r--r--src/print_ip_addr.c13
-rw-r--r--src/print_time.c2
-rw-r--r--src/print_volume.c11
-rw-r--r--src/print_wireless_info.c65
-rw-r--r--testcases/016-ethernet-down/expected_output.txt1
-rw-r--r--testcases/016-ethernet-down/i3status.conf10
-rw-r--r--testcases/017-wireless-down/expected_output.txt1
-rw-r--r--testcases/017-wireless-down/i3status.conf10
-rw-r--r--testcases/018-battery-capacity/1/uevent2
-rw-r--r--testcases/018-battery-capacity/expected_output.txt1
-rw-r--r--testcases/018-battery-capacity/i3status.conf10
-rw-r--r--testcases/019-battery-capacity/1/uevent2
-rw-r--r--testcases/019-battery-capacity/expected_output.txt1
-rw-r--r--testcases/019-battery-capacity/i3status.conf11
21 files changed, 224 insertions, 54 deletions
diff --git a/Makefile b/Makefile
index 3084ae7..86830ef 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ ifndef SYSCONFDIR
endif
endif
+PKG_CONFIG ?= pkg-config
CFLAGS+=-Wall -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare
CFLAGS+=-g
CFLAGS+=-std=gnu99
@@ -40,8 +41,8 @@ OS:=$(shell uname)
ifeq ($(OS),Linux)
CPPFLAGS+=-DLINUX
CPPFLAGS+=-D_GNU_SOURCE
-CFLAGS += $(shell pkg-config --cflags libnl-genl-3.0)
-LIBS += $(shell pkg-config --libs libnl-genl-3.0)
+CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-genl-3.0)
+LIBS += $(shell $(PKG_CONFIG) --libs libnl-genl-3.0)
LIBS+=-lasound
endif
diff --git a/README.md b/README.md
index 16c4bd3..75704ea 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,12 @@
## Description
-i3status is a small program (about 1500 SLOC) for generating a status bar for
-i3bar, dzen2, xmobar or similar programs. It is designed to be very efficient by
-issuing a very small number of system calls, as one generally wants to update
-such a status line every second. This ensures that even under high load, your
-status bar is updated correctly. Also, it saves a bit of energy by not hogging
-your CPU as much as spawning the corresponding amount of shell commands would.
+i3status is a small program for generating a status bar for i3bar, dzen2, xmobar
+or similar programs. It is designed to be very efficient by issuing a very small
+number of system calls, as one generally wants to update such a status line
+every second. This ensures that even under high load, your status bar is updated
+correctly. Also, it saves a bit of energy by not hogging your CPU as much as
+spawning the corresponding amount of shell commands would.
## Development
diff --git a/i3status.c b/i3status.c
index 558f8ab..5088c96 100644
--- a/i3status.c
+++ b/i3status.c
@@ -111,6 +111,9 @@ static void *scalloc(size_t size) {
}
char *sstrdup(const char *str) {
+ if (str == NULL) {
+ return NULL;
+ }
char *result = strdup(str);
exit_if_null(result, "Error: out of memory (strdup())\n");
return result;
@@ -628,6 +631,11 @@ int main(int argc, char *argv[]) {
die("Could not create socket\n");
int interval = cfg_getint(cfg_general, "interval");
+ if (interval <= 0) {
+ die("Invalid interval attribute found in section %s, line %d: %d\n"
+ "Expected positive integer\n",
+ cfg_general->name, cfg_general->line, interval);
+ }
/* One memory page which each plugin can use to buffer output.
* Even though it’s unclean, we just assume that the user will not
diff --git a/include/i3status.h b/include/i3status.h
index 5042832..9ac471d 100644
--- a/include/i3status.h
+++ b/include/i3status.h
@@ -215,7 +215,7 @@ void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const ch
void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_unk, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds);
void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, const char *locale, const char *format_time, time_t t);
void print_ddate(yajl_gen json_gen, char *buffer, const char *format, time_t t);
-const char *get_ip_addr(const char *interface);
+const char *get_ip_addr(const char *interface, int family);
void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down);
void print_run_watch(yajl_gen json_gen, char *buffer, const char *title, const char *pidfile, const char *format, const char *format_down);
void print_path_exists(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_down);
diff --git a/man/i3status.man b/man/i3status.man
index f1b6221..7d81203 100644
--- a/man/i3status.man
+++ b/man/i3status.man
@@ -24,13 +24,12 @@ configuration files in the following order:
== DESCRIPTION
-i3status is a small program (about 1500 SLOC) for generating a status bar for
-i3bar, dzen2, xmobar, lemonbar or similar programs. It is designed to be very
-efficient by issuing a very small number of system calls, as one generally
-wants to update such a status line every second. This ensures that even under
-high load, your status bar is updated correctly. Also, it saves a bit of energy
-by not hogging your CPU as much as spawning the corresponding amount of shell
-commands would.
+i3status is a small program for generating a status bar for i3bar, dzen2,
+xmobar, lemonbar or similar programs. It is designed to be very efficient by
+issuing a very small number of system calls, as one generally wants to update
+such a status line every second. This ensures that even under high load, your
+status bar is updated correctly. Also, it saves a bit of energy by not hogging
+your CPU as much as spawning the corresponding amount of shell commands would.
== CONFIGURATION
@@ -142,7 +141,7 @@ no effect when +output_format+ is set to +i3bar+ or +none+.
The +interval+ directive specifies the time in seconds for which i3status will
sleep before printing the next status line.
-Using +output_format+ you can chose which format strings i3status should
+Using +output_format+ you can choose which format strings i3status should
use in its output. Currently available are:
i3bar::
@@ -178,14 +177,14 @@ section just for this module.
If you don't fancy the vertical separators between modules i3status/i3bar
uses by default, you can employ the +separator+ directive to configure how
-modules are separated. You can either disable the default separator altogether
+modules are separated. You can also disable the default separator altogether by
setting it to the empty string. You might then define separation as part of a
module's format string. This is your only option when using the i3bar output
format as the separator is drawn by i3bar directly otherwise. For the other
output formats, the provided non-empty string will be automatically enclosed
with the necessary coloring bits if color support is enabled.
-i3bar supports Pango markup, allowing your format strings to specify font
+i3bar supports Pango markup, allowing your format strings to specify font,
color, size, etc. by setting the +markup+ directive to "pango". Note that the
ampersand ("&"), less-than ("<"), greater-than (">"), single-quote ("'"), and
double-quote (""") characters need to be replaced with "`&amp;`", "`&lt;`",
@@ -305,8 +304,9 @@ network interface found on the system (excluding devices starting with "lo").
=== Ethernet
Gets the IP address and (if possible) the link speed of the given ethernet
-interface. Getting the link speed requires the cap_net_admin capability. Set
-it using +setcap cap_net_admin=ep $(which i3status)+.
+interface. If no IPv4 address is available and an IPv6 address is, it will be
+displayed. Getting the link speed requires the cap_net_admin capability.
+Set it using +setcap cap_net_admin=ep $(which i3status)+.
The special interface name `_first_` will be replaced by the first non-wireless
network interface found on the system (excluding devices starting with "lo").
diff --git a/src/print_battery_info.c b/src/print_battery_info.c
index e9077ec..7a462f7 100644
--- a/src/print_battery_info.c
+++ b/src/print_battery_info.c
@@ -175,11 +175,19 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
* 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 && voltage != -1) {
- batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 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->full_last = (((float)voltage / 1000.0) * ((float)batt_info->full_last / 1000.0));
+ if (!watt_as_unit && voltage >= 0) {
+ if (batt_info->present_rate > 0) {
+ batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0));
+ }
+ if (batt_info->remaining > 0) {
+ batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0));
+ }
+ if (batt_info->full_design > 0) {
+ batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0));
+ }
+ if (batt_info->full_last > 0) {
+ batt_info->full_last = (((float)voltage / 1000.0) * ((float)batt_info->full_last / 1000.0));
+ }
}
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
int state;
@@ -499,8 +507,19 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
return;
}
- int full = (last_full_capacity ? batt_info.full_last : batt_info.full_design);
- if (full < 0 && batt_info.percentage_remaining < 0) {
+ // *Choose* a measure of the 'full' battery. It is whichever is better of
+ // the battery's (hardware-given) design capacity (batt_info.full_design)
+ // and the battery's last known good charge (batt_info.full_last).
+ // We prefer the design capacity, but use the last capacity if we don't have it,
+ // or if we are asked to (last_full_capacity == true); but similarly we use
+ // the design capacity if we don't have the last capacity.
+ // If we don't have either then both full_design and full_last <= 0,
+ // which implies full <= 0, which bails out on the following line.
+ int full = batt_info.full_design;
+ if (full <= 0 || (last_full_capacity && batt_info.full_last > 0)) {
+ full = batt_info.full_last;
+ }
+ if (full <= 0 && batt_info.remaining < 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);
diff --git a/src/print_eth_info.c b/src/print_eth_info.c
index e4bf15e..b30d2b0 100644
--- a/src/print_eth_info.c
+++ b/src/print_eth_info.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <limits.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -118,25 +119,60 @@ static int print_eth_speed(char *outwalk, const char *interface) {
/*
* Combines ethernet IP addresses and speed (if requested) for displaying
*
+ * Table summarizing what is the decision to prefer IPv4 or IPv6
+ * based their values.
+ *
+ * | ipv4_address | ipv6_address | Chosen IP | Color |
+ * |--------------|--------------|-----------|-------------------|
+ * | NULL | NULL | None | bad (red) |
+ * | NULL | no IP | IPv6 | degraded (orange) |
+ * | NULL | ::1/128 | IPv6 | ok (green) |
+ * | no IP | NULL | IPv4 | degraded |
+ * | no IP | no IP | IPv4 | degraded |
+ * | no IP | ::1/128 | IPv6 | ok |
+ * | 127.0.0.1 | NULL | IPv4 | ok |
+ * | 127.0.0.1 | no IP | IPv4 | ok |
+ * | 127.0.0.1 | ::1/128 | IPv4 | ok |
*/
void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) {
const char *walk;
- const char *ip_address = get_ip_addr(interface);
char *outwalk = buffer;
INSTANCE(interface);
- if (ip_address == NULL) {
- START_COLOR("color_bad");
- outwalk += sprintf(outwalk, "%s", format_down);
- goto out;
+ char *ipv4_address = sstrdup(get_ip_addr(interface, AF_INET));
+ char *ipv6_address = sstrdup(get_ip_addr(interface, AF_INET6));
+
+ /*
+ * Removing '%' and following characters from IPv6 since the interface identifier is redundant,
+ * as the output already includes the interface name.
+ */
+ if (ipv6_address != NULL) {
+ char *prct_ptr = strstr(ipv6_address, "%");
+ if (prct_ptr != NULL) {
+ *prct_ptr = '\0';
+ }
}
- if (BEGINS_WITH(ip_address, "no IP"))
+ bool prefer_ipv4 = true;
+ if (ipv4_address == NULL) {
+ if (ipv6_address == NULL) {
+ START_COLOR("color_bad");
+ outwalk += sprintf(outwalk, "%s", format_down);
+ goto out;
+ } else {
+ prefer_ipv4 = false;
+ }
+ } else if (BEGINS_WITH(ipv4_address, "no IP") && ipv6_address != NULL && !BEGINS_WITH(ipv6_address, "no IP")) {
+ prefer_ipv4 = false;
+ }
+
+ const char *ip_address = (prefer_ipv4) ? ipv4_address : ipv6_address;
+ if (BEGINS_WITH(ip_address, "no IP")) {
START_COLOR("color_degraded");
- else
+ } else {
START_COLOR("color_good");
-
+ }
for (walk = format_up; *walk != '\0'; walk++) {
if (*walk != '%') {
*(outwalk++) = *walk;
@@ -154,5 +190,7 @@ void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, cons
out:
END_COLOR;
+ free(ipv4_address);
+ free(ipv6_address);
OUTPUT_FULL_TEXT(buffer);
}
diff --git a/src/print_ip_addr.c b/src/print_ip_addr.c
index 09b0992..f9cd1f4 100644
--- a/src/print_ip_addr.c
+++ b/src/print_ip_addr.c
@@ -17,9 +17,14 @@
* interface is up and running but hasn't got an IP address yet
*
*/
-const char *get_ip_addr(const char *interface) {
+const char *get_ip_addr(const char *interface, int family) {
static char part[512];
- socklen_t len = sizeof(struct sockaddr_in);
+ socklen_t len = 0;
+ if (family == AF_INET)
+ len = sizeof(struct sockaddr_in);
+ else if (family == AF_INET6)
+ len = sizeof(struct sockaddr_in6);
+
memset(part, 0, sizeof(part));
struct ifaddrs *ifaddr, *addrp;
@@ -30,13 +35,13 @@ const char *get_ip_addr(const char *interface) {
if (ifaddr == NULL)
return NULL;
- /* Skip until we are at the AF_INET address of interface */
+ /* Skip until we are at the input family address of interface */
for (addrp = ifaddr;
(addrp != NULL &&
(strcmp(addrp->ifa_name, interface) != 0 ||
addrp->ifa_addr == NULL ||
- addrp->ifa_addr->sa_family != AF_INET));
+ addrp->ifa_addr->sa_family != family));
addrp = addrp->ifa_next) {
/* Check if the interface is down */
diff --git a/src/print_time.c b/src/print_time.c
index c8da9d6..3ed32b0 100644
--- a/src/print_time.c
+++ b/src/print_time.c
@@ -29,9 +29,9 @@ void set_timezone(const char *tz) {
} else {
unsetenv("TZ");
}
- tzset();
current_timezone = tz;
}
+ tzset();
}
void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, const char *locale, const char *format_time, time_t t) {
diff --git a/src/print_volume.c b/src/print_volume.c
index 51e84f3..be6a1d7 100644
--- a/src/print_volume.c
+++ b/src/print_volume.c
@@ -211,6 +211,7 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *
#if defined(__OpenBSD__)
int oclass_idx = -1, master_idx = -1, master_mute_idx = -1;
+ int master_next = AUDIO_MIXER_LAST;
mixer_devinfo_t devinfo, devinfo2;
mixer_ctrl_t vinfo;
@@ -228,12 +229,17 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *
devinfo2.index = 0;
while (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo2) >= 0) {
- if ((devinfo2.type == AUDIO_MIXER_VALUE) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmaster, MAX_AUDIO_DEV_LEN) == 0))
+ if ((devinfo2.type == AUDIO_MIXER_VALUE) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmaster, MAX_AUDIO_DEV_LEN) == 0)) {
master_idx = devinfo2.index;
+ master_next = devinfo2.next;
+ }
if ((devinfo2.type == AUDIO_MIXER_ENUM) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmute, MAX_AUDIO_DEV_LEN) == 0))
- master_mute_idx = devinfo2.index;
+ if (master_next == devinfo2.index)
+ master_mute_idx = devinfo2.index;
+ if (master_next != AUDIO_MIXER_LAST)
+ master_next = devinfo2.next;
devinfo2.index++;
}
@@ -246,6 +252,7 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *
vinfo.dev = master_idx;
vinfo.type = AUDIO_MIXER_VALUE;
+ vinfo.un.value.num_channels = devinfo.un.v.num_channels;
if (ioctl(mixfd, AUDIO_MIXER_READ, &vinfo) == -1)
goto out;
diff --git a/src/print_wireless_info.c b/src/print_wireless_info.c
index 13f8184..c3b5270 100644
--- a/src/print_wireless_info.c
+++ b/src/print_wireless_info.c
@@ -464,6 +464,21 @@ error1:
return 0;
}
+/* Table summarizing what is the decision to prefer IPv4 or IPv6
+ * based their values.
+ *
+ * | ipv4_address | ipv6_address | Chosen IP | Color |
+ * |--------------|--------------|-----------|-------------------|
+ * | NULL | NULL | None | bad (red) |
+ * | NULL | no IP | IPv6 | degraded (orange) |
+ * | NULL | ::1/128 | IPv6 | ok (green) |
+ * | no IP | NULL | IPv4 | degraded |
+ * | no IP | no IP | IPv4 | degraded |
+ * | no IP | ::1/128 | IPv6 | ok |
+ * | 127.0.0.1 | NULL | IPv4 | ok |
+ * | 127.0.0.1 | no IP | IPv4 | ok |
+ * | 127.0.0.1 | ::1/128 | IPv4 | ok |
+ */
void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) {
const char *walk;
char *outwalk = buffer;
@@ -471,22 +486,48 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
INSTANCE(interface);
- const char *ip_address = get_ip_addr(interface);
- if (ip_address == NULL) {
- START_COLOR("color_bad");
- outwalk += sprintf(outwalk, "%s", format_down);
- goto out;
+ char *ipv4_address = sstrdup(get_ip_addr(interface, AF_INET));
+ char *ipv6_address = sstrdup(get_ip_addr(interface, AF_INET6));
+
+ /*
+ * Removing '%' and following characters from IPv6 since the interface identifier is redundant,
+ * as the output already includes the interface name.
+ */
+ if (ipv6_address != NULL) {
+ char *prct_ptr = strstr(ipv6_address, "%");
+ if (prct_ptr != NULL) {
+ *prct_ptr = '\0';
+ }
+ }
+
+ bool prefer_ipv4 = true;
+ if (ipv4_address == NULL) {
+ if (ipv6_address == NULL) {
+ START_COLOR("color_bad");
+ outwalk += sprintf(outwalk, "%s", format_down);
+ goto out;
+ } else {
+ prefer_ipv4 = false;
+ }
+ } else if (BEGINS_WITH(ipv4_address, "no IP") && ipv6_address != NULL && !BEGINS_WITH(ipv6_address, "no IP")) {
+ prefer_ipv4 = false;
}
- if (get_wireless_info(interface, &info)) {
+ const char *ip_address = (prefer_ipv4) ? ipv4_address : ipv6_address;
+ if (!get_wireless_info(interface, &info)) {
+ walk = format_down;
+ START_COLOR("color_bad");
+ } else {
walk = format_up;
if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY)
START_COLOR((info.quality < info.quality_average ? "color_degraded" : "color_good"));
- else
- START_COLOR((BEGINS_WITH(ip_address, "no IP") ? "color_degraded" : "color_good"));
- } else {
- walk = format_down;
- START_COLOR("color_bad");
+ else {
+ if (BEGINS_WITH(ip_address, "no IP")) {
+ START_COLOR("color_degraded");
+ } else {
+ START_COLOR("color_good");
+ }
+ }
}
for (; *walk != '\0'; walk++) {
@@ -568,5 +609,7 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
out:
END_COLOR;
+ free(ipv4_address);
+ free(ipv6_address);
OUTPUT_FULL_TEXT(buffer);
}
diff --git a/testcases/016-ethernet-down/expected_output.txt b/testcases/016-ethernet-down/expected_output.txt
new file mode 100644
index 0000000..be02a09
--- /dev/null
+++ b/testcases/016-ethernet-down/expected_output.txt
@@ -0,0 +1 @@
+E: down
diff --git a/testcases/016-ethernet-down/i3status.conf b/testcases/016-ethernet-down/i3status.conf
new file mode 100644
index 0000000..7835053
--- /dev/null
+++ b/testcases/016-ethernet-down/i3status.conf
@@ -0,0 +1,10 @@
+general {
+ output_format = "none"
+}
+
+order += "ethernet nonsense"
+
+ethernet nonsense {
+ format_up = "E: %ip (%speed)"
+ format_down = "E: down"
+}
diff --git a/testcases/017-wireless-down/expected_output.txt b/testcases/017-wireless-down/expected_output.txt
new file mode 100644
index 0000000..aa33e33
--- /dev/null
+++ b/testcases/017-wireless-down/expected_output.txt
@@ -0,0 +1 @@
+W: down
diff --git a/testcases/017-wireless-down/i3status.conf b/testcases/017-wireless-down/i3status.conf
new file mode 100644
index 0000000..0cfc964
--- /dev/null
+++ b/testcases/017-wireless-down/i3status.conf
@@ -0,0 +1,10 @@
+general {
+ output_format = "none"
+}
+
+order += "wireless nonsense"
+
+wireless nonsense {
+ format_up = "W: (%quality at %essid) %ip"
+ format_down = "W: down"
+}
diff --git a/testcases/018-battery-capacity/1/uevent b/testcases/018-battery-capacity/1/uevent
new file mode 100644
index 0000000..00b1148
--- /dev/null
+++ b/testcases/018-battery-capacity/1/uevent
@@ -0,0 +1,2 @@
+POWER_SUPPLY_CHARGE_FULL=100
+POWER_SUPPLY_CHARGE_NOW=50
diff --git a/testcases/018-battery-capacity/expected_output.txt b/testcases/018-battery-capacity/expected_output.txt
new file mode 100644
index 0000000..ab07d0a
--- /dev/null
+++ b/testcases/018-battery-capacity/expected_output.txt
@@ -0,0 +1 @@
+50.00%
diff --git a/testcases/018-battery-capacity/i3status.conf b/testcases/018-battery-capacity/i3status.conf
new file mode 100644
index 0000000..7a3443c
--- /dev/null
+++ b/testcases/018-battery-capacity/i3status.conf
@@ -0,0 +1,10 @@
+general {
+ output_format = "none"
+}
+
+order += "battery all"
+
+battery all {
+ format = "%percentage"
+ path = "testcases/018-battery-capacity/%d/uevent"
+}
diff --git a/testcases/019-battery-capacity/1/uevent b/testcases/019-battery-capacity/1/uevent
new file mode 100644
index 0000000..d004bf1
--- /dev/null
+++ b/testcases/019-battery-capacity/1/uevent
@@ -0,0 +1,2 @@
+POWER_SUPPLY_CHARGE_FULL_DESIGN=100
+POWER_SUPPLY_CHARGE_NOW=50
diff --git a/testcases/019-battery-capacity/expected_output.txt b/testcases/019-battery-capacity/expected_output.txt
new file mode 100644
index 0000000..ab07d0a
--- /dev/null
+++ b/testcases/019-battery-capacity/expected_output.txt
@@ -0,0 +1 @@
+50.00%
diff --git a/testcases/019-battery-capacity/i3status.conf b/testcases/019-battery-capacity/i3status.conf
new file mode 100644
index 0000000..8152d44
--- /dev/null
+++ b/testcases/019-battery-capacity/i3status.conf
@@ -0,0 +1,11 @@
+general {
+ output_format = "none"
+}
+
+order += "battery all"
+
+battery all {
+ format = "%percentage"
+ path = "testcases/019-battery-capacity/%d/uevent"
+ last_full_capacity = true
+}