Initial commit.
authorFrancois Fleuret <francois@fleuret.org>
Mon, 30 Mar 2009 08:59:42 +0000 (10:59 +0200)
committerFrancois Fleuret <francois@fleuret.org>
Mon, 30 Mar 2009 08:59:42 +0000 (10:59 +0200)
Makefile [new file with mode: 0644]
xacpi.1 [new file with mode: 0644]
xacpi.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..daa750c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+xacpi: xacpi.c
+       gcc -g -DNARROWPROTO -o xacpi -L /usr/X11R6/lib/ -lX11 -lXaw xacpi.c
diff --git a/xacpi.1 b/xacpi.1
new file mode 100644 (file)
index 0000000..17755f0
--- /dev/null
+++ b/xacpi.1
@@ -0,0 +1,151 @@
+.\" xacpi.1 -- 
+.\" Created: Thu Jan 11 16:29:49 1996 by r.faith@ieee.org
+.\" Revised: Sun Apr 21 16:37:42 1996 by r.faith@ieee.org
+.\" Revised: Sun Jun 13 01:59:58 2004 by francois.fleuret@epfl.ch
+.\" Revised: Wed Oct 13 18:33:12 2004 by francois.fleuret@epfl.ch
+.\" Revised: Wed Dec 03 08:52:14 2004 by francois.fleuret@epfl.ch
+.\" Copyright 1996 Rickard E. Faith (r.faith@ieee.org)
+.\" 
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\" 
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\" 
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.  The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\" 
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\" 
+.TH XACPI 1 "June 2004" "" ""
+.SH NAME
+xacpi \- display Advanced Configuration and Power Interface (ACPI) BIOS information
+.SH SYNOPSIS
+.B xacpi [ ... ]
+.SH DESCRIPTION
+.B xacpi
+puts up an X window that displays, from left to right and optionally:
+the time and date, the AC state (L for line and B for battery), the
+battery charge (in percentage or remaining time for complete charge or
+discharge), a colored bar representing the same, the CPU temperature
+and the CPU frequency in Ghz. Clicking on the charge time will toggle
+the display between time and percentage, and clicking on the temperature
+will toggle between Celsius and Fahrenheit.
+.SH "OPTIONS AND RESOURCES"
+In addition to standard Xt options, the following options are available:
+.TP
+.B \-V
+Print the program version number and exit.
+.TP
+.B \-H
+Print a short help
+.TP
+.BI \-highcolor " color"
+The color of the bar when the battery percentage is above
+.IR highvalue .
+Defaults to "green". (*highColor)
+.TP
+.BI \-lowcolor " color"
+The color of the bar when the battery percentage is between
+.I lowvalue
+and
+.IR highvalue .
+Defaults to "yellow". (*lowColor)
+.TP
+.BI \-criticalcolor " color"
+The color of the bar when the battery percentage is below
+.IR lowvalue
+or the color of the temperature when higher then the critical temperature.
+Defaults to "red".
+This color is also used to flash the alphanumeric part of the display
+when the power status is critical. (*criticalColor)
+.TP
+.BI \-highvalue " highvalue"
+Set
+.IR highvalue .
+The default is 50. (*highValue)
+.TP
+.BI \-lowvalue " lowvalue"
+Set
+.IR lowvalue .
+The default is 10. (*lowValue)
+.TP
+.BI \-criticaltemperature " criticaltemperature"
+Set
+.IR criticaltemperature .
+If not set the default is 70C or 158F. (*criticalTemperature)
+.TP
+.BI \-chargingcolor " color"
+The color of the printed display when charging.
+The default is "blue".
+Use
+.B \-foreground
+to change the color used when on battery power. (*chargingColor)
+.TP
+.BI \-delay " delay"
+Sets the number of seconds delay between each update.
+The default is 1. (*delay)
+.TP
+.B \-percent
+Display percentage on startup, instead of time remaining. (*percent, boolean)
+.TP
+.B \-percentauto
+Swap automatically between displaying percentage and displaying the
+time remaining. When on AC power, display percentage, when on battery
+power, display the time remaining. (*percentAuto, boolean)
+.TP
+.B \-showbar
+Show the battery colored bar. (*showBar, boolean)
+.TP
+.B \-showdate
+Show the current time and date. (*showDate, boolean)
+.TP
+.B \-dateformat
+Specifies the format for the date (cf. strftime). Default is
+.I %a %b %e %H:%M:%S %Z %Y
+ (*dateFormat).
+.TP
+.B \-showtemp
+Show the temperature (maximum among the sensors). (*showTemperature, boolean)
+.TP
+.B \-showcpufreq
+Show the CPU frequency (in Ghz). (*showCpufreq, boolean)
+.TP
+.B \-fahrenheit
+Display the temperature in Fahrenheit. (*fahrenheit, boolean)
+.TP
+.B \-acpidir
+Specifies the ACPI directory. Default is
+.I /proc/acpi
+ (*acpiDir).
+.TP
+.B \-cpufreqdir
+Specifies the cpu frequency directory. Default is
+.I /sys/devices/system/cpu/cpu0/cpufreq
+ (*cpuFreqDir).
+.SH BUGS
+When the battery percentage is low, using a bright color to warn you
+of impending doom may be futile because the bar is too short. Because
+of the form widget's desire to maintain proportions, dynamic resizing
+of the window may not do what you want it to do.
+.SH FILES
+.I /proc/acpi
+.SH AUTHOR
+This program is an extension of xapm, originally written by Rik Faith
+(faith@cs.unc.edu). The changes to xacpi were written by Francois
+Fleuret (francois.fleuret@epfl.ch). Xacpi may be freely distributed
+under the terms of the GNU General Public License.  There is
+ABSOLUTELY NO WARRANTY for this program.
+.SH "SEE ALSO"
+.BR acpi (1),
+.BR acpid (8),
+.BR strftime (3).
diff --git a/xacpi.c b/xacpi.c
new file mode 100644 (file)
index 0000000..c371296
--- /dev/null
+++ b/xacpi.c
@@ -0,0 +1,799 @@
+/* xacpi.c --
+ *
+ * This is a patched xapm working with acpi (which does not support
+ * apm anymore)
+ *
+ * xapm was written and (C) by Rickard E. Faith (r.faith@ieee.org)
+ *
+ * 2004/06/10 Patch for acpi by Francois Fleuret (francois.fleuret@epfl.ch)
+ * 2004/07/20 Patch from Bryan Cardillo (dillo@seas.upenn.edu)
+ *
+ * To compile:
+ *
+ *  gcc -DNARROWPROTO -o xacpi -L /usr/X11R6/lib/ -lX11 -lXaw xacpi.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * $Id: xacpi.c,v 1.40 2004/12/03 07:52:30 fleuret Exp $
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <err.h>
+#include <time.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Scrollbar.h>
+
+/* The acpi file readings can fail sometime, but we will immediately
+   retry. We display ERR in the window if we fail more than that
+   number of times . */
+
+#define NB_TOLERATED_FAILURES 1
+
+#define XACPI_VERSION "1.3"
+#define MIN_BAR_WIDTH 32
+
+#define DEFAULT_ACPI_DIR "/proc/acpi"
+#define DEFAULT_CPUFREQ_DIR "/sys/devices/system/cpu/cpu0/cpufreq"
+
+#define BUFFER_LEN 1024
+
+#define DEFAULT_DELAY 5
+#define DEFAULT_DATE_FORMAT "%a %b %e %H:%M:%S %Z %Y"
+
+typedef struct _instance_variables {
+  Pixel highColor, lowColor, criticalColor, chargingColor;
+  Pixel foreground;
+  int highValue, lowValue, criticalTemp;
+  String geometry;
+  int delay;
+  Boolean displayPercent, displayPercentAuto;
+  Boolean showBar, showTemperature;
+  Boolean showCpufreq;
+  String cpuFreqDir;
+  String dateFormat;
+  Boolean showDate;
+  Boolean fahrenheit;
+  String acpiDir;
+} instance_variable_rec;
+
+struct acpi_info {
+  int design_capacity, last_full_capacity, remaining_capacity;
+  int present_rate;
+  int ac_on_line;
+  int charging;
+  int battery_percentage, battery_time;
+  int temperature;
+  int cpufreq;
+};
+
+static XtAppContext app_context;
+static Widget scrollbar;
+static Widget topLevel;
+static Widget form;
+static Widget delay;
+static Widget temperature;
+static Widget cpufreq;
+static Widget date;
+static XtIntervalId timerId;
+static instance_variable_rec iv;
+static int debug;
+
+static int nb_failures;
+
+char *get_field_name(char *buffer, char *r) {
+  while(*r && *r != ':') *(buffer++) = *(r++);
+  while(*r == ' ' || *r == ':') r++;
+  *(buffer++) = '\0';
+  return r;
+}
+
+int get_int(char *r) {
+  char tmp[BUFFER_LEN];
+  char *t = tmp;
+  while(*r >= '0' && *r <= '9') *(t++) = *(r++);
+  *t = '\0';
+  return atoi(tmp);
+}
+
+char *goto_next_line(char *r) {
+  while(*r && *r != '\n') r++;
+  while(*r == '\n') r++;
+  if(*r) return r; else return 0;
+}
+
+/* We make the assumption that the file is shorter than BUFFER_LEN
+   bytes. */
+
+void read_proc_file(char *path, char *subdir, char *filename, char *content) {
+  char name[BUFFER_LEN];
+  size_t len;
+  FILE *file;
+
+  snprintf(name, BUFFER_LEN, "%s/%s/%s", path, subdir, filename);
+
+  file = fopen(name, "r");
+  if(file) {
+#ifdef DEBUG
+    printf("reading of [%s]\n", name);
+#endif
+    len = fread(content, 1, BUFFER_LEN - 1, file);
+    fclose(file);
+  } else {
+#ifdef DEBUG
+    printf("opening of [%s] failed\n", name);
+#endif
+    len = 0;
+  }
+
+  if(len == 0) {
+    err(1, "%s", name);
+#ifdef DEBUG
+    printf("reading of [%s] failed\n", name);
+#endif
+  }
+  else content[len] = '\0';
+}
+
+void check_acpi(char *acpi_dir) {
+  DIR *directory;
+
+  directory= opendir(acpi_dir);
+  if(directory) closedir(directory);
+  else err(1, "%s", acpi_dir);
+}
+
+void acpi_read(char *acpi_dir, char *cpu_dir, struct acpi_info *acpi) {
+  DIR *directory;
+  struct dirent *dir_entry;
+  char content[BUFFER_LEN], word[BUFFER_LEN], dir_name[BUFFER_LEN];
+
+  int error = 0;
+
+  memset(acpi, 0, sizeof(*acpi));
+
+  /* Read the battery information */
+
+  snprintf(dir_name, BUFFER_LEN, "%s/battery", acpi_dir);
+  directory = opendir(dir_name);
+  if (!(directory)) err(1, "%s", dir_name);
+
+  while((dir_entry = readdir(directory))) {
+    if(dir_entry->d_name[0] != '.') {
+      char *s;
+
+      read_proc_file(dir_name, dir_entry->d_name, "info", content);
+
+      s = content;
+      while(s) {
+        s = get_field_name(word, s);
+
+        if(strcmp(word, "ERROR") == 0) {
+#ifdef DEBUG
+          printf("Read \"ERROR\"\n");
+#endif
+          error = 1;
+        }
+
+        else if(strcmp(word, "design capacity") == 0 && s) acpi->design_capacity += get_int(s);
+        else if(strcmp(word, "last full capacity") == 0 && s) acpi->last_full_capacity += get_int(s);
+        s = goto_next_line(s);
+      }
+
+      read_proc_file(dir_name, dir_entry->d_name, "state", content);
+      s = content;
+      while(s) {
+        s = get_field_name(word, s);
+        if(strcmp(word, "ERROR") == 0) {
+#ifdef DEBUG
+          printf("Read \"ERROR\"\n");
+#endif
+          error = 1;
+        }
+
+        else if(strcmp(word, "present rate") == 0 && s) acpi->present_rate += get_int(s);
+        else if(strcmp(word, "remaining capacity") == 0 && s) acpi->remaining_capacity += get_int(s);
+        else if(strcmp(word, "charging state") == 0 && s) {
+          if(strncmp(s, "charging", 8) == 0) acpi->charging = 1;
+          else if(strncmp(s, "charged", 7) == 0) acpi->charging = 0;
+          else if(strncmp(s, "discharging", 11) == 0) acpi->charging = -1;
+        }
+        s = goto_next_line(s);
+      }
+    }
+  }
+
+  closedir(directory);
+
+  /* Read the AC line information */
+
+  snprintf(dir_name, BUFFER_LEN, "%s/ac_adapter", acpi_dir);
+  directory = opendir(dir_name);
+  if (!(directory)) err(1, "%s", dir_name);
+
+  while((dir_entry = readdir(directory))) {
+    if(dir_entry->d_name[0] != '.') {
+      char *s;
+      read_proc_file(dir_name, dir_entry->d_name, "state", content);
+      s = content;
+      while(s) {
+        s = get_field_name(word, s);
+        if(strcmp(word, "ERROR") == 0) {
+#ifdef DEBUG
+          printf("Read \"ERROR\"\n");
+#endif
+          error = 1;
+        }
+
+        else if(strcmp(word, "state") == 0 && s)
+          acpi->ac_on_line = (strncmp(s, "on-line", 7) == 0);
+        s = goto_next_line(s);
+      }
+    }
+  }
+
+  closedir(directory);
+
+  /* When required, read the temperature information */
+
+  if(temperature) {
+    snprintf(dir_name, BUFFER_LEN, "%s/thermal_zone", acpi_dir);
+    directory = opendir(dir_name);
+    if (!(directory)) err(1, "%s", dir_name);
+
+    acpi->temperature = -1;
+
+    while((dir_entry = readdir(directory))) {
+      if(dir_entry->d_name[0] != '.') {
+        char *s;
+        read_proc_file(dir_name, dir_entry->d_name, "temperature", content);
+        s = content;
+        while(s) {
+          s = get_field_name(word, s);
+          if(strcmp(word, "ERROR") == 0) {
+#ifdef DEBUG
+          printf("Read \"ERROR\"\n");
+#endif
+            error = 1;
+          }
+
+          else if(strcmp(word, "temperature") == 0 && s) {
+            int temp = get_int(s);
+            if(temp > acpi->temperature) acpi->temperature = temp;
+          }
+          s = goto_next_line(s);
+        }
+      }
+    }
+
+    closedir(directory);
+  }
+
+  /* When required, read the frequency information */
+
+  if(cpufreq) {
+    read_proc_file(cpu_dir, "", "scaling_cur_freq", content);
+    char *s = content;
+    acpi->cpufreq = get_int(s)/1000;
+  }
+
+  if(error) nb_failures++; else nb_failures = 0;
+
+  if(nb_failures == 0) {
+
+    // Bryan Cardillo preferes to see the percentage of the design
+    // capacity. I think it's kind of depressive to see that the
+    // battery gets worst and worst ...
+
+    /*     if (acpi->charging == 0) { */
+    /*       acpi->battery_percentage = (acpi->remaining_capacity * 100) / acpi->design_capacity; */
+    /*     } else { */
+    acpi->battery_percentage = (acpi->remaining_capacity * 100) / acpi->last_full_capacity;
+    /*     } */
+
+    if(acpi->present_rate) {
+      if(acpi->charging > 0)
+        acpi->battery_time = ((acpi->last_full_capacity - acpi->remaining_capacity) * 3600) / acpi->present_rate;
+      else if(acpi->charging < 0)
+        acpi->battery_time = (acpi->remaining_capacity * 3600) / acpi->present_rate;
+    }
+  } else if(nb_failures > NB_TOLERATED_FAILURES) {
+    acpi->battery_time = -1;
+  }
+}
+
+void put_date(char *buf, char *format) {
+  time_t t;
+  time(&t);
+  strftime(buf, BUFFER_LEN, format, localtime(&t));
+}
+
+static void update(XtPointer client_data, XtIntervalId * id) {
+  struct acpi_info acpi;
+  char buf[128];
+
+  static int current_bar_color = -1;
+  static int current_charging_color = -1;
+  static int current_temperature_color = -1;
+  static int blink_counter = 0;
+
+  static int lastPercentage = -1;
+  static int lastMinutes = -1;
+  static int lastDisplay = -1;
+  static int lastACStatus = -1;
+  static int lastTemperature = -1;
+  static int lastCpufreq = -1;
+
+  acpi_read(iv.acpiDir, iv.cpuFreqDir, &acpi);
+
+  if(nb_failures > NB_TOLERATED_FAILURES) {
+
+    XtVaSetValues(delay, XtNlabel, "ERR", NULL);
+
+    if(scrollbar && current_bar_color != iv.criticalColor) {
+      current_bar_color = iv.criticalColor;
+      XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
+      XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
+    }
+
+    /* All this will have to be refreshed */
+
+    lastPercentage = -1;
+    lastMinutes = -1;
+    lastDisplay = -1;
+    blink_counter = 0;
+    lastACStatus = -1;
+    lastTemperature = -1;
+    lastCpufreq = -1;
+
+  }
+
+  /* We refresh only if there were no error during updating */
+
+  else if(nb_failures == 0) {
+
+    if(iv.displayPercentAuto && acpi.ac_on_line != lastACStatus){
+      lastDisplay = -1;
+      iv.displayPercent = acpi.ac_on_line;
+    }
+
+    if (iv.displayPercent) {
+      if (lastDisplay != iv.displayPercent
+          || acpi.battery_percentage != lastPercentage
+          || acpi.ac_on_line != lastACStatus) {
+
+        /* lastPercentage updated at end */
+
+        snprintf(buf, BUFFER_LEN, "%s%d%%", acpi.ac_on_line ? "L" : "B", acpi.battery_percentage);
+        XtVaSetValues(delay, XtNlabel, buf, NULL);
+      }
+    } else {
+
+      /* Negative value means we could not estimate it in acpi_read
+         because the charging speed was 0 */
+
+      if(acpi.battery_time < 0) {
+        snprintf(buf, BUFFER_LEN, "%s", acpi.ac_on_line ? "L" : "B???");
+        XtVaSetValues(delay, XtNlabel, buf, NULL);
+
+      } else {
+
+        int minutes = acpi.battery_time / 60;
+        if (lastDisplay != iv.displayPercent || lastMinutes != minutes
+            || acpi.ac_on_line != lastACStatus
+            ) {
+          lastMinutes = minutes;
+          snprintf(buf, BUFFER_LEN, "%s%lu:%02lu", acpi.ac_on_line ? "L" : "B", minutes/60, minutes%60);
+          XtVaSetValues(delay, XtNlabel, buf, NULL);
+        }
+
+      }
+    }
+
+    lastDisplay = iv.displayPercent;
+    lastACStatus = acpi.ac_on_line;
+
+    if(scrollbar) {
+      if (acpi.battery_percentage <= iv.lowValue) {
+        if (current_bar_color != iv.criticalColor) {
+          current_bar_color = iv.criticalColor;
+          XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
+          XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
+        }
+      } else if (acpi.battery_percentage <= iv.highValue) {
+        if (current_bar_color != iv.lowColor) {
+          current_bar_color = iv.lowColor;
+          XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
+          XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
+        }
+      } else {
+        if (current_bar_color != iv.highColor) {
+          current_bar_color = iv.highColor;
+          XtVaSetValues(scrollbar, XtNforeground, current_bar_color, NULL);
+          XtVaSetValues(scrollbar, XtNborderColor, current_bar_color, NULL);
+        }
+      }
+    }
+
+    if(temperature) {
+      int degrees = (iv.fahrenheit) ?
+            32 + (acpi.temperature * 18) / 10 : acpi.temperature;
+
+      if (degrees != lastTemperature) {
+        if(iv.fahrenheit) {
+          snprintf(buf, BUFFER_LEN, "%dF", degrees);
+        } else {
+          snprintf(buf, BUFFER_LEN, "%dC", degrees);
+        }
+        XtVaSetValues(temperature, XtNlabel, buf, NULL);
+      }
+
+      if(degrees >= iv.criticalTemp) {
+        if (current_temperature_color != iv.criticalColor) {
+          current_temperature_color = iv.criticalColor;
+          XtVaSetValues(temperature, XtNforeground, current_temperature_color, NULL);
+        }
+      } else {
+        if (current_temperature_color != iv.foreground) {
+          current_temperature_color = iv.foreground;
+          XtVaSetValues(temperature, XtNforeground, current_temperature_color, NULL);
+        }
+      }
+
+      lastTemperature = degrees;
+    }
+
+    if(cpufreq && acpi.cpufreq != lastCpufreq) {
+      snprintf(buf, BUFFER_LEN, "%.1fGhz", ((float) acpi.cpufreq)/1000.0);
+      XtVaSetValues(cpufreq, XtNlabel, buf, NULL);
+    }
+
+    if(date) {
+      put_date(buf, iv.dateFormat);
+      XtVaSetValues(date, XtNlabel, buf, NULL);
+    }
+
+    if (acpi.charging > 0) {
+      if (current_charging_color != iv.chargingColor)
+        XtVaSetValues(delay, XtNforeground,
+                      current_charging_color = iv.chargingColor, NULL);
+    } else {
+      if (acpi.battery_percentage < iv.lowValue && blink_counter++ % 2) {
+        if (current_charging_color != iv.criticalColor)
+          XtVaSetValues(delay,
+                        XtNforeground, current_charging_color = iv.criticalColor, NULL);
+      } else {
+        if (current_charging_color != iv.foreground)
+          XtVaSetValues(delay,
+                        XtNforeground, current_charging_color = iv.foreground, NULL);
+      }
+    }
+
+    if (scrollbar && acpi.battery_percentage != lastPercentage) {
+      XawScrollbarSetThumb(scrollbar, 0.0,
+                           acpi.battery_percentage < 0 ? 0.0 : acpi.battery_percentage / 100.0);
+      lastPercentage = acpi.battery_percentage;
+    }
+  }
+
+  /* We come back in 0.5s if there was an error */
+
+  timerId = XtAppAddTimeOut(app_context,
+                            ((nb_failures > 0 && nb_failures <= NB_TOLERATED_FAILURES)) ? 250 : (1000 * iv.delay + 500),
+                            update, app_context);
+}
+
+static void press(Widget w, XtPointer client_data, XtPointer call_data){
+  if (w == delay) {
+    iv.displayPercent = !iv.displayPercent;
+  } else if (w == temperature) {
+    iv.fahrenheit = !iv.fahrenheit;
+  }
+  XtRemoveTimeOut(timerId);
+  timerId = XtAppAddTimeOut(app_context, 0, update, app_context);
+}
+
+static XrmOptionDescRec options[] = {
+  {"-highcolor", "*highColor", XrmoptionSepArg, NULL},
+  {"-lowcolor", "*lowColor", XrmoptionSepArg, NULL},
+  {"-criticalcolor", "*criticalColor", XrmoptionSepArg, NULL},
+  {"-criticaltemperature", "*criticalTemperature", XrmoptionSepArg, NULL},
+  {"-chargingcolor", "*chargingColor", XrmoptionSepArg, NULL},
+  {"-highvalue", "*highValue", XrmoptionSepArg, NULL},
+  {"-lowvalue", "*lowValue", XrmoptionSepArg, NULL},
+  {"-delay", "*delay", XrmoptionSepArg, NULL},
+  {"-percent", "*percent", XrmoptionNoArg, (XtPointer) "true"},
+  {"-percentauto", "*percentAuto", XrmoptionNoArg, (XtPointer) "true"},
+  {"-showbar", "*showBar", XrmoptionNoArg, (XtPointer) "true"},
+  {"-showtemp", "*showTemperature", XrmoptionNoArg, (XtPointer) "true"},
+  {"-showcpufreq", "*showCpuFreq", XrmoptionNoArg, (XtPointer) "true"},
+  {"-showdate", "*showDate", XrmoptionNoArg, (XtPointer) "true"},
+  {"-fahrenheit", "*fahrenheit", XrmoptionNoArg, (XtPointer) "true"},
+  {"-dateformat", "*dateFormat", XrmoptionSepArg, NULL},
+  {"-acpidir", "*acpiDir", XrmoptionSepArg, NULL},
+  {"-cpufreqdir", "*cpuFreqDir", XrmoptionSepArg, NULL},
+};
+
+#define offset(field) XtOffsetOf( instance_variable_rec, field )
+
+static XtResource resources[] = {
+
+  {"highColor", XtCForeground, XtRPixel, sizeof(Pixel),
+   offset(highColor), XtRString, "green"},
+  {"lowColor", XtCForeground, XtRPixel, sizeof(Pixel),
+   offset(lowColor), XtRString, "yellow"},
+  {"criticalColor", XtCForeground, XtRPixel, sizeof(Pixel),
+   offset(criticalColor), XtRString, "red"},
+  {"chargingColor", XtCForeground, XtRPixel, sizeof(Pixel),
+   offset(chargingColor), XtRString, "blue"},
+
+  {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
+   offset(foreground), XtRString, XtDefaultForeground},
+  {"highValue", XtCValue, XtRInt, sizeof(int),
+   offset(highValue), XtRImmediate, (XtPointer) 50},
+  {"lowValue", XtCValue, XtRInt, sizeof(int),
+   offset(lowValue), XtRImmediate, (XtPointer) 10},
+  {"criticalTemperature", XtCValue, XtRInt, sizeof(int),
+   offset(criticalTemp), XtRImmediate, (XtPointer) 0},
+  {"geometry", XtCString, XtRString, sizeof(String),
+   offset(geometry), XtRString, (XtPointer) "170x10"},
+  {"delay", XtCValue, XtRInt, sizeof(int),
+   offset(delay), XtRImmediate, (XtPointer) DEFAULT_DELAY},
+  {"percent", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(displayPercent), XtRImmediate, (XtPointer) FALSE},
+  {"percentAuto", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(displayPercentAuto), XtRImmediate, (XtPointer) FALSE},
+  {"showBar", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(showBar), XtRImmediate, (XtPointer) FALSE},
+  {"showTemperature", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(showTemperature), XtRImmediate, (XtPointer) FALSE},
+  {"showCpuFreq", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(showCpufreq), XtRImmediate, (XtPointer) FALSE},
+  {"showDate", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(showDate), XtRImmediate, (XtPointer) FALSE},
+  {"fahrenheit", XtCValue, XtRBoolean, sizeof(Boolean),
+   offset(fahrenheit), XtRImmediate, (XtPointer) FALSE},
+  {"dateFormat", XtCString, XtRString, sizeof(String),
+   offset(dateFormat), XtRString, (XtPointer) DEFAULT_DATE_FORMAT},
+  {"acpiDir", XtCString, XtRString, sizeof(String),
+   offset(acpiDir), XtRString, (XtPointer) DEFAULT_ACPI_DIR},
+  {"cpuFreqDir", XtCString, XtRString, sizeof(String),
+   offset(cpuFreqDir), XtRString, (XtPointer) DEFAULT_CPUFREQ_DIR},
+};
+
+static void quit(Widget w, XtPointer client_data, XtPointer call_data){
+  exit (0);
+}
+
+static XtActionsRec main_actions[] = { {"Quit", (XtActionProc) quit},
+};
+
+static String main_translations = "<Message>WM_PROTOCOLS:Quit()\n";
+
+int main(int argc, char **argv){
+  char c;
+
+  int x = 0, y = 0, height = 0, width = 0;
+  int current_width = 0, own_width;
+  XFontStruct *fs;
+  Atom wm_protocols[1];
+  Widget last;
+
+  nb_failures = 0;
+
+  topLevel = XtVaAppInitialize(&app_context, "XAcpi",
+                               options, XtNumber(options),
+                               &argc, argv, NULL, NULL);
+
+  XtGetApplicationResources(topLevel,
+                            &iv,
+                            resources,
+                            XtNumber(resources),
+                            NULL, 0);
+
+  if(iv.criticalTemp <= 0) {
+    if(iv.fahrenheit) iv.criticalTemp = 158;
+    else              iv.criticalTemp = 70;
+  }
+
+  check_acpi(iv.acpiDir);
+
+  if (iv.delay < 1) iv.delay = DEFAULT_DELAY;
+
+  XParseGeometry(iv.geometry, &x, &y, &width, &height);
+
+  while ((c = getopt(argc, argv, "DVH")) != -1)
+    switch (c) {
+    case 'D':
+      ++debug;
+      break;
+    case 'V':
+      fprintf(stderr, "xacpi version " XACPI_VERSION ".\n");
+      fprintf(stderr, "$Id: xacpi.c,v 1.40 2004/12/03 07:52:30 fleuret Exp $.\n");
+      exit(0);
+    case 'H':
+      printf("Options:\n"
+             " -D\n"
+             " -V\n"
+             " -H\n"
+             " -highcolor <string: color>\n"
+             " -lowcolor <string: color>\n"
+             " -criticalcolor <string: color>\n"
+             " -chargingcolor <string: color>\n"
+             " -highvalue <int: percentage>\n"
+             " -lowvalue <int: percentage>\n"
+             " -delay <int: seconds>\n"
+             " -percent\n"
+             " -percentauto\n"
+             " -showbar\n"
+             " -showtemp\n"
+             " -showcpufreq\n"
+             " -showdate\n"
+             " -dateformat <string: format>\n"
+             " -fahrenheit\n"
+             " -acpidir <string: path>\n"
+             " -cpufreqdir <string: path>\n"
+             "\n"
+             "See man xacpi for more detailed information.\n");
+      exit(0);
+      break;
+    }
+
+  form = XtVaCreateManagedWidget("form",
+                                 formWidgetClass, topLevel,
+                                 XtNorientation, XtorientHorizontal,
+                                 XtNborderWidth, 0,
+                                 XtNdefaultDistance, 0,
+                                 NULL);
+
+  /* Add the date */
+
+  if(iv.showDate) {
+    date = XtVaCreateManagedWidget("date",
+                                   labelWidgetClass, form,
+                                   XtNinternalHeight, 0,
+                                   XtNinternalWidth, 0,
+                                   XtNborderWidth, 0,
+                                   XtNlabel, "",
+                                   XtNresize, FALSE,
+                                   NULL);
+
+    char buf[BUFFER_LEN];
+    put_date(buf, iv.dateFormat);
+    last = date;
+    XtVaGetValues(last, XtNfont, &fs, NULL);
+    own_width = ((strlen(buf) + 2) * fs->max_bounds.width);
+    XtVaSetValues(last, XtNwidth, own_width, NULL);
+    current_width += own_width;
+  } else date = 0;
+
+  /* Add the widget to display the remaining battery time */
+
+  delay = XtVaCreateManagedWidget("delay",
+                                  commandWidgetClass, form,
+                                  XtNleft, XtChainLeft,
+                                  XtNhighlightThickness, 0,
+                                  XtNinternalHeight, 0,
+                                  XtNinternalWidth, 0,
+                                  XtNborderWidth, 0,
+                                  XtNlabel, "",
+                                  XtNresize, FALSE,
+                                  NULL);
+
+
+  if(date) XtVaSetValues(delay, XtNfromHoriz, last, 0);
+  XtAddCallback(delay, XtNcallback, press, NULL);
+
+  last = delay;
+  XtVaGetValues(last, XtNfont, &fs, NULL);
+  own_width = 6 * fs->max_bounds.width;
+  XtVaSetValues(last, XtNwidth, own_width, NULL);
+  current_width += own_width;
+
+  /* Add the widget to display the colored bar */
+
+  if(iv.showBar) {
+
+    scrollbar = XtVaCreateManagedWidget("scrollbar",
+                                        scrollbarWidgetClass, form,
+                                        XtNhorizDistance, 3,
+                                        XtNfromHoriz, last,
+                                        XtNorientation, XtorientHorizontal,
+                                        NULL);
+
+    XawScrollbarSetThumb(scrollbar, 0.0, 0.0);
+
+    XtVaSetValues(scrollbar,
+                  XtNtranslations, XtParseTranslationTable(""),
+                  NULL);
+
+    last = scrollbar;
+  } else scrollbar = 0;
+
+  /* Add the widget to display the temperature */
+
+  if(iv.showTemperature) {
+    temperature = XtVaCreateManagedWidget("temperature",
+                                          commandWidgetClass, form,
+                                          XtNhorizDistance, 6,
+                                          XtNfromHoriz, last,
+                                          XtNhighlightThickness, 0,
+                                          XtNinternalHeight, 0,
+                                          XtNinternalWidth, 0,
+                                          XtNborderWidth, 0,
+                                          XtNlabel, iv.fahrenheit ? "000F " : "00C ",
+                                          XtNresize, FALSE,
+                                          NULL);
+    last = temperature;
+    XtVaGetValues(last, XtNfont, &fs, NULL);
+    own_width = (iv. fahrenheit ? 5 : 4) * fs->max_bounds.width;
+    XtVaSetValues(last, XtNwidth, own_width, NULL);
+    current_width += own_width;
+    XtAddCallback(temperature, XtNcallback, press, NULL);
+  } else temperature = 0;
+
+  /* Add the widget to display the cpu frequency */
+
+  if(iv.showCpufreq) {
+    cpufreq = XtVaCreateManagedWidget("cpufreq",
+                                      labelWidgetClass, form,
+                                      XtNhorizDistance, 6,
+                                      XtNfromHoriz, last,
+                                      XtNinternalHeight, 0,
+                                      XtNinternalWidth, 0,
+                                      XtNborderWidth, 0,
+                                      XtNlabel, "?.?Ghz",
+                                      XtNresize, FALSE,
+                                      NULL);
+    last = cpufreq;
+    XtVaGetValues(last, XtNfont, &fs, NULL);
+    own_width = 7 * fs->max_bounds.width;
+    XtVaSetValues(last, XtNwidth, own_width, NULL);
+    current_width += own_width;
+  } else cpufreq = 0;
+
+  /* Changed the bar length to the unused width */
+
+  if(scrollbar) {
+    if(width > current_width + MIN_BAR_WIDTH)
+      XtVaSetValues(scrollbar, XtNwidth, width - current_width, 0);
+    else {
+      XtVaSetValues(scrollbar, XtNwidth, MIN_BAR_WIDTH, 0);
+    }
+  }
+
+  XtRealizeWidget(topLevel);
+
+  /* Add code to handle WM_DELETE_WINDOW cleanly.  */
+
+  XtAppAddActions(app_context, main_actions, XtNumber(main_actions));
+  XtOverrideTranslations(topLevel, XtParseTranslationTable(main_translations));
+  wm_protocols[0]
+    = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False);
+  XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel), wm_protocols, 1);
+
+  timerId = XtAppAddTimeOut(app_context, 0, update, app_context);
+  XtAppMainLoop(app_context);
+
+  return 0;
+}