xbattmon

simple battery monitor for X
git clone git://git.2f30.org/xbattmon.git
Log | Files | Refs | README | LICENSE

commit 3c0603742b987ac7513e110eb8010c0d36570f52
Author: sin <sin@2f30.org>
Date:   Thu, 30 Oct 2014 18:40:00 +0000

Initial commit

Diffstat:
xbattmon.c | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 226 insertions(+), 0 deletions(-)

diff --git a/xbattmon.c b/xbattmon.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2014 sin <sin@2f30.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/ioctl.h> +#include <sys/select.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> + +#include <machine/apmvar.h> +#include <X11/Xlib.h> + +#define LEN(x) (sizeof(x) / sizeof(*(x))) + +enum { + BATT_CHARGING, + BATT_DRAINING +}; + +enum { + COLOR_BATT_CHARGED, + COLOR_BATT_LEFT2CHARGE, + COLOR_BATT_DRAINED, + COLOR_BATT_LEFT2DRAIN +}; + +struct { + char *name; + unsigned long pixel; +} colmap[] = { + [COLOR_BATT_CHARGED] = { "green", 0 }, + [COLOR_BATT_LEFT2CHARGE] = { "grey", 0 }, + [COLOR_BATT_DRAINED] = { "red", 0 }, + [COLOR_BATT_LEFT2DRAIN] = { "blue", 0 } +}; + +Display *dpy; +Window winbar; +GC gcbar; +int barx; +int bary; +unsigned int barwidth; +unsigned int barheight; +unsigned int thickness = 4; /* 4 pixels by default */ +time_t pollinterval = 5; /* poll battery state every 5 seconds */ +int state; /* BATT_CHARGING or BATT_DRAINING */ +int howmuch; /* 0 if completely discharged or 100 if completely charged */ +int bottom = 1; /* set to 0 if you want the bar to be at the top */ + +void +setup(void) +{ + XSetWindowAttributes attr; + XColor color, exact; + int r; + int screen; + unsigned int width, height; + size_t i; + + dpy = XOpenDisplay(NULL); + if (!dpy) + errx(1, "cannot open display"); + + screen = DefaultScreen(dpy); + width = DisplayWidth(dpy, screen); + height = DisplayHeight(dpy, screen); + + if (bottom == 1) { + barx = 0; + bary = height - thickness; + barwidth = width; + barheight = thickness; + } else { + barx = 0; + bary = 0; + barwidth = width; + barheight = thickness; + } + + winbar = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), barx, bary, barwidth, + barheight, 0, BlackPixel(dpy, screen), + WhitePixel(dpy, screen)); + + attr.override_redirect = True; + XChangeWindowAttributes(dpy, winbar, CWOverrideRedirect, &attr); + + XSelectInput(dpy, winbar, ExposureMask | VisibilityChangeMask); + XMapRaised(dpy, winbar); + + gcbar = XCreateGC(dpy, winbar, 0, 0); + + for (i = 0; i < LEN(colmap); i++) { + r = XAllocNamedColor(dpy, DefaultColormap(dpy, 0), + colmap[i].name, &color, &exact); + if (r == 0) + errx(1, "cannot allocate color resources"); + colmap[i].pixel = color.pixel; + } +} + +void +redraw(void) +{ + int pos; + + pos = barwidth * howmuch / 100; + switch (state) { + case BATT_CHARGING: + XSetForeground(dpy, gcbar, colmap[COLOR_BATT_CHARGED].pixel); + XFillRectangle(dpy, winbar, gcbar, 0, 0, pos, thickness); + XSetForeground(dpy, gcbar, colmap[COLOR_BATT_LEFT2CHARGE].pixel); + XFillRectangle(dpy, winbar, gcbar, pos, 0, barwidth, thickness); + break; + case BATT_DRAINING: + XSetForeground(dpy, gcbar, colmap[COLOR_BATT_LEFT2DRAIN].pixel); + XFillRectangle(dpy, winbar, gcbar, 0, 0, pos, thickness); + XSetForeground(dpy, gcbar, colmap[COLOR_BATT_DRAINED].pixel); + XFillRectangle(dpy, winbar, gcbar, pos, 0, barwidth, thickness); + break; + } + XFlush(dpy); +} + +void +update(void) +{ + int r; + int fd; + int v; + struct apm_power_info info; + +#define _PATH_APM "/dev/apm" + fd = open(_PATH_APM, O_RDONLY); + if (fd < 0) + err(1, "open %s", _PATH_APM); + r = ioctl(fd, APM_IOC_GETPOWER, &info); + if (r != 0) + err(1, "APM_IOC_GETPOWER %s", _PATH_APM); + close(fd); + + howmuch = info.battery_life; + if (howmuch > 100) + howmuch = 100; + + if (info.ac_state == APM_AC_ON) + state = BATT_CHARGING; + else + state = BATT_DRAINING; + + fflush(stdout); +} + +Bool +evpredicate() +{ + return True; +} + +void +loop(void) +{ + XEvent ev; + fd_set rfds; + struct timeval tv; + int dpyfd; + int n; + + dpyfd = ConnectionNumber(dpy); + while (1) { + FD_ZERO(&rfds); + FD_SET(dpyfd, &rfds); + tv.tv_sec = pollinterval; + tv.tv_usec = 0; +again: + n = select(dpyfd + 1, &rfds, NULL, NULL, &tv); + if (n < 0) { + if (errno == EINTR) + goto again; + err(1, "select"); + } + if (n == 0) { + update(); + redraw(); + continue; + } + if (FD_ISSET(dpyfd, &rfds) != 0) { + while (XCheckIfEvent(dpy, &ev, evpredicate, NULL) == True) { + switch (ev.type) { + case Expose: + redraw(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, winbar); + break; + } + } + } + } +} + +int +main(void) +{ + setup(); + update(); + redraw(); + loop(); + return 0; +}