diff --git a/src/modules/sysinfo/batman/batman.c b/src/modules/sysinfo/batman/batman.c new file mode 100644 index 000000000..2e959b712 --- /dev/null +++ b/src/modules/sysinfo/batman/batman.c @@ -0,0 +1,561 @@ +#include "batman.h" + +Eina_Bool upower; +Eina_List *batman_device_batteries; +Eina_List *batman_device_ac_adapters; +double batman_init_time; + +static Eina_Bool _batman_cb_warning_popup_timeout(void *data); +static void _batman_cb_warning_popup_hide(void *data, Evas *e, Evas_Object *obj, void *event); +static void _batman_warning_popup_destroy(Instance *inst); +static void _batman_warning_popup(Instance *inst, int time, double percent); + +Battery * +_batman_battery_find(const char *udi) +{ + Eina_List *l; + Battery *bat; + EINA_LIST_FOREACH(batman_device_batteries, l, bat) + { /* these are always stringshared */ + if (udi == bat->udi) return bat; + } + + return NULL; +} + +Ac_Adapter * +_batman_ac_adapter_find(const char *udi) +{ + Eina_List *l; + Ac_Adapter *ac; + EINA_LIST_FOREACH(batman_device_ac_adapters, l, ac) + { /* these are always stringshared */ + if (udi == ac->udi) return ac; + } + + return NULL; +} + +static void +_batman_face_level_set(Evas_Object *battery, double level) +{ + Edje_Message_Float msg; + char buf[256]; + + snprintf(buf, sizeof(buf), "%i", (int)(level * 100.0)); + elm_layout_text_set(battery, "e.text.reading", buf); + if (level < 0.0) level = 0.0; + else if (level > 1.0) + level = 1.0; + msg.val = level; + edje_object_message_send(elm_layout_edje_get(battery), EDJE_MESSAGE_FLOAT, 1, &msg); +} + +static void +_batman_face_time_set(Evas_Object *battery, int t) +{ + char buf[256]; + int hrs, mins; + + if (t < 0) return; + + hrs = (t / 3600); + mins = ((t) / 60 - (hrs * 60)); + if (mins < 0) mins = 0; + snprintf(buf, sizeof(buf), "%i:%02i", hrs, mins); + elm_layout_text_set(battery, "e.text.time", buf); +} + +void +_batman_update(Instance *inst, int full, int time_left, int time_full, Eina_Bool have_battery, Eina_Bool have_power) +{ + static double debounce_time = 0.0; + + if (!inst) return; + if (inst->cfg->esm != E_SYSINFO_MODULE_BATMAN && inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return; + if (!inst->cfg) return; + if (have_power != inst->cfg->batman.have_power) + { + if (have_power && (full < 100)) + elm_layout_signal_emit(inst->cfg->batman.o_gadget, + "e,state,charging", + "e"); + else + { + elm_layout_signal_emit(inst->cfg->batman.o_gadget, + "e,state,discharging", + "e"); + if (inst->popup_battery) + elm_layout_signal_emit(inst->popup_battery, + "e,state,discharging", "e"); + } + } + if (have_battery) + { + if (inst->cfg->batman.full != full) + { + double val; + + if (full >= 100) val = 1.0; + else val = (double)full / 100.0; + _batman_face_level_set(inst->cfg->batman.o_gadget, val); + if (inst->popup_battery) + _batman_face_level_set(inst->popup_battery, val); + } + } + else + { + _batman_face_level_set(inst->cfg->batman.o_gadget, 0.0); + elm_layout_text_set(inst->cfg->batman.o_gadget, + "e.text.reading", + _("N/A")); + } + + if ((time_full < 0) && (time_left != inst->cfg->batman.time_left)) + { + _batman_face_time_set(inst->cfg->batman.o_gadget, time_left); + if (inst->popup_battery) + _batman_face_time_set(inst->popup_battery, + time_left); + } + else if ((time_left < 0) && (time_full != inst->cfg->batman.time_full)) + { + _batman_face_time_set(inst->cfg->batman.o_gadget, time_full); + if (inst->popup_battery) + _batman_face_time_set(inst->popup_battery, + time_full); + } + if (have_battery && + (!have_power) && + (full < 100) && + ( + ( + (time_left > 0) && + inst->cfg->batman.alert && + ((time_left / 60) <= inst->cfg->batman.alert) + ) || + ( + inst->cfg->batman.alert_p && + (full <= inst->cfg->batman.alert_p) + ) + ) + ) + { + double t; + + printf("-------------------------------------- bat warn .. why below\n"); + printf("have_battery = %i\n", (int)have_battery); + printf("have_power = %i\n", (int)have_power); + printf("full = %i\n", (int)full); + printf("time_left = %i\n", (int)time_left); + printf("inst->cfg->batman.alert = %i\n", (int)inst->cfg->batman.alert); + printf("inst->cfg->batman.alert_p = %i\n", (int)inst->cfg->batman.alert_p); + t = ecore_time_get(); + if ((t - debounce_time) > 30.0) + { + printf("t-debounce = %3.3f\n", (t - debounce_time)); + debounce_time = t; + if ((t - batman_init_time) > 5.0) + _batman_warning_popup(inst, time_left, (double)full / 100.0); + } + } + else if (have_power || ((time_left / 60) > inst->cfg->batman.alert)) + _batman_warning_popup_destroy(inst); + if ((have_battery) && (!have_power) && (full >= 0) && + (inst->cfg->batman.suspend_below > 0) && + (full < inst->cfg->batman.suspend_below)) + { + if (inst->cfg->batman.suspend_method == SUSPEND) + e_sys_action_do(E_SYS_SUSPEND, NULL); + else if (inst->cfg->batman.suspend_method == HIBERNATE) + e_sys_action_do(E_SYS_HIBERNATE, NULL); + else if (inst->cfg->batman.suspend_method == SHUTDOWN) + e_sys_action_do(E_SYS_HALT, NULL); + } + inst->cfg->batman.full = full; + inst->cfg->batman.time_left = time_left; + inst->cfg->batman.have_battery = have_battery; + inst->cfg->batman.have_power = have_power; + + if (!have_battery) + e_powersave_mode_set(E_POWERSAVE_MODE_LOW); + else + { + if (have_power) + e_powersave_mode_set(E_POWERSAVE_MODE_LOW); + else if (full > 95) + e_powersave_mode_set(E_POWERSAVE_MODE_MEDIUM); + else if (full > 30) + e_powersave_mode_set(E_POWERSAVE_MODE_HIGH); + else + e_powersave_mode_set(E_POWERSAVE_MODE_EXTREME); + } +} + +void +_batman_device_update(Instance *inst) +{ + Eina_List *l; + Battery *bat; + Ac_Adapter *ac; + int full = -1; + int time_left = -1; + int time_full = -1; + int have_battery = 0; + int have_power = 0; + int charging = 0; + + int batnum = 0; + int acnum = 0; + + EINA_LIST_FOREACH(batman_device_ac_adapters, l, ac) + if (ac->present) acnum++; + + EINA_LIST_FOREACH(batman_device_batteries, l, bat) + { + if (!bat->got_prop) + continue; + have_battery = 1; + batnum++; + if (bat->charging == 1) have_power = 1; + if (full == -1) full = 0; + if (bat->percent >= 0) + full += bat->percent; + else if (bat->last_full_charge > 0) + full += (bat->current_charge * 100) / bat->last_full_charge; + else if (bat->design_charge > 0) + full += (bat->current_charge * 100) / bat->design_charge; + if (bat->time_left > 0) + { + if (time_left < 0) time_left = bat->time_left; + else time_left += bat->time_left; + } + if (bat->time_full > 0) + { + if (time_full < 0) time_full = bat->time_full; + else time_full += bat->time_full; + } + charging += bat->charging; + } + + if ((batman_device_batteries) && (batnum == 0)) + return; /* not ready yet, no properties received for any battery */ + + if (batnum > 0) full /= batnum; + if ((full == 100) && have_power) + { + time_left = -1; + time_full = -1; + } + if (time_left < 1) time_left = -1; + if (time_full < 1) time_full = -1; + + _batman_update(inst, full, time_left, time_full, have_battery, have_power); +} + +void +_batman_config_updated(Instance *inst) +{ + int ok = 0; + + if (!inst->cfg) return; + + if ((inst->cfg->batman.force_mode == UNKNOWN) || + (inst->cfg->batman.force_mode == SUBSYSTEM)) + { +#ifdef HAVE_EEZE + ok = _batman_udev_start(inst); +#elif defined __OpenBSD__ || defined __DragonFly__ || defined __FreeBSD__ || defined __NetBSD__ + ok = _batman_sysctl_start(inst); +#else + ok = _batman_upower_start(); + if (ok) + upower = EINA_TRUE; +#endif + } + if (ok) return; + + if ((inst->cfg->batman.force_mode == UNKNOWN) || + (inst->cfg->batman.force_mode == NOSUBSYSTEM)) + { + ok = _batman_fallback_start(inst); + } +} + +static void _warning_popup_dismissed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + + E_FREE_FUNC(inst->popup_battery, evas_object_del); + E_FREE_FUNC(inst->warning, evas_object_del); +} + + +static Eina_Bool +_batman_cb_warning_popup_timeout(void *data) +{ + Instance *inst; + + inst = data; + elm_ctxpopup_dismiss(inst->warning); + inst->cfg->batman.alert_timer = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static void +_batman_cb_warning_popup_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + Instance *inst = NULL; + + inst = (Instance *)data; + if ((!inst) || (!inst->warning)) return; + elm_ctxpopup_dismiss(inst->warning); +} + +static void +_batman_warning_popup_destroy(Instance *inst) +{ + if (inst->cfg->batman.alert_timer) + { + ecore_timer_del(inst->cfg->batman.alert_timer); + inst->cfg->batman.alert_timer = NULL; + } + if ((!inst) || (!inst->warning)) return; + elm_ctxpopup_dismiss(inst->warning); +} + +static void +_batman_warning_popup_cb(void *data, unsigned int id) +{ + Instance *inst = data; + + inst->notification_id = id; +} + +static void +_batman_warning_popup(Instance *inst, int t, double percent) +{ + Evas_Object *popup_bg = NULL; + int x, y, w, h; + + if ((!inst) || (inst->warning)) return; + + if (inst->cfg->batman.desktop_notifications) + { + E_Notification_Notify n; + memset(&n, 0, sizeof(E_Notification_Notify)); + n.app_name = _("Battery"); + n.replaces_id = 0; + n.icon.icon = "battery-low"; + n.summary = _("Your battery is low!"); + n.body = _("AC power is recommended."); + n.timeout = inst->cfg->batman.alert_timeout * 1000; + e_notification_client_send(&n, _batman_warning_popup_cb, inst); + return; + } + + inst->warning = elm_ctxpopup_add(e_comp->elm); + elm_object_style_set(inst->warning, "noblock"); + evas_object_smart_callback_add(inst->warning, "dismissed", _warning_popup_dismissed, inst); + if (!inst->warning) return; + + popup_bg = elm_layout_add(inst->warning); + inst->popup_battery = elm_layout_add(popup_bg); + + if ((!popup_bg) || (!inst->popup_battery)) + { + elm_ctxpopup_dismiss(inst->warning); + inst->warning = NULL; + return; + } + + e_theme_edje_object_set(popup_bg, "base/theme/modules/battery/popup", + "e/modules/battery/popup"); + e_theme_edje_object_set(inst->popup_battery, "base/theme/modules/battery", + "e/modules/battery/main"); + if (edje_object_part_exists(elm_layout_edje_get(popup_bg), "e.swallow.battery")) + elm_layout_content_set(popup_bg, "e.swallow.battery", inst->popup_battery); + else + elm_layout_content_set(popup_bg, "battery", inst->popup_battery); + + elm_layout_text_set(popup_bg, "e.text.title", + _("Your battery is low!")); + elm_layout_text_set(popup_bg, "e.text.label", + _("AC power is recommended.")); + evas_object_show(inst->popup_battery); + evas_object_show(popup_bg); + + elm_object_content_set(inst->warning, popup_bg); + e_gadget_util_ctxpopup_place(inst->o_main, inst->warning, inst->cfg->batman.o_gadget); + evas_object_layer_set(inst->warning, E_LAYER_POPUP); + evas_object_show(inst->warning); + + evas_object_geometry_get(inst->warning, &x, &y, &w, &h); + evas_object_event_callback_add(inst->warning, EVAS_CALLBACK_MOUSE_DOWN, + _batman_cb_warning_popup_hide, inst); + + _batman_face_time_set(inst->popup_battery, t); + _batman_face_level_set(inst->popup_battery, percent); + edje_object_signal_emit(inst->popup_battery, "e,state,discharging", "e"); + + if ((inst->cfg->batman.alert_timeout > 0) && + (!inst->cfg->batman.alert_timer)) + { + inst->cfg->batman.alert_timer = + ecore_timer_add(inst->cfg->batman.alert_timeout, + _batman_cb_warning_popup_timeout, inst); + } +} + +static void +_batman_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data) +{ + Instance *inst = data; + + if (inst->o_main != event_data) return; + +#ifdef HAVE_EEZE + _batman_udev_stop(inst); +#elif defined __OpenBSD__ || defined __DragonFly__ || defined __FreeBSD__ || defined __NetBSD__ + _batman_sysctl_stop(); +#elif upower + _batman_upower_stop(); +#else + _batman_fallback_stop(); +#endif + +#ifdef HAVE_EEZE + eeze_shutdown(); +#endif + + sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg); + E_FREE(inst->cfg); +} + +void +sysinfo_batman_remove(Instance *inst) +{ +#ifdef HAVE_EEZE + _batman_udev_stop(inst); +#elif defined __OpenBSD__ || defined __DragonFly__ || defined __FreeBSD__ || defined __NetBSD__ + _batman_sysctl_stop(); +#elif upower + _batman_upower_stop(); +#else + _batman_fallback_stop(); +#endif + +#ifdef HAVE_EEZE + eeze_shutdown(); +#endif +} + +static void +_batman_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED) +{ + Instance *inst = data; + + inst->cfg->batman.full = -2; + inst->cfg->batman.time_left = -2; + inst->cfg->batman.time_full = -2; + inst->cfg->batman.have_battery = -2; + inst->cfg->batman.have_power = -2; + + inst->cfg->batman.o_gadget = elm_layout_add(inst->o_main); + e_theme_edje_object_set(inst->cfg->batman.o_gadget, "base/theme/modules/battery", + "e/modules/battery/main"); + E_EXPAND(inst->cfg->batman.o_gadget); + E_FILL(inst->cfg->batman.o_gadget); + elm_box_pack_end(inst->o_main, inst->cfg->batman.o_gadget); + evas_object_show(inst->cfg->batman.o_gadget); + evas_object_smart_callback_del_full(obj, "gadget_created", _batman_created_cb, data); + _batman_config_updated(inst); +} + +Evas_Object * +sysinfo_batman_create(Evas_Object *parent, Instance *inst) +{ + inst->cfg->batman.full = -2; + inst->cfg->batman.time_left = -2; + inst->cfg->batman.time_full = -2; + inst->cfg->batman.have_battery = -2; + inst->cfg->batman.have_power = -2; + + inst->cfg->batman.o_gadget = elm_layout_add(parent); + e_theme_edje_object_set(inst->cfg->batman.o_gadget, "base/theme/modules/battery", + "e/modules/battery/main"); + E_EXPAND(inst->cfg->batman.o_gadget); + E_FILL(inst->cfg->batman.o_gadget); + evas_object_show(inst->cfg->batman.o_gadget); + _batman_config_updated(inst); + + return inst->cfg->batman.o_gadget; +} + +static Config_Item * +_conf_item_get(int *id) +{ + Config_Item *ci; + Eina_List *l; + + if (*id > 0) + { + EINA_LIST_FOREACH(sysinfo_config->items, l, ci) + if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_BATMAN) return ci; + } + + ci = E_NEW(Config_Item, 1); + + if (*id != -1) + ci->id = eina_list_count(sysinfo_config->items)+1; + else + ci->id = -1; + + ci->esm = E_SYSINFO_MODULE_BATMAN; + ci->batman.poll_interval = 512; + ci->batman.alert = 30; + ci->batman.alert_p = 10; + ci->batman.alert_timeout = 0; + ci->batman.suspend_below = 0; + ci->batman.force_mode = 0; + ci->batman.full = -2; + ci->batman.time_left = -2; + ci->batman.time_full = -2; + ci->batman.have_battery = -2; + ci->batman.have_power = -2; +#if defined HAVE_EEZE || defined __OpenBSD__ || defined __NetBSD__ + ci->batman.fuzzy = 0; +#endif + ci->batman.desktop_notifications = 0; + + sysinfo_config->items = eina_list_append(sysinfo_config->items, ci); + + return ci; +} + +Evas_Object * +batman_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED) +{ + Instance *inst; + + inst = E_NEW(Instance, 1); + inst->cfg = _conf_item_get(id); + *id = inst->cfg->id; + inst->o_main = elm_box_add(parent); + E_EXPAND(inst->o_main); + evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + evas_object_smart_callback_add(parent, "gadget_created", _batman_created_cb, inst); + evas_object_smart_callback_add(parent, "gadget_removed", _batman_removed_cb, inst); + evas_object_show(inst->o_main); + +#ifdef HAVE_EEZE + eeze_init(); +#endif + + if (inst->cfg->id < 0) return inst->o_main; + + sysinfo_instances = + eina_list_append(sysinfo_instances, inst); + + return inst->o_main; +} diff --git a/src/modules/sysinfo/batman/batman.h b/src/modules/sysinfo/batman/batman.h new file mode 100644 index 000000000..7d6184a1e --- /dev/null +++ b/src/modules/sysinfo/batman/batman.h @@ -0,0 +1,107 @@ +#ifndef BATMAN_H +#define BATMAN_H + +#include "../sysinfo.h" + +#define CHECK_NONE 0 +#define CHECK_ACPI 1 +#define CHECK_APM 2 +#define CHECK_PMU 3 +#define CHECK_SYS_ACPI 4 + +#define UNKNOWN 0 +#define NOSUBSYSTEM 1 +#define SUBSYSTEM 2 + +#define SUSPEND 0 +#define HIBERNATE 1 +#define SHUTDOWN 2 + +#define POPUP_DEBOUNCE_CYCLES 2 + +typedef struct _Battery Battery; +typedef struct _Ac_Adapter Ac_Adapter; +typedef struct _Battery_Config Battery_Config; + +struct _Battery +{ + Instance *inst; + const char *udi; +#if defined HAVE_EEZE || defined __OpenBSD__ || defined __DragonFly__ || defined __FreeBSD__ || defined __NetBSD__ + Ecore_Poller *poll; +#endif + Eina_Bool present:1; + Eina_Bool charging:1; +#if defined HAVE_EEZE || defined __OpenBSD__ || defined __DragonFly__ || defined __FreeBSD__ || defined __NetBSD__ + double last_update; + double percent; + double current_charge; + double design_charge; + double last_full_charge; + double charge_rate; + double time_full; + double time_left; +#else + int percent; + int current_charge; + int design_charge; + int last_full_charge; + int charge_rate; + int time_full; + int time_left; + const char *type; + const char *charge_units; +#endif + const char *technology; + const char *model; + const char *vendor; + Eina_Bool got_prop:1; + Eldbus_Proxy *proxy; + int * mib; +#if defined(__FreeBSD__) || defined(__DragonFly__) + int * mib_state; + int * mib_units; + int * mib_time; + int batteries; + int time_min; +#endif +}; + +struct _Ac_Adapter +{ + Instance *inst; + const char *udi; + Eina_Bool present:1; + const char *product; + Eldbus_Proxy *proxy; + int * mib; +}; + +Battery *_batman_battery_find(const char *udi); +Ac_Adapter *_batman_ac_adapter_find(const char *udi); +void _batman_update(Instance *inst, int full, int time_left, int time_full, Eina_Bool have_battery, Eina_Bool have_power); +void _batman_device_update(Instance *inst); +/* in batman_fallback.c */ +int _batman_fallback_start(Instance *inst); +void _batman_fallback_stop(void); +/* end batman_fallback.c */ +#ifdef HAVE_EEZE +/* in batman_udev.c */ +int _batman_udev_start(Instance *inst); +void _batman_udev_stop(Instance *inst); +/* end batman_udev.c */ +#elif !defined __OpenBSD__ && !defined __DragonFly__ && !defined __FreeBSD__ && !defined __NetBSD__ +/* in batman_upower.c */ +int _batman_upower_start(Instance *inst); +void _batman_upower_stop(void); +/* end batman_upower.c */ +#else +/* in batman_sysctl.c */ +int _batman_sysctl_start(Instance *inst); +void _batman_sysctl_stop(void); +/* end batman_sysctl.c */ +#endif + +void _batman_config_updated(Instance *inst); + +#endif diff --git a/src/modules/sysinfo/batman/batman_fallback.c b/src/modules/sysinfo/batman/batman_fallback.c new file mode 100644 index 000000000..e10ecafe3 --- /dev/null +++ b/src/modules/sysinfo/batman/batman_fallback.c @@ -0,0 +1,1297 @@ +#include "batman.h" + +#ifdef HAVE_CFBASE_H +# include +# include +# include +# include +# include +# include +# include +#endif + +/* 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 + +#define SYS_PWR + +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; + +static const char *sys_power_dir = "/sys/class/power_supply"; +static Eina_Bool _batman_fallback_poll_cb(void *data EINA_UNUSED); + +static int +int_file_get(const char *file) +{ + int val = -1; + FILE *f = fopen(file, "r"); + if (f) + { + char buf[256]; + char *str = fgets(buf, sizeof(buf), f); + if (str) val = atoi(str); + fclose(f); + } + return val; +} + +static char * +str_file_get(const char *file) +{ + char *val = NULL; + FILE *f = fopen(file, "r"); + if (f) + { + char buf[4096]; + char *str = fgets(buf, sizeof(buf), f); + if (str) + { + size_t len = strlen(str); + if ((len > 0) && (str[len - 1] == '\n')) + { + len--; + str[len] = 0; + } + val = malloc(len + 1); + if (val) memcpy(val, str, len + 1); + } + fclose(f); + } + return val; +} + +static int +int_get(const char *buf) +{ + const char *p = strchr(buf, ':'); + if (!p) return 0; + p++; + while (*p == ' ') + p++; + return atoi(p); +} + +static char * +str_get(const char *buf) +{ + const char *p = strchr(buf, ':'); + const char *q; + char *ret; + + if (!p) return NULL; + p++; + while (*p == ' ') + p++; + + q = p + strlen(p) - 1; + while ((q > p) && ((*q == ' ') || (*q == '\n'))) + q--; + + if (q < p) return NULL; + q++; + ret = malloc(q - p + 1); + if (!ret) return NULL; + memcpy(ret, p, q - p); + ret[q - p] = '\0'; + return ret; +} + +static char * +file_str_entry_get(FILE *f, + const char *entry) +{ + char buf[4096]; + char *tmp; + + tmp = fgets(buf, sizeof(buf), f); + if (!tmp) + { + EINA_LOG_ERR("unexpected end of file, expected: '%s'", entry); + return NULL; + } + if (strcmp(tmp, entry) != 0) + { + EINA_LOG_ERR("unexpected file entry, expected: '%s'", entry); + return NULL; + } + tmp = str_get(tmp); + if (!tmp) + { + EINA_LOG_ERR("unexpected file entry, missing value for '%s'", entry); + return NULL; + } + return tmp; +} + +#if 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; + 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, ¤tval); + /* 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, ¤tval); + time_left = currentval * 60; + } + else + { + values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToFullChargeKey)); + CFNumberGetValue(values, kCFNumberSInt32Type, ¤tval); + 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 */ +#if 0 +static Eina_Bool linux_sys_class_power_supply_cb_event_fd_active(void *data, + Ecore_Fd_Handler *fd_handler); +static void linux_sys_class_power_supply_check(void); +#endif +static void linux_sys_class_power_supply_init(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 Eina_List *events = NULL; + +#if 0 +static Ecore_Timer *sys_class_delay_check = NULL; + +static Eina_Bool +linux_sys_class_power_supply_cb_delay_check(void *data) +{ + linux_sys_class_power_supply_init(); + _batman_fallback_poll_cb(NULL); + sys_class_delay_check = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static Ecore_Timer *re_init_timer = NULL; + +static Eina_Bool +linux_sys_class_power_supply_cb_re_init(void *data) +{ + Sys_Class_Power_Supply_Uevent *sysev; + + if (events) + { + EINA_LIST_FREE(events, sysev) + { +// if (sysev->fd_handler) +// ecore_main_fd_handler_del(sysev->fd_handler); +// if (sysev->fd >= 0) close(sysev->fd); + free(sysev->name); + free(sysev); + } + } + linux_sys_class_power_supply_init(); + re_init_timer = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +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) + { + events = eina_list_remove(events, sysev); + +// 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 ECORE_CALLBACK_CANCEL; +} + +#endif +static void +linux_sys_class_power_supply_sysev_init(Sys_Class_Power_Supply_Uevent *sysev) +{ + char buf[4096]; + const char *dir = sys_power_dir; + + sysev->basis = 0; + sysev->have_current_avg = 0; + sysev->have_current_now = 0; + snprintf(buf, sizeof(buf), "%s/%s/present", dir, sysev->name); + sysev->present = int_file_get(buf); + if (!sysev->present) return; + snprintf(buf, sizeof(buf), "%s/%s/current_avg", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->have_current_avg = 1; + snprintf(buf, sizeof(buf), "%s/%s/current_now", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->have_current_now = 1; + + snprintf(buf, sizeof(buf), "%s/%s/voltage_max", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE; + snprintf(buf, sizeof(buf), "%s/%s/voltage_max_design", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE; + + snprintf(buf, sizeof(buf), "%s/%s/energy_full", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY; + snprintf(buf, sizeof(buf), "%s/%s/energy_full_design", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY; + + snprintf(buf, sizeof(buf), "%s/%s/charge_full", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE; + snprintf(buf, sizeof(buf), "%s/%s/charge_full_design", dir, sysev->name); + if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE; + + if (sysev->basis == BASIS_CHARGE) + { + snprintf(buf, sizeof(buf), "%s/%s/charge_full", dir, sysev->name); + sysev->basis_full = int_file_get(buf); + snprintf(buf, sizeof(buf), "%s/%s/charge_empty", dir, sysev->name); + sysev->basis_empty = int_file_get(buf); + if (sysev->basis_full < 0) + { + snprintf(buf, sizeof(buf), "%s/%s/charge_full_design", dir, sysev->name); + sysev->basis_full = int_file_get(buf); + } + if (sysev->basis_empty < 0) + { + snprintf(buf, sizeof(buf), "%s/%s/charge_empty_design", dir, sysev->name); + sysev->basis_empty = int_file_get(buf); + } + } + else if (sysev->basis == BASIS_ENERGY) + { + snprintf(buf, sizeof(buf), "%s/%s/energy_full", dir, sysev->name); + sysev->basis_full = int_file_get(buf); + snprintf(buf, sizeof(buf), "%s/%s/energy_empty", dir, sysev->name); + sysev->basis_empty = int_file_get(buf); + if (sysev->basis_full < 0) + { + snprintf(buf, sizeof(buf), "%s/%s/energy_full_design", dir, sysev->name); + sysev->basis_full = int_file_get(buf); + } + if (sysev->basis_empty < 0) + { + snprintf(buf, sizeof(buf), "%s/%s/energy_empty_design", dir, sysev->name); + sysev->basis_empty = int_file_get(buf); + } + } + else if (sysev->basis == BASIS_VOLTAGE) + { + snprintf(buf, sizeof(buf), "%s/%s/voltage_max", dir, sysev->name); + sysev->basis_full = int_file_get(buf); + snprintf(buf, sizeof(buf), "%s/%s/voltage_min", dir, sysev->name); + sysev->basis_empty = int_file_get(buf); + if (sysev->basis_full < 0) + { + snprintf(buf, sizeof(buf), "%s/%s/voltage_max_design", dir, sysev->name); + sysev->basis_full = int_file_get(buf); + } + if (sysev->basis_empty < 0) + { + snprintf(buf, sizeof(buf), "%s/%s/voltage_min_design", dir, 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]; + const char *dir = sys_power_dir; + + snprintf(buf, sizeof(buf), "%s/%s/type", dir, 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) +{ + Eina_List *l; + + if (events) + { + Sys_Class_Power_Supply_Uevent *sysev; + + EINA_LIST_FOREACH(events, l, sysev) + linux_sys_class_power_supply_sysev_init(sysev); + } + else + { + Eina_List *bats; + char *name; +// char buf[4096]; + + bats = ecore_file_ls("/sys/class/power_supply/"); +// bats = ecore_file_ls("./TST"); + if (bats) + { + events = NULL; + + EINA_LIST_FREE(bats, name) + { + Sys_Class_Power_Supply_Uevent *sysev; + + if (!(linux_sys_class_power_supply_is_battery(name))) + { + free(name); + continue; + } + sysev = (Sys_Class_Power_Supply_Uevent *)calloc(1, sizeof(Sys_Class_Power_Supply_Uevent)); + sysev->name = 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); +// + events = eina_list_append(events, sysev); + linux_sys_class_power_supply_sysev_init(sysev); + } + } + } +} + +static void +linux_sys_class_power_supply_check(void) +{ + Eina_List *l; + char *name; + char buf[4096]; + const char *dir = sys_power_dir; + + 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; + EINA_LIST_FOREACH(events, l, sysev) + { + 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), "%s/%s/capacity", dir, name); + capacity = int_file_get(buf); + if (sysev->have_current_avg) + { + snprintf(buf, sizeof(buf), "%s/%s/current_avg", dir, name); + current = int_file_get(buf); + } + else if (sysev->have_current_now) + { + snprintf(buf, sizeof(buf), "%s/%s/current_now", dir, 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), "%s/%s/status", dir, 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), "%s/%s/time_to_full_now", dir, name); + time_to_full = int_file_get(buf); + } + else + { + snprintf(buf, sizeof(buf), "%s/%s/time_to_empty_now", dir, 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), "%s/%s/charge_now", dir, name); + else if (sysev->basis == BASIS_ENERGY) + snprintf(buf, sizeof(buf), "%s/%s/energy_now", dir, name); + else if (sysev->basis == BASIS_VOLTAGE) + snprintf(buf, sizeof(buf), "%s/%s/voltage_now", dir, name); + pwr_now = int_file_get(buf); + pwr_empty = sysev->basis_empty; + pwr_full = sysev->basis_full; + if ((sysev->basis == BASIS_VOLTAGE) && + (capacity >= 0)) + { + /* if we use voltage as basis.. we're not very accurate + * so we should prefer capacity readings */ + pwr_empty = -1; + pwr_full = -1; + pwr_now = -1; + } + + if (pwr_empty < 0) pwr_empty = 0; + + if ((pwr_full > 0) && (pwr_full > pwr_empty)) + { + 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) + { + 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; + } + /* simple current battery fallback */ + else + { + if (sysev->present) have_battery = 1; + if (charging) have_power = 1; + total_pwr_max = 100; + total_pwr_now = capacity; + if (total_pwr_now < 100) nofull = 1; + } + } + 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 Eina_Bool linux_acpi_cb_acpid_add(void *data, + int type, + void *event); +static Eina_Bool linux_acpi_cb_acpid_del(void *data, + int type, + void *event); +static Eina_Bool 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 Eina_Bool +linux_acpi_cb_delay_check(void *data EINA_UNUSED) +{ + linux_acpi_init(); + _batman_fallback_poll_cb(NULL); + delay_check = NULL; + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +linux_acpi_cb_acpid_add(void *data EINA_UNUSED, + int type EINA_UNUSED, + void *event EINA_UNUSED) +{ + return ECORE_CALLBACK_PASS_ON; +} + + +static Eina_Bool +linux_acpi_cb_acpid_del(void *data EINA_UNUSED, + int type EINA_UNUSED, + void *event EINA_UNUSED) +{ + 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 ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +linux_acpi_cb_acpid_data(void *data EINA_UNUSED, + int type EINA_UNUSED, + void *event EINA_UNUSED) +{ + if (delay_check) ecore_timer_del(delay_check); + delay_check = ecore_timer_add(0.2, linux_acpi_cb_delay_check, NULL); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +linux_acpi_cb_event_fd_active(void *data EINA_UNUSED, + 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 ECORE_CALLBACK_RENEW; +} + +static void +linux_acpi_init(void) +{ + Eina_Iterator *powers; + Eina_Iterator *bats; + + bats = eina_file_direct_ls("/proc/acpi/battery"); + if (bats) + { + Eina_File_Direct_Info *info; + + have_power = 0; + powers = eina_file_direct_ls("/proc/acpi/ac_adapter"); + if (powers) + { + EINA_ITERATOR_FOREACH(powers, info) + { + char buf[PATH_MAX]; + FILE *f; + + if (info->name_length + sizeof("/state") >= sizeof(buf)) continue; + snprintf(buf, sizeof(buf), "%s/state", info->path); + f = fopen(buf, "r"); + if (f) + { + char *tmp; + + /* state */ + tmp = fgets(buf, sizeof(buf), f); + if (tmp) tmp = str_get(tmp); + if (tmp) + { + if (!strcmp(tmp, "on-line")) have_power = 1; + free(tmp); + } + fclose(f); + } + } + eina_iterator_free(powers); + } + + have_battery = 0; + acpi_max_full = 0; + acpi_max_design = 0; + EINA_ITERATOR_FOREACH(bats, info) + { + char buf[PATH_MAX + 6]; + FILE *f; + + snprintf(buf, sizeof(buf), "%s/info", info->path); + f = fopen(buf, "r"); + if (f) + { + char *tmp; + + /* present */ + tmp = fgets(buf, sizeof(buf), f); + if (tmp) tmp = str_get(tmp); + if (tmp) + { + if (!strcmp(tmp, "yes")) have_battery = 1; + free(tmp); + } + /* design cap */ + tmp = fgets(buf, sizeof(buf), f); + if (tmp) tmp = str_get(tmp); + if (tmp) + { + if (strcmp(tmp, "unknown")) acpi_max_design += atoi(tmp); + free(tmp); + } + /* last full cap */ + tmp = fgets(buf, sizeof(buf), f); + if (tmp) tmp = str_get(tmp); + if (tmp) + { + if (strcmp(tmp, "unknown")) acpi_max_full += atoi(tmp); + free(tmp); + } + fclose(f); + } + } + + eina_iterator_free(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) +{ + Eina_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; + + EINA_LIST_FREE(bats, name) + { + char buf[4096]; + char *tmp; + FILE *f; + + snprintf(buf, sizeof(buf), "/proc/acpi/battery/%s/state", name); + free(name); + f = fopen(buf, "r"); + if (!f) continue; + + tmp = file_str_entry_get(f, "present:"); + if (!tmp) goto fclose_and_continue; + if (!strcasecmp(tmp, "yes")) have_battery = 1; + free(tmp); + + tmp = file_str_entry_get(f, "capacity state:"); + if (!tmp) goto fclose_and_continue; + free(tmp); + + tmp = file_str_entry_get(f, "charging state:"); + if (!tmp) goto fclose_and_continue; + if ((have_power == 0) && (!strcasecmp(tmp, "charging"))) + have_power = 1; + free(tmp); + + tmp = file_str_entry_get(f, "present rate:"); + if (!tmp) goto fclose_and_continue; + if (strcasecmp(tmp, "unknown")) rate += atoi(tmp); + free(tmp); + + tmp = file_str_entry_get(f, "remaining capacity:"); + if (!tmp) goto fclose_and_continue; + if (strcasecmp(tmp, "unknown")) capacity += atoi(tmp); + free(tmp); + +fclose_and_continue: + fclose(f); + } + + 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 s1[32], s2[32], s3[32], *endptr; + 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; + + if (fscanf(f, "%*s %*s %x %x %x %x %31s %31s %31s", + &apm_flags, &ac_stat, &bat_stat, &bat_flags, s1, s2, s3) != 7) + { + fclose(f); + return; + } + fclose(f); + + bat_val = strtol(s1, &endptr, 10); + if (*endptr != '%') return; + + else if (!strcmp(s3, "min")) + time_val = atoi(s2) * 60; + else time_val = 0; + + 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]; + Eina_List *bats; + char *name; + int ac = 0; + int charge = 0; + int max_charge = 0; + int seconds = 0; + int curcharge = 0; + int curmax = 0; + + f = fopen("/proc/pmu/info", "r"); + if (f) + { + char *tmp; + /* Skip driver */ + tmp = fgets(buf, sizeof(buf), f); + if (!tmp) + { + EINA_LOG_ERR("no driver info in /proc/pmu/info"); + goto fclose_and_continue; + } + /* Skip firmware */ + tmp = fgets(buf, sizeof(buf), f); + if (!tmp) + { + EINA_LOG_ERR("no firmware info in /proc/pmu/info"); + goto fclose_and_continue; + } + /* Read ac */ + tmp = fgets(buf, sizeof(buf), f); + if (!tmp) + { + EINA_LOG_ERR("no AC info in /proc/pmu/info"); + goto fclose_and_continue; + } + ac = int_get(buf); +fclose_and_continue: + fclose(f); + } + bats = ecore_file_ls("/proc/pmu"); + if (bats) + { + have_battery = 1; + have_power = ac; + EINA_LIST_FREE(bats, name) + { + 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("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 + 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 parallel */ + seconds = MAX(timeleft, seconds); + } + } + + free(name); + } + 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 Eina_Bool +_batman_fallback_poll_cb(void *data) +{ + Instance *inst = data; + +#if defined(HAVE_CFBASE_H) /* OS X */ + darwin_check(); + return ECORE_CALLBACK_RENEW; +#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 + + _batman_update(inst, battery_full, time_left, time_left, have_battery, have_power); + + return EINA_TRUE; +} + +static int +dir_has_contents(const char *dir) +{ + Eina_List *bats; + char *file; + int count; + + bats = ecore_file_ls(dir); + + count = eina_list_count(bats); + EINA_LIST_FREE(bats, file) + free(file); + if (count > 0) return 1; + return 0; +} + +int +_batman_fallback_start(Instance *inst) +{ +#if defined(HAVE_CFBASE_H) /* OS X */ + darwin_init(); +#else + if ((ecore_file_is_dir(sys_power_dir)) && (dir_has_contents(sys_power_dir))) + { + 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 + + poller = ecore_poller_add(ECORE_POLLER_CORE, inst->cfg->batman.poll_interval, _batman_fallback_poll_cb, inst); + + return 1; +} + +void +_batman_fallback_stop(void) +{ + E_FREE_FUNC(poller, ecore_poller_del); +} + diff --git a/src/modules/sysinfo/batman/batman_sysctl.c b/src/modules/sysinfo/batman/batman_sysctl.c new file mode 100644 index 000000000..7dd568894 --- /dev/null +++ b/src/modules/sysinfo/batman/batman_sysctl.c @@ -0,0 +1,347 @@ +#include +#include +#include + +#if defined(__OpenBSD__) || defined(__NetBSD__) +#include +#include +#endif + +#include "batman.h" + +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +static Eina_Bool _batman_sysctl_battery_update_poll(void *data); +#endif +static int _batman_sysctl_battery_update(Instance *inst); + +extern Eina_List *batman_device_batteries; +extern Eina_List *batman_device_ac_adapters; +extern double batman_init_time; + +Ac_Adapter *ac = NULL; +Battery *bat = NULL; + +int +_batman_sysctl_start(Instance *inst) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0}; + int devn; + struct sensordev snsrdev; + size_t sdlen = sizeof(struct sensordev); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + int result; + size_t len; +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) + for (devn = 0;; devn++) { + mib[2] = devn; + if (sysctl(mib, 3, &snsrdev, &sdlen, NULL, 0) == -1) + { + if (errno == ENXIO) + continue; + if (errno == ENOENT) + break; + } + + if (!strcmp("acpibat0", snsrdev.xname)) + { + if (!(bat = E_NEW(Battery, 1))) + return 0; + bat->inst = inst; + bat->udi = eina_stringshare_add("acpibat0"), + bat->mib = malloc(sizeof(int) * 5); + if (!bat->mib) return 0; + bat->mib[0] = mib[0]; + bat->mib[1] = mib[1]; + bat->mib[2] = mib[2]; + bat->technology = eina_stringshare_add("Unknown"); + bat->model = eina_stringshare_add("Unknown"); + bat->vendor = eina_stringshare_add("Unknown"); + bat->poll = ecore_poller_add(ECORE_POLLER_CORE, + inst->cfg->batman.poll_interval, + _batman_sysctl_battery_update_poll, inst); + batman_device_batteries = eina_list_append(batman_device_batteries, bat); + } + else if (!strcmp("acpiac0", snsrdev.xname)) + { + if (!(ac = E_NEW(Ac_Adapter, 1))) + return 0; + ac->inst = inst; + ac->udi = eina_stringshare_add("acpiac0"); + ac->mib = malloc(sizeof(int) * 5); + if (!ac->mib) return 0; + ac->mib[0] = mib[0]; + ac->mib[1] = mib[1]; + ac->mib[2] = mib[2]; + batman_device_ac_adapters = eina_list_append(batman_device_ac_adapters, ac); + } + } +#elif defined(__FreeBSD__) || defined(__DragonFly__) + if ((sysctlbyname("hw.acpi.battery.life", NULL, &len, NULL, 0)) != -1) + { + if (!(bat = E_NEW(Battery, 1))) + return 0; + bat->inst = inst; + bat->mib = malloc(sizeof(int) * 4); + if (!bat->mib) return 0; + sysctlnametomib("hw.acpi.battery.life", bat->mib, &len); + + bat->mib_state = malloc(sizeof(int) * 4); + if (!bat->mib_state) return 0; + sysctlnametomib("hw.acpi.battery.state", bat->mib_state, &len); + + bat->mib_time = malloc(sizeof(int) * 4); + if (!bat->mib_time) return 0; + sysctlnametomib("hw.acpi.battery.time", bat->mib_time, &len); + + bat->mib_units = malloc(sizeof(int) * 4); + if(!bat->mib_units) return 0; + sysctlnametomib("hw.acpi.battery.units", bat->mib_units, &len); + + bat->udi = eina_stringshare_add("hw.acpi.battery"); + bat->technology = eina_stringshare_add("Unknown"); + bat->model = eina_stringshare_add("Unknown"); + bat->vendor = eina_stringshare_add("Unknown"); + + bat->poll = ecore_poller_add(ECORE_POLLER_CORE, + inst->cfg->batman.poll_interval, + _batman_sysctl_battery_update_poll, inst); + + batman_device_batteries = eina_list_append(batman_device_batteries, bat); + } + + if ((sysctlbyname("hw.acpi.acline", NULL, &len, NULL, 0)) != -1) + { + if (!(ac = E_NEW(Ac_Adapter, 1))) + return 0; + ac->inst = inst; + ac->mib = malloc(sizeof(int) * 3); + if (!ac->mib) return 0; + len = sizeof(ac->mib); + sysctlnametomib("hw.acpi.acline", ac->mib, &len); + + ac->udi = eina_stringshare_add("hw.acpi.acline"); + + batman_device_ac_adapters = eina_list_append(batman_device_ac_adapters, ac); + } +#endif + _batman_sysctl_battery_update(inst); + + bat->last_update = ecore_time_get(); + + return 1; +} + +void +_batman_sysctl_stop(void) +{ + if (ac) + { + E_FREE_FUNC(ac->udi, eina_stringshare_del); + E_FREE_FUNC(ac->mib, free); + E_FREE_FUNC(ac, free); + } + + if (bat) + { + E_FREE_FUNC(bat->udi, eina_stringshare_del); + E_FREE_FUNC(bat->technology, eina_stringshare_del); + E_FREE_FUNC(bat->model, eina_stringshare_del); + E_FREE_FUNC(bat->vendor, eina_stringshare_del); + E_FREE_FUNC(bat->poll, ecore_poller_del); +#if defined(__FreeBSD__) || defined(__DragonFly__) + E_FREE_FUNC(bat->mib_state, free); + E_FREE_FUNC(bat->mib_time, free); + E_FREE_FUNC(bat->mib_units, free); +#endif + E_FREE_FUNC(bat->mib, free); + E_FREE_FUNC(bat, free); + } +} + +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +static Eina_Bool +_batman_sysctl_battery_update_poll(void *data) +{ + Instance *inst = data; + _batman_sysctl_battery_update(inst); + return EINA_TRUE; +} +#endif + +static int +_batman_sysctl_battery_update(Instance *inst) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + double _time, charge; + struct sensor s; + size_t slen = sizeof(struct sensor); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + double _time, charge; + int value; + size_t len; +#endif + + if (bat) + { + /* update the poller interval */ + ecore_poller_poller_interval_set(bat->poll, + inst->cfg->batman.poll_interval); +#if defined(__OpenBSD__) || defined(__NetBSD__) + /* last full capacity */ + bat->mib[3] = 7; + bat->mib[4] = 0; + if (sysctl(bat->mib, 5, &s, &slen, NULL, 0) != -1) + { + bat->last_full_charge = (double)s.value; + } + + /* remaining capacity */ + bat->mib[3] = 7; + bat->mib[4] = 3; + if (sysctl(bat->mib, 5, &s, &slen, NULL, 0) != -1) + { + charge = (double)s.value; + } + + /* This is a workaround because there's an ACPI bug */ + if (charge == 0 || bat->last_full_charge == 0) + { + /* last full capacity */ + bat->mib[3] = 8; + bat->mib[4] = 0; + if (sysctl(bat->mib, 5, &s, &slen, NULL, 0) != -1) + { + bat->last_full_charge = (double)s.value; + } + + /* remaining capacity */ + bat->mib[3] = 8; + bat->mib[4] = 3; + if (sysctl(bat->mib, 5, &s, &slen, NULL, 0) != -1) + { + charge = (double)s.value; + } + } + + _time = ecore_time_get(); + if ((bat->got_prop) && (charge != bat->current_charge)) + bat->charge_rate = ((charge - bat->current_charge) / (_time - bat->last_update)); + bat->last_update = _time; + bat->current_charge = charge; + bat->percent = 100 * (bat->current_charge / bat->last_full_charge); + if (bat->current_charge >= bat->last_full_charge) + bat->percent = 100; + + if (bat->got_prop) + { + if (bat->charge_rate > 0) + { + if (inst->cfg->batman.fuzzy && (++inst->cfg->batman.fuzzcount <= 10) && (bat->time_full > 0)) + bat->time_full = (((bat->last_full_charge - bat->current_charge) / bat->charge_rate) + bat->time_full) / 2; + else + bat->time_full = (bat->last_full_charge - bat->current_charge) / bat->charge_rate; + bat->time_left = -1; + } + else + { + if (inst->cfg->batman.fuzzy && (inst->cfg->batman.fuzzcount <= 10) && (bat->time_left > 0)) + bat->time_left = (((0 - bat->current_charge) / bat->charge_rate) + bat->time_left) / 2; + else + bat->time_left = (0 - bat->current_charge) / bat->charge_rate; + bat->time_full = -1; + } + } + else + { + bat->time_full = -1; + bat->time_left = -1; + } + + /* battery state 1: discharge, 2: charge */ + bat->mib[3] = 10; + bat->mib[4] = 0; + if (sysctl(bat->mib, 5, &s, &slen, NULL, 0) == -1) + { + if (s.value == 2) + bat->charging = 1; + else + bat->charging = 0; + } + +#elif defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(value); + if ((sysctl(bat->mib, 4, &value, &len, NULL, 0)) == -1) + { + return 0; + } + + bat->percent = value; + + _time = ecore_time_get(); + bat->last_update = _time; + + len = sizeof(value); + if ((sysctl(bat->mib_state, 4, &value, &len, NULL, 0)) == -1) + { + return 0; + } + + bat->charging = !value; + bat->got_prop = 1; + + bat->time_full = -1; + bat->time_left = -1; + + len = sizeof(bat->time_min); + if ((sysctl(bat->mib_time, 4, &bat->time_min, &len, NULL, 0)) == -1) + { + bat->time_min = -1; + } + + len = sizeof(bat->batteries); + if ((sysctl(bat->mib_units, 4, &bat->batteries, &len, NULL, 0)) == -1) + { + bat->batteries = 1; + } + + if (bat->time_min >= 0) bat->time_left = bat->time_min * 60; + if (bat->batteries == 1) bat->time_left = -1; +#endif + } + + if (ac) + { +#if defined(__OpenBSD__) || defined(__NetBSD__) + /* AC State */ + ac->mib[3] = 9; + ac->mib[4] = 0; + if (sysctl(ac->mib, 5, &s, &slen, NULL, 0) != -1) + { + if (s.value) + ac->present = 1; + else + ac->present = 0; + } +#elif defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(value); + if ((sysctl(ac->mib, 3, &value, &len, NULL, 0)) != -1) + { + ac->present = value; + } +#endif + } + + if (bat) + { + if (bat->got_prop) + _batman_device_update(bat->inst); + bat->got_prop = 1; + } + return 0; +} + + + diff --git a/src/modules/sysinfo/batman/batman_udev.c b/src/modules/sysinfo/batman/batman_udev.c new file mode 100644 index 000000000..d4498fc7e --- /dev/null +++ b/src/modules/sysinfo/batman/batman_udev.c @@ -0,0 +1,320 @@ +#include "batman.h" + +static void _batman_udev_event_battery(const char *syspath, Eeze_Udev_Event event, void *data, Eeze_Udev_Watch *watch); +static void _batman_udev_event_ac(const char *syspath, Eeze_Udev_Event event, void *data, Eeze_Udev_Watch *watch); +static void _batman_udev_battery_add(const char *syspath, Instance *inst); +static void _batman_udev_ac_add(const char *syspath, Instance *inst); +static void _batman_udev_battery_del(const char *syspath); +static void _batman_udev_ac_del(const char *syspath); +static Eina_Bool _batman_udev_battery_update_poll(void *data); +static void _batman_udev_battery_update(const char *syspath, Battery *bat, Instance *inst); +static void _batman_udev_ac_update(const char *syspath, Ac_Adapter *ac, Instance *inst); + +extern Eina_List *batman_device_batteries; +extern Eina_List *batman_device_ac_adapters; +extern double batman_init_time; + +int +_batman_udev_start(Instance *inst) +{ + Eina_List *devices; + const char *dev; + + devices = eeze_udev_find_by_type(EEZE_UDEV_TYPE_POWER_BAT, NULL); + EINA_LIST_FREE(devices, dev) + _batman_udev_battery_add(dev, inst); + + devices = eeze_udev_find_by_type(EEZE_UDEV_TYPE_POWER_AC, NULL); + EINA_LIST_FREE(devices, dev) + _batman_udev_ac_add(dev, inst); + + if (!inst->cfg->batman.batwatch) + inst->cfg->batman.batwatch = eeze_udev_watch_add(EEZE_UDEV_TYPE_POWER_BAT, EEZE_UDEV_EVENT_NONE, _batman_udev_event_battery, inst); + if (!inst->cfg->batman.acwatch) + inst->cfg->batman.acwatch = eeze_udev_watch_add(EEZE_UDEV_TYPE_POWER_AC, EEZE_UDEV_EVENT_NONE, _batman_udev_event_ac, inst); + + batman_init_time = ecore_time_get(); + return 1; +} + +void +_batman_udev_stop(Instance *inst) +{ + Ac_Adapter *ac; + Battery *bat; + + if (inst->cfg->batman.batwatch) + E_FREE_FUNC(inst->cfg->batman.batwatch, eeze_udev_watch_del); + if (inst->cfg->batman.acwatch) + E_FREE_FUNC(inst->cfg->batman.acwatch, eeze_udev_watch_del); + + EINA_LIST_FREE(batman_device_ac_adapters, ac) + { + E_FREE_FUNC(ac, free); + } + EINA_LIST_FREE(batman_device_batteries, bat) + { + eina_stringshare_del(bat->udi); + eina_stringshare_del(bat->technology); + eina_stringshare_del(bat->model); + eina_stringshare_del(bat->vendor); + E_FREE_FUNC(bat->poll, ecore_poller_del); + E_FREE_FUNC(bat, free); + } +} + +static void +_batman_udev_event_battery(const char *syspath, Eeze_Udev_Event event, void *data, Eeze_Udev_Watch *watch EINA_UNUSED) +{ + Instance *inst = data; + + if ((event & EEZE_UDEV_EVENT_ADD) || + (event & EEZE_UDEV_EVENT_ONLINE)) + _batman_udev_battery_add(syspath, inst); + else if ((event & EEZE_UDEV_EVENT_REMOVE) || + (event & EEZE_UDEV_EVENT_OFFLINE)) + _batman_udev_battery_del(syspath); + else /* must be change */ + _batman_udev_battery_update(syspath, NULL, inst); +} + +static void +_batman_udev_event_ac(const char *syspath, Eeze_Udev_Event event, void *data, Eeze_Udev_Watch *watch EINA_UNUSED) +{ + Instance *inst = data; + + if ((event & EEZE_UDEV_EVENT_ADD) || + (event & EEZE_UDEV_EVENT_ONLINE)) + _batman_udev_ac_add(syspath, inst); + else if ((event & EEZE_UDEV_EVENT_REMOVE) || + (event & EEZE_UDEV_EVENT_OFFLINE)) + _batman_udev_ac_del(syspath); + else /* must be change */ + _batman_udev_ac_update(syspath, NULL, inst); +} + +static void +_batman_udev_battery_add(const char *syspath, Instance *inst) +{ + Battery *bat; + + if ((bat = _batman_battery_find(syspath))) + { + eina_stringshare_del(syspath); + bat->inst = inst; + _batman_udev_battery_update(NULL, bat, inst); + return; + } + + if (!(bat = E_NEW(Battery, 1))) + { + eina_stringshare_del(syspath); + return; + } + bat->inst = inst; + bat->last_update = ecore_time_get(); + bat->udi = eina_stringshare_add(syspath); + bat->poll = ecore_poller_add(ECORE_POLLER_CORE, + bat->inst->cfg->batman.poll_interval, + _batman_udev_battery_update_poll, bat); + batman_device_batteries = eina_list_append(batman_device_batteries, bat); + _batman_udev_battery_update(syspath, bat, inst); +} + +static void +_batman_udev_ac_add(const char *syspath, Instance *inst) +{ + Ac_Adapter *ac; + + if ((ac = _batman_ac_adapter_find(syspath))) + { + eina_stringshare_del(syspath); + _batman_udev_ac_update(NULL, ac, inst); + return; + } + + if (!(ac = E_NEW(Ac_Adapter, 1))) + { + eina_stringshare_del(syspath); + return; + } + ac->inst = inst; + ac->udi = eina_stringshare_add(syspath); + batman_device_ac_adapters = eina_list_append(batman_device_ac_adapters, ac); + _batman_udev_ac_update(syspath, ac, inst); +} + +static void +_batman_udev_battery_del(const char *syspath) +{ + Battery *bat; + + if (!(bat = _batman_battery_find(syspath))) + { + eina_stringshare_del(syspath); + return; + } + + batman_device_batteries = eina_list_remove(batman_device_batteries, bat); + eina_stringshare_del(bat->udi); + eina_stringshare_del(bat->technology); + eina_stringshare_del(bat->model); + eina_stringshare_del(bat->vendor); + E_FREE_FUNC(bat->poll, ecore_poller_del); + E_FREE_FUNC(bat, free); +} + +static void +_batman_udev_ac_del(const char *syspath) +{ + Ac_Adapter *ac; + + if (!(ac = _batman_ac_adapter_find(syspath))) + { + eina_stringshare_del(syspath); + _batman_device_update(ac->inst); + return; + } + + batman_device_ac_adapters = eina_list_remove(batman_device_ac_adapters, ac); + eina_stringshare_del(ac->udi); + E_FREE_FUNC(ac, free); +} + +static Eina_Bool +_batman_udev_battery_update_poll(void *data) +{ + Battery *bat = data; + + _batman_udev_battery_update(NULL, bat, bat->inst); + + return EINA_TRUE; +} + +#define GET_NUM(TYPE, VALUE, PROP) \ + do \ + { \ + test = eeze_udev_syspath_get_property(TYPE->udi, #PROP); \ + if (test) \ + { \ + TYPE->VALUE = strtod(test, NULL); \ + eina_stringshare_del(test); \ + } \ + } \ + while (0) + +#define GET_STR(TYPE, VALUE, PROP) TYPE->VALUE = eeze_udev_syspath_get_property(TYPE->udi, #PROP) + +static void +_batman_udev_battery_update(const char *syspath, Battery *bat, Instance *inst) +{ + const char *test; + double t, charge; + + if (!bat) + { + if (!(bat = _batman_battery_find(syspath))) + { + _batman_udev_battery_add(syspath, inst); + return; + } + } + /* update the poller interval */ + ecore_poller_poller_interval_set(bat->poll, bat->inst->cfg->batman.poll_interval); + + GET_NUM(bat, present, POWER_SUPPLY_PRESENT); + if (!bat->got_prop) /* only need to get these once */ + { + GET_STR(bat, technology, POWER_SUPPLY_TECHNOLOGY); + GET_STR(bat, model, POWER_SUPPLY_MODEL_NAME); + GET_STR(bat, vendor, POWER_SUPPLY_MANUFACTURER); + GET_NUM(bat, design_charge, POWER_SUPPLY_ENERGY_FULL_DESIGN); + if (!bat->design_charge) + GET_NUM(bat, design_charge, POWER_SUPPLY_CHARGE_FULL_DESIGN); + } + GET_NUM(bat, last_full_charge, POWER_SUPPLY_ENERGY_FULL); + if (!bat->last_full_charge) + GET_NUM(bat, last_full_charge, POWER_SUPPLY_CHARGE_FULL); + test = eeze_udev_syspath_get_property(bat->udi, "POWER_SUPPLY_ENERGY_NOW"); + if (!test) + test = eeze_udev_syspath_get_property(bat->udi, "POWER_SUPPLY_CHARGE_NOW"); + if (test) + { + double charge_rate = 0; + + charge = strtod(test, NULL); + eina_stringshare_del(test); + t = ecore_time_get(); + if ((bat->got_prop) && (charge != bat->current_charge) && bat->current_charge != 0) + charge_rate = ((charge - bat->current_charge) / (t - bat->last_update)); + if (charge_rate != 0 || bat->last_update == 0 || bat->current_charge == 0) + { + bat->last_update = t; + bat->current_charge = charge; + bat->charge_rate = charge_rate; + } + bat->percent = 100 * (bat->current_charge / bat->last_full_charge); + if (bat->got_prop) + { + if (bat->charge_rate > 0) + { + if (bat->inst->cfg->batman.fuzzy && (++bat->inst->cfg->batman.fuzzcount <= 10) && (bat->time_full > 0)) + bat->time_full = (((bat->last_full_charge - bat->current_charge) / bat->charge_rate) + bat->time_full) / 2; + else + bat->time_full = (bat->last_full_charge - bat->current_charge) / bat->charge_rate; + bat->time_left = -1; + } + else + { + if (bat->inst->cfg->batman.fuzzy && (bat->inst->cfg->batman.fuzzcount <= 10) && (bat->time_left > 0)) + bat->time_left = (((0 - bat->current_charge) / bat->charge_rate) + bat->time_left) / 2; + else + bat->time_left = (0 - bat->current_charge) / bat->charge_rate; + bat->time_full = -1; + } + } + else + { + bat->time_full = -1; + bat->time_left = -1; + } + } + if (bat->inst->cfg->batman.fuzzcount > 10) bat->inst->cfg->batman.fuzzcount = 0; + test = eeze_udev_syspath_get_property(bat->udi, "POWER_SUPPLY_STATUS"); + if (test) + { + if (!strcmp(test, "Charging")) + bat->charging = 1; + else if ((!strcmp(test, "Unknown")) && (bat->charge_rate > 0)) + bat->charging = 1; + else + bat->charging = 0; + eina_stringshare_del(test); + } + else + bat->charging = 0; + if (bat->got_prop) + _batman_device_update(bat->inst); + bat->got_prop = 1; +} + +static void +_batman_udev_ac_update(const char *syspath, Ac_Adapter *ac, Instance *inst) +{ + const char *test; + + if (!ac) + { + if (!(ac = _batman_ac_adapter_find(syspath))) + { + _batman_udev_ac_add(syspath, inst); + return; + } + } + + GET_NUM(ac, present, POWER_SUPPLY_ONLINE); + /* yes, it's really that simple. */ + + _batman_device_update(inst); +} + diff --git a/src/modules/sysinfo/batman/batman_upower.c b/src/modules/sysinfo/batman/batman_upower.c new file mode 100644 index 000000000..2476b1509 --- /dev/null +++ b/src/modules/sysinfo/batman/batman_upower.c @@ -0,0 +1,376 @@ +#include "batman.h" + +#define BUS "org.freedesktop.UPower" +#define PATH "/org/freedesktop/UPower" +#define IFACE "org.freedesktop.UPower" + +extern Eina_List *batman_device_batteries; +extern Eina_List *batman_device_ac_adapters; +extern double batman_init_time; + +static Eldbus_Connection *conn; +static Eldbus_Proxy *upower_proxy; + +typedef struct _Upower_Data Upower_Data; +struct _Upower_Data +{ + Eldbus_Proxy *proxy; + Instance *inst; +}; + +static void +_battery_free(Battery *bat) +{ + Eldbus_Object *obj = eldbus_proxy_object_get(bat->proxy); + eldbus_proxy_unref(bat->proxy); + eldbus_object_unref(obj); + + batman_device_batteries = eina_list_remove(batman_device_batteries, bat); + eina_stringshare_del(bat->udi); + if (bat->model) + eina_stringshare_del(bat->model); + if (bat->vendor) + eina_stringshare_del(bat->vendor); + free(bat); +} + +static void +_ac_free(Ac_Adapter *ac) +{ + Eldbus_Object *obj = eldbus_proxy_object_get(ac->proxy); + eldbus_proxy_unref(ac->proxy); + eldbus_object_unref(obj); + + batman_device_ac_adapters = eina_list_remove(batman_device_ac_adapters, ac); + eina_stringshare_del(ac->udi); + free(ac); +} + +static void +_ac_get_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Ac_Adapter *ac = data; + Eldbus_Message_Iter *array, *dict, *variant; + + if (!eldbus_message_arguments_get(msg, "a{sv}", &array)) + return; + + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + const char *key; + + if (!eldbus_message_iter_arguments_get(dict, "sv", &key, &variant)) + continue; + + if (!strcmp(key, "Online")) + { + Eina_Bool b; + eldbus_message_iter_arguments_get(variant, "b", &b); + ac->present = b; + break; + } + } + _batman_device_update(ac->inst); +} + +static void +_ac_changed_cb(void *data, const Eldbus_Message *msg EINA_UNUSED) +{ + Ac_Adapter *ac = data; + eldbus_proxy_property_get_all(ac->proxy, _ac_get_all_cb, ac); +} + +static void +_process_ac(Eldbus_Proxy *proxy, Instance *inst) +{ + Ac_Adapter *ac; + ac = E_NEW(Ac_Adapter, 1); + if (!ac) goto error; + ac->inst = inst; + ac->proxy = proxy; + ac->udi = eina_stringshare_add(eldbus_object_path_get(eldbus_proxy_object_get(proxy))); + eldbus_proxy_property_get_all(proxy, _ac_get_all_cb, ac); + eldbus_proxy_signal_handler_add(proxy, "Changed", _ac_changed_cb, ac); + batman_device_ac_adapters = eina_list_append(batman_device_ac_adapters, ac); + return; + +error: + eldbus_object_unref(eldbus_proxy_object_get(proxy)); + eldbus_proxy_unref(proxy); + return; +} + +static const char *bat_techologies[] = { + "Unknown", + "Lithium ion", + "Lithium polymer", + "Lithium iron phosphate", + "Lead acid", + "Nickel cadmium", + "Nickel metal hydride" +}; + +static void +_bat_get_all_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Battery *bat = data; + Eldbus_Message_Iter *array, *dict, *variant; + + bat->got_prop = EINA_TRUE; + if (!eldbus_message_arguments_get(msg, "a{sv}", &array)) + return; + while (eldbus_message_iter_get_and_next(array, 'e', &dict)) + { + const char *key; + union + { + Eina_Bool b; + int64_t i64; + unsigned u; + double d; + const char *s; + } val; + + if (!eldbus_message_iter_arguments_get(dict, "sv", &key, &variant)) + continue; + if (!strcmp(key, "IsPresent")) + { + eldbus_message_iter_arguments_get(variant, "b", &val.b); + bat->present = val.b; + } + else if (!strcmp(key, "TimeToEmpty")) + { + eldbus_message_iter_arguments_get(variant, "x", &val.i64); + bat->time_left = (int) val.i64; + if (bat->time_left > 0) + bat->charging = EINA_FALSE; + else + bat->charging = EINA_TRUE; + } + else if (!strcmp(key, "Percentage")) + { + eldbus_message_iter_arguments_get(variant, "d", &val.d); + bat->percent = (int) val.d; + } + else if (!strcmp(key, "Energy")) + { + eldbus_message_iter_arguments_get(variant, "d", &val.d); + bat->current_charge = (int) val.d; + } + else if (!strcmp(key, "EnergyFullDesign")) + { + eldbus_message_iter_arguments_get(variant, "d", &val.d); + bat->design_charge = (int) val.d; + } + else if (!strcmp(key, "EnergyFull")) + { + eldbus_message_iter_arguments_get(variant, "d", &val.d); + bat->last_full_charge = (int) val.d; + } + else if (!strcmp(key, "TimeToFull")) + { + eldbus_message_iter_arguments_get(variant, "x", &val.i64); + bat->time_full = (int) val.i64; + } + else if (!strcmp(key, "Technology")) + { + eldbus_message_iter_arguments_get(variant, "u", &val.u); + if (val.u > EINA_C_ARRAY_LENGTH(bat_techologies)) + val.u = 0; + bat->technology = bat_techologies[val.u]; + } + else if (!strcmp(key, "Model")) + { + if (!eldbus_message_iter_arguments_get(variant, "s", &val.s)) + continue; + eina_stringshare_replace(&bat->model, val.s); + } + else if (!strcmp(key, "Vendor")) + { + if (!eldbus_message_iter_arguments_get(variant, "s", &val.s)) + continue; + if (bat->vendor) + eina_stringshare_del(bat->vendor); + bat->vendor = eina_stringshare_add(val.s); + } + } + _batman_device_update(bat->inst); +} + +static void +_bat_changed_cb(void *data, const Eldbus_Message *msg EINA_UNUSED) +{ + Battery *bat = data; + eldbus_proxy_property_get_all(bat->proxy, _bat_get_all_cb, bat); +} + +static void +_process_battery(Eldbus_Proxy *proxy, Instance *inst) +{ + Battery *bat; + + bat = E_NEW(Battery, 1); + if (!bat) + { + eldbus_object_unref(eldbus_proxy_object_get(proxy)); + eldbus_proxy_unref(proxy); + return; + } + bat->inst = inst; + bat->proxy = proxy; + bat->udi = eina_stringshare_add(eldbus_object_path_get(eldbus_proxy_object_get(proxy))); + eldbus_proxy_property_get_all(proxy, _bat_get_all_cb, bat); + eldbus_proxy_signal_handler_add(proxy, "Changed", _bat_changed_cb, bat); + batman_device_batteries = eina_list_append(batman_device_batteries, bat); + _batman_device_update(bat->inst); +} + +static void +_device_type_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Upower_Data *ud = data; + Eldbus_Message_Iter *variant; + Eldbus_Object *obj; + unsigned int type; + + if (!eldbus_message_arguments_get(msg, "v", &variant)) + goto error; + + eldbus_message_iter_arguments_get(variant, "u", &type); + if (type == 1) + _process_ac(ud->proxy, ud->inst); + else if (type == 2) + _process_battery(ud->proxy, ud->inst); + else + goto error; + + free(ud); + + return; + +error: + obj = eldbus_proxy_object_get(ud->proxy); + eldbus_proxy_unref(ud->proxy); + eldbus_object_unref(obj); + free(ud); + return; +} + +static void +_process_enumerate_path(const char *path, Instance *inst) +{ + Eldbus_Object *obj; + Eldbus_Proxy *proxy; + Upower_Data *ud; + + obj = eldbus_object_get(conn, BUS, path); + EINA_SAFETY_ON_FALSE_RETURN(obj); + proxy = eldbus_proxy_get(obj, "org.freedesktop.UPower.Device"); + ud = E_NEW(Upower_Data, 1); + if (!ud) + { + eldbus_object_unref(eldbus_proxy_object_get(proxy)); + eldbus_proxy_unref(proxy); + return; + } + ud->proxy = proxy; + ud->inst = inst; + eldbus_proxy_property_get(proxy, "Type", _device_type_cb, ud); +} + +static void +_enumerate_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Instance *inst = data; + const char *path; + Eldbus_Message_Iter *array; + + if (!eldbus_message_arguments_get(msg, "ao", &array)) + return; + + while (eldbus_message_iter_get_and_next(array, 'o', &path)) + _process_enumerate_path(path, inst); +} + +static void +_device_added_cb(void *data, const Eldbus_Message *msg) +{ + const char *path; + Instance *inst = data; + + if (!eldbus_message_arguments_get(msg, "o", &path)) + return; + _process_enumerate_path(path, inst); +} + +static void +_device_removed_cb(void *data EINA_UNUSED, const Eldbus_Message *msg) +{ + Battery *bat; + Ac_Adapter *ac; + const char *path; + Instance *inst; + + if (!eldbus_message_arguments_get(msg, "o", &path)) + return; + bat = _batman_battery_find(path); + if (bat) + { + inst = bat->inst; + _battery_free(bat); + _batman_device_update(inst); + return; + } + ac = _batman_ac_adapter_find(path); + if (ac) + { + inst = ac->inst; + _ac_free(ac); + _batman_device_update(inst); + + } +} + +int +_batman_upower_start(Instance *inst) +{ + Eldbus_Object *obj; + + conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0); + + obj = eldbus_object_get(conn, BUS, PATH); + EINA_SAFETY_ON_NULL_GOTO(obj, obj_error); + upower_proxy = eldbus_proxy_get(obj, IFACE); + EINA_SAFETY_ON_NULL_GOTO(upower_proxy, proxy_error); + + eldbus_proxy_call(upower_proxy, "EnumerateDevices", _enumerate_cb, inst, -1, ""); + eldbus_proxy_signal_handler_add(upower_proxy, "DeviceAdded", _device_added_cb, inst); + eldbus_proxy_signal_handler_add(upower_proxy, "DeviceRemoved", _device_removed_cb, NULL); + return 1; + +proxy_error: + eldbus_object_unref(obj); +obj_error: + eldbus_connection_unref(conn); + return 0; +} + +void +_batman_upower_stop(void) +{ + Eina_List *list, *list2; + Battery *bat; + Ac_Adapter *ac; + Eldbus_Object *obj; + + EINA_LIST_FOREACH_SAFE(batman_device_batteries, list, list2, bat) + E_FREE_FUNC(bat, _battery_free); + EINA_LIST_FOREACH_SAFE(batman_device_ac_adapters, list, list2, ac) + E_FREE_FUNC(ac, _ac_free); + + obj = eldbus_proxy_object_get(upower_proxy); + E_FREE_FUNC(upower_proxy, eldbus_proxy_unref); + E_FREE_FUNC(obj, eldbus_object_unref); + E_FREE_FUNC(conn, eldbus_connection_unref); +} diff --git a/src/modules/sysinfo/cpuclock/cpuclock.c b/src/modules/sysinfo/cpuclock/cpuclock.c new file mode 100644 index 000000000..4665bccf8 --- /dev/null +++ b/src/modules/sysinfo/cpuclock/cpuclock.c @@ -0,0 +1,913 @@ +#include "cpuclock.h" + +typedef struct _Thread_Config Thread_Config; + +struct _Thread_Config +{ + int interval; + Instance *inst; +}; + +typedef struct _Pstate_Config Pstate_Config; + +struct _Pstate_Config +{ + Instance *inst; + int min; + int max; + int turbo; +}; + +static Cpu_Status * +_cpuclock_status_new(void) +{ + Cpu_Status *s; + + s = E_NEW(Cpu_Status, 1); + if (!s) return NULL; + s->active = -1; + return s; +} + +static void +_cpuclock_status_free(Cpu_Status *s) +{ + Eina_List *l; + + if (s->frequencies) eina_list_free(s->frequencies); + if (s->governors) + { + for (l = s->governors; l; l = l->next) + E_FREE_FUNC(l->data, free); + eina_list_free(s->governors); + } + E_FREE_FUNC(s->cur_governor, free); + if (s->orig_governor) eina_stringshare_del(s->orig_governor); + E_FREE_FUNC(s, free); +} + +static int +_cpuclock_cb_sort(const void *item1, const void *item2) +{ + int a, b; + + a = (long)item1; + b = (long)item2; + if (a < b) return -1; + else if (a > b) + return 1; + return 0; +} + +static void +_cpuclock_set_thread_governor(void *data, Ecore_Thread *th EINA_UNUSED) +{ + const char *governor = data; + + _cpuclock_sysfs_setall("scaling_governor", governor); + if (!strcmp(governor, "ondemand")) + _cpuclock_sysfs_set("ondemand/ignore_nice_load", "0"); + else if (!strcmp(governor, "conservative")) + _cpuclock_sysfs_set("conservative/ignore_nice_load", "0"); +} + +static void +_cpuclock_set_thread_frequency(void *data, Ecore_Thread *th EINA_UNUSED) +{ + const char *freq = data; + +#if defined __FreeBSD__ || defined __OpenBSD__ + _cpuclock_sysctl_set("frequency", freq); + return; +#endif + _cpuclock_sysfs_setall("scaling_setspeed", freq); +} + +static void +_cpuclock_set_thread_pstate(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Pstate_Config *pc = data; + + _cpuclock_sysfs_pstate(pc->min, pc->max, pc->turbo); +} + +static void +_cpuclock_set_thread_done(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED) +{ + return; +} + +static void +_cpuclock_set_thread_pstate_done(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Pstate_Config *pc = data; + + E_FREE_FUNC(pc, free); + + return; +} + +void +_cpuclock_set_governor(const char *governor) +{ +#if defined __FreeBSD__ || defined __OpenBSD__ + return; +#endif + + ecore_thread_run(_cpuclock_set_thread_governor, _cpuclock_set_thread_done, NULL, governor); +} + +static void +_cpuclock_set_frequency(int frequency) +{ + char buf[4096]; + const char *freq; + +#ifdef __FreeBSD__ + frequency /= 1000; +#endif + + snprintf(buf, sizeof(buf), "%i", frequency); + freq = eina_stringshare_add(buf); + + ecore_thread_run(_cpuclock_set_thread_frequency, _cpuclock_set_thread_done, NULL, freq); +} + +void +_cpuclock_set_pstate(int min, int max, int turbo) +{ +#if defined __FreeBSD__ || defined __OpenBSD__ + return; +#endif + Pstate_Config *pc; + + pc = E_NEW(Pstate_Config, 1); + if (!pc) return; + + pc->turbo = turbo; + pc->min = min; + pc->max = max; + + ecore_thread_run(_cpuclock_set_thread_pstate, _cpuclock_set_thread_pstate_done, NULL, pc); +} + +static void +_cpuclock_face_cb_set_frequency(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *src EINA_UNUSED) +{ + Eina_List *l; + int next_frequency = 0; + Instance *inst = data; + + for (l = inst->cfg->cpuclock.status->frequencies; l; l = l->next) + { + if (inst->cfg->cpuclock.status->cur_frequency == (long)l->data) + { + if (!strcmp(emission, "e,action,frequency,increase")) + { + if (l->next) next_frequency = (long)l->next->data; + break; + } + else if (!strcmp(emission, "e,action,frequency,decrease")) + { + if (l->prev) next_frequency = (long)l->prev->data; + break; + } + else + break; + } + } + if (inst->cfg->cpuclock.status->can_set_frequency && next_frequency) + _cpuclock_set_frequency(next_frequency); +} + +static void +_cpuclock_face_cb_set_governor(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *src EINA_UNUSED) +{ + Eina_List *l; + char *next_governor = NULL; + Instance *inst = data; + + for (l = inst->cfg->cpuclock.status->governors; l; l = l->next) + { + if (!strcmp(l->data, inst->cfg->cpuclock.status->cur_governor)) + { + if (l->next) + next_governor = l->next->data; + else + next_governor = inst->cfg->cpuclock.status->governors->data; + break; + } + } + if (next_governor) _cpuclock_set_governor(next_governor); +} + +static Eina_Bool +_cpuclock_event_cb_powersave(void *data, int type, void *event) +{ + Instance *inst = data; + E_Event_Powersave_Update *ev; + Eina_List *l; + Eina_Bool has_powersave = EINA_FALSE; + Eina_Bool has_conservative = EINA_FALSE; + + if (type != E_EVENT_POWERSAVE_UPDATE) return ECORE_CALLBACK_PASS_ON; + if (!inst->cfg->cpuclock.auto_powersave) return ECORE_CALLBACK_PASS_ON; + + ev = event; + if (!inst->cfg->cpuclock.status->orig_governor) + inst->cfg->cpuclock.status->orig_governor = eina_stringshare_add(inst->cfg->cpuclock.status->cur_governor); + + for (l = inst->cfg->cpuclock.status->governors; l; l = l->next) + { + if (!strcmp(l->data, "conservative")) + has_conservative = EINA_TRUE; + else if (!strcmp(l->data, "powersave")) + has_powersave = EINA_TRUE; + else if (!strcmp(l->data, "interactive")) + has_powersave = EINA_TRUE; + } + + switch (ev->mode) + { + case E_POWERSAVE_MODE_NONE: + case E_POWERSAVE_MODE_LOW: + _cpuclock_set_governor(inst->cfg->cpuclock.status->orig_governor); + eina_stringshare_del(inst->cfg->cpuclock.status->orig_governor); + inst->cfg->cpuclock.status->orig_governor = NULL; + break; + + case E_POWERSAVE_MODE_MEDIUM: + case E_POWERSAVE_MODE_HIGH: + if ((inst->cfg->cpuclock.powersave_governor) || (has_conservative)) + { + if (inst->cfg->cpuclock.powersave_governor) + _cpuclock_set_governor(inst->cfg->cpuclock.powersave_governor); + else + _cpuclock_set_governor("conservative"); + break; + } + + case E_POWERSAVE_MODE_EXTREME: + if (has_powersave) + _cpuclock_set_governor("powersave"); + break; + } + + return ECORE_CALLBACK_PASS_ON; +} + +void +_cpuclock_config_updated(Instance *inst) +{ + Edje_Message_Int_Set *frequency_msg; + Edje_Message_String_Set *governor_msg; + Eina_List *l; + int i; + unsigned int count; + + if (inst->cfg->cpuclock.status->frequencies) + { + count = eina_list_count(inst->cfg->cpuclock.status->frequencies); + frequency_msg = malloc(sizeof(Edje_Message_Int_Set) + (count - 1) * sizeof(int)); + EINA_SAFETY_ON_NULL_RETURN(frequency_msg); + frequency_msg->count = count; + for (l = inst->cfg->cpuclock.status->frequencies, i = 0; l; l = l->next, i++) + frequency_msg->val[i] = (long)l->data; + edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_INT_SET, 1, frequency_msg); + free(frequency_msg); + } + + if (inst->cfg->cpuclock.status->governors) + { + count = eina_list_count(inst->cfg->cpuclock.status->governors); + governor_msg = malloc(sizeof(Edje_Message_String_Set) + (count - 1) * sizeof(char *)); + governor_msg->count = count; + for (l = inst->cfg->cpuclock.status->governors, i = 0; l; l = l->next, i++) + governor_msg->str[i] = (char *)l->data; + edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_STRING_SET, 2, governor_msg); + free(governor_msg); + } +} + +static void +_cpuclock_face_update_current(Instance *inst) +{ + Edje_Message_Int_Set *frequency_msg; + Edje_Message_String governor_msg; + + frequency_msg = malloc(sizeof(Edje_Message_Int_Set) + (sizeof(int) * 4)); + EINA_SAFETY_ON_NULL_RETURN(frequency_msg); + frequency_msg->count = 5; + frequency_msg->val[0] = inst->cfg->cpuclock.status->cur_frequency; + frequency_msg->val[1] = inst->cfg->cpuclock.status->can_set_frequency; + frequency_msg->val[2] = inst->cfg->cpuclock.status->cur_min_frequency; + frequency_msg->val[3] = inst->cfg->cpuclock.status->cur_max_frequency; + frequency_msg->val[4] = 0; // pad + edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_INT_SET, 3, + frequency_msg); + free(frequency_msg); + + /* BSD crashes here without the if-condition + * since it has no governors (yet) */ + if (inst->cfg->cpuclock.status->cur_governor) + { + governor_msg.str = inst->cfg->cpuclock.status->cur_governor; + edje_object_message_send(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), EDJE_MESSAGE_STRING, 4, + &governor_msg); + } +} + +static void +_cpuclock_status_check_available(Cpu_Status *s) +{ + char buf[4096]; + Eina_List *l; + // FIXME: this assumes all cores accept the same freqs/ might be wrong + +#if defined (__OpenBSD__) + int p; + + if (s->frequencies) + { + eina_list_free(s->frequencies); + s->frequencies = NULL; + } + + /* storing percents */ + p = 100; + s->frequencies = eina_list_append(s->frequencies, (void *)p); + p = 75; + s->frequencies = eina_list_append(s->frequencies, (void *)p); + p = 50; + s->frequencies = eina_list_append(s->frequencies, (void *)p); + p = 25; + s->frequencies = eina_list_append(s->frequencies, (void *)p); +#elif defined (__FreeBSD__) + int freq; + size_t len = sizeof(buf); + char *pos, *q; + + /* read freq_levels sysctl and store it in freq */ + if (sysctlbyname("dev.cpu.0.freq_levels", buf, &len, NULL, 0) == 0) + { + /* sysctl returns 0 on success */ + if (s->frequencies) + { + eina_list_free(s->frequencies); + s->frequencies = NULL; + } + + /* parse freqs and store the frequencies in s->frequencies */ + pos = buf; + while (pos) + { + q = strchr(pos, '/'); + if (!q) break; + + *q = '\0'; + freq = atoi(pos); + freq *= 1000; + s->frequencies = eina_list_append(s->frequencies, (void *)freq); + + pos = q + 1; + pos = strchr(pos, ' '); + } + } + + /* sort is not necessary because freq_levels is already sorted */ + /* freebsd doesn't have governors */ + if (s->governors) + { + for (l = s->governors; l; l = l->next) + free(l->data); + eina_list_free(s->governors); + s->governors = NULL; + } +#else + FILE *f; + + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", "r"); + if (f) + { + char *freq; + + if (s->frequencies) + { + eina_list_free(s->frequencies); + s->frequencies = NULL; + } + + if (fgets(buf, sizeof(buf), f) == NULL) + { + fclose(f); + return; + } + fclose(f); + + freq = strtok(buf, " "); + do + { + if (atoi(freq) != 0) + { + s->frequencies = eina_list_append(s->frequencies, + (void *)(long)atoi(freq)); + } + freq = strtok(NULL, " "); + } + while (freq); + + s->frequencies = eina_list_sort(s->frequencies, + eina_list_count(s->frequencies), + _cpuclock_cb_sort); + } + else + do + { +#define CPUFREQ_SYSFSDIR "/sys/devices/system/cpu/cpu0/cpufreq" + f = fopen(CPUFREQ_SYSFSDIR "/scaling_cur_freq", "r"); + if (!f) break; + fclose(f); + + f = fopen(CPUFREQ_SYSFSDIR "/scaling_driver", "r"); + if (!f) break; + if (fgets(buf, sizeof(buf), f) == NULL) + { + fclose(f); + break; + } + fclose(f); + if (strcmp(buf, "intel_pstate\n")) break; + + if (s->frequencies) + { + eina_list_free(s->frequencies); + s->frequencies = NULL; + } +#define CPUFREQ_ADDF(filename) \ + f = fopen(CPUFREQ_SYSFSDIR filename, "r"); \ + if (f) \ + { \ + if (fgets(buf, sizeof(buf), f) != NULL) \ + s->frequencies = eina_list_append(s->frequencies, \ + (void *)(long)(atoi(buf))); \ + fclose(f); \ + } + CPUFREQ_ADDF("/cpuinfo_min_freq"); + CPUFREQ_ADDF("/cpuinfo_max_freq"); + } + while (0); + + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors", "r"); + if (f) + { + char *gov; + int len; + + if (s->governors) + { + for (l = s->governors; l; l = l->next) + free(l->data); + eina_list_free(s->governors); + s->governors = NULL; + } + + if (fgets(buf, sizeof(buf), f) == NULL) + { + fclose(f); + return; + } + fclose(f); + len = strlen(buf); + if (len > 0) + { + gov = buf + len - 1; + while ((gov > buf) && (isspace(*gov))) + { + *gov = 0; + gov--; + } + } + gov = strtok(buf, " "); + do + { + while ((*gov) && (isspace(*gov))) + gov++; + if (strlen(gov) != 0) + s->governors = eina_list_append(s->governors, strdup(gov)); + gov = strtok(NULL, " "); + } + while (gov); + + s->governors = + eina_list_sort(s->governors, eina_list_count(s->governors), + (int (*)(const void *, const void *))strcmp); + } +#endif +} + +static int +_cpuclock_status_check_current(Cpu_Status *s) +{ + int ret = 0; + int frequency = 0; + +#if defined (__OpenBSD__) + size_t len = sizeof(frequency); + int percent, mib[] = {CTL_HW, HW_CPUSPEED}; + s->active = 0; + + _cpuclock_status_check_available(s); + + if (sysctl(mib, 2, &frequency, &len, NULL, 0) == 0) + { + frequency *= 1000; + if (frequency != s->cur_frequency) ret = 1; + s->cur_frequency = frequency; + s->active = 1; + } + + mib[1] = HW_SETPERF; + + if (sysctl(mib, 2, &percent, &len, NULL, 0) == 0) + { + s->cur_percent = percent; + } + + s->can_set_frequency = 1; + s->cur_governor = NULL; + +#elif defined (__FreeBSD__) + size_t len = sizeof(frequency); + s->active = 0; + + /* frequency is stored in dev.cpu.0.freq */ + if (sysctlbyname("dev.cpu.0.freq", &frequency, &len, NULL, 0) == 0) + { + frequency *= 1000; + if (frequency != s->cur_frequency) ret = 1; + s->cur_frequency = frequency; + s->active = 1; + } + + /* hardcoded for testing */ + s->can_set_frequency = 1; + s->cur_governor = NULL; + +#else + char buf[4096]; + FILE *f; + int frequency_min = 0x7fffffff; + int frequency_max = 0; + int freqtot = 0; + int i; + + s->active = 0; + + _cpuclock_status_check_available(s); + // average out frequencies of all cores + for (i = 0; i < 64; i++) + { + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_cur_freq", i); + f = fopen(buf, "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) == NULL) + { + fclose(f); + continue; + } + fclose(f); + + frequency = atoi(buf); + if (frequency > frequency_max) frequency_max = frequency; + if (frequency < frequency_min) frequency_min = frequency; + freqtot += frequency; + s->active = 1; + } + else + break; + } + if (i < 1) i = 1; + frequency = freqtot / i; + if (frequency != s->cur_frequency) ret = 1; + if (frequency_min != s->cur_min_frequency) ret = 1; + if (frequency_max != s->cur_max_frequency) ret = 1; + s->cur_frequency = frequency; + s->cur_min_frequency = frequency_min; + s->cur_max_frequency = frequency_max; + +// printf("%i | %i %i\n", frequency, frequency_min, frequency_max); + + // FIXME: this assumes all cores are on the same governor + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed", "r"); + if (f) + { + s->can_set_frequency = 1; + fclose(f); + } + else + { + s->can_set_frequency = 0; + } + + f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "r"); + if (f) + { + char *p; + + if (fgets(buf, sizeof(buf), f) == NULL) + { + fclose(f); + return ret; + } + fclose(f); + + for (p = buf; (*p != 0) && (isalnum(*p)); p++) ; + *p = 0; + + if ((!s->cur_governor) || (strcmp(buf, s->cur_governor))) + { + ret = 1; + + free(s->cur_governor); + s->cur_governor = strdup(buf); + + for (i = strlen(s->cur_governor) - 1; i >= 0; i--) + { + if (isspace(s->cur_governor[i])) + s->cur_governor[i] = 0; + else + break; + } + } + } + f = fopen("/sys/devices/system/cpu/intel_pstate/min_perf_pct", "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) != NULL) + { + s->pstate_min = atoi(buf); + s->pstate = 1; + } + fclose(f); + } + f = fopen("/sys/devices/system/cpu/intel_pstate/max_perf_pct", "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) != NULL) + { + s->pstate_max = atoi(buf); + s->pstate = 1; + } + fclose(f); + } + f = fopen("/sys/devices/system/cpu/intel_pstate/no_turbo", "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) != NULL) + { + s->pstate_turbo = atoi(buf); + if (s->pstate_turbo) s->pstate_turbo = 0; + else s->pstate_turbo = 1; + s->pstate = 1; + } + fclose(f); + } +#endif + return ret; +} + +static void +_cpuclock_cb_frequency_check_main(void *data, Ecore_Thread *th) +{ + Thread_Config *thc = data; + for (;;) + { + Cpu_Status *status; + + if (ecore_thread_check(th)) break; + status = _cpuclock_status_new(); + if (_cpuclock_status_check_current(status)) + ecore_thread_feedback(th, status); + else + _cpuclock_status_free(status); + if (ecore_thread_check(th)) break; + usleep((1000000.0 / 8.0) * (double)thc->interval); + } + E_FREE_FUNC(thc, free); +} + +static void +_cpuclock_cb_frequency_check_notify(void *data, + Ecore_Thread *th EINA_UNUSED, + void *msg) +{ + Cpu_Status *status = msg; + Eina_Bool freq_changed = EINA_FALSE; + Thread_Config *thc = data; + Instance *inst = thc->inst; + + if (inst->cfg->esm != E_SYSINFO_MODULE_CPUCLOCK && inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return; + if (!inst->cfg) return; + if ((inst->cfg->cpuclock.status) && (status) && + ( +#ifdef __OpenBSD__ + (status->cur_percent != inst->cfg->cpuclock.status->cur_percent ) || +#endif + (status->cur_frequency != inst->cfg->cpuclock.status->cur_frequency ) || + (status->cur_min_frequency != inst->cfg->cpuclock.status->cur_min_frequency) || + (status->cur_max_frequency != inst->cfg->cpuclock.status->cur_max_frequency) || + (status->can_set_frequency != inst->cfg->cpuclock.status->can_set_frequency))) + freq_changed = EINA_TRUE; + _cpuclock_status_free(inst->cfg->cpuclock.status); + inst->cfg->cpuclock.status = status; + if (freq_changed) + { + _cpuclock_face_update_current(inst); + } + if (inst->cfg->cpuclock.status->active == 0) + elm_layout_signal_emit(inst->cfg->cpuclock.o_gadget, "e,state,disabled", "e"); + else if (inst->cfg->cpuclock.status->active == 1) + elm_layout_signal_emit(inst->cfg->cpuclock.o_gadget, "e,state,enabled", "e"); + + _cpuclock_set_pstate(inst->cfg->cpuclock.pstate_min - 1, + inst->cfg->cpuclock.pstate_max - 1, inst->cfg->cpuclock.status->pstate_turbo); +} + +void +_cpuclock_poll_interval_update(Instance *inst) +{ + Thread_Config *thc; + + if (inst->cfg->cpuclock.frequency_check_thread) + { + ecore_thread_cancel(inst->cfg->cpuclock.frequency_check_thread); + inst->cfg->cpuclock.frequency_check_thread = NULL; + } + thc = E_NEW(Thread_Config, 1); + if (thc) + { + thc->inst = inst; + thc->interval = inst->cfg->cpuclock.poll_interval; + inst->cfg->cpuclock.frequency_check_thread = + ecore_thread_feedback_run(_cpuclock_cb_frequency_check_main, + _cpuclock_cb_frequency_check_notify, + NULL, NULL, thc, EINA_TRUE); + } + e_config_save_queue(); +} + +static void +_cpuclock_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data) +{ + Instance *inst = data; + + if (inst->o_main != event_data) return; + + if (inst->cfg->cpuclock.handler) + ecore_event_handler_del(inst->cfg->cpuclock.handler); + + if (inst->cfg->cpuclock.frequency_check_thread) + { + ecore_thread_cancel(inst->cfg->cpuclock.frequency_check_thread); + inst->cfg->cpuclock.frequency_check_thread = NULL; + } + + if (inst->cfg->cpuclock.governor) + eina_stringshare_del(inst->cfg->cpuclock.governor); + if (inst->cfg->cpuclock.status) _cpuclock_status_free(inst->cfg->cpuclock.status); + + sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg); + E_FREE(inst->cfg); +} + +void +sysinfo_cpuclock_remove(Instance *inst) +{ + if (inst->cfg->cpuclock.handler) + ecore_event_handler_del(inst->cfg->cpuclock.handler); + + if (inst->cfg->cpuclock.frequency_check_thread) + { + ecore_thread_cancel(inst->cfg->cpuclock.frequency_check_thread); + inst->cfg->cpuclock.frequency_check_thread = NULL; + } + + if (inst->cfg->cpuclock.governor) + eina_stringshare_del(inst->cfg->cpuclock.governor); + if (inst->cfg->cpuclock.status) _cpuclock_status_free(inst->cfg->cpuclock.status); +} + +static void +_cpuclock_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED) +{ + Instance *inst = data; + + if (inst->cfg->cpuclock.pstate_min == 0) inst->cfg->cpuclock.pstate_min = 1; + if (inst->cfg->cpuclock.pstate_max == 0) inst->cfg->cpuclock.pstate_max = 101; + + inst->cfg->cpuclock.o_gadget = elm_layout_add(inst->o_main); + e_theme_edje_object_set(inst->cfg->cpuclock.o_gadget, "base/theme/modules/cpufreq", + "e/modules/cpufreq/main"); + E_EXPAND(inst->cfg->cpuclock.o_gadget); + E_FILL(inst->cfg->cpuclock.o_gadget); + edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,governor,next", "*", + _cpuclock_face_cb_set_governor, inst); + edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,increase", "*", + _cpuclock_face_cb_set_frequency, inst); + edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,decrease", "*", + _cpuclock_face_cb_set_frequency, inst); + elm_box_pack_end(inst->o_main, inst->cfg->cpuclock.o_gadget); + evas_object_show(inst->cfg->cpuclock.o_gadget); + evas_object_smart_callback_del_full(obj, "gadget_created", _cpuclock_created_cb, data); + inst->cfg->cpuclock.status = _cpuclock_status_new(); + _cpuclock_status_check_available(inst->cfg->cpuclock.status); + _cpuclock_poll_interval_update(inst); + inst->cfg->cpuclock.handler = ecore_event_handler_add(E_EVENT_POWERSAVE_UPDATE, + _cpuclock_event_cb_powersave, inst); + _cpuclock_config_updated(inst); +} + +Evas_Object * +sysinfo_cpuclock_create(Evas_Object *parent, Instance *inst) +{ + if (inst->cfg->cpuclock.pstate_min == 0) inst->cfg->cpuclock.pstate_min = 1; + if (inst->cfg->cpuclock.pstate_max == 0) inst->cfg->cpuclock.pstate_max = 101; + + inst->cfg->cpuclock.o_gadget = elm_layout_add(parent); + e_theme_edje_object_set(inst->cfg->cpuclock.o_gadget, "base/theme/modules/cpufreq", + "e/modules/cpufreq/main"); + E_EXPAND(inst->cfg->cpuclock.o_gadget); + E_FILL(inst->cfg->cpuclock.o_gadget); + edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,governor,next", "*", + _cpuclock_face_cb_set_governor, inst); + edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,increase", "*", + _cpuclock_face_cb_set_frequency, inst); + edje_object_signal_callback_add(elm_layout_edje_get(inst->cfg->cpuclock.o_gadget), "e,action,frequency,decrease", "*", + _cpuclock_face_cb_set_frequency, inst); + evas_object_show(inst->cfg->cpuclock.o_gadget); + inst->cfg->cpuclock.status = _cpuclock_status_new(); + _cpuclock_status_check_available(inst->cfg->cpuclock.status); + _cpuclock_poll_interval_update(inst); + inst->cfg->cpuclock.handler = ecore_event_handler_add(E_EVENT_POWERSAVE_UPDATE, + _cpuclock_event_cb_powersave, inst); + _cpuclock_config_updated(inst); + + return inst->cfg->cpuclock.o_gadget; +} + +static Config_Item * +_conf_item_get(int *id) +{ + Config_Item *ci; + Eina_List *l; + + if (*id > 0) + { + EINA_LIST_FOREACH(sysinfo_config->items, l, ci) + if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_CPUCLOCK) return ci; + } + + ci = E_NEW(Config_Item, 1); + + if (*id != -1) + ci->id = eina_list_count(sysinfo_config->items)+1; + else + ci->id = -1; + + ci->esm = E_SYSINFO_MODULE_CPUCLOCK; + ci->cpuclock.poll_interval = 32; + ci->cpuclock.restore_governor = 0; + ci->cpuclock.auto_powersave = 1; + ci->cpuclock.powersave_governor = NULL; + ci->cpuclock.governor = NULL; + ci->cpuclock.pstate_min = 1; + ci->cpuclock.pstate_max = 101; + + sysinfo_config->items = eina_list_append(sysinfo_config->items, ci); + + return ci; +} + +Evas_Object * +cpuclock_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED) +{ + Instance *inst; + + inst = E_NEW(Instance, 1); + inst->cfg = _conf_item_get(id); + *id = inst->cfg->id; + inst->o_main = elm_box_add(parent); + E_EXPAND(inst->o_main); + evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + evas_object_smart_callback_add(parent, "gadget_created", _cpuclock_created_cb, inst); + evas_object_smart_callback_add(parent, "gadget_removed", _cpuclock_removed_cb, inst); + evas_object_show(inst->o_main); + + if (inst->cfg->id < 0) return inst->o_main; + + sysinfo_instances = + eina_list_append(sysinfo_instances, inst); + + return inst->o_main; +} diff --git a/src/modules/sysinfo/cpuclock/cpuclock.h b/src/modules/sysinfo/cpuclock/cpuclock.h new file mode 100644 index 000000000..4bce79da4 --- /dev/null +++ b/src/modules/sysinfo/cpuclock/cpuclock.h @@ -0,0 +1,14 @@ +#ifndef CPUCLOCK_H +#define CPUCLOCK_H + +#include "../sysinfo.h" + +void _cpuclock_config_updated(Instance *inst); +int _cpuclock_sysfs_setall(const char *control, const char *value); +int _cpuclock_sysfs_set(const char *control, const char *value); +int _cpuclock_sysfs_pstate(int min, int max, int turbo); +#if defined __OpenBSD__ || defined __FreeBSD__ +int _cpuclock_sysctl_frequency(int new_perf) +#endif + +#endif diff --git a/src/modules/sysinfo/cpuclock/cpuclock_sysctl.c b/src/modules/sysinfo/cpuclock/cpuclock_sysctl.c new file mode 100644 index 000000000..ecf0ec401 --- /dev/null +++ b/src/modules/sysinfo/cpuclock/cpuclock_sysctl.c @@ -0,0 +1,34 @@ +#include "cpuclock.h" +#ifdef __FreeBSD__ +# include +#endif + +#ifdef __OpenBSD__ +# include +# include +# include +#endif + + +#if defined __OpenBSD__ +int +_cpuclock_sysctl_frequency(int new_perf) +{ + int mib[] = {CTL_HW, HW_SETPERF}; + size_t len = sizeof(new_perf); + + if (sysctl(mib, 2, NULL, 0, &new_perf, len) == -1) + return 1; + else + return 0; +} +#elif defined __FreeBSD__ +int +_cpuclock_sysctl_frequency(int new_perf) +{ + if (sysctlbyname("dev.cpu.0.freq", NULL, NULL, &new_perf, sizeof(new_perf)) == -1) + return 1; + else + return 0; +} +#endif diff --git a/src/modules/sysinfo/cpuclock/cpuclock_sysfs.c b/src/modules/sysinfo/cpuclock/cpuclock_sysfs.c new file mode 100644 index 000000000..3a504753f --- /dev/null +++ b/src/modules/sysinfo/cpuclock/cpuclock_sysfs.c @@ -0,0 +1,72 @@ +#include "cpuclock.h" + +int +_cpuclock_sysfs_setall(const char *control, const char *value) +{ + int num = 0; + char filename[4096]; + FILE *f; + + while (1) + { + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/%s", num, control); + f = fopen(filename, "w"); + + if (!f) + { + return num; + } + fprintf(f, "%s\n", value); + fclose(f); + num++; + } + return 1; +} + +int +_cpuclock_sysfs_set(const char *control, const char *value) +{ + char filename[4096]; + FILE *f; + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpufreq/%s", control); + f = fopen(filename, "w"); + + if (!f) + { + if (_cpuclock_sysfs_setall(control, value) > 0) + return 1; + else + return 0; + } + + fprintf(f, "%s\n", value); + fclose(f); + + return 1; +} + +int +_cpuclock_sysfs_pstate(int min, int max, int turbo) +{ + FILE *f; + + f = fopen("/sys/devices/system/cpu/intel_pstate/min_perf_pct", "w"); + if (!f) return 0; + fprintf(f, "%i\n", min); + fclose(f); + + f = fopen("/sys/devices/system/cpu/intel_pstate/max_perf_pct", "w"); + if (!f) return 0; + fprintf(f, "%i\n", max); + fclose(f); + + f = fopen("/sys/devices/system/cpu/intel_pstate/no_turbo", "w"); + if (!f) return 0; + fprintf(f, "%i\n", turbo ? 0 : 1); + fclose(f); + + return 1; +} + + diff --git a/src/modules/sysinfo/cpumonitor/cpumonitor.c b/src/modules/sysinfo/cpumonitor/cpumonitor.c new file mode 100644 index 000000000..bed7b7122 --- /dev/null +++ b/src/modules/sysinfo/cpumonitor/cpumonitor.c @@ -0,0 +1,187 @@ +#include "cpumonitor.h" + +typedef struct _Thread_Config Thread_Config; + +struct _Thread_Config +{ + int interval; + Instance *inst; + int status; +}; + +static void +_cpumonitor_face_update(Instance *inst, int status) +{ + Edje_Message_Int_Set *usage_msg; + + usage_msg = malloc(sizeof(Edje_Message_Int_Set) + 1 * sizeof(int)); + EINA_SAFETY_ON_NULL_RETURN(usage_msg); + usage_msg->count = 1; + usage_msg->val[0] = status; + edje_object_message_send(elm_layout_edje_get(inst->cfg->cpumonitor.o_gadget), EDJE_MESSAGE_INT_SET, 1, + usage_msg); +} + +static void +_cpumonitor_cb_usage_check_main(void *data, Ecore_Thread *th) +{ + Thread_Config *thc = data; + for (;;) + { + if (ecore_thread_check(th)) break; + thc->status = _cpumonitor_proc_getusage(thc->inst); + ecore_thread_feedback(th, NULL); + if (ecore_thread_check(th)) break; + usleep((1000000.0 / 8.0) * (double)thc->interval); + } + E_FREE_FUNC(thc, free); +} + +static void +_cpumonitor_cb_usage_check_notify(void *data, + Ecore_Thread *th EINA_UNUSED, + void *msg EINA_UNUSED) +{ + Thread_Config *thc = data; + Instance *inst = thc->inst; + + if (inst->cfg->esm != E_SYSINFO_MODULE_CPUMONITOR && inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return; + if (!inst->cfg) return; + _cpumonitor_face_update(inst, thc->status); +} + +void +_cpumonitor_config_updated(Instance *inst) +{ + Thread_Config *thc; + + if (inst->cfg->cpumonitor.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread); + inst->cfg->cpumonitor.usage_check_thread = NULL; + } + thc = E_NEW(Thread_Config, 1); + if (thc) + { + thc->inst = inst; + thc->interval = inst->cfg->cpumonitor.poll_interval; + thc->status = 0; + inst->cfg->cpumonitor.usage_check_thread = + ecore_thread_feedback_run(_cpumonitor_cb_usage_check_main, + _cpumonitor_cb_usage_check_notify, + NULL, NULL, thc, EINA_TRUE); + } + e_config_save_queue(); +} + +static void +_cpumonitor_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data) +{ + Instance *inst = data; + + if (inst->o_main != event_data) return; + + if (inst->cfg->cpumonitor.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread); + inst->cfg->cpumonitor.usage_check_thread = NULL; + } + + sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg); + E_FREE(inst->cfg); +} + +void +sysinfo_cpumonitor_remove(Instance *inst) +{ + if (inst->cfg->cpumonitor.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->cpumonitor.usage_check_thread); + inst->cfg->cpumonitor.usage_check_thread = NULL; + } +} + +static void +_cpumonitor_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED) +{ + Instance *inst = data; + + inst->cfg->cpumonitor.o_gadget = elm_layout_add(inst->o_main); + e_theme_edje_object_set(inst->cfg->cpumonitor.o_gadget, "base/theme/modules/cpumonitor", + "e/modules/cpumonitor/main"); + E_EXPAND(inst->cfg->cpumonitor.o_gadget); + E_FILL(inst->cfg->cpumonitor.o_gadget); + elm_box_pack_end(inst->o_main, inst->cfg->cpumonitor.o_gadget); + evas_object_show(inst->cfg->cpumonitor.o_gadget); + evas_object_smart_callback_del_full(obj, "gadget_created", _cpumonitor_created_cb, data); + _cpumonitor_config_updated(inst); +} + +Evas_Object * +sysinfo_cpumonitor_create(Evas_Object *parent, Instance *inst) +{ + inst->cfg->cpumonitor.o_gadget = elm_layout_add(parent); + e_theme_edje_object_set(inst->cfg->cpumonitor.o_gadget, "base/theme/modules/cpumonitor", + "e/modules/cpumonitor/main"); + E_EXPAND(inst->cfg->cpumonitor.o_gadget); + E_FILL(inst->cfg->cpumonitor.o_gadget); + evas_object_show(inst->cfg->cpumonitor.o_gadget); + _cpumonitor_config_updated(inst); + + return inst->cfg->cpumonitor.o_gadget; +} + +static Config_Item * +_conf_item_get(int *id) +{ + Config_Item *ci; + Eina_List *l; + + if (*id > 0) + { + EINA_LIST_FOREACH(sysinfo_config->items, l, ci) + if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_CPUMONITOR) return ci; + } + + ci = E_NEW(Config_Item, 1); + + if (*id != -1) + ci->id = eina_list_count(sysinfo_config->items)+1; + else + ci->id = -1; + + ci->esm = E_SYSINFO_MODULE_CPUMONITOR; + ci->cpumonitor.poll_interval = 32; + ci->cpumonitor.total = 0; + ci->cpumonitor.idle = 0; + + sysinfo_config->items = eina_list_append(sysinfo_config->items, ci); + + return ci; +} + +Evas_Object * +cpumonitor_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED) +{ + Instance *inst; + + inst = E_NEW(Instance, 1); + inst->cfg = _conf_item_get(id); + *id = inst->cfg->id; + inst->cfg->cpumonitor.total = 0; + inst->cfg->cpumonitor.idle = 0; + inst->o_main = elm_box_add(parent); + E_EXPAND(inst->o_main); + evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + evas_object_smart_callback_add(parent, "gadget_created", _cpumonitor_created_cb, inst); + evas_object_smart_callback_add(parent, "gadget_removed", _cpumonitor_removed_cb, inst); + evas_object_show(inst->o_main); + + if (inst->cfg->id < 0) return inst->o_main; + + sysinfo_instances = + eina_list_append(sysinfo_instances, inst); + + return inst->o_main; +} + diff --git a/src/modules/sysinfo/cpumonitor/cpumonitor.h b/src/modules/sysinfo/cpumonitor/cpumonitor.h new file mode 100644 index 000000000..410fa0b9b --- /dev/null +++ b/src/modules/sysinfo/cpumonitor/cpumonitor.h @@ -0,0 +1,9 @@ +#ifndef CPUMONITOR_H +#define CPUMONITOR_H + +#include "../sysinfo.h" + +void _cpuclock_config_updated(Instance *inst); +int _cpumonitor_proc_getusage(Instance *inst); + +#endif diff --git a/src/modules/sysinfo/cpumonitor/cpumonitor_proc.c b/src/modules/sysinfo/cpumonitor/cpumonitor_proc.c new file mode 100644 index 000000000..dd8322e79 --- /dev/null +++ b/src/modules/sysinfo/cpumonitor/cpumonitor_proc.c @@ -0,0 +1,42 @@ +#include "cpumonitor.h" + +int _cpumonitor_proc_getusage(Instance *inst) +{ + long total = 0, i = 0, idle = 0, use, total_change, idle_change; + int percent = 0; + char buf[4096], *line, *tok; + FILE *f; + + f = fopen("/proc/stat", "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) == NULL) + { + fclose(f); + return 0; + } + fclose(f); + + line = strchr(buf, ' ')+1; + tok = strtok(line, " "); + while (tok) + { + use = atol(tok); + total += use; + i++; + if (i == 4) + idle = use; + tok = strtok(NULL, " "); + } + } + total_change = total - inst->cfg->cpumonitor.total; + idle_change = idle - inst->cfg->cpumonitor.idle; + if (total_change != 0) + percent = 100 * (1 - ((float)idle_change / (float)total_change)); + if (percent > 100) percent = 100; + else if (percent < 0) percent = 0; + inst->cfg->cpumonitor.total = total; + inst->cfg->cpumonitor.idle = idle; + + return percent; +} diff --git a/src/modules/sysinfo/memusage/memusage.c b/src/modules/sysinfo/memusage/memusage.c new file mode 100644 index 000000000..f7daae1fb --- /dev/null +++ b/src/modules/sysinfo/memusage/memusage.c @@ -0,0 +1,191 @@ +#include "memusage.h" + +typedef struct _Thread_Config Thread_Config; + +struct _Thread_Config +{ + int interval; + Instance *inst; + int memstatus; + int swapstatus; +}; + +static void +_memusage_face_update(Instance *inst, Eina_Bool swap, int status) +{ + Edje_Message_Int_Set *usage_msg; + + usage_msg = malloc(sizeof(Edje_Message_Int_Set) + 1 * sizeof(int)); + EINA_SAFETY_ON_NULL_RETURN(usage_msg); + usage_msg->count = 1; + usage_msg->val[0] = status; + if (!swap) + edje_object_message_send(elm_layout_edje_get(inst->cfg->memusage.o_gadget), EDJE_MESSAGE_INT_SET, 1, + usage_msg); + else + edje_object_message_send(elm_layout_edje_get(inst->cfg->memusage.o_gadget), EDJE_MESSAGE_INT_SET, 2, + usage_msg); +} + +static void +_memusage_cb_usage_check_main(void *data, Ecore_Thread *th) +{ + Thread_Config *thc = data; + for (;;) + { + if (ecore_thread_check(th)) break; + thc->memstatus = _memusage_proc_getmemusage(); + thc->swapstatus = _memusage_proc_getswapusage(); + ecore_thread_feedback(th, NULL); + if (ecore_thread_check(th)) break; + usleep((1000000.0 / 8.0) * (double)thc->interval); + } + E_FREE_FUNC(thc, free); +} + +static void +_memusage_cb_usage_check_notify(void *data, + Ecore_Thread *th EINA_UNUSED, + void *msg EINA_UNUSED) +{ + Thread_Config *thc = data; + Instance *inst = thc->inst; + + if (inst->cfg->esm != E_SYSINFO_MODULE_MEMUSAGE && inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return; + if (!inst->cfg) return; + _memusage_face_update(inst, EINA_FALSE, thc->memstatus); + _memusage_face_update(inst, EINA_TRUE, thc->swapstatus); +} + +void +_memusage_config_updated(Instance *inst) +{ + Thread_Config *thc; + + if (inst->cfg->memusage.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->memusage.usage_check_thread); + inst->cfg->memusage.usage_check_thread = NULL; + } + thc = E_NEW(Thread_Config, 1); + if (thc) + { + thc->inst = inst; + thc->interval = inst->cfg->memusage.poll_interval; + thc->memstatus = 0; + thc->swapstatus = 0; + inst->cfg->memusage.usage_check_thread = + ecore_thread_feedback_run(_memusage_cb_usage_check_main, + _memusage_cb_usage_check_notify, + NULL, NULL, thc, EINA_TRUE); + } + e_config_save_queue(); +} + +static void +_memusage_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data) +{ + Instance *inst = data; + + if (inst->o_main != event_data) return; + + if (inst->cfg->memusage.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->memusage.usage_check_thread); + inst->cfg->memusage.usage_check_thread = NULL; + } + + sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg); + E_FREE(inst->cfg); +} + +void +sysinfo_memusage_remove(Instance *inst) +{ + if (inst->cfg->memusage.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->memusage.usage_check_thread); + inst->cfg->memusage.usage_check_thread = NULL; + } +} + +static void +_memusage_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED) +{ + Instance *inst = data; + + inst->cfg->memusage.o_gadget = elm_layout_add(inst->o_main); + e_theme_edje_object_set(inst->cfg->memusage.o_gadget, "base/theme/modules/memusage", + "e/modules/memusage/main"); + E_EXPAND(inst->cfg->memusage.o_gadget); + E_FILL(inst->cfg->memusage.o_gadget); + elm_box_pack_end(inst->o_main, inst->cfg->memusage.o_gadget); + evas_object_show(inst->cfg->memusage.o_gadget); + evas_object_smart_callback_del_full(obj, "gadget_created", _memusage_created_cb, data); + _memusage_config_updated(inst); +} + +Evas_Object * +sysinfo_memusage_create(Evas_Object *parent, Instance *inst) +{ + inst->cfg->memusage.o_gadget = elm_layout_add(parent); + e_theme_edje_object_set(inst->cfg->memusage.o_gadget, "base/theme/modules/memusage", + "e/modules/memusage/main"); + E_EXPAND(inst->cfg->memusage.o_gadget); + E_FILL(inst->cfg->memusage.o_gadget); + evas_object_show(inst->cfg->memusage.o_gadget); + _memusage_config_updated(inst); + + return inst->cfg->memusage.o_gadget; +} + +static Config_Item * +_conf_item_get(int *id) +{ + Config_Item *ci; + Eina_List *l; + + if (*id > 0) + { + EINA_LIST_FOREACH(sysinfo_config->items, l, ci) + if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_MEMUSAGE) return ci; + } + + ci = E_NEW(Config_Item, 1); + + if (*id != -1) + ci->id = eina_list_count(sysinfo_config->items)+1; + else + ci->id = -1; + + ci->esm = E_SYSINFO_MODULE_MEMUSAGE; + ci->memusage.poll_interval = 32; + + sysinfo_config->items = eina_list_append(sysinfo_config->items, ci); + + return ci; +} + +Evas_Object * +memusage_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED) +{ + Instance *inst; + + inst = E_NEW(Instance, 1); + inst->cfg = _conf_item_get(id); + *id = inst->cfg->id; + inst->o_main = elm_box_add(parent); + E_EXPAND(inst->o_main); + evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + evas_object_smart_callback_add(parent, "gadget_created", _memusage_created_cb, inst); + evas_object_smart_callback_add(parent, "gadget_removed", _memusage_removed_cb, inst); + evas_object_show(inst->o_main); + + if (inst->cfg->id < 0) return inst->o_main; + + sysinfo_instances = + eina_list_append(sysinfo_instances, inst); + + return inst->o_main; +} + diff --git a/src/modules/sysinfo/memusage/memusage.h b/src/modules/sysinfo/memusage/memusage.h new file mode 100644 index 000000000..5419142b0 --- /dev/null +++ b/src/modules/sysinfo/memusage/memusage.h @@ -0,0 +1,10 @@ +#ifndef MEMUSAGE_H +#define MEMUSAGE_H + +#include "../sysinfo.h" + +void _memusage_config_updated(Instance *inst); +int _memusage_proc_getmemusage(); +int _memusage_proc_getswapusage(); + +#endif diff --git a/src/modules/sysinfo/memusage/memusage_proc.c b/src/modules/sysinfo/memusage/memusage_proc.c new file mode 100644 index 000000000..5411e539a --- /dev/null +++ b/src/modules/sysinfo/memusage/memusage_proc.c @@ -0,0 +1,82 @@ +#include "memusage.h" + +int _memusage_proc_getswapusage(void) +{ + long swap_total = 0, swap_free = 0, swap_used = 0; + int percent = 0; + char buf[4096], *line, *tok; + FILE *f; + + f = fopen("/proc/meminfo", "r"); + if (f) + { + while(fgets(buf, sizeof(buf), f) != NULL) + { + if (!strncmp("SwapTotal:", buf, 10)) + { + line = strchr(buf, ':')+1; + while(isspace(*line)) line++; + tok = strtok(line, " "); + swap_total = atol(tok); + } + else if (!strncmp("SwapFree:", buf, 9)) + { + line = strchr(buf, ':')+1; + while(isspace(*line)) line++; + tok = strtok(line, " "); + swap_free = atol(tok); + } + if (swap_total && swap_free) + break; + } + fclose(f); + + swap_used = swap_total - swap_free; + percent = 100 * ((float)swap_used / (float)swap_total); + } + if (percent > 100) percent = 100; + else if (percent < 0) percent = 0; + + return percent; +} + +int _memusage_proc_getmemusage(void) +{ + long mem_total = 0, mem_free = 0, mem_used = 0; + int percent = 0; + char buf[4096], *line, *tok; + FILE *f; + + f = fopen("/proc/meminfo", "r"); + if (f) + { + while(fgets(buf, sizeof(buf), f) != NULL) + { + if (!strncmp("MemTotal:", buf, 9)) + { + line = strchr(buf, ':')+1; + while(isspace(*line)) line++; + tok = strtok(line, " "); + mem_total = atol(tok); + } + else if (!strncmp("MemFree:", buf, 8)) + { + line = strchr(buf, ':')+1; + while(isspace(*line)) line++; + tok = strtok(line, " "); + mem_free = atol(tok); + } + if (mem_total && mem_free) + break; + } + fclose(f); + + mem_used = mem_total - mem_free; + percent = 100 * ((float)mem_used / (float)mem_total); + } + if (percent > 100) percent = 100; + else if (percent < 0) percent = 0; + + return percent; +} + diff --git a/src/modules/sysinfo/netstatus/netstatus.c b/src/modules/sysinfo/netstatus/netstatus.c new file mode 100644 index 000000000..693ea0a5b --- /dev/null +++ b/src/modules/sysinfo/netstatus/netstatus.c @@ -0,0 +1,204 @@ +#include "netstatus.h" + +typedef struct _Thread_Config Thread_Config; + +struct _Thread_Config +{ + int interval; + Instance *inst; + const char *rstatus; + const char *tstatus; +}; + +static void +_netstatus_face_update(Instance *inst, Eina_Bool trans, const char *status) +{ + if (!trans && status) + { + elm_layout_signal_emit(inst->cfg->netstatus.o_gadget, "e,state,received,active", "e"); + elm_layout_text_set(inst->cfg->netstatus.o_gadget, "e.text.received", status); + } + else if (trans && status) + { + elm_layout_signal_emit(inst->cfg->netstatus.o_gadget, "e,state,transmitted,active", "e"); + elm_layout_text_set(inst->cfg->netstatus.o_gadget, "e.text.transmitted", status); + } + else if (!trans && !status) + { + elm_layout_signal_emit(inst->cfg->netstatus.o_gadget, "e,state,received,idle", "e"); + elm_layout_text_set(inst->cfg->netstatus.o_gadget, "e.text.received", "Rx: 0"); + } + else if (trans && !status) + { + elm_layout_signal_emit(inst->cfg->netstatus.o_gadget, "e,state,transmitted,idle", "e"); + elm_layout_text_set(inst->cfg->netstatus.o_gadget, "e.text.transmitted", "Tx: 0"); + } +} + +static void +_netstatus_cb_usage_check_main(void *data, Ecore_Thread *th) +{ + Thread_Config *thc = data; + for (;;) + { + if (ecore_thread_check(th)) break; + E_FREE_FUNC(thc->rstatus, eina_stringshare_del); + E_FREE_FUNC(thc->tstatus, eina_stringshare_del); + thc->rstatus = _netstatus_proc_getrstatus(thc->inst); + thc->tstatus = _netstatus_proc_gettstatus(thc->inst); + ecore_thread_feedback(th, NULL); + if (ecore_thread_check(th)) break; + usleep((1000000.0 / 8.0) * (double)thc->interval); + } + E_FREE_FUNC(thc->rstatus, eina_stringshare_del); + E_FREE_FUNC(thc->tstatus, eina_stringshare_del); + E_FREE_FUNC(thc, free); +} + +static void +_netstatus_cb_usage_check_notify(void *data, + Ecore_Thread *th EINA_UNUSED, + void *msg EINA_UNUSED) +{ + Thread_Config *thc = data; + Instance *inst = thc->inst; + + if (inst->cfg->esm != E_SYSINFO_MODULE_NETSTATUS && inst->cfg->esm != E_SYSINFO_MODULE_SYSINFO) return; + if (!inst->cfg) return; + _netstatus_face_update(inst, EINA_FALSE, thc->rstatus); + _netstatus_face_update(inst, EINA_TRUE, thc->tstatus); +} + +void +_netstatus_config_updated(Instance *inst) +{ + Thread_Config *thc; + + if (inst->cfg->netstatus.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->netstatus.usage_check_thread); + inst->cfg->netstatus.usage_check_thread = NULL; + } + thc = E_NEW(Thread_Config, 1); + if (thc) + { + thc->inst = inst; + thc->interval = inst->cfg->netstatus.poll_interval; + inst->cfg->netstatus.usage_check_thread = + ecore_thread_feedback_run(_netstatus_cb_usage_check_main, + _netstatus_cb_usage_check_notify, + NULL, NULL, thc, EINA_TRUE); + } + e_config_save_queue(); +} + +static void +_netstatus_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data) +{ + Instance *inst = data; + + if (inst->o_main != event_data) return; + + if (inst->cfg->netstatus.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->netstatus.usage_check_thread); + inst->cfg->netstatus.usage_check_thread = NULL; + } + + sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg); + E_FREE(inst->cfg); +} + +void +sysinfo_netstatus_remove(Instance *inst) +{ + if (inst->cfg->netstatus.usage_check_thread) + { + ecore_thread_cancel(inst->cfg->netstatus.usage_check_thread); + inst->cfg->netstatus.usage_check_thread = NULL; + } +} + +static void +_netstatus_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED) +{ + Instance *inst = data; + + inst->cfg->netstatus.o_gadget = elm_layout_add(inst->o_main); + e_theme_edje_object_set(inst->cfg->netstatus.o_gadget, "base/theme/modules/netstatus", + "e/modules/netstatus/main"); + E_EXPAND(inst->cfg->netstatus.o_gadget); + E_FILL(inst->cfg->netstatus.o_gadget); + elm_box_pack_end(inst->o_main, inst->cfg->netstatus.o_gadget); + evas_object_show(inst->cfg->netstatus.o_gadget); + evas_object_smart_callback_del_full(obj, "gadget_created", _netstatus_created_cb, data); + _netstatus_config_updated(inst); +} + +Evas_Object * +sysinfo_netstatus_create(Evas_Object *parent, Instance *inst) +{ + inst->cfg->netstatus.o_gadget = elm_layout_add(parent); + e_theme_edje_object_set(inst->cfg->netstatus.o_gadget, "base/theme/modules/netstatus", + "e/modules/netstatus/main"); + E_EXPAND(inst->cfg->netstatus.o_gadget); + E_FILL(inst->cfg->netstatus.o_gadget); + evas_object_show(inst->cfg->netstatus.o_gadget); + _netstatus_config_updated(inst); + + return inst->cfg->netstatus.o_gadget; +} + +static Config_Item * +_conf_item_get(int *id) +{ + Config_Item *ci; + Eina_List *l; + + if (*id > 0) + { + EINA_LIST_FOREACH(sysinfo_config->items, l, ci) + if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_NETSTATUS) return ci; + } + + ci = E_NEW(Config_Item, 1); + + if (*id != -1) + ci->id = eina_list_count(sysinfo_config->items)+1; + else + ci->id = -1; + + ci->esm = E_SYSINFO_MODULE_NETSTATUS; + ci->netstatus.poll_interval = 32; + ci->netstatus.in = 0; + ci->netstatus.out = 0; + sysinfo_config->items = eina_list_append(sysinfo_config->items, ci); + + return ci; +} + +Evas_Object * +netstatus_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED) +{ + Instance *inst; + + inst = E_NEW(Instance, 1); + inst->cfg = _conf_item_get(id); + *id = inst->cfg->id; + inst->cfg->netstatus.in = 0; + inst->cfg->netstatus.out = 0; + inst->o_main = elm_box_add(parent); + E_EXPAND(inst->o_main); + evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + evas_object_smart_callback_add(parent, "gadget_created", _netstatus_created_cb, inst); + evas_object_smart_callback_add(parent, "gadget_removed", _netstatus_removed_cb, inst); + evas_object_show(inst->o_main); + + if (inst->cfg->id < 0) return inst->o_main; + + sysinfo_instances = + eina_list_append(sysinfo_instances, inst); + + return inst->o_main; +} + diff --git a/src/modules/sysinfo/netstatus/netstatus.h b/src/modules/sysinfo/netstatus/netstatus.h new file mode 100644 index 000000000..353017fdf --- /dev/null +++ b/src/modules/sysinfo/netstatus/netstatus.h @@ -0,0 +1,10 @@ +#ifndef NETSTATUS_H +#define NETSTATUS_H + +#include "../sysinfo.h" + +void _netstatus_config_updated(Instance *inst); +const char *_netstatus_proc_getrstatus(Instance *inst); +const char *_netstatus_proc_gettstatus(Instance *inst); + +#endif diff --git a/src/modules/sysinfo/netstatus/netstatus_proc.c b/src/modules/sysinfo/netstatus/netstatus_proc.c new file mode 100644 index 000000000..4020d0c6d --- /dev/null +++ b/src/modules/sysinfo/netstatus/netstatus_proc.c @@ -0,0 +1,78 @@ +#include "netstatus.h" + +const char * +_netstatus_proc_getrstatus(Instance *inst) +{ + long in, dummy, tot_in = 0; + long diffin; + char buf[4096], rin[4096], dummys[64]; + FILE *f; + + f = fopen("/proc/net/dev", "r"); + if (f) + { + while(fgets(buf, sizeof(buf), f) != NULL) + { + if (sscanf (buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " + "%lu %lu %lu %lu\n", dummys, &in, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy) < 17) + continue; + tot_in += in; + } + fclose(f); + } + diffin = tot_in - inst->cfg->netstatus.in; + inst->cfg->netstatus.in = tot_in; + if (!diffin) return NULL; + else + { + diffin /= 0.5; + if (diffin > 1048576) + snprintf(rin, sizeof(rin), "Rx: %.2f MB/s", ((float)diffin / 1048576)); + else if ((diffin > 1024) && (diffin < 1048576)) + snprintf(rin, sizeof(rin), "Rx: %lu KB/s", (diffin / 1024)); + else + snprintf(rin, sizeof(rin), "Rx: %lu B/s", diffin); + } + return eina_stringshare_add(rin); +} + +const char * + _netstatus_proc_gettstatus(Instance *inst) +{ + long out, dummy, tot_out = 0; + long diffout; + char buf[4096], rout[4096], dummys[64]; + FILE *f; + + f = fopen("/proc/net/dev", "r"); + if (f) + { + while(fgets(buf, sizeof(buf), f) != NULL) + { + if (sscanf (buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " + "%lu %lu %lu %lu\n", dummys, &dummy, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &out, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy) < 17) + continue; + tot_out += out; + } + fclose(f); + } + diffout = tot_out - inst->cfg->netstatus.out; + inst->cfg->netstatus.out = tot_out; + if (!diffout) return NULL; + else + { + diffout /= 0.5; + if (diffout > 1048576) + snprintf(rout, sizeof(rout), "Tx: %.2f MB/s", ((float)diffout / 1048576)); + else if ((diffout > 1024) && (diffout < 1048576)) + snprintf(rout, sizeof(rout), "Tx: %lu KB/s", (diffout / 1024)); + else + snprintf(rout, sizeof(rout), "Tx: %lu B/s", diffout); + } + return eina_stringshare_add(rout); +} + diff --git a/src/modules/sysinfo/thermal/thermal.c b/src/modules/sysinfo/thermal/thermal.c new file mode 100644 index 000000000..d58a68848 --- /dev/null +++ b/src/modules/sysinfo/thermal/thermal.c @@ -0,0 +1,306 @@ +#include "thermal.h" + +static void +_thermal_thread_free(Tempthread *tth) +{ + const char *s; + + eina_stringshare_del(tth->sensor_name); + eina_stringshare_del(tth->sensor_path); +#ifdef HAVE_EEZE + EINA_LIST_FREE(tth->tempdevs, s) eina_stringshare_del(s); +#endif + free(tth->extn); + free(tth); +} + +static void +_thermal_face_level_set(Instance *inst, double level) +{ + Edje_Message_Float msg; + + if (level < 0.0) level = 0.0; + else if (level > 1.0) level = 1.0; + msg.val = level; + edje_object_message_send(elm_layout_edje_get(inst->cfg->thermal.o_gadget), EDJE_MESSAGE_FLOAT, 1, &msg); +} + +static void +_thermal_apply(Instance *inst, int temp) +{ + char buf[64]; + + if (inst->cfg->thermal.temp == temp) return; + inst->cfg->thermal.temp = temp; + if (temp != -999) + { + if (inst->cfg->thermal.units == FAHRENHEIT) temp = (temp * 9.0 / 5.0) + 32; + + if (!inst->cfg->thermal.have_temp) + { + /* enable therm object */ + elm_layout_signal_emit(inst->cfg->thermal.o_gadget, "e,state,known", ""); + inst->cfg->thermal.have_temp = EINA_TRUE; + } + if (inst->cfg->thermal.units == FAHRENHEIT) + snprintf(buf, sizeof(buf), "%i°F", temp); + else + snprintf(buf, sizeof(buf), "%i°C", temp); + + _thermal_face_level_set(inst, + (double)(temp - inst->cfg->thermal.low) / + (double)(inst->cfg->thermal.high - inst->cfg->thermal.low)); + elm_layout_text_set(inst->cfg->thermal.o_gadget, "e.text.reading", buf); + } + else + { + if (inst->cfg->thermal.have_temp) + { + /* disable therm object */ + elm_layout_signal_emit(inst->cfg->thermal.o_gadget, "e,state,unknown", ""); + elm_layout_text_set(inst->cfg->thermal.o_gadget, "e.text.reading", "N/A"); + _thermal_face_level_set(inst, 0.5); + inst->cfg->thermal.have_temp = EINA_FALSE; + } + } +} + +#ifdef HAVE_EEZE +static Eina_Bool +_thermal_udev_poll(void *data) +{ + Tempthread *tth = data; + int temp = thermal_udev_get(tth); + + _thermal_apply(tth->inst, temp); + return EINA_TRUE; +} +#endif + +#if defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonflyBSD__ +static void +_thermal_check_sysctl(void *data, Ecore_Thread *th) +{ + Tempthread *tth = data; + int ptemp = -500, temp; + + for (;;) + { + if (ecore_thread_check(th)) break; + temp = thermal_sysctl_get(tth); + if (ptemp != temp) ecore_thread_feedback(th, (void *)((long)temp)); + ptemp = temp; + usleep((1000000.0 / 8.0) * (double)tth->poll_interval); + if (ecore_thread_check(th)) break; + } +} +#endif + +#if !defined HAVE_EEZE && !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __DragonflyBSD__ +static void +_thermal_check_fallback(void *data, Ecore_Thread *th) +{ + Tempthread *tth = data; + int ptemp = -500, temp; + + for (;;) + { + if (ecore_thread_check(th)) break; + temp = thermal_fallback_get(tth); + if (ptemp != temp) ecore_thread_feedback(th, (void *)((long)temp)); + ptemp = temp; + usleep((1000000.0 / 8.0) * (double)tth->poll_interval); + if (ecore_thread_check(th)) break; + } +} +#endif + +#if !defined HAVE_EEZE +static void +_thermal_check_notify(void *data, Ecore_Thread *th, void *msg) +{ + Tempthread *tth = data; + Instance *inst = tth->inst; + int temp = (int)((long)msg); + + if (th != inst->cfg->thermal.th) return; + _thermal_apply(inst, temp); +} + +static void +_thermal_check_done(void *data, Ecore_Thread *th EINA_UNUSED) +{ + _thermal_thread_free(data); +} +#endif + +void +_thermal_config_updated(Instance *inst) +{ + Tempthread *tth; + + if (inst->cfg->thermal.th) ecore_thread_cancel(inst->cfg->thermal.th); + + tth = calloc(1, sizeof(Tempthread)); + tth->poll_interval = inst->cfg->thermal.poll_interval; + tth->sensor_type = inst->cfg->thermal.sensor_type; + tth->inst = inst; + if (inst->cfg->thermal.sensor_name) + tth->sensor_name = eina_stringshare_add(inst->cfg->thermal.sensor_name); + +#ifdef HAVE_EEZE + _thermal_udev_poll(tth); + inst->cfg->thermal.poller = ecore_poller_add(ECORE_POLLER_CORE, inst->cfg->thermal.poll_interval, + _thermal_udev_poll, tth); + inst->cfg->thermal.tth = tth; +#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonflyBSD__ + inst->cfg->thermal.th = ecore_thread_feedback_run(_thermal_check_sysctl, + _thermal_check_notify, + _thermal_check_done, + _thermal_check_done, + tth, EINA_TRUE); +#else + inst->cfg->thermal.th = ecore_thread_feedback_run(_thermal_check_main, + _thermal_check_notify, + _thermal_check_done, + _thermal_check_done, + tth, EINA_TRUE); +#endif +} + +static void +_thermal_face_shutdown(Instance *inst) +{ + if (inst->cfg->thermal.th) ecore_thread_cancel(inst->cfg->thermal.th); + if (inst->cfg->thermal.sensor_name) eina_stringshare_del(inst->cfg->thermal.sensor_name); +#ifdef HAVE_EEZE + if (inst->cfg->thermal.poller) + { + ecore_poller_del(inst->cfg->thermal.poller); + _thermal_thread_free(inst->cfg->thermal.tth); + } +#endif +} + +static void +_thermal_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data) +{ + Instance *inst = data; + + if (inst->o_main != event_data) return; + + _thermal_face_shutdown(inst); + +#ifdef HAVE_EEZE + eeze_shutdown(); +#endif + + sysinfo_config->items = eina_list_remove(sysinfo_config->items, inst->cfg); + E_FREE(inst->cfg); +} + +void +sysinfo_thermal_remove(Instance *inst) +{ + _thermal_face_shutdown(inst); + +#ifdef HAVE_EEZE + eeze_shutdown(); +#endif +} + +static void +_thermal_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED) +{ + Instance *inst = data; + + inst->cfg->thermal.temp = 900; + inst->cfg->thermal.have_temp = EINA_FALSE; + + inst->cfg->thermal.o_gadget = elm_layout_add(inst->o_main); + e_theme_edje_object_set(inst->cfg->thermal.o_gadget, "base/theme/modules/temperature", + "e/modules/temperature/main"); + E_EXPAND(inst->cfg->thermal.o_gadget); + E_FILL(inst->cfg->thermal.o_gadget); + elm_box_pack_end(inst->o_main, inst->cfg->thermal.o_gadget); + evas_object_show(inst->cfg->thermal.o_gadget); + evas_object_smart_callback_del_full(obj, "gadget_created", _thermal_created_cb, data); + _thermal_config_updated(inst); +} + +Evas_Object * +sysinfo_thermal_create(Evas_Object *parent, Instance *inst) +{ + inst->cfg->thermal.temp = 900; + inst->cfg->thermal.have_temp = EINA_FALSE; + + inst->cfg->thermal.o_gadget = elm_layout_add(parent); + e_theme_edje_object_set(inst->cfg->thermal.o_gadget, "base/theme/modules/temperature", + "e/modules/temperature/main"); + E_EXPAND(inst->cfg->thermal.o_gadget); + E_FILL(inst->cfg->thermal.o_gadget); + evas_object_show(inst->cfg->thermal.o_gadget); + _thermal_config_updated(inst); + + return inst->cfg->thermal.o_gadget; +} + +static Config_Item * +_conf_item_get(int *id) +{ + Config_Item *ci; + Eina_List *l; + + if (*id > 0) + { + EINA_LIST_FOREACH(sysinfo_config->items, l, ci) + if (*id == ci->id && ci->esm == E_SYSINFO_MODULE_THERMAL) return ci; + } + + ci = E_NEW(Config_Item, 1); + + if (*id != -1) + ci->id = eina_list_count(sysinfo_config->items)+1; + else + ci->id = -1; + + ci->esm = E_SYSINFO_MODULE_THERMAL; + ci->thermal.poll_interval = 128; + ci->thermal.low = 30; + ci->thermal.high = 80; + ci->thermal.sensor_type = SENSOR_TYPE_NONE; + ci->thermal.sensor_name = NULL; + ci->thermal.units = CELSIUS; + + sysinfo_config->items = eina_list_append(sysinfo_config->items, ci); + + return ci; +} + +Evas_Object * +thermal_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED) +{ + Instance *inst; + + inst = E_NEW(Instance, 1); + inst->cfg = _conf_item_get(id); + *id = inst->cfg->id; + inst->o_main = elm_box_add(parent); + E_EXPAND(inst->o_main); + evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + evas_object_smart_callback_add(parent, "gadget_created", _thermal_created_cb, inst); + evas_object_smart_callback_add(parent, "gadget_removed", _thermal_removed_cb, inst); + evas_object_show(inst->o_main); + +#ifdef HAVE_EEZE + eeze_init(); +#endif + + if (inst->cfg->id < 0) return inst->o_main; + + sysinfo_instances = + eina_list_append(sysinfo_instances, inst); + + return inst->o_main; +} + diff --git a/src/modules/sysinfo/thermal/thermal.h b/src/modules/sysinfo/thermal/thermal.h new file mode 100644 index 000000000..3ecfb6255 --- /dev/null +++ b/src/modules/sysinfo/thermal/thermal.h @@ -0,0 +1,17 @@ +#ifndef THERMAL_H +#define THERMAL_H + +#include "../sysinfo.h" + +#ifdef HAVE_EEZE +int thermal_udev_get(Tempthread *tth); +#endif + +#if defined __OpenBSD__ || defined __DragonFly__ || defined __FreeBSD__ +int thermal_sysctl_get(Tempthread *tth); +#endif + +int thermal_fallback_get(Tempthread *tth); +void _thermal_config_updated(Instance *inst); + +#endif diff --git a/src/modules/sysinfo/thermal/thermal_fallback.c b/src/modules/sysinfo/thermal/thermal_fallback.c new file mode 100644 index 000000000..7c70c98ab --- /dev/null +++ b/src/modules/sysinfo/thermal/thermal_fallback.c @@ -0,0 +1,431 @@ +#include "thermal.h" + +typedef struct +{ + int dummy; +} Extn; + +Eina_List * +temperature_get_bus_files(const char *bus) +{ + Eina_List *result; + Eina_List *therms; + char path[PATH_MAX]; + char busdir[PATH_MAX]; + char *name; + + result = NULL; + + snprintf(busdir, sizeof(busdir), "/sys/bus/%s/devices", bus); + /* Look through all the devices for the given bus. */ + therms = ecore_file_ls(busdir); + + EINA_LIST_FREE(therms, name) + { + Eina_List *files; + char *file; + + /* Search each device for temp*_input, these should be + * temperature devices. */ + snprintf(path, sizeof(path), "%s/%s", busdir, name); + files = ecore_file_ls(path); + EINA_LIST_FREE(files, file) + { + if ((!strncmp("temp", file, 4)) && + (!strcmp("_input", &file[strlen(file) - 6]))) + { + char *f; + + snprintf(path, sizeof(path), + "%s/%s/%s", busdir, name, file); + f = strdup(path); + if (f) result = eina_list_append(result, f); + } + free(file); + } + free(name); + } + return result; +} + +static void +init(Tempthread *tth) +{ + Eina_List *therms; + char path[512]; + Extn *extn; + + if (tth->initted) return; + tth->initted = EINA_TRUE; + + extn = calloc(1, sizeof(Extn)); + tth->extn = extn; + + if ((!tth->sensor_type) || + ((!tth->sensor_name) || + (tth->sensor_name[0] == 0))) + { + eina_stringshare_del(tth->sensor_name); + tth->sensor_name = NULL; + eina_stringshare_del(tth->sensor_path); + tth->sensor_path = NULL; + therms = ecore_file_ls("/proc/acpi/thermal_zone"); + if (therms) + { + char *name; + + name = eina_list_data_get(therms); + tth->sensor_type = SENSOR_TYPE_LINUX_ACPI; + tth->sensor_name = eina_stringshare_add(name); + eina_list_free(therms); + } + else + { + eina_list_free(therms); + therms = ecore_file_ls("/sys/class/thermal"); + if (therms) + { + char *name; + Eina_List *l; + + EINA_LIST_FOREACH(therms, l, name) + { + if (!strncmp(name, "thermal", 7)) + { + tth->sensor_type = SENSOR_TYPE_LINUX_SYS; + tth->sensor_name = eina_stringshare_add(name); + eina_list_free(therms); + therms = NULL; + break; + } + } + if (therms) eina_list_free(therms); + } + if (therms) + { + if (ecore_file_exists("/proc/omnibook/temperature")) + { + tth->sensor_type = SENSOR_TYPE_OMNIBOOK; + tth->sensor_name = eina_stringshare_add("dummy"); + } + else if (ecore_file_exists("/sys/devices/temperatures/sensor1_temperature")) + { + tth->sensor_type = SENSOR_TYPE_LINUX_PBOOK; + tth->sensor_name = eina_stringshare_add("dummy"); + } + else if (ecore_file_exists("/sys/devices/temperatures/cpu_temperature")) + { + tth->sensor_type = SENSOR_TYPE_LINUX_MACMINI; + tth->sensor_name = eina_stringshare_add("dummy"); + } + else if (ecore_file_exists("/sys/devices/platform/coretemp.0/temp1_input")) + { + tth->sensor_type = SENSOR_TYPE_LINUX_INTELCORETEMP; + tth->sensor_name = eina_stringshare_add("dummy"); + } + else if (ecore_file_exists("/sys/devices/platform/thinkpad_hwmon/temp1_input")) + { + tth->sensor_type = SENSOR_TYPE_LINUX_THINKPAD; + tth->sensor_name = eina_stringshare_add("dummy"); + } + else + { + // try the i2c bus + therms = temperature_get_bus_files("i2c"); + if (therms) + { + char *name; + + if ((name = eina_list_data_get(therms))) + { + if (ecore_file_exists(name)) + { + int len; + + snprintf(path, sizeof(path), + "%s", ecore_file_file_get(name)); + len = strlen(path); + if (len > 6) path[len - 6] = '\0'; + tth->sensor_type = SENSOR_TYPE_LINUX_I2C; + tth->sensor_path = eina_stringshare_add(name); + tth->sensor_name = eina_stringshare_add(path); + } + } + eina_list_free(therms); + } + if (!tth->sensor_path) + { + // try the pci bus + therms = temperature_get_bus_files("pci"); + if (therms) + { + char *name; + + if ((name = eina_list_data_get(therms))) + { + if (ecore_file_exists(name)) + { + int len; + + snprintf(path, sizeof(path), + "%s", ecore_file_file_get(name)); + len = strlen(path); + if (len > 6) path[len - 6] = '\0'; + tth->sensor_type = SENSOR_TYPE_LINUX_PCI; + tth->sensor_path = eina_stringshare_add(name); + eina_stringshare_del(tth->sensor_name); + tth->sensor_name = eina_stringshare_add(path); + } + } + eina_list_free(therms); + } + } + } + } + } + } + if ((tth->sensor_type) && (tth->sensor_name) && (!tth->sensor_path)) + { + char *name; + + switch (tth->sensor_type) + { + case SENSOR_TYPE_NONE: + break; + case SENSOR_TYPE_OMNIBOOK: + tth->sensor_path = eina_stringshare_add("/proc/omnibook/temperature"); + break; + + case SENSOR_TYPE_LINUX_MACMINI: + tth->sensor_path = eina_stringshare_add("/sys/devices/temperatures/cpu_temperature"); + break; + + case SENSOR_TYPE_LINUX_PBOOK: + tth->sensor_path = eina_stringshare_add("/sys/devices/temperatures/sensor1_temperature"); + break; + + case SENSOR_TYPE_LINUX_INTELCORETEMP: + tth->sensor_path = eina_stringshare_add("/sys/devices/platform/coretemp.0/temp1_input"); + break; + + case SENSOR_TYPE_LINUX_THINKPAD: + tth->sensor_path = eina_stringshare_add("/sys/devices/platform/thinkpad_hwmon/temp1_input"); + break; + + case SENSOR_TYPE_LINUX_I2C: + therms = ecore_file_ls("/sys/bus/i2c/devices"); + + EINA_LIST_FREE(therms, name) + { + snprintf(path, sizeof(path), + "/sys/bus/i2c/devices/%s/%s_input", + name, tth->sensor_name); + if (ecore_file_exists(path)) + { + tth->sensor_path = eina_stringshare_add(path); + /* We really only care about the first + * one for the default. */ + break; + } + free(name); + } + break; + + case SENSOR_TYPE_LINUX_PCI: + therms = ecore_file_ls("/sys/bus/pci/devices"); + + EINA_LIST_FREE(therms, name) + { + snprintf(path, sizeof(path), + "/sys/bus/pci/devices/%s/%s_input", + name, tth->sensor_name); + if (ecore_file_exists(path)) + { + tth->sensor_path = eina_stringshare_add(path); + /* We really only care about the first + * one for the default. */ + break; + } + free(name); + } + break; + + case SENSOR_TYPE_LINUX_ACPI: + snprintf(path, sizeof(path), + "/proc/acpi/thermal_zone/%s/temperature", + tth->sensor_name); + tth->sensor_path = eina_stringshare_add(path); + break; + + case SENSOR_TYPE_LINUX_SYS: + snprintf(path, sizeof(path), + "/sys/class/thermal/%s/temp", tth->sensor_name); + tth->sensor_path = eina_stringshare_add(path); + break; + + default: + break; + } + } +} + +static int +check(Tempthread *tth) +{ + FILE *f = NULL; + int ret = 0; + int temp = 0; + char buf[512]; + /* TODO: Make standard parser. Seems to be two types of temperature string: + * - Somename: C + * - + */ + switch (tth->sensor_type) + { + case SENSOR_TYPE_NONE: + /* TODO: Slow down poller? */ + break; + + case SENSOR_TYPE_OMNIBOOK: + f = fopen(tth->sensor_path, "r"); + if (f) + { + char dummy[4096]; + + + fclose(f); + f = NULL; + if (sscanf(buf, "%s %s %i", dummy, dummy, &temp) == 3) + ret = 1; + else + goto error; + } + else + goto error; + break; + + case SENSOR_TYPE_LINUX_MACMINI: + case SENSOR_TYPE_LINUX_PBOOK: + f = fopen(tth->sensor_path, "rb"); + if (f) + { + if (fgets(buf, sizeof(buf), f) == NULL) goto error; + fclose(f); + f = NULL; + if (sscanf(buf, "%i", &temp) == 1) + ret = 1; + else + goto error; + } + else + goto error; + break; + + case SENSOR_TYPE_LINUX_INTELCORETEMP: + case SENSOR_TYPE_LINUX_I2C: + case SENSOR_TYPE_LINUX_THINKPAD: + f = fopen(tth->sensor_path, "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) == NULL) goto error; + fclose(f); + f = NULL; + /* actually read the temp */ + if (sscanf(buf, "%i", &temp) == 1) + ret = 1; + else + goto error; + /* Hack for temp */ + temp = temp / 1000; + } + else + goto error; + break; + + case SENSOR_TYPE_LINUX_PCI: + f = fopen(tth->sensor_path, "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) == NULL) goto error; + fclose(f); + f = NULL; + /* actually read the temp */ + if (sscanf(buf, "%i", &temp) == 1) + ret = 1; + else + goto error; + /* Hack for temp */ + temp = temp / 1000; + } + else + goto error; + break; + + case SENSOR_TYPE_LINUX_ACPI: + f = fopen(tth->sensor_path, "r"); + if (f) + { + char *p, *q; + + if (fgets(buf, sizeof(buf), f) == NULL) goto error; + fclose(f); + f = NULL; + p = strchr(buf, ':'); + if (p) + { + p++; + while (*p == ' ') + p++; + q = strchr(p, ' '); + if (q) *q = 0; + temp = atoi(p); + ret = 1; + } + else + goto error; + } + else + goto error; + break; + + case SENSOR_TYPE_LINUX_SYS: + f = fopen(tth->sensor_path, "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f) == NULL) goto error; + fclose(f); + f = NULL; + temp = atoi(buf); + temp /= 1000; + ret = 1; + } + else + goto error; + break; + + default: + break; + } + + if (ret) return temp; + + return -999; +error: + if (f) fclose(f); + tth->sensor_type = SENSOR_TYPE_NONE; + eina_stringshare_del(tth->sensor_name); + tth->sensor_name = NULL; + eina_stringshare_del(tth->sensor_path); + tth->sensor_path = NULL; + return -999; +} + +int +thermal_fallback_get(Tempthread *tth) +{ + int temp; + + init(tth); + temp = check(tth); + return temp; +} diff --git a/src/modules/sysinfo/thermal/thermal_sysctl.c b/src/modules/sysinfo/thermal/thermal_sysctl.c new file mode 100644 index 000000000..5ea86c962 --- /dev/null +++ b/src/modules/sysinfo/thermal/thermal_sysctl.c @@ -0,0 +1,214 @@ +#include "thermal.h" + +#if defined (__FreeBSD__) || defined(__DragonFly__) +# include +# include +# include +#endif + +#ifdef __OpenBSD__ +#include +#include +#include +#include +#include +#endif + +#if defined (__FreeBSD__) || defined(__DragonFly__) || defined (__OpenBSD__) +typedef struct +{ + int mib[CTL_MAXNAME]; +#if defined (__FreeBSD__) || defined(__DragonFly__) + unsigned int miblen; +#endif + int dummy; +} Extn; + +#if defined (__FreeBSD__) || defined(__DragonFly__) +static const char *sources[] = + { + "hw.acpi.thermal.tz0.temperature", + "dev.cpu.0.temperature", + "dev.aibs.0.temp.0", + "dev.lm75.0.temperature", + NULL + }; +#endif + +#ifdef __OpenBSD__ +static struct sensor snsr; +static size_t slen = sizeof(snsr); +#endif + +static void +init(Tempthread *tth) +{ + Eina_List *therms; + char path[512]; +#ifdef __OpenBSD__ + int dev, numt; + struct sensordev snsrdev; + size_t sdlen = sizeof(snsrdev); +#endif +#if defined (__FreeBSD__) || defined(__DragonFly__) + unsigned i; + size_t len; + int rc; +#endif + Extn *extn; + + if (tth->initted) return; + tth->initted = EINA_TRUE; + + extn = calloc(1, sizeof(Extn)); + tth->extn = extn; + + if ((!tth->sensor_type) || + ((!tth->sensor_name) || + (tth->sensor_name[0] == 0))) + { + eina_stringshare_del(tth->sensor_name); + tth->sensor_name = NULL; + eina_stringshare_del(tth->sensor_path); + tth->sensor_path = NULL; +#if defined (__FreeBSD__) || defined(__DragonFly__) + for (i = 0; sources[i]; i++) + { + rc = sysctlbyname(sources[i], NULL, NULL, NULL, 0); + if (rc == 0) + { + tth->sensor_type = SENSOR_TYPE_FREEBSD; + tth->sensor_name = eina_stringshare_add(sources[i]); + break; + } + } +#elif defined(__OpenBSD__) + extn->mib[0] = CTL_HW; + extn->mib[1] = HW_SENSORS; + + for (dev = 0;; dev++) + { + extn->mib[2] = dev; + if (sysctl(extn->mib, 3, &snsrdev, &sdlen, NULL, 0) == -1) + { + if (errno == ENOENT) /* no further sensors */ + break; + else + continue; + } + if (strcmp(snsrdev.xname, "cpu0") == 0) + { + tth->sensor_type = SENSOR_TYPE_OPENBSD; + tth->sensor_name = strdup("cpu0"); + break; + } + else if (strcmp(snsrdev.xname, "km0") == 0) + { + tth->sensor_type = SENSOR_TYPE_OPENBSD; + tth->sensor_name = strdup("km0"); + break; + } + } +#endif + } + if ((tth->sensor_type) && (tth->sensor_name) && (!tth->sensor_path)) + { + char *name; + + if (tth->sensor_type == SENSOR_TYPE_FREEBSD) + { +#if defined (__FreeBSD__) || defined(__DragonFly__) + len = sizeof(extn->mib) / sizeof(extn->mib[0]); + rc = sysctlnametomib(tth->sensor_name, extn->mib, &len); + if (rc == 0) + { + extn->miblen = len; + tth->sensor_path = eina_stringshare_add(tth->sensor_name); + } +#endif + } + else if (tth->sensor_type == SENSOR_TYPE_OPENBSD) + { +#ifdef __OpenBSD__ + for (numt = 0; numt < snsrdev.maxnumt[SENSOR_TEMP]; numt++) + { + extn->mib[4] = numt; + slen = sizeof(snsr); + if (sysctl(extn->mib, 5, &snsr, &slen, NULL, 0) == -1) + continue; + if (slen > 0 && (snsr.flags & SENSOR_FINVALID) == 0) + { + break; + } + } +#endif + } + } +} + +static int +check(Tempthread *tth) +{ + FILE *f = NULL; + int ret = 0; + int temp = 0; + char buf[512]; +#if defined (__FreeBSD__) || defined(__DragonFly__) + size_t len; + size_t ftemp = 0; +#endif +#if defined (__FreeBSD__) || defined(__DragonFly__) || defined (__OpenBSD__) + Extn *extn = tth->extn; +#endif + + /* TODO: Make standard parser. Seems to be two types of temperature string: + * - Somename: C + * - + */ + if (tth->sensor_type == SENSOR_TYPE_FREEBSD) + { +#if defined (__FreeBSD__) || defined(__DragonFly__) + len = sizeof(ftemp); + if (sysctl(extn->mib, extn->miblen, &ftemp, &len, NULL, 0) == 0) + { + temp = (ftemp - 2732) / 10; + ret = 1; + } + else + goto error; +#endif + else if (tth->sensor_type == SENSOR_TYPE_OPENBSD) + { +#ifdef __OpenBSD_ + if (sysctl(extn->mib, 5, &snsr, &slen, NULL, 0) != -1) + { + temp = (snsr.value - 273150000) / 1000000.0; + ret = 1; + } + else + goto error; +#endif + } +if (ret) return temp; + + return -999; +error: + if (f) fclose(f); + tth->sensor_type = SENSOR_TYPE_NONE; + eina_stringshare_del(tth->sensor_name); + tth->sensor_name = NULL; + eina_stringshare_del(tth->sensor_path); + tth->sensor_path = NULL; + return -999; +} + +int +thermal_sysctl_get(Tempthread *tth) +{ + int temp; + + init(tth); + temp = check(tth); + return temp; +} +#endif diff --git a/src/modules/sysinfo/thermal/thermal_udev.c b/src/modules/sysinfo/thermal/thermal_udev.c new file mode 100644 index 000000000..c84d1571c --- /dev/null +++ b/src/modules/sysinfo/thermal/thermal_udev.c @@ -0,0 +1,50 @@ +#include "thermal.h" + +int +thermal_udev_get(Tempthread *tth) +{ + Eina_List *l; + double cur, temp; + char *syspath; + const char *test; + char buf[256]; + int x, y, cpus = 0; + + temp = -999; + + if (!tth->tempdevs) + tth->tempdevs = + eeze_udev_find_by_type(EEZE_UDEV_TYPE_IS_IT_HOT_OR_IS_IT_COLD_SENSOR, + NULL); + if (tth->tempdevs) + { + temp = 0; + EINA_LIST_FOREACH(tth->tempdevs, l, syspath) + { + for (x = 1, y = 0; x < 15; x++) + { + if (y >= 2) break; + sprintf(buf, "temp%d_input", x); + if ((test = eeze_udev_syspath_get_sysattr(syspath, buf))) + { + y = 0; + cur = atoi(test); + if (cur > 0) + { + /* udev reports temp in (celsius * 1000) */ + temp += (cur / 1000); + cpus++; + } + } + /* keep checking for sensors until 2 in a row don't exist */ + else y++; + } + } + if (cpus > 0) + { + temp /= (double)cpus; + return temp; + } + } + return -999; +}