$NetBSD: patch-ab,v 1.6 2003/11/10 23:53:32 mrg Exp $

--- xbattbar.c.orig	2001-02-02 16:25:29.000000000 +1100
+++ xbattbar.c	2003-11-10 17:50:08.000000000 +1100
@@ -27,6 +27,14 @@
 
 #include <sys/types.h>
 #include <sys/time.h>
+
+#ifdef __NetBSD__
+#define ENVSYSUNITNAMES
+#include <sys/param.h>
+#include <sys/envsys.h>
+#include <paths.h>
+#endif /* __NetBSD__ */
+
 #include <signal.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -583,39 +591,174 @@
 #define _PATH_APM_CTLDEV       "/dev/apmctl"
 #define _PATH_APM_NORMAL       "/dev/apm"
 
+/*
+ * pre:  fd contains a valid file descriptor of an envsys(4) supporting device
+ *       && ns is the number of sensors
+ *       && etds and ebis are arrays of sufficient size
+ * post: returns 0 and etds and ebis arrays are filled with sensor info
+ *       or returns -1 on failure
+ */
+static int
+fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis,
+    size_t ns)
+{
+	int i;
+
+	for (i = 0; i < ns; ++i) {
+		ebis[i].sensor = i;
+		if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1) {
+			warn("Can't get sensor info for sensor %d", i);
+			return 0;
+		}
+
+		etds[i].sensor = i;
+		if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1) {
+			warn("Can't get sensor data for sensor %d", i);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ * pre:  fd contains a valid file descriptor of an envsys(4) supporting device
+ * post: returns the number of valid sensors provided by the device
+ *       or -1 on error
+ */
+static size_t
+numsensors(int fd)
+{
+	int count = 0, valid = 1;
+	envsys_tre_data_t etd;
+	etd.sensor = 0;
+
+	while (valid) {
+		if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1)
+			err(1, "Can't get sensor data");
+
+		valid = etd.validflags & ENVSYS_FVALID;
+		if (valid)
+			++count;
+
+		++etd.sensor;
+	}
+
+	return count;
+}
+
+static envsys_tre_data_t *etds;
+static envsys_basic_info_t *ebis;
+static int *cetds;
+
+#if defined(_PATH_SYSMON) && __NetBSD_Version__ >= 106110000
+#define HAVE_NETBSD_ACPI
+#endif
+
 int first = 1;
 void battery_check(void)
 {
        int fd, r, p;
        struct apm_power_info info;
-
-       if ((fd = open(_PATH_APM_NORMAL, O_RDONLY)) == -1) {
-               fprintf(stderr, "xbattbar: cannot open apm device\n");
-               exit(1);
+       int acpi;
+       size_t ns;
+       size_t cc;
+       char *apmdev;
+       int i;
+
+       acpi = 0;
+       apmdev = _PATH_APM_NORMAL;
+       if ((fd = open(apmdev, O_RDONLY)) == -1) {
+#ifdef HAVE_NETBSD_ACPI
+	       apmdev = _PATH_SYSMON;
+	       fd = open(apmdev, O_RDONLY);
+	       acpi = 1;
+#endif
        }
-
-       if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
-               fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n");
+       if (fd < 0) {
+               fprintf(stderr, "xbattbar: cannot open %s device\n", apmdev);
                exit(1);
        }
 
-       close(fd);
+       if (acpi) {
+#ifdef HAVE_NETBSD_ACPI
+		if ((ns = numsensors(fd)) == 0) {
+		       fprintf(stderr, "xbattbar: no sensors found\n");
+		       exit(1);
+		}
+		if (first) {
+			cetds = (int *)malloc(ns * sizeof(int));
+			etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t));
+			ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t));
+
+			if ((cetds == NULL) || (etds == NULL) || (ebis == NULL)) {
+				err(1, "Out of memory");
+			}
+		}
 
-       ++elapsed_time;
+		fillsensors(fd, etds, ebis, ns);
 
-       /* get current remoain */
-       if (info.battery_life > 100) {
-               /* some APM BIOSes return values slightly > 100 */
-               r = 100;
+#endif
        } else {
-               r = info.battery_life;
+
+	       memset(&info, 0, sizeof(info));
+	       if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
+		       fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n");
+		       exit(1);
+	       }
        }
 
-       /* get AC-line status */
-       if (info.ac_state == APM_AC_ON) {
-               p = APM_AC_ON;
+       close(fd);
+
+       ++elapsed_time;
+
+       if (acpi) {
+#ifdef HAVE_NETBSD_ACPI
+		int32_t rtot = 0, maxtot = 0;
+		p = APM_AC_ON;
+		for (i = 0 ; i < ns ; i++) {
+			if ((etds[i].validflags & ENVSYS_FCURVALID) == 0)
+				continue;
+			cc = strlen(ebis[i].desc);
+			if (strncmp(ebis[i].desc, "acpibat", 7) == 0 &&
+			    (strcmp(&ebis[i].desc[cc - 7], " charge") == 0 ||
+			     strcmp(&ebis[i].desc[cc - 7], " energy") == 0)) {
+				rtot += etds[i].cur.data_s;
+				maxtot += etds[i].max.data_s;
+			}
+			/*
+			 * XXX: We should use acpiacad driver and look for
+			 * " connected", but that's broken on some machines
+			 * and we want this to work everywhere.  With this
+			 * we will occasionally catch a machine conditioning
+			 * a battery while connected, while other machines take
+			 * 10-15 seconds to switch from "charging" to
+			 * "discharging" and vice versa, but this is the best
+			 * compromise.
+			 */
+			if (ebis[i].units == ENVSYS_INDICATOR &&
+			    etds[i].cur.data_s &&
+			    strncmp(ebis[i].desc, "acpibat", 7) == 0 &&
+			    strcmp(&ebis[i].desc[cc - 11], "discharging") == 0) {
+				p = APM_AC_OFF;
+			}
+		}
+		r = (rtot * 100.0) / maxtot;
+#endif
        } else {
-               p = APM_AC_OFF;
+	       /* get current remain */
+	       if (info.battery_life > 100) {
+		       /* some APM BIOSes return values slightly > 100 */
+		       r = 100;
+	       } else {
+		       r = info.battery_life;
+	       }
+
+	       /* get AC-line status */
+	       if (info.ac_state == APM_AC_ON) {
+		       p = APM_AC_ON;
+	       } else {
+		       p = APM_AC_OFF;
+	       }
        }
 
        if (first || ac_line != p || battery_level != r) {
