$NetBSD: patch-ab,v 1.10 2018/05/06 14:32:06 jmcneill Exp $

--- xbattbar.c.orig	2001-02-02 05:25:29.000000000 +0000
+++ xbattbar.c
@@ -27,6 +27,16 @@ static char *ReleaseVersion="1.4.2";
 
 #include <sys/types.h>
 #include <sys/time.h>
+
+#ifdef __NetBSD__
+#define ENVSYSUNITNAMES
+#include <sys/param.h>
+#include <sys/envsys.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#endif /* __NetBSD__ */
+
 #include <signal.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -577,33 +587,190 @@ void battery_check(void)
 
 #ifdef __NetBSD__
 
+#ifndef _NO_APM
 #include <machine/apmvar.h>
+#else
+#define APM_AC_OFF	0x00
+#define APM_AC_ON	0x01
+#endif
 
 #define _PATH_APM_SOCKET       "/var/run/apmdev"
 #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;
+#ifndef _NO_APM
        struct apm_power_info info;
+#endif
+       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 (fd < 0) {
+               fprintf(stderr, "xbattbar: cannot open %s device\n", apmdev);
+               exit(1);
+       }
 
-       if ((fd = open(_PATH_APM_NORMAL, O_RDONLY)) == -1) {
-               fprintf(stderr, "xbattbar: cannot open apm device\n");
+       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");
+			}
+		}
+
+		fillsensors(fd, etds, ebis, ns);
+
+#endif
+#ifndef _NO_APM
+       } else {
 
+	       memset(&info, 0, sizeof(info));
        if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) {
                fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n");
                exit(1);
        }
+#endif
+       }
 
        close(fd);
 
        ++elapsed_time;
 
-       /* get current remoain */
+       if (acpi) {
+#ifdef HAVE_NETBSD_ACPI
+		int32_t rtot = 0, maxtot = 0;
+		int have_pct = 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_SWATTS || ebis[i].units == ENVSYS_SAMPS) &&
+			    etds[i].cur.data_s &&
+			    strncmp(ebis[i].desc, "acpibat", 7) == 0 &&
+			    strcmp(&ebis[i].desc[cc - 14], "discharge rate") == 0) {
+				p = APM_AC_OFF;
+			}
+
+			if (ebis[i].units == ENVSYS_INTEGER &&
+			    strcmp(ebis[i].desc, "battery percent") == 0) {
+				have_pct = 1;
+				r = etds[i].cur.data_s;
+			}
+			if (ebis[i].units == ENVSYS_INDICATOR &&
+			    strcmp(ebis[i].desc, "ACIN present") == 0 &&
+			    etds[i].cur.data_s == 0) {
+				p = APM_AC_OFF;
+			}
+		}
+		if (!have_pct)
+			r = (rtot * 100.0) / maxtot;
+#endif
+#ifndef _NO_APM
+       } else {
+	       /* get current remain */
        if (info.battery_life > 100) {
                /* some APM BIOSes return values slightly > 100 */
                r = 100;
@@ -617,6 +784,8 @@ void battery_check(void)
        } else {
                p = APM_AC_OFF;
        }
+#endif
+       }
 
        if (first || ac_line != p || battery_level != r) {
                first = 0;
