Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.unix.programmer > #17204

a battery monitor

From Anton Antimo <anton@safunu.org>
Newsgroups comp.unix.programmer, comp.windows.x, comp.lang.c
Subject a battery monitor
Followup-To comp.windows.x
Date 2026-09-01 14:28 -0300
Organization A noiseless patient Spider
Message-ID <87v78oop87.fsf@safunu.org> (permalink)

Cross-posted to 3 groups.

Followups directed to: comp.windows.x

Show all headers | View raw


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

Back to comp.unix.programmer | Previous | Next — Next in thread | Find similar | Unroll thread


Thread

a battery monitor Anton Antimo <anton@safunu.org> - 2026-09-01 14:28 -0300
  Re: a battery monitor Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-02 21:45 +0000
    Re: a battery monitor Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> - 2026-09-03 12:34 +0800
      Re: a battery monitor Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-03 21:24 +0000
        Re: a battery monitor Eli the Bearded <*@eli.users.panix.com> - 2026-09-04 02:51 +0000
    Re: a battery monitor David Brown <david.brown@hesbynett.no> - 2026-09-03 09:28 +0200
      Re: a battery monitor boltar@caprica.universe - 2026-09-03 15:55 +0000
        Re: a battery monitor David Brown <david.brown@hesbynett.no> - 2026-09-04 09:07 +0200
          Re: a battery monitor Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> - 2026-09-04 15:14 +0800
          Re: a battery monitor Lane W <cactus_DAC@yahoo.com> - 2026-09-04 05:32 -0600
          Re: a battery monitor scott@slp53.sl.home (Scott Lurndal) - 2026-09-04 14:29 +0000
          Re: a battery monitor Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> - 2026-09-04 23:17 +0800
        Re: a battery monitor gazelle@shell.xmission.com (Kenny McCormack) - 2026-09-04 12:05 +0000

csiph-web