056ad859260b53fba0141ebeee85c2312046990f
[xacpi.git] / xacpi.c
1 /* xacpi.c --
2  *
3  * This is a patched xapm working with acpi (which does not support
4  * apm anymore)
5  *
6  * xapm was written and (C) by Rickard E. Faith (r.faith@ieee.org)
7  *
8  * 2004/06/10 Patch for acpi by Francois Fleuret (francois@fleuret.org)
9  * 2004/07/20 Patch from Bryan Cardillo (dillo@seas.upenn.edu)
10  *
11  * To compile:
12  *
13  *  gcc -DNARROWPROTO -o xacpi -L /usr/X11R6/lib/ -lX11 -lXaw xacpi.c
14  *
15  * This program is free software; you can redistribute it and/or modify it
16  * under the terms of the GNU General Public License as published by the
17  * Free Software Foundation; either version 2, or (at your option) any
18  * later version.
19  *
20  * This program is distributed in the hope that it will be useful, but
21  * WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * General Public License for more details.
24  *
25  * $Id: xacpi.c,v 1.40 2004/12/03 07:52:30 fleuret Exp $
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, write to the Free Software Foundation, Inc.,
29  * 675 Mass Ave, Cambridge, MA 02139, USA.
30  *
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <dirent.h>
36 #include <sys/types.h>
37 #include <err.h>
38 #include <time.h>
39
40 #include <X11/Intrinsic.h>
41 #include <X11/StringDefs.h>
42 #include <X11/cursorfont.h>
43 #include <X11/Xaw/Form.h>
44 #include <X11/Xaw/Command.h>
45 #include <X11/Xaw/Scrollbar.h>
46
47 /* The acpi file readings can fail sometime, but we will immediately
48    retry. We display ERR in the window if we fail more than that
49    number of times . */
50
51 #define NB_TOLERATED_FAILURES 1
52
53 #define XACPI_VERSION "1.3"
54 #define MIN_BAR_WIDTH 32
55
56 #define DEFAULT_ACPI_DIR "/proc/acpi"
57 #define DEFAULT_CPUFREQ_DIR "/sys/devices/system/cpu/cpu0/cpufreq"
58
59 #define BUFFER_LEN 1024
60
61 #define DEFAULT_DELAY 5
62 #define DEFAULT_DATE_FORMAT "%a %b %e %H:%M:%S %Z %Y"
63
64 typedef struct _instance_variables {
65   Pixel highColor, lowColor, criticalColor, chargingColor;
66   Pixel foreground;
67   int highValue, lowValue, criticalTemp;
68   String geometry;
69   int delay;
70   Boolean displayPercent, displayPercentAuto;
71   Boolean showBar, showTemperature;
72   Boolean showCpufreq;
73   String cpuFreqDir;
74   String dateFormat;
75   Boolean showDate;
76   Boolean fahrenheit;
77   String acpiDir;
78 } instance_variable_rec;
79
80 struct acpi_info {
81   int design_capacity, last_full_capacity, remaining_capacity;
82   int present_rate;
83   int ac_on_line;
84   int charging;
85   int battery_percentage, battery_time;
86   int temperature;
87   int cpufreq;
88 };
89
90 static XtAppContext app_context;
91 static Widget scrollbar;
92 static Widget topLevel;
93 static Widget form;
94 static Widget delay;
95 static Widget temperature;
96 static Widget cpufreq;
97 static Widget date;
98 static XtIntervalId timerId;
99 static instance_variable_rec iv;
100 static int debug;
101
102 static int nb_failures;
103
104 char *get_field_name(char *buffer, char *r) {
105   while(*r && *r != ':') *(buffer++) = *(r++);
106   while(*r == ' ' || *r == ':') r++;
107   *(buffer++) = '\0';
108   return r;
109 }
110
111 int get_int(char *r) {
112   char tmp[BUFFER_LEN];
113   char *t = tmp;
114   while(*r >= '0' && *r <= '9') *(t++) = *(r++);
115   *t = '\0';
116   return atoi(tmp);
117 }
118
119 char *goto_next_line(char *r) {
120   while(*r && *r != '\n') r++;
121   while(*r == '\n') r++;
122   if(*r) return r; else return 0;
123 }
124
125 /* We make the assumption that the file is shorter than BUFFER_LEN
126    bytes. */
127
128 void read_proc_file(char *path, char *subdir, char *filename, char *content) {
129   char name[BUFFER_LEN];
130   size_t len;
131   FILE *file;
132
133   snprintf(name, BUFFER_LEN, "%s/%s/%s", path, subdir, filename);
134
135   file = fopen(name, "r");
136   if(file) {
137 #ifdef DEBUG
138     printf("reading of [%s]\n", name);
139 #endif
140     len = fread(content, 1, BUFFER_LEN - 1, file);
141     fclose(file);
142   } else {
143 #ifdef DEBUG
144     printf("opening of [%s] failed\n", name);
145 #endif
146     len = 0;
147   }
148
149   if(len == 0) {
150     err(1, "%s", name);
151 #ifdef DEBUG
152     printf("reading of [%s] failed\n", name);
153 #endif
154   }
155   else content[len] = '\0';
156 }
157
158 void check_acpi(char *acpi_dir) {
159   DIR *directory;
160
161   directory= opendir(acpi_dir);
162   if(directory) closedir(directory);
163   else err(1, "%s", acpi_dir);
164 }
165
166 void acpi_read(char *acpi_dir, char *cpu_dir, struct acpi_info *acpi) {
167   DIR *directory;
168   struct dirent *dir_entry;
169   char content[BUFFER_LEN], word[BUFFER_LEN], dir_name[BUFFER_LEN];
170
171   int error = 0;
172
173   memset(acpi, 0, sizeof(*acpi));
174
175   /* Read the battery information */
176
177   snprintf(dir_name, BUFFER_LEN, "%s/battery", acpi_dir);
178   directory = opendir(dir_name);
179   if (!(directory)) err(1, "%s", dir_name);
180
181   while((dir_entry = readdir(directory))) {
182     if(dir_entry->d_name[0] != '.') {
183       char *s;
184
185       read_proc_file(dir_name, dir_entry->d_name, "info", content);
186
187       s = content;
188       while(s) {
189         s = get_field_name(word, s);
190
191         if(strcmp(word, "ERROR") == 0) {
192 #ifdef DEBUG
193           printf("Read \"ERROR\"\n");
194 #endif
195           error = 1;
196         }
197
198         else if(strcmp(word, "design capacity") == 0 && s) acpi->design_capacity += get_int(s);
199         else if(strcmp(word, "last full capacity") == 0 && s) acpi->last_full_capacity += get_int(s);
200         s = goto_next_line(s);
201       }
202
203       read_proc_file(dir_name, dir_entry->d_name, "state", content);
204       s = content;
205       while(s) {
206         s = get_field_name(word, s);
207         if(strcmp(word, "ERROR") == 0) {
208 #ifdef DEBUG
209           printf("Read \"ERROR\"\n");
210 #endif
211           error = 1;
212         }
213
214         else if(strcmp(word, "present rate") == 0 && s) acpi->present_rate += get_int(s);
215         else if(strcmp(word, "remaining capacity") == 0 && s) acpi->remaining_capacity += get_int(s);
216         else if(strcmp(word, "charging state") == 0 && s) {
217           if(strncmp(s, "charging", 8) == 0) acpi->charging = 1;
218           else if(strncmp(s, "charged", 7) == 0) acpi->charging = 0;
219           else if(strncmp(s, "discharging", 11) == 0) acpi->charging = -1;
220         }
221         s = goto_next_line(s);
222       }
223     }
224   }
225
226   closedir(directory);
227
228   /* Read the AC line information */
229
230   snprintf(dir_name, BUFFER_LEN, "%s/ac_adapter", acpi_dir);
231   directory = opendir(dir_name);
232   if (!(directory)) err(1, "%s", dir_name);
233
234   while((dir_entry = readdir(directory))) {
235     if(dir_entry->d_name[0] != '.') {
236       char *s;
237       read_proc_file(dir_name, dir_entry->d_name, "state", content);
238       s = content;
239       while(s) {
240         s = get_field_name(word, s);
241         if(strcmp(word, "ERROR") == 0) {
242 #ifdef DEBUG
243           printf("Read \"ERROR\"\n");
244 #endif
245           error = 1;
246         }
247
248         else if(strcmp(word, "state") == 0 && s)
249           acpi->ac_on_line = (strncmp(s, "on-line", 7) == 0);
250         s = goto_next_line(s);
251       }
252     }
253   }
254
255   closedir(directory);
256
257   /* When required, read the temperature information */
258
259   if(temperature) {
260     snprintf(dir_name, BUFFER_LEN, "%s/thermal_zone", acpi_dir);
261     directory = opendir(dir_name);
262     if (!(directory)) err(1, "%s", dir_name);
263
264     acpi->temperature = -1;
265
266     while((dir_entry = readdir(directory))) {
267       if(dir_entry->d_name[0] != '.') {
268         char *s;
269         read_proc_file(dir_name, dir_entry->d_name, "temperature", content);
270         s = content;
271         while(s) {
272           s = get_field_name(word, s);
273           if(strcmp(word, "ERROR") == 0) {
274 #ifdef DEBUG
275           printf("Read \"ERROR\"\n");
276 #endif
277             error = 1;
278           }
279
280           else if(strcmp(word, "temperature") == 0 && s) {
281             int temp = get_int(s);
282             if(temp > acpi->temperature) acpi->temperature = temp;
283           }
284           s = goto_next_line(s);
285         }
286       }
287     }
288
289     closedir(directory);
290   }
291
292   /* When required, read the frequency information */
293
294   if(cpufreq) {
295     read_proc_file(cpu_dir, "", "scaling_cur_freq", content);
296     char *s = content;
297     acpi->cpufreq = get_int(s)/1000;
298   }
299
300   if(error) nb_failures++; else nb_failures = 0;
301
302   if(nb_failures == 0) {
303
304     // Bryan Cardillo preferes to see the percentage of the design
305     // capacity. I think it's kind of depressive to see that the
306     // battery gets worst and worst ...
307
308     /*     if (acpi->charging == 0) { */
309     /*       acpi->battery_percentage = (acpi->remaining_capacity * 100) / acpi->design_capacity; */
310     /*     } else { */
311     acpi->battery_percentage = (acpi->remaining_capacity * 100) / acpi->last_full_capacity;
312     /*     } */
313
314     if(acpi->present_rate) {
315       if(acpi->charging > 0)
316         acpi->battery_time = ((acpi->last_full_capacity - acpi->remaining_capacity) * 3600) / acpi->present_rate;
317       else if(acpi->charging < 0)
318         acpi->battery_time = (acpi->remaining_capacity * 3600) / acpi->present_rate;
319     }
320   } else if(nb_failures > NB_TOLERATED_FAILURES) {
321     acpi->battery_time = -1;
322   }
323 }
324
325 void put_date(char *buf, char *format) {
326   time_t t;
327   time(&t);
328   strftime(buf, BUFFER_LEN, format, localtime(&t));
329 }
330
331 static void update(XtPointer client_data, XtIntervalId * id) {
332   struct acpi_info acpi;
333   char buf[128];
334
335   static int current_bar_color = -1;
336   static int current_charging_color = -1;
337   static int current_temperature_color = -1;
338   static int blink_counter = 0;
339
340   static int lastPercentage = -1;
341   static int lastMinutes = -1;
342   static int lastDisplay = -1;
343   static int lastACStatus = -1;
344   static int lastTemperature = -1;
345   static int lastCpufreq = -1;
346
347   acpi_read(iv.acpiDir, iv.cpuFreqDir, &acpi);
348
349   if(nb_failures > NB_TOLERATED_FAILURES) {
350
351     XtVaSetValues(delay, XtNlabel, "ERR", NULL);
352
353     if(scrollbar && current_bar_color != iv.criticalColor) {
354       current_bar_color = iv.criticalColor;
355       XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
356       XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
357     }
358
359     /* All this will have to be refreshed */
360
361     lastPercentage = -1;
362     lastMinutes = -1;
363     lastDisplay = -1;
364     blink_counter = 0;
365     lastACStatus = -1;
366     lastTemperature = -1;
367     lastCpufreq = -1;
368
369   }
370
371   /* We refresh only if there were no error during updating */
372
373   else if(nb_failures == 0) {
374
375     if(iv.displayPercentAuto && acpi.ac_on_line != lastACStatus){
376       lastDisplay = -1;
377       iv.displayPercent = acpi.ac_on_line;
378     }
379
380     if (iv.displayPercent) {
381       if (lastDisplay != iv.displayPercent
382           || acpi.battery_percentage != lastPercentage
383           || acpi.ac_on_line != lastACStatus) {
384
385         /* lastPercentage updated at end */
386
387         snprintf(buf, BUFFER_LEN, "%s%d%%", acpi.ac_on_line ? "L" : "B", acpi.battery_percentage);
388         XtVaSetValues(delay, XtNlabel, buf, NULL);
389       }
390     } else {
391
392       /* Negative value means we could not estimate it in acpi_read
393          because the charging speed was 0 */
394
395       if(acpi.battery_time < 0) {
396         snprintf(buf, BUFFER_LEN, "%s", acpi.ac_on_line ? "L" : "B???");
397         XtVaSetValues(delay, XtNlabel, buf, NULL);
398
399       } else {
400
401         int minutes = acpi.battery_time / 60;
402         if (lastDisplay != iv.displayPercent || lastMinutes != minutes
403             || acpi.ac_on_line != lastACStatus
404             ) {
405           lastMinutes = minutes;
406           snprintf(buf, BUFFER_LEN, "%s%lu:%02lu", acpi.ac_on_line ? "L" : "B", minutes/60, minutes%60);
407           XtVaSetValues(delay, XtNlabel, buf, NULL);
408         }
409
410       }
411     }
412
413     lastDisplay = iv.displayPercent;
414     lastACStatus = acpi.ac_on_line;
415
416     if(scrollbar) {
417       if (acpi.battery_percentage <= iv.lowValue) {
418         if (current_bar_color != iv.criticalColor) {
419           current_bar_color = iv.criticalColor;
420           XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
421           XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
422         }
423       } else if (acpi.battery_percentage <= iv.highValue) {
424         if (current_bar_color != iv.lowColor) {
425           current_bar_color = iv.lowColor;
426           XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
427           XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
428         }
429       } else {
430         if (current_bar_color != iv.highColor) {
431           current_bar_color = iv.highColor;
432           XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
433           XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
434         }
435       }
436     }
437
438     if(temperature) {
439       int degrees = (iv.fahrenheit) ?
440             32 + (acpi.temperature * 18) / 10 : acpi.temperature;
441
442       if (degrees != lastTemperature) {
443         if(iv.fahrenheit) {
444           snprintf(buf, BUFFER_LEN, "%dF", degrees);
445         } else {
446           snprintf(buf, BUFFER_LEN, "%dC", degrees);
447         }
448         XtVaSetValues(temperature, XtNlabel, buf, NULL);
449       }
450
451       if(degrees >= iv.criticalTemp) {
452         if (current_temperature_color != iv.criticalColor) {
453           current_temperature_color = iv.criticalColor;
454           XtVaSetValues(temperature, XtNforeground, current_temperature_color, NULL);
455         }
456       } else {
457         if (current_temperature_color != iv.foreground) {
458           current_temperature_color = iv.foreground;
459           XtVaSetValues(temperature, XtNforeground, current_temperature_color, NULL);
460         }
461       }
462
463       lastTemperature = degrees;
464     }
465
466     if(cpufreq && acpi.cpufreq != lastCpufreq) {
467       snprintf(buf, BUFFER_LEN, "%.1fGhz", ((float) acpi.cpufreq)/1000.0);
468       XtVaSetValues(cpufreq, XtNlabel, buf, NULL);
469     }
470
471     if(date) {
472       put_date(buf, iv.dateFormat);
473       XtVaSetValues(date, XtNlabel, buf, NULL);
474     }
475
476     if (acpi.charging > 0) {
477       if (current_charging_color != iv.chargingColor)
478         XtVaSetValues(delay, XtNforeground,
479                       current_charging_color = iv.chargingColor, NULL);
480     } else {
481       if (acpi.battery_percentage < iv.lowValue && blink_counter++ % 2) {
482         if (current_charging_color != iv.criticalColor)
483           XtVaSetValues(delay,
484                         XtNforeground, current_charging_color = iv.criticalColor, NULL);
485       } else {
486         if (current_charging_color != iv.foreground)
487           XtVaSetValues(delay,
488                         XtNforeground, current_charging_color = iv.foreground, NULL);
489       }
490     }
491
492     if (scrollbar && acpi.battery_percentage != lastPercentage) {
493       XawScrollbarSetThumb(scrollbar, 0.0,
494                            acpi.battery_percentage < 0 ? 0.0 : acpi.battery_percentage / 100.0);
495       lastPercentage = acpi.battery_percentage;
496     }
497   }
498
499   /* We come back in 0.5s if there was an error */
500
501   timerId = XtAppAddTimeOut(app_context,
502                             ((nb_failures > 0 && nb_failures <= NB_TOLERATED_FAILURES)) ? 250 : (1000 * iv.delay + 500),
503                             update, app_context);
504 }
505
506 static void press(Widget w, XtPointer client_data, XtPointer call_data){
507   if (w == delay) {
508     iv.displayPercent = !iv.displayPercent;
509   } else if (w == temperature) {
510     iv.fahrenheit = !iv.fahrenheit;
511   }
512   XtRemoveTimeOut(timerId);
513   timerId = XtAppAddTimeOut(app_context, 0, update, app_context);
514 }
515
516 static XrmOptionDescRec options[] = {
517   {"-highcolor", "*highColor", XrmoptionSepArg, NULL},
518   {"-lowcolor", "*lowColor", XrmoptionSepArg, NULL},
519   {"-criticalcolor", "*criticalColor", XrmoptionSepArg, NULL},
520   {"-criticaltemperature", "*criticalTemperature", XrmoptionSepArg, NULL},
521   {"-chargingcolor", "*chargingColor", XrmoptionSepArg, NULL},
522   {"-highvalue", "*highValue", XrmoptionSepArg, NULL},
523   {"-lowvalue", "*lowValue", XrmoptionSepArg, NULL},
524   {"-delay", "*delay", XrmoptionSepArg, NULL},
525   {"-percent", "*percent", XrmoptionNoArg, (XtPointer) "true"},
526   {"-percentauto", "*percentAuto", XrmoptionNoArg, (XtPointer) "true"},
527   {"-showbar", "*showBar", XrmoptionNoArg, (XtPointer) "true"},
528   {"-showtemp", "*showTemperature", XrmoptionNoArg, (XtPointer) "true"},
529   {"-showcpufreq", "*showCpuFreq", XrmoptionNoArg, (XtPointer) "true"},
530   {"-showdate", "*showDate", XrmoptionNoArg, (XtPointer) "true"},
531   {"-fahrenheit", "*fahrenheit", XrmoptionNoArg, (XtPointer) "true"},
532   {"-dateformat", "*dateFormat", XrmoptionSepArg, NULL},
533   {"-acpidir", "*acpiDir", XrmoptionSepArg, NULL},
534   {"-cpufreqdir", "*cpuFreqDir", XrmoptionSepArg, NULL},
535 };
536
537 #define offset(field) XtOffsetOf( instance_variable_rec, field )
538
539 static XtResource resources[] = {
540
541   {"highColor", XtCForeground, XtRPixel, sizeof(Pixel),
542    offset(highColor), XtRString, "green"},
543   {"lowColor", XtCForeground, XtRPixel, sizeof(Pixel),
544    offset(lowColor), XtRString, "yellow"},
545   {"criticalColor", XtCForeground, XtRPixel, sizeof(Pixel),
546    offset(criticalColor), XtRString, "red"},
547   {"chargingColor", XtCForeground, XtRPixel, sizeof(Pixel),
548    offset(chargingColor), XtRString, "blue"},
549
550   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
551    offset(foreground), XtRString, XtDefaultForeground},
552   {"highValue", XtCValue, XtRInt, sizeof(int),
553    offset(highValue), XtRImmediate, (XtPointer) 50},
554   {"lowValue", XtCValue, XtRInt, sizeof(int),
555    offset(lowValue), XtRImmediate, (XtPointer) 10},
556   {"criticalTemperature", XtCValue, XtRInt, sizeof(int),
557    offset(criticalTemp), XtRImmediate, (XtPointer) 0},
558   {"geometry", XtCString, XtRString, sizeof(String),
559    offset(geometry), XtRString, (XtPointer) "170x10"},
560   {"delay", XtCValue, XtRInt, sizeof(int),
561    offset(delay), XtRImmediate, (XtPointer) DEFAULT_DELAY},
562   {"percent", XtCValue, XtRBoolean, sizeof(Boolean),
563    offset(displayPercent), XtRImmediate, (XtPointer) FALSE},
564   {"percentAuto", XtCValue, XtRBoolean, sizeof(Boolean),
565    offset(displayPercentAuto), XtRImmediate, (XtPointer) FALSE},
566   {"showBar", XtCValue, XtRBoolean, sizeof(Boolean),
567    offset(showBar), XtRImmediate, (XtPointer) FALSE},
568   {"showTemperature", XtCValue, XtRBoolean, sizeof(Boolean),
569    offset(showTemperature), XtRImmediate, (XtPointer) FALSE},
570   {"showCpuFreq", XtCValue, XtRBoolean, sizeof(Boolean),
571    offset(showCpufreq), XtRImmediate, (XtPointer) FALSE},
572   {"showDate", XtCValue, XtRBoolean, sizeof(Boolean),
573    offset(showDate), XtRImmediate, (XtPointer) FALSE},
574   {"fahrenheit", XtCValue, XtRBoolean, sizeof(Boolean),
575    offset(fahrenheit), XtRImmediate, (XtPointer) FALSE},
576   {"dateFormat", XtCString, XtRString, sizeof(String),
577    offset(dateFormat), XtRString, (XtPointer) DEFAULT_DATE_FORMAT},
578   {"acpiDir", XtCString, XtRString, sizeof(String),
579    offset(acpiDir), XtRString, (XtPointer) DEFAULT_ACPI_DIR},
580   {"cpuFreqDir", XtCString, XtRString, sizeof(String),
581    offset(cpuFreqDir), XtRString, (XtPointer) DEFAULT_CPUFREQ_DIR},
582 };
583
584 static void quit(Widget w, XtPointer client_data, XtPointer call_data){
585   exit (0);
586 }
587
588 static XtActionsRec main_actions[] = { {"Quit", (XtActionProc) quit},
589 };
590
591 static String main_translations = "<Message>WM_PROTOCOLS:Quit()\n";
592
593 int main(int argc, char **argv){
594   char c;
595
596   int x = 0, y = 0, height = 0, width = 0;
597   int current_width = 0, own_width;
598   XFontStruct *fs;
599   Atom wm_protocols[1];
600   Widget last;
601
602   nb_failures = 0;
603
604   topLevel = XtVaAppInitialize(&app_context, "XAcpi",
605                                options, XtNumber(options),
606                                &argc, argv, NULL, NULL);
607
608   XtGetApplicationResources(topLevel,
609                             &iv,
610                             resources,
611                             XtNumber(resources),
612                             NULL, 0);
613
614   if(iv.criticalTemp <= 0) {
615     if(iv.fahrenheit) iv.criticalTemp = 158;
616     else              iv.criticalTemp = 70;
617   }
618
619   check_acpi(iv.acpiDir);
620
621   if (iv.delay < 1) iv.delay = DEFAULT_DELAY;
622
623   XParseGeometry(iv.geometry, &x, &y, &width, &height);
624
625   while ((c = getopt(argc, argv, "DVH")) != -1)
626     switch (c) {
627     case 'D':
628       ++debug;
629       break;
630     case 'V':
631       fprintf(stderr, "xacpi version " XACPI_VERSION ".\n");
632       fprintf(stderr, "$Id: xacpi.c,v 1.40 2004/12/03 07:52:30 fleuret Exp $.\n");
633       exit(0);
634     case 'H':
635       printf("Options:\n"
636              " -D\n"
637              " -V\n"
638              " -H\n"
639              " -highcolor <string: color>\n"
640              " -lowcolor <string: color>\n"
641              " -criticalcolor <string: color>\n"
642              " -chargingcolor <string: color>\n"
643              " -highvalue <int: percentage>\n"
644              " -lowvalue <int: percentage>\n"
645              " -delay <int: seconds>\n"
646              " -percent\n"
647              " -percentauto\n"
648              " -showbar\n"
649              " -showtemp\n"
650              " -showcpufreq\n"
651              " -showdate\n"
652              " -dateformat <string: format>\n"
653              " -fahrenheit\n"
654              " -acpidir <string: path>\n"
655              " -cpufreqdir <string: path>\n"
656              "\n"
657              "See man xacpi for more detailed information.\n");
658       exit(0);
659       break;
660     }
661
662   form = XtVaCreateManagedWidget("form",
663                                  formWidgetClass, topLevel,
664                                  XtNorientation, XtorientHorizontal,
665                                  XtNborderWidth, 0,
666                                  XtNdefaultDistance, 0,
667                                  NULL);
668
669   /* Add the date */
670
671   if(iv.showDate) {
672     date = XtVaCreateManagedWidget("date",
673                                    labelWidgetClass, form,
674                                    XtNinternalHeight, 0,
675                                    XtNinternalWidth, 0,
676                                    XtNborderWidth, 0,
677                                    XtNlabel, "",
678                                    XtNresize, FALSE,
679                                    NULL);
680
681     char buf[BUFFER_LEN];
682     put_date(buf, iv.dateFormat);
683     last = date;
684     XtVaGetValues(last, XtNfont, &fs, NULL);
685     own_width = ((strlen(buf) + 2) * fs->max_bounds.width);
686     XtVaSetValues(last, XtNwidth, own_width, NULL);
687     current_width += own_width;
688   } else date = 0;
689
690   /* Add the widget to display the remaining battery time */
691
692   delay = XtVaCreateManagedWidget("delay",
693                                   commandWidgetClass, form,
694                                   XtNleft, XtChainLeft,
695                                   XtNhighlightThickness, 0,
696                                   XtNinternalHeight, 0,
697                                   XtNinternalWidth, 0,
698                                   XtNborderWidth, 0,
699                                   XtNlabel, "",
700                                   XtNresize, FALSE,
701                                   NULL);
702
703
704   if(date) XtVaSetValues(delay, XtNfromHoriz, last, 0);
705   XtAddCallback(delay, XtNcallback, press, NULL);
706
707   last = delay;
708   XtVaGetValues(last, XtNfont, &fs, NULL);
709   own_width = 6 * fs->max_bounds.width;
710   XtVaSetValues(last, XtNwidth, own_width, NULL);
711   current_width += own_width;
712
713   /* Add the widget to display the colored bar */
714
715   if(iv.showBar) {
716
717     scrollbar = XtVaCreateManagedWidget("scrollbar",
718                                         scrollbarWidgetClass, form,
719                                         XtNhorizDistance, 3,
720                                         XtNfromHoriz, last,
721                                         XtNorientation, XtorientHorizontal,
722                                         NULL);
723
724     XawScrollbarSetThumb(scrollbar, 0.0, 0.0);
725
726     XtVaSetValues(scrollbar,
727                   XtNtranslations, XtParseTranslationTable(""),
728                   NULL);
729
730     last = scrollbar;
731   } else scrollbar = 0;
732
733   /* Add the widget to display the temperature */
734
735   if(iv.showTemperature) {
736     temperature = XtVaCreateManagedWidget("temperature",
737                                           commandWidgetClass, form,
738                                           XtNhorizDistance, 6,
739                                           XtNfromHoriz, last,
740                                           XtNhighlightThickness, 0,
741                                           XtNinternalHeight, 0,
742                                           XtNinternalWidth, 0,
743                                           XtNborderWidth, 0,
744                                           XtNlabel, iv.fahrenheit ? "000F " : "00C ",
745                                           XtNresize, FALSE,
746                                           NULL);
747     last = temperature;
748     XtVaGetValues(last, XtNfont, &fs, NULL);
749     own_width = (iv. fahrenheit ? 5 : 4) * fs->max_bounds.width;
750     XtVaSetValues(last, XtNwidth, own_width, NULL);
751     current_width += own_width;
752     XtAddCallback(temperature, XtNcallback, press, NULL);
753   } else temperature = 0;
754
755   /* Add the widget to display the cpu frequency */
756
757   if(iv.showCpufreq) {
758     cpufreq = XtVaCreateManagedWidget("cpufreq",
759                                       labelWidgetClass, form,
760                                       XtNhorizDistance, 6,
761                                       XtNfromHoriz, last,
762                                       XtNinternalHeight, 0,
763                                       XtNinternalWidth, 0,
764                                       XtNborderWidth, 0,
765                                       XtNlabel, "?.?Ghz",
766                                       XtNresize, FALSE,
767                                       NULL);
768     last = cpufreq;
769     XtVaGetValues(last, XtNfont, &fs, NULL);
770     own_width = 7 * fs->max_bounds.width;
771     XtVaSetValues(last, XtNwidth, own_width, NULL);
772     current_width += own_width;
773   } else cpufreq = 0;
774
775   /* Changed the bar length to the unused width */
776
777   if(scrollbar) {
778     if(width > current_width + MIN_BAR_WIDTH)
779       XtVaSetValues(scrollbar, XtNwidth, width - current_width, 0);
780     else {
781       XtVaSetValues(scrollbar, XtNwidth, MIN_BAR_WIDTH, 0);
782     }
783   }
784
785   XtRealizeWidget(topLevel);
786
787   /* Add code to handle WM_DELETE_WINDOW cleanly.  */
788
789   XtAppAddActions(app_context, main_actions, XtNumber(main_actions));
790   XtOverrideTranslations(topLevel, XtParseTranslationTable(main_translations));
791   wm_protocols[0]
792     = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False);
793   XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel), wm_protocols, 1);
794
795   timerId = XtAppAddTimeOut(app_context, 0, update, app_context);
796   XtAppMainLoop(app_context);
797
798   return 0;
799 }