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