enlightenment/src/modules/battery/batget.c

1549 lines
35 KiB
C

/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
#include "e.h"
#ifdef __FreeBSD__
# include <sys/types.h>
# include <sys/sysctl.h>
# include <fcntl.h>
# ifdef __i386__
# include <machine/apm_bios.h>
# endif
# include <stdio.h>
#endif
#ifdef HAVE_CFBASE_H
# include <CFBase.h>
# include <CFNumber.h>
# include <CFArray.h>
# include <CFDictionary.h>
# include <CFRunLoop.h>
# include <ps/IOPSKeys.h>
# include <ps/IOPowerSources.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* supported batery system schemes - irrespective of OS */
#define CHECK_NONE 0
#define CHECK_ACPI 1
#define CHECK_APM 2
#define CHECK_PMU 3
#define CHECK_SYS_CLASS_POWER_SUPPLY 4
static void init(void);
static int poll_cb(void *data);
static int poll_interval = 512;
static Ecore_Poller *poller = NULL;
static int mode = CHECK_NONE;
static int time_left = -2;
static int battery_full = -2;
static int have_battery = -2;
static int have_power = -2;
/* UTILS */
static int
int_file_get(const char *file)
{
FILE *f;
int val = -1;
char buf[256];
f = fopen(file, "r");
if (f)
{
fgets(buf, sizeof(buf), f);
val = atoi(buf);
fclose(f);
}
return val;
}
static char *
str_file_get(const char *file)
{
FILE *f;
char *val = NULL;
char buf[4096];
int len;
f = fopen(file, "r");
if (f)
{
fgets(buf, sizeof(buf), f);
buf[sizeof(buf) - 1] = 0;
len = strlen(buf);
if (len > 0) buf[len - 1] = 0;
val = strdup(buf);
fclose(f);
}
return val;
}
static int
int_get(char *buf)
{
char *p, *q;
p = strchr(buf, ':');
if (!p) return 0;
p++;
while (*p == ' ') p++;
q = p;
while ((*q != ' ') && (*q != '\n')) q++;
if (q) *q = 0;
return atoi(p);
}
static char *
str_get(char *buf)
{
char *p, *q;
p = strchr(buf, ':');
if (!p) return NULL;
p++;
while (*p == ' ') p++;
q = p;
while ((*q) && (*q != ' ') && (*q != '\n')) q++;
if (q) *q = 0;
return strdup(p);
}
static int
axtoi(char *arg)
{
int n, val, pwr=1, m, rc = 0;
char hex[9], c;
for (n = 0, m = 0; n < strlen(arg); n++)
{
if (arg[n] != ' ')
{
hex[m++] = c = toupper(arg[n]);
if ((m == sizeof(hex)) || (c < '0') || (c > 'F'))
return 0; /* overflow or invalid */
}
}
hex[m] = 0; /* terminate string */
for (n = 0; n < m; n++)
{
c = hex[m-n-1];
if ((c >= 'A') && (c <= 'F'))
val = c -'A' + 10;
else
val = c - '0';
rc = rc + val * pwr;
pwr *= 16;
}
return rc;
}
#ifdef __FreeBSD__
#define BATTERY_STATE_NONE 0
#define BATTERY_STATE_DISCHARGING 1
#define BATTERY_STATE_CHARGING 2
#define BATTERY_STATE_REMOVED 7
/***---***/
static void
bsd_acpi_init(void)
{
/* nothing to do */
}
static void
bsd_acpi_check(void)
{
int bat_val = 0;
int mib_state[4];
int mib_life[4];
int mib_time[4];
int mib_units[4];
size_t len;
int state = 0;
int level = 0;
int time_min = 0;
int life = 0;
int batteries = 0;
time_left = -1;
battery_full = -1;
have_battery = 0;
have_power = 0;
/* Read some information on first run. */
len = 4;
sysctlnametomib("hw.acpi.battery.state", mib_state, &len);
len = sizeof(state);
if (sysctl(mib_state, 4, &state, &len, NULL, 0) == -1)
/* ERROR */
state = -1;
len = 4;
sysctlnametomib("hw.acpi.battery.life", mib_life, &len);
len = sizeof(life);
if (sysctl(mib_life, 4, &life, &len, NULL, 0) == -1)
/* ERROR */
level = -1;
bat_val = life;
len = 4;
sysctlnametomib("hw.acpi.battery.time", mib_time, &len);
len = sizeof(time);
if (sysctl(mib_time, 4, &time_min, &len, NULL, 0) == -1)
/* ERROR */
time_min = -1;
len = 4;
sysctlnametomib("hw.acpi.battery.units", mib_units, &len);
len = sizeof(batteries);
if (sysctl(mib_time, 4, &batteries, &len, NULL, 0) == -1)
/* ERROR */
batteries = 1;
if (time_min >= 0) time_left = time_min * 60;
if (batteries == 1) /* hw.acpi.battery.units = 1 means NO BATTS */
time_left = -1;
else if ((state == BATTERY_STATE_CHARGING) ||
(state == BATTERY_STATE_DISCHARGING))
{
have_battery = 1;
if (state == BATTERY_STATE_CHARGING) have_power = 1;
else if (state == BATTERY_STATE_DISCHARGING) have_power = 0;
if (level == -1) time_left = -1;
else if (time_min == -1)
{
time_left = -1;
battery_full = bat_val;
}
else battery_full = bat_val;
}
else
{
have_battery = 1;
battery_full = 100;
time_left = -1;
have_power = 1;
}
}
/***---***/
static void
bsd_apm_init(void)
{
/* nothing to do */
}
static void
bsd_apm_check(void)
{
int ac_stat, bat_stat, bat_val, time_val;
char buf[4096];
int hours, minutes;
int apm_fd = -1;
struct apm_info info;
time_left = -1;
battery_full = -1;
have_battery = 0;
have_power = 0;
apm_fd = open("/dev/apm", O_RDONLY);
if ((apm_fd != -1) && (ioctl(apm_fd, APMIO_GETINFO, &info) != -1))
{
/* set values */
ac_stat = info.ai_acline;
bat_stat = info.ai_batt_stat;
bat_val = info.ai_batt_life;
time_val = info.ai_batt_time;
}
else return;
if (info.ai_batteries == 1) /* ai_batteries == 1 means NO battery,
* ai_batteries == 2 means 1 battery */
{
have_power = 1;
return;
}
if (ac_stat) /* Wallpowered */
{
have_power= 1;
have_battery = 1;
switch (bat_stat) /* On FreeBSD the time_val is -1 when AC ist plugged
* in. This means we don't know how long the battery
* will recharge */
{
case 0:
battery_full = 100;
break;
case 1:
battery_full = 50;
break;
case 2:
battery_full = 25;
break;
case 3:
battery_full = 100;
break;
}
}
else /* Running on battery */
{
have_battery = 1;
battery_full = bat_val;
time_left = time_val;
}
}
#elif defined(HAVE_CFBASE_H) /* OS X */
/***---***/
static void darwin_init(void);
static void darwin_check(void);
static void
darwin_init(void)
{
/* nothing to do */
}
static void
darwin_check(void)
{
const void *values;
int device_num, device_count;
int currentval = 0, maxval = 0;
char buf[4096];
CFTypeRef blob;
CFArrayRef sources;
CFDictionaryRef device_dict;
time_left = -1;
battery_full = -1;
have_battery = 0;
have_power = 0;
/* Retrieve the power source data and the array of sources. */
blob = IOPSCopyPowerSourcesInfo();
sources = IOPSCopyPowerSourcesList(blob);
device_count = CFArrayGetCount(sources);
for (device_num = 0; device_num < device_count; device_num++)
{
CFTypeRef ps;
/* Retrieve a dictionary of values for this device and the count of keys in the dictionary. */
ps = CFArrayGetValueAtIndex(sources, device_num);
device_dict = IOPSGetPowerSourceDescription(blob, ps);
/* Retrieve the charging key and save the present charging value if one exists. */
if (CFDictionaryGetValueIfPresent(device_dict,
CFSTR(kIOPSIsChargingKey), &values))
{
have_battery = 1;
if (CFBooleanGetValue(values) > 0) have_power = 1;
break;
}
}
if (!have_battery)
{
CFRelease(sources);
CFRelease(blob);
have_power = 1;
return;
}
/* Retrieve the current capacity key. */
values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSCurrentCapacityKey));
CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
/* Retrieve the max capacity key. */
values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSMaxCapacityKey));
CFNumberGetValue(values, kCFNumberSInt32Type, &maxval);
/* Calculate the percentage charged. */
battery_full = (currentval * 100) / maxval;
/* Retrieve the remaining battery power or time until charged in minutes. */
if (!have_power)
{
values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToEmptyKey));
CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
time_left = currentval * 60;
}
else
{
values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToFullChargeKey));
CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
time_left = currentval * 60;
}
CFRelease(sources);
CFRelease(blob);
}
#else
/***---***/
/* new linux power class api to get power info - brand new and this code
* may have bugs, but it is a good attempt to get it right */
static int linux_sys_class_power_supply_cb_event_fd_active(void *data, Ecore_Fd_Handler *fd_handler);
static void linux_sys_class_power_supply_init(void);
static void linux_sys_class_power_supply_check(void);
typedef struct _Sys_Class_Power_Supply_Uevent Sys_Class_Power_Supply_Uevent;
#define BASIS_CHARGE 1
#define BASIS_ENERGY 2
#define BASIS_VOLTAGE 3
struct _Sys_Class_Power_Supply_Uevent
{
char *name;
int fd;
Ecore_Fd_Handler *fd_handler;
int present;
int basis;
int basis_empty;
int basis_full;
unsigned char have_current_avg : 1;
unsigned char have_current_now : 1;
};
static Ecore_List *events = NULL;
static Ecore_Timer *sys_class_delay_check = NULL;
static int
linux_sys_class_power_supply_cb_delay_check(void *data)
{
linux_sys_class_power_supply_init();
poll_cb(NULL);
sys_class_delay_check = NULL;
return 0;
}
static Ecore_Timer *re_init_timer = NULL;
static int
linux_sys_class_power_supply_cb_re_init(void *data)
{
Sys_Class_Power_Supply_Uevent *sysev;
if (events)
{
while ((sysev = ecore_list_first_goto(events)))
{
ecore_list_remove(events);
if (sysev->fd_handler)
ecore_main_fd_handler_del(sysev->fd_handler);
if (sysev->fd >= 0) close(sysev->fd);
free(sysev->name);
free(sysev);
}
ecore_list_destroy(events);
events = NULL;
}
linux_sys_class_power_supply_init();
re_init_timer = NULL;
return 0;
}
static int
linux_sys_class_power_supply_cb_event_fd_active(void *data, Ecore_Fd_Handler *fd_handler)
{
Sys_Class_Power_Supply_Uevent *sysev;
sysev = data;
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
{
int lost = 0;
for (;;)
{
char buf[1024];
int num;
if ((num = read(sysev->fd, buf, sizeof(buf))) < 1)
{
lost = ((errno == EIO) ||
(errno == EBADF) ||
(errno == EPIPE) ||
(errno == EINVAL) ||
(errno == ENOSPC) ||
(errno == ENODEV));
if (num <= 0) break;
}
}
if (lost)
{
ecore_list_goto(events, sysev);
ecore_list_remove(events);
if (sysev->fd_handler)
ecore_main_fd_handler_del(sysev->fd_handler);
if (sysev->fd >= 0) close(sysev->fd);
free(sysev->name);
free(sysev);
if (re_init_timer) ecore_timer_del(re_init_timer);
re_init_timer = ecore_timer_add(1.0, linux_sys_class_power_supply_cb_re_init, NULL);
}
else
{
if (sys_class_delay_check) ecore_timer_del(sys_class_delay_check);
sys_class_delay_check = ecore_timer_add(0.2, linux_sys_class_power_supply_cb_delay_check, NULL);
}
}
return 1;
}
static void
linux_sys_class_power_supply_sysev_init(Sys_Class_Power_Supply_Uevent *sysev)
{
char buf[4096];
sysev->basis = 0;
sysev->have_current_avg = 0;
sysev->have_current_now = 0;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/present", sysev->name);
sysev->present = int_file_get(buf);
if (!sysev->present) return;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_avg", sysev->name);
if (ecore_file_exists(buf)) sysev->have_current_avg = 1;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_now", sysev->name);
if (ecore_file_exists(buf)) sysev->have_current_now = 1;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full", sysev->name);
if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full_design", sysev->name);
if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full", sysev->name);
if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full_design", sysev->name);
if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full", sysev->name);
if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full_design", sysev->name);
if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE;
if (sysev->basis == BASIS_CHARGE)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full", sysev->name);
sysev->basis_full = int_file_get(buf);
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_empty", sysev->name);
sysev->basis_empty = int_file_get(buf);
if (sysev->basis_full < 0)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full_design", sysev->name);
sysev->basis_full = int_file_get(buf);
}
if (sysev->basis_empty < 0)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_empty_design", sysev->name);
sysev->basis_empty = int_file_get(buf);
}
}
else if (sysev->basis == BASIS_ENERGY)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full", sysev->name);
sysev->basis_full = int_file_get(buf);
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_empty", sysev->name);
sysev->basis_empty = int_file_get(buf);
if (sysev->basis_full < 0)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full_design", sysev->name);
sysev->basis_full = int_file_get(buf);
}
if (sysev->basis_empty < 0)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_empty_design", sysev->name);
sysev->basis_empty = int_file_get(buf);
}
}
else if (sysev->basis == BASIS_VOLTAGE)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full", sysev->name);
sysev->basis_full = int_file_get(buf);
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_empty", sysev->name);
sysev->basis_empty = int_file_get(buf);
if (sysev->basis_full < 0)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full_design", sysev->name);
sysev->basis_full = int_file_get(buf);
}
if (sysev->basis_empty < 0)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_empty_design", sysev->name);
sysev->basis_empty = int_file_get(buf);
}
}
}
static int
linux_sys_class_power_supply_is_battery(char *name)
{
int fd;
int ret = 0;
char buf[256];
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/type", name);
fd = open(buf, O_RDONLY);
if (fd < 0)
{
ret = 0;
goto NO_OPEN;
}
else if (read(fd, buf, sizeof(buf)) < 1)
ret = 0;
else if (!strncmp(buf, "Battery", 7))
ret = 1;
close(fd);
NO_OPEN:
return ret;
}
static void
linux_sys_class_power_supply_init(void)
{
if (events)
{
Sys_Class_Power_Supply_Uevent *sysev;
ecore_list_first_goto(events);
while ((sysev = ecore_list_next(events)))
linux_sys_class_power_supply_sysev_init(sysev);
}
else
{
Ecore_List *bats;
char *name;
char buf[4096];
bats = ecore_file_ls("/sys/class/power_supply/");
if (bats)
{
events = ecore_list_new();
while ((name = ecore_list_next(bats)))
{
Sys_Class_Power_Supply_Uevent *sysev;
if (!(linux_sys_class_power_supply_is_battery(name)))
continue;
sysev = E_NEW(Sys_Class_Power_Supply_Uevent, 1);
sysev->name = strdup(name);
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/uevent", name);
sysev->fd = open(buf, O_RDONLY);
if (sysev->fd >= 0)
sysev->fd_handler = ecore_main_fd_handler_add(sysev->fd,
ECORE_FD_READ,
linux_sys_class_power_supply_cb_event_fd_active,
sysev,
NULL, NULL);
ecore_list_append(events, sysev);
linux_sys_class_power_supply_sysev_init(sysev);
}
ecore_list_destroy(bats);
}
}
}
static void
linux_sys_class_power_supply_check(void)
{
char *name;
char buf[4096];
battery_full = -1;
time_left = -1;
have_battery = 0;
have_power = 0;
if (events)
{
Sys_Class_Power_Supply_Uevent *sysev;
int total_pwr_now;
int total_pwr_max;
int nofull = 0;
total_pwr_now = 0;
total_pwr_max = 0;
time_left = 0;
ecore_list_first_goto(events);
while ((sysev = ecore_list_next(events)))
{
char *tmp;
int present = 0;
int charging = -1;
int capacity = -1;
int current = -1;
int time_to_full = -1;
int time_to_empty = -1;
int full = -1;
int pwr_now = -1;
int pwr_empty = -1;
int pwr_full = -1;
int pwr = 0;
name = sysev->name;
/* fetch more generic info */
// init
present = sysev->present;
if (!present) continue;
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/capacity", name);
capacity = int_file_get(buf);
if (sysev->have_current_avg)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_avg", name);
current = int_file_get(buf);
}
else if (sysev->have_current_now)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_now", name);
current = int_file_get(buf);
}
/* FIXME: do we get a uevent on going from charging to full?
* if so, move this to init */
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/status", name);
tmp = str_file_get(buf);
if (tmp)
{
full = 0;
if (!strncasecmp("discharging", tmp, 11)) charging = 0;
else if (!strncasecmp("unknown", tmp, 7)) charging = 0;
else if (!strncasecmp("not charging", tmp, 12)) charging = 0;
else if (!strncasecmp("charging", tmp, 8)) charging = 1;
else if (!strncasecmp("full", tmp, 4))
{
full = 1;
charging = 0;
}
free(tmp);
}
/* some batteries can/will/want to predict how long they will
* last. if so - take what the battery says. too bad if it's
* wrong. that's a buggy battery or driver */
if (!full)
{
nofull++;
if (charging)
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/time_to_full_now", name);
time_to_full = int_file_get(buf);
}
else
{
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/time_to_empty_now", name);
time_to_empty = int_file_get(buf);
}
}
/* now get charge, energy and voltage. take the one that provides
* the best info (charge first, then energy, then voltage */
if (sysev->basis == BASIS_CHARGE)
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_now", name);
else if (sysev->basis == BASIS_ENERGY)
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_now", name);
else if (sysev->basis == BASIS_VOLTAGE)
snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_now", name);
pwr_now = int_file_get(buf);
pwr_empty = sysev->basis_empty;
pwr_full = sysev->basis_full;
if (pwr_empty < 0) pwr_empty = 0;
if (full) pwr_now = pwr_full;
else
{
if (pwr_now < 0)
pwr_now = (((long long)capacity * ((long long)pwr_full - (long long)pwr_empty)) / 100) + pwr_empty;
}
if (sysev->present) have_battery = 1;
if (charging)
{
pwr_now = pwr_now;
have_power = 1;
if (time_to_full >= 0)
{
if (time_to_full > time_left)
time_left = time_to_full;
}
else
{
if (current == 0) time_left = 0;
else if (current < 0) time_left = -1;
else
{
pwr = (((long long)pwr_full - (long long)pwr_now) * 3600) / -current;
if (pwr > time_left) time_left = pwr;
}
}
}
else
{
have_power = 0;
if (time_to_empty >= 0) time_left += time_to_empty;
else
{
if (time_to_empty < 0)
{
if (current > 0)
{
pwr = (((long long)pwr_now - (long long)pwr_empty) * 3600) / current;
time_left += pwr;
}
}
}
}
total_pwr_now += pwr_now - pwr_empty;
total_pwr_max += pwr_full - pwr_empty;
}
if (total_pwr_max > 0)
battery_full = ((long long)total_pwr_now * 100) / total_pwr_max;
if (nofull == 0)
time_left = -1;
}
}
/***---***/
/* "here and now" ACPI based power checking. is there for linux and most
* modern laptops. as of linux 2.6.24 it is replaced with
* linux_sys_class_power_supply_init/check() though as this is the new
* power class api to poll for power stuff
*/
static int linux_acpi_cb_acpid_add(void *data, int type, void *event);
static int linux_acpi_cb_acpid_del(void *data, int type, void *event);
static int linux_acpi_cb_acpid_data(void *data, int type, void *event);
static void linux_acpi_init(void);
static void linux_acpi_check(void);
static int acpi_max_full = -1;
static int acpi_max_design = -1;
static Ecore_Con_Server *acpid = NULL;
static Ecore_Event_Handler *acpid_handler_add = NULL;
static Ecore_Event_Handler *acpid_handler_del = NULL;
static Ecore_Event_Handler *acpid_handler_data = NULL;
static Ecore_Timer *delay_check = NULL;
static int event_fd = -1;
static Ecore_Fd_Handler *event_fd_handler = NULL;
static int
linux_acpi_cb_delay_check(void *data)
{
linux_acpi_init();
poll_cb(NULL);
delay_check = NULL;
return 0;
}
static int
linux_acpi_cb_acpid_add(void *data, int type, void *event)
{
return 1;
}
static int
linux_acpi_cb_acpid_del(void *data, int type, void *event)
{
ecore_con_server_del(acpid);
acpid = NULL;
if (acpid_handler_add) ecore_event_handler_del(acpid_handler_add);
acpid_handler_add = NULL;
if (acpid_handler_del) ecore_event_handler_del(acpid_handler_del);
acpid_handler_del = NULL;
if (acpid_handler_data) ecore_event_handler_del(acpid_handler_data);
acpid_handler_data = NULL;
return 1;
}
static int
linux_acpi_cb_acpid_data(void *data, int type, void *event)
{
Ecore_Con_Event_Server_Data *ev;
ev = event;
if (delay_check) ecore_timer_del(delay_check);
delay_check = ecore_timer_add(0.2, linux_acpi_cb_delay_check, NULL);
return 1;
}
static int
linux_acpi_cb_event_fd_active(void *data, Ecore_Fd_Handler *fd_handler)
{
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
{
int lost = 0;
for (;;)
{
char buf[1024];
int num;
if ((num = read(event_fd, buf, sizeof(buf))) < 1)
{
lost = ((errno == EIO) ||
(errno == EBADF) ||
(errno == EPIPE) ||
(errno == EINVAL) ||
(errno == ENOSPC));
if (num == 0) break;
}
}
if (lost)
{
ecore_main_fd_handler_del(event_fd_handler);
event_fd_handler = NULL;
close(event_fd);
event_fd = -1;
}
else
{
if (delay_check) ecore_timer_del(delay_check);
delay_check = ecore_timer_add(0.2, linux_acpi_cb_delay_check, NULL);
}
}
return 1;
}
static void
linux_acpi_init(void)
{
Ecore_List *powers;
Ecore_List *bats;
bats = ecore_file_ls("/proc/acpi/battery");
if (bats)
{
char *name;
have_power = 0;
powers = ecore_file_ls("/proc/acpi/ac_adapter");
if (powers)
{
char *name;
while ((name = ecore_list_next(powers)))
{
char buf[4096];
FILE *f;
snprintf(buf, sizeof(buf), "/proc/acpi/ac_adapter/%s/state", name);
f = fopen(buf, "r");
if (f)
{
char *tmp;
/* state */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (!strcmp(tmp, "on-line")) have_power = 1;
free(tmp);
}
}
}
ecore_list_destroy(powers);
}
have_battery = 0;
acpi_max_full = 0;
acpi_max_design = 0;
while ((name = ecore_list_next(bats)))
{
char buf[4096];
FILE *f;
snprintf(buf, sizeof(buf), "/proc/acpi/battery/%s/info", name);
f = fopen(buf, "r");
if (f)
{
char *tmp;
/* present */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (!strcmp(tmp, "yes")) have_battery = 1;
free(tmp);
}
/* design cap */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (strcmp(tmp, "unknown")) acpi_max_design += atoi(tmp);
free(tmp);
}
/* last full cap */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (strcmp(tmp, "unknown")) acpi_max_full += atoi(tmp);
free(tmp);
}
fclose(f);
}
}
ecore_list_destroy(bats);
}
if (!acpid)
{
acpid = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM,
"/var/run/acpid.socket", -1, NULL);
if (acpid)
{
acpid_handler_add = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD,
linux_acpi_cb_acpid_add, NULL);
acpid_handler_del = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL,
linux_acpi_cb_acpid_del, NULL);
acpid_handler_data = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA,
linux_acpi_cb_acpid_data, NULL);
}
else
{
if (event_fd < 0)
{
event_fd = open("/proc/acpi/event", O_RDONLY);
if (event_fd >= 0)
event_fd_handler = ecore_main_fd_handler_add(event_fd,
ECORE_FD_READ,
linux_acpi_cb_event_fd_active,
NULL,
NULL, NULL);
}
}
}
}
static void
linux_acpi_check(void)
{
Ecore_List *bats;
battery_full = -1;
time_left = -1;
have_battery = 0;
have_power = 0;
bats = ecore_file_ls("/proc/acpi/battery");
if (bats)
{
char *name;
int rate = 0;
int capacity = 0;
while ((name = ecore_list_next(bats)))
{
char buf[4096];
FILE *f;
snprintf(buf, sizeof(buf), "/proc/acpi/battery/%s/state", name);
f = fopen(buf, "r");
if (f)
{
char *tmp;
/* present */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (!strcasecmp(tmp, "yes")) have_battery = 1;
free(tmp);
}
/* capacity state: ok/? */
fgets(buf, sizeof(buf), f);
/* charging state: charging/? */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (have_power == 0)
{
if (!strcasecmp(tmp, "charging")) have_power = 1;
}
free(tmp);
}
/* present rate: unknown/NNN */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (strcasecmp(tmp, "unknown")) rate += atoi(tmp);
free(tmp);
}
/* remaining capacity: NNN */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
tmp = str_get(buf);
if (tmp)
{
if (strcasecmp(tmp, "unknown")) capacity += atoi(tmp);
free(tmp);
}
fclose(f);
}
}
ecore_list_destroy(bats);
if (acpi_max_full > 0)
battery_full = 100 * (long long)capacity / acpi_max_full;
else if (acpi_max_design > 0)
battery_full = 100 * (long long)capacity / acpi_max_design;
else
battery_full = -1;
if (rate <= 0) time_left = -1;
else
{
if (have_power)
time_left = (3600 * ((long long)acpi_max_full - (long long)capacity)) / rate;
else
time_left = (3600 * (long long)capacity) / rate;
}
}
}
/***---***/
/* old school apm support - very old laptops and some devices support this.
* this is here for legacy support and i wouldn't suggest spending any
* effort on it as it is complete below as best i know, but could have missed
* one or 2 things, but not worth fixing */
static void linux_apm_init(void);
static void linux_apm_check(void);
static void
linux_apm_init(void)
{
/* nothing to do */
}
static void
linux_apm_check(void)
{
FILE *f;
char s[256], s1[32], s2[32], s3[32];
int apm_flags, ac_stat, bat_stat, bat_flags, bat_val, time_val;
battery_full = -1;
time_left = -1;
have_battery = 0;
have_power = 0;
f = fopen("/proc/apm", "r");
if (!f) return;
fgets(s, sizeof(s), f); s[sizeof(s) - 1] = 0;
if (sscanf(s, "%*s %*s %x %x %x %x %s %s %s",
&apm_flags, &ac_stat, &bat_stat, &bat_flags, s1, s2, s3) != 7)
{
fclose(f);
return;
}
s1[strlen(s1) - 1] = 0;
bat_val = atoi(s1);
if (!strcmp(s3, "sec")) time_val = atoi(s2);
else if (!strcmp(s3, "min")) time_val = atoi(s2) * 60;
fclose(f);
if ((bat_flags != 0xff) && (bat_flags & 0x80))
{
have_battery = 0;
have_power = 0;
battery_full = 100;
time_left = 0;
return;
}
if (bat_val >= 0)
{
have_battery = 1;
have_power = ac_stat;
battery_full = bat_val;
if (battery_full > 100) battery_full = 100;
if (ac_stat == 1) time_left = -1;
else time_left = time_val;
}
else
{
switch (bat_stat)
{
case 0: /* high */
have_battery = 1;
have_power = ac_stat;
battery_full = 100;
time_left = -1;
break;
case 1: /* medium */
have_battery = 1;
have_power = ac_stat;
battery_full = 50;
time_left = -1;
break;
case 2: /* low */
have_battery = 1;
have_power = ac_stat;
battery_full = 25;
time_left = -1;
break;
case 3: /* charging */
have_battery = 1;
have_power = ac_stat;
battery_full = 100;
time_left = -1;
break;
}
}
}
/***---***/
/* for older mac powerbooks. legacy as well like linux_apm_init/check. leave
* it alone unless you have to touch it */
static void linux_pmu_init(void);
static void linux_pmu_check(void);
static void
linux_pmu_init(void)
{
/* nothing to do */
}
static void
linux_pmu_check(void)
{
FILE *f;
char buf[4096];
Ecore_List *bats;
char *name;
int ac = 0;
int flags = 0;
int charge = 0;
int max_charge = 0;
int voltage = 0;
int seconds = 0;
int curcharge = 0;
int curmax = 0;
f = fopen("/proc/pmu/info", "r");
if (f)
{
/* Skip driver */
fgets(buf, sizeof(buf), f);
/* Skip firmware */
fgets(buf, sizeof(buf), f);
/* Read ac */
fgets(buf, sizeof(buf), f); buf[sizeof(buf) - 1] = 0;
ac = int_get(buf);
fclose(f);
}
bats = ecore_file_ls("/proc/pmu");
if (bats)
{
have_battery = 1;
have_power = ac;
while ((name = ecore_list_next(bats)))
{
if (strncmp(name, "battery", 7)) continue;
snprintf(buf, sizeof(buf), "/proc/pmu/%s", name);
f = fopen(buf, "r");
if (f)
{
int timeleft = 0;
int current = 0;
while (fgets(buf,sizeof (buf), f))
{
char *token;
if ((token = strtok(buf, ":")))
{
if (!strncmp("flags", token, 5))
flags = axtoi(strtok (0, ": "));
else if (!strncmp("charge", token, 6))
charge = atoi(strtok(0, ": "));
else if (!strncmp("max_charge", token, 9))
max_charge = atoi(strtok(0, ": "));
else if (!strncmp("current", token, 7))
current = atoi(strtok(0, ": "));
else if (!strncmp("time rem", token, 8))
timeleft = atoi(strtok(0, ": "));
else if (!strncmp("voltage", token, 7))
voltage = atoi(strtok(0, ": "));
else
strtok(0, ": ");
}
}
curmax += max_charge;
curcharge += charge;
fclose(f);
if (!current)
{
/* Neither charging nor discharging */
}
else if (!ac)
{
/* When on dc, we are discharging */
seconds += timeleft;
}
else
{
/* Charging - works in paralell */
seconds = MAX(timeleft, seconds);
}
}
}
ecore_list_destroy(bats);
if (max_charge > 0) battery_full = ((long long)charge * 100) / max_charge;
else battery_full = 0;
time_left = seconds;
}
else
{
have_power = ac;
have_battery = 0;
battery_full = -1;
time_left = -1;
}
}
#endif
static int
dir_has_contents(const char *dir)
{
Ecore_List *bats;
bats = ecore_file_ls(dir);
if (bats)
{
int count;
count = ecore_list_count(bats);
ecore_list_destroy(bats);
if (count > 0) return 1;
}
return 0;
}
static void
init(void)
{
#ifdef __FreeBSD__
int acline;
size_t len;
len = sizeof(acline);
if (!sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0))
{
int acline_mib[3] = {-1};
len = 3;
if (!sysctlnametomib("hw.acpi.acline", acline_mib, &len))
{
mode = CHECK_ACPI;
bsd_acpi_init();
}
}
else
{
if (ecore_file_exists("/dev/apm"))
{
mode = CHECK_APM;
bsd_apm_init();
}
}
#elif defined(HAVE_CFBASE_H) /* OS X */
darwin_init();
#else
if ((ecore_file_is_dir("/sys/class/power_supply")) &&
(dir_has_contents("/sys/class/power_supply")))
{
mode = CHECK_SYS_CLASS_POWER_SUPPLY;
linux_sys_class_power_supply_init();
}
else if (ecore_file_is_dir("/proc/acpi")) /* <= 2.6.24 */
{
mode = CHECK_ACPI;
linux_acpi_init();
}
else if (ecore_file_exists("/proc/apm"))
{
mode = CHECK_APM;
linux_apm_init();
}
else if (ecore_file_is_dir("/proc/pmu"))
{
mode = CHECK_PMU;
linux_pmu_init();
}
#endif
}
static int
poll_cb(void *data)
{
int ptime_left;
int pbattery_full;
int phave_battery;
int phave_power;
ptime_left = time_left;
pbattery_full = battery_full;
phave_battery = have_battery;
phave_power = have_power;
#ifdef __FreeBSD__
switch (mode)
{
case CHECK_ACPI:
bsd_acpi_check();
break;
case CHECK_APM:
bsd_apm_check();
break;
default:
battery_full = -1;
time_left = -1;
have_battery = 0;
have_power = 0;
break;
}
#elif defined(HAVE_CFBASE_H) /* OS X */
darwin_check();
break;
#else
switch (mode)
{
case CHECK_ACPI:
linux_acpi_check();
break;
case CHECK_APM:
linux_apm_check();
break;
case CHECK_PMU:
linux_pmu_check();
break;
case CHECK_SYS_CLASS_POWER_SUPPLY:
linux_sys_class_power_supply_check();
break;
default:
battery_full = -1;
time_left = -1;
have_battery = 0;
have_power = 0;
break;
}
#endif
if ((ptime_left != time_left) ||
(pbattery_full != battery_full) ||
(phave_battery != have_battery) ||
(phave_power != have_power))
{
if ((time_left < 0) &&
((have_battery) && (battery_full < 0)))
printf("ERROR\n");
else
printf("%i %i %i %i\n",
battery_full, time_left, have_battery, have_power);
fflush(stdout);
}
return 1;
}
int
main(int argc, char *argv[])
{
if (argc != 2)
{
printf("ARGS INCORRECT!\n");
return 0;
}
poll_interval = atoi(argv[1]);
ecore_init();
ecore_file_init();
ecore_con_init();
init();
poller = ecore_poller_add(ECORE_POLLER_CORE, poll_interval, poll_cb, NULL);
poll_cb(NULL);
ecore_main_loop_begin();
ecore_con_shutdown();
ecore_file_shutdown();
ecore_shutdown();
return 0;
}