#include #include #include #include #include "read-battery.h" int battery_read_charge(char * percentage, char * ac_online, char * hours_remaining, char * minutes_remaining) { #ifdef __linux__ static FILE * apm_file = NULL; char buffer[256], units[10]; apm_info i; /* * (1) Open /proc/apm */ if (apm_file == NULL) { apm_file = fopen("/proc/apm", "r"); if (apm_file == NULL) { g_warning(_("Cannot open /proc/apm! Make sure that you built APM " "support into your kernel.\n")); *ac_online = 1; *percentage = 100; *hours_remaining = -1; return TRUE; } } else { /* * If we are here, then this routine has been called before and * apm_file is valid. We do not want to call open() over and * over again, since each call to open() requires an access to * the root inode, and disk accesses are bad on laptops. An * alternative would be to chdir() into /proc, but then core * dumps will disappear, and that's not good either. * * Unfortunately, we can't just lseek() to the beginning of * /proc/apm and re-read the data, either. It ends up always * giving you the same data. So we dup() the fd associated with * apm_file, close the old one, and use the duplicate. This * gives us up-to-date apm information and never touches the * filesystem. * */ int dup_fd; dup_fd = dup( fileno(apm_file) ); if (dup_fd == -1) { g_error(_("Could not dup() APM file descriptor: %s\n"), g_strerror(errno)); abort(); } fclose(apm_file); apm_file = fdopen(dup_fd, "r"); fseek(apm_file, 0, SEEK_SET); } /* * (2) Read the battery charge information */ fgets( buffer, sizeof( buffer ) - 1, apm_file ); buffer[ sizeof( buffer ) - 1 ] = '\0'; sscanf( buffer, "%s %d.%d %x %x %x %x %d%% %d %s\n", (char *)i.driver_version, &i.apm_version_major, &i.apm_version_minor, &i.apm_flags, &i.ac_line_status, &i.battery_status, &i.battery_flags, &i.battery_percentage, &i.battery_time, units ); /* * (3) Interpret it and return. */ i.using_minutes = !strncmp( units, "min", 3 ) ? 1 : 0; if (i.using_minutes) { *hours_remaining = i.battery_time / 60; *minutes_remaining = i.battery_time % 60; } else /* Otherwise, the battery_time is in seconds */ { *hours_remaining = i.battery_time / 3600; i.battery_time %= 3600; *minutes_remaining = i.battery_time / 60; } if (i.battery_percentage > 100) i.battery_percentage = 0; /* * If we cannot read the battery percentage (indicated by a * reading of -1), then just set it to zero. It will sometimes * appear as -1 if, for example, the battery is unplugged, so this * might be a FIXME if we want to provide that kind of information * later */ if (i.battery_percentage < 0) i.battery_percentage = 0; *percentage = i.battery_percentage; *ac_online = i.ac_line_status; return TRUE; #elif __FreeBSD__ /* was #ifdef __linux__ */ struct apm_info aip; int fd; fd = open(APMDEV, O_RDWR); if (fd == -1) { g_error (_("Cannot open /dev/apm; can't get data.")); return FALSE; } if (ioctl(fd, APMIO_GETINFO, &aip) == -1) { g_error(_("ioctl failed on /dev/apm.")); return FALSE; } /* We cannot read these under FreeBSD. */ *hours_remaining = -1; *minutes_remaining = 1; /* if APM is not turned on */ if (!aip.ai_status) { g_error(_("APM is disabled! Cannot read battery charge information.")); } *ac_online = aip.ai_acline; *percentage = aip.ai_batt_life; close(fd); return TRUE; #else /* ! ( __linux__ || __FreeBSD__) */ /* Assume always connected to power. */ *ac_online = 1; *percentage = 100; *hours_remaining = -1; *minutes_remaining = 1; #endif /* __linux__ || __FreeBSD__ */ } /* battery_read_charge */