I wrote a battery monitor. The program watches its stdin for lines
containing numbers that tell what is the current percentage of the
battery. You can see a picture of it on the lower right of the image at
https://archive.org/details/x11-battery-monitor
If you compile it, then how you can see a demonstration of it running
(using bash):
for i in $(seq 100 -5 0); do echo $i && sleep 0.5 ; done | ./xbattery
So, yes, the program is more like a progress bar to be used by some kind
of shell program. Here's how I've been using it. My system has this
program called acpi, which gives me battery information:
%acpi -b
Battery 0: Charging, 91%, 00:18:43 until charged
I run CWM, the calm window manager. So from my ~/xinitrc, I run:
while true; \
do acpi -b | awk '{printf("%d\n",$4)}' | tr -d '%,' && sleep 60; \
done | xbattery 220x40-0-0 &
So, yes, xbattery.c (source code below) is able to read the desired
window geometry from the command line (as it's typical of X programs).
Be kind with criticism---that's my first one.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
void draw(Display *d, Window w, GC gc, int p,
unsigned int width, unsigned int height,
unsigned long green, unsigned long red) {
XClearWindow(d, w);
if (p < 0) p = 0;
if (p > 100) p = 100;
int howmuch;
if (p >= 100)
howmuch = width;
else
howmuch = (width * p) / 100;
if (howmuch > 0) {
if (p <= 10) XSetForeground(d, gc, red); else XSetForeground(d, gc, green);
XFillRectangle(d, w, gc, 0, 0, howmuch, width);
}
}
int main(int argc, char *argv[]) {
Display *d = NULL; Window w = 0; GC gc = NULL;
XClassHint *ch = NULL; XSizeHints *hs = NULL; int exit_code = 0;
d = XOpenDisplay(NULL);
if (!d) {
fprintf(stderr, "cannot open X display\n");
return 1;
}
int s = DefaultScreen(d);
/* Variables x, y store the position of the window on the screen.
Variables width and height store the size of the window.
Variable g stores the bitmask produced by XParseGeometry. */
int x = 10, y = 10; unsigned int width = 300, height = 50; int g = 0;
if (argc > 1) {
g = XParseGeometry(argv[1], &x, &y, &width, &height);
if ((g & XValue) && (g & XNegative)) {
x = DisplayWidth(d, s) - width - abs(x);
}
if ((g & YValue) && (g & YNegative)) {
y = DisplayHeight(d, s) - height - abs(y);
}
}
/* We've decided to use no border. But if you were to specify a
border tomorrow, it would be black. */
w = XCreateSimpleWindow(d, RootWindow(d, s), x, y, width, height, 0,
BlackPixel(d, s), WhitePixel(d, s));
if (!w) {
fprintf(stderr, "cannot create window\n");
exit_code = 1;
goto cleanup;
}
/* The name of the window. That's relevant to CWM, for example, so
that it can ignore the window and let it stand an applet. */
XStoreName(d, w, "xbattery");
ch = XAllocClassHint();
if (!ch) {
fprintf(stderr, "no memory for XClassHint\n");
exit_code = 1;
goto cleanup;
}
ch->res_name = "xbattery";
ch->res_class = "XBattery";
XSetClassHint(d, w, ch);
XFree(ch);
ch = NULL;
/* Here we annotate information to be read by the system's window
manager. It says for example what position we wish the window
to have. */
hs = XAllocSizeHints();
if (!hs) {
fprintf(stderr, "no memory for XSizeHints\n");
exit_code = 1;
goto cleanup;
}
if (g & XValue) hs->flags |= USPosition;
if (g & YValue) hs->flags |= USPosition;
if ((g & WidthValue) || (g & HeightValue)) hs->flags |= USSize;
hs->x = x;
hs->y = y;
hs->width = width;
hs->height = height;
XSetWMNormalHints(d, w, hs);
XFree(hs);
hs = NULL;
/* Here we set the events we are interested in getting. */
XSelectInput(d, w, ExposureMask | KeyPressMask | StructureNotifyMask);
/* This tells X to actually paint the window on screen for the first time. */
XMapWindow(d, w);
/* The graphics context is memory the X server keeps for knowing
how to draw elements on windows such as the size of the pen, the
color of paint and so on. */
gc = XCreateGC(d, w, 0, NULL);
if (!gc) {
fprintf(stderr, "no memory for XCreateGC\n");
exit_code = 1;
goto cleanup;
}
/* The use of a color map here exposes a bit of the design of X. */
Colormap cmap = DefaultColormap(d, s);
XColor screen_col, exact_col;
unsigned long green, red;
if (XAllocNamedColor(d, cmap, "green", &screen_col, &exact_col))
green = screen_col.pixel;
else
green = BlackPixel(d, s);
if (XAllocNamedColor(d, cmap, "red", &screen_col, &exact_col))
red = screen_col.pixel;
else
red = BlackPixel(d, s);
int x11_fd = ConnectionNumber(d);
int percentage = 90;
draw(d, w, gc, percentage, width, height, green, red); XFlush(d);
for (;;) {
fd_set in_fds; FD_ZERO(&in_fds);
FD_SET(STDIN_FILENO, &in_fds); FD_SET(x11_fd, &in_fds);
int max_fd = (STDIN_FILENO > x11_fd) ? STDIN_FILENO : x11_fd;
if (select(max_fd + 1, &in_fds, NULL, NULL, NULL) < 0) {
perror("select: "); exit_code = 1; goto cleanup;
}
if (FD_ISSET(STDIN_FILENO, &in_fds)) {
char buf[16]; int val;
if (fgets(buf, sizeof buf, stdin) != NULL) {
char *endptr; errno = 0;
unsigned long ulval = strtoul(buf, &endptr, 10);
if (endptr == buf || errno == ERANGE || ulval > 100) {
fprintf(stderr, "Oops; invalid input or out of range (0--100); trying again...\n");
continue;
}
val = (int) ulval;
percentage = val;
fprintf(stderr, "Read a line with number %d.\n", val);
draw(d, w, gc, percentage, width, height, green, red);
XFlush(d);
} else {
fprintf(stderr, "EOF; exiting gracefully...\n");
exit_code = 0;
goto cleanup;
}
}
if (FD_ISSET(x11_fd, &in_fds)) {
/* X is notifying us of something. We keep on extracting events
with XNextEvent while XPending says there's more. */
while (XPending(d)) {
XEvent e;
XNextEvent(d, &e);
if (e.type == ConfigureNotify) {
width = e.xconfigure.width;
height = e.xconfigure.height;
}
if (e.type == Expose || e.type == ConfigureNotify) {
draw(d, w, gc, percentage, width, height, green, red);
}
if (e.type == KeyPress) {
KeySym keysym = XLookupKeysym(&e.xkey, 0);
if (keysym == 'q') {
fprintf(stderr, "Quitting...\n");
goto cleanup;
}
}
}
}
}
cleanup:
if (hs) XFree(hs);
if (ch) XFree(ch);
if (gc) XFreeGC(d, gc);
if (w) XDestroyWindow(d, w);
if (d) XCloseDisplay(d);
return exit_code;
}
Followup-To: comp.windows.x
--- PyGate Linux v1.5.19
* Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)