.so /home/john/prj/ref/ref.tmac .ds Sf MR .de Q \\$3\(lq\\$1\(rq\\$2 .. .DA .TL .BI xbattext , an X11 battery monitor for NetBSD .AU John Ankarström .AB .LP .I xbattext is a simple X11 program that displays, in text, the current battery level. Its source code serves as a good introduction to X11 programming. It is short, simple and easy to follow, as it accounts only for a single system \(en NetBSD. It makes use of both Xt (X toolkit intrinsics) and Xm (Motif), two of the most popular X libraries. It demonstrates how to access resources from .I ~/.Xdefaults , how to display text in various colors and fonts and how to set timers outside of the main event loop to perform asynchronous tasks that are not triggered by user interaction. .PP This document is a commented version of the .I xbattext source code. It is generated with .I ref , a reference-based literate programming system available at http://git.\:ankarstrom.se/ref/. The uncommented source code is hosted at http://git.\:ankarstrom.se/x11/xbattext/. .PP Feel free to read the three sections of the document out of order. As the structure of the document strictly mirrors the structure of the source code, it begins with the least interesting part and ends with the most exciting part; as mentioned above, it is simply an annotated version of the original source code, not a story in itself. If you want to see a screenshot of the resulting program, skip ahead to the last page. .AE .SH .ce .sp -1v Definitions .sp 1.2v .Re xbattext.c:/^#include/ .LP Most of the includes regard standard C features, but there are two special ones worth mentioning: .PP .I Xm/Label.h contains the definitions for Motif's label widget, which is used to display the battery percentage. It includes the rest of the relevant X11 headers for us. .PP .I machine/apmvar.h defines the .I apm_power_info structure, which is needed in order to inspect the battery status on NetBSD. It will be retrieved via an .I ioctl request, so .I sys/ioctl.h is included as well. .Re xbattext.c:/^\/\* interval in seconds/ .LP The battery status is checked every five seconds by default. .Re xbattext.c:/^\/\* low battery level/ .LP The battery level is considered to be .Q low if it is below 25 percent. .PP The user is encouraged to modify this and the previous constant according to his own personal preferences. .bp .Re xbattext.c:/^\/\* resources/ .LP Two structures are needed to access the application's resources: .I res , which will hold the values of the resources, and .I res_opts , which de\%fines the manner in which the re\%sources should be assigned to the members of the .I res structure.\** .FS .Q Resources are the settings set by the user in .I ~/.Xdefaults or .I ~/.Xresources . For more information about resource management and the structure of the .I XtResource type, see .nh http://lesstif.sourceforge.net/doc/super-ux/g1ae03e/part1/\:chap9.html. .hy .FE .PP At the beginning of the program's execution, the .I res structure is filled by the function .I XtGetApp\%licationResources according to the definitions in .I res_opts . .PP The .I Pixel type is an unsigned long value representing a color, like .I black or .I red3 . The .I XmFont\%List type technically represents a list of fonts, but for all intents and purposes, it will be used here only to represent a single font selection. .PP As you might guess from these resource definitions, .I xbattext allows the user to control the color and font of the battery display depending on the battery status. .Re xbattext.c:/^\/\* state changes/ .LP .I xbattext inspects the state of the battery every five seconds. On every iteration, it updates the percentage displayed, but the font and color are changed only when it detects a relevant change in the battery's state. .PP When such a change is de\%tected, the .I change variable is set. Its possible values represent the range of relevant changes in battery state. The program then changes the color and font depending on the value of .I change . .bp .Re xbattext.c:/^\/\* application state/ .LP .I wargs is an array used by .I XtSet\%Arg , which stores arguments in it, and .I XtSetValues , which applies new settings to a given widget according to the arguments stored in it. .PP The boolean values .I alerting and .I charging that are set to true whenever .I xbattext detects that the battery is low or that the AC adapter is plugged in. .PP The other variables will be explained as we go along. .Rr .SH .ce Initial setup .sp 0.6v .Re xbattext.c:/\/\* program start/ \& .Re xbattext.c:/XtVaAppInitialize(/ .LP The application is initialized. .I top\%level is set, which will serve as the parent for all widgets. .PP Note that many Xt functions have two variants: one that uses .I Xt\%SetArg to collect arguments, and a variadic variant, marked by .I Va . .Re xbattext.c:/open("\/dev\/apm"/ .LP The battery level is queried through .I ioctl requests to .I /dev/apm . The file descriptor is closed by the kernel when the program exits. .Re xbattext.c:/^ \/\* load application resources/ .LP .I XtGetApplicationResources fills the .I res structure with the resources set in .I res_opts . .Re xbattext.c:/^ \/\* create motif label/ .LP The battery level will be displayed in a Motif label widget. It starts out containing an empty string. .Re xbattext.c:/^ update(/ .LP Before starting the main event loop, the .I update function is called, which creates a timer that will run independently of the event loop.\** .FS For more information about timeouts, see http://motifdeveloper.com/tips/tip16.html. .FE .bp .Rr .SH .ce Operation .sp -0.1v .Re xbattext.c:/^\/\* update battery status and (re-)add timer/ .LP The .I update both sets up the timer and is called at the end of each timeout. Along the way, it checks the battery status and updates the text displayed in the label widget. .PP The first argument to .I update is a pointer to an arbitrary value set by the user when the timeout is registered. The second argument contains a pointer to the timeout identifier. Neither argument is used here. .Re xbattext.c:/^ \/\* get battery info/ .LP As mentioned above, the battery status is retrieved through an .I ioctl request, .CW APM_IOC_GETPOWER . It returns an .I apm_power_info structure (which must be zeroed first). .Re xbattext.c:/^ \/\* put battery status into label/ .LP The battery percentage, contained in .I info.battery_life , is written to an .I XmString , a special type of string used by Motif. It is associated with a .Q "font list element tag" , containing information about the visual characteristics of the text. We just use the default. .Re xbattext.c:/^ XtSetArg(/ .LP The .I wargs array mentioned above now starts being filled with arguments that determine the state of the label widget. The number of arguments set is kept track of in the .I i variable. To begin with, the label string is set to the .I XmString value defined earlier. .Re xbattext.c:/^ \/\* check charging status/ .LP The .CW SET_CHARGE or .CW SET_NO\:CHARGE bit is added to the .I change bitmap when a change in .I info.ac_\:state is detected. .PP (The value of .I charging is checked and updated in order to prevent the font and color changes from unnecessarily being applied at every timeout regardless of whether the charging status has changed.) .bp .Re xbattext.c:/^ \/\* check low battery/ .LP The same applies when the program checks whether the battery level is below .CW ALERT . .Re xbattext.c:/^ \/\* prioritize/ .LP Before the bits in .I change are acted upon, some prioritization is necessary. The charging indication overrides any other indication. The low battery indication is activated if the AC adapter is plugged out, but the battery is still low. Likewise, the charging indication is activated if the battery rises above the .CW ALERT threshold, but the AC adapter is still plugged in. .PP (Remember that the .I change bitmap reflects a .B change in state. It does not reflect the current .B state .) .Re xbattext.c:/^ \/\* act on state changes/ .LP After collecting and prioritizing the state changes, the foreground color and font of the label widget are set accordingly. .PP Note that if a .I Pixel resource is not defined, .I XtGetApplicationResources gives it the integer value zero, which also signifies the color black. Thus, the program cannot tell the difference between a missing value and a value of .I black . .PP Font lists, however, are set to null if undefined. If the .I alert or .I charge font list is undefined, .I xbattext uses the main font list instead. .Re xbattext.c:/^ case SET_CHARGE:/ \& .Re xbattext.c:/^set:/ .LP Finally, the values collected in .I wargs are associated with the label widget through the .I XtSetValues function. The .I XmString is freed, as a new one will be created on the next call to .I update , which at the end is registered through the .I Xt\%AppAddTimeOut function to occur in .CW INTERVAL seconds. .Rr .sp \(PN .LP That is the totality of the .I xbattext source (almost; a long comment at the beginning of the file, explaining what the program does and how it should be compiled, was excluded). Hopefully, it shows that graphical UNIX programming is nothing to be afraid of. While Xlib, Xt and Xm tend nowadays not to be considered .Q "best practice" , they have a low barrier to entry, require little resources of the computer and are often installed by default on UNIX systems. It is better to use worse practices to create .I something than it is to use best practices and create nothing at all; if those are the alternatives, then perhaps best practices aren't. .PP If, after reading all this, you are still wondering why anyone would want a small window on their screen displaying the current battery level, then you should get acquainted with the X11 window manager .I jwm \** .FS .I jwm is available at https://joewing.net/projects/jwm/. The author's personal fork of .I jwm 1.8, which has a more traditional visual appearance, is available at http://git.ankarstrom.se/jwm/. .FE and its .Q swallowing feature, which displays an arbitrary X11 program in the tray. It's like tray icons, but in a UNIX sort of way. .sp 0.6v .PSPIC tray.eps .sp 0.2v .ad c .ps -1p .B "Figure 1." .I xbattext beside .I xclock .