# include #include "process.h" #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) # include # include #endif #define POLL_INTERVAL 2.0 static Ecore_Event_Handler *_proc_stats_handler_fullscreen = NULL; static int64_t _mem_total; typedef struct { E_Powersave_Sleeper *sleeper; Ecore_Thread *thread; Eina_List *clients; int poll_interval; E_Module *e_module; } Proc_Stats_Module; static Proc_Stats_Module *_this_module = NULL; typedef struct { E_Client *ec; Evas_Object *obj; Evas_Object *frame_obj; Evas_Object *popup; E_Object_Delfn *delfn; pid_t pid; uint64_t mem_size; uint64_t cpu_time; uint64_t cpu_time_prev; Eina_Bool was_maximized; } Proc_Stats_Client; static void _proc_stats_client_add(E_Client *ec); static void _proc_stats_client_display_update(Proc_Stats_Client *client); static void _proc_stats_client_remove(Proc_Stats_Client *client); static Eina_Bool _proc_stats_client_exists(E_Client *ec); static void _proc_stats_client_del(Proc_Stats_Client *client); static Eina_Bool _proc_stats_client_gone(Proc_Stats_Client *client); static void _proc_stats_client_children_update(Eina_List *children, Proc_Stats_Client *client); static void _proc_stats_client_update(Eina_List *procs, Proc_Stats_Client *client); static void _proc_stats_client_popup_add(Proc_Stats_Client *client); static void _proc_stats_client_popup_del(Proc_Stats_Client *client); static Eina_Bool _memory_total(void) { #if defined(__linux__) char line[256]; FILE *f = fopen("/proc/meminfo", "r"); if (!f) return 0; while (fgets(line, sizeof(line), f) != NULL) { if (!strncmp("MemTotal:", line, 9)) { char *p, *t; p = strchr(line, ':') + 1; while (isspace(*p)) p++; t = strtok(p, " "); _mem_total = atoll(t) * 1024; break; } } fclose(f); if(!_mem_total) return 0; #else size_t len = sizeof(_mem_total); int mib[5] = { CTL_HW, HW_PHYSMEM, 0, 0, 0 }; if (sysctl(mib, 2, &_mem_total, &len, NULL, 0) == -1) return 0; #endif return 1; } static Eina_Bool _proc_stats_handler_fullscreen_check_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Proc_Stats_Client *client; E_Event_Client *ev; Eina_List *l; Proc_Stats_Module *module = _this_module; ev = event; EINA_LIST_FOREACH(module->clients, l, client) { if (client->ec == ev->ec) { _proc_stats_client_remove(client); break; } } return ECORE_CALLBACK_PASS_ON; } static Eina_Bool _proc_stats_client_exists(E_Client *ec) { Proc_Stats_Client *client; Eina_List *l; Proc_Stats_Module *module = _this_module; EINA_LIST_FOREACH(module->clients, l, client) { if (client->pid == ec->netwm.pid) return 1; } return 0; } static void _proc_stats_client_del(Proc_Stats_Client *client) { _proc_stats_client_popup_del(client); edje_object_signal_emit(client->frame_obj, "e,state,procstats,off", "e"); evas_object_del(client->obj); e_object_delfn_del(E_OBJECT(client->ec), client->delfn); free(client); client = NULL; } static void _proc_stats_client_del_cb(void *data, void *obj EINA_UNUSED) { Proc_Stats_Client *client = data; _proc_stats_client_remove(client); } static void _proc_stats_client_popup_add(Proc_Stats_Client *client) { Evas_Object *o, *tb; Evas_Object *pb; Evas_Coord ox, oy, ow, oh; evas_object_geometry_get(client->obj, &ox, &oy, &ow, &oh); client->popup = o = elm_ctxpopup_add(e_comp->elm); E_FILL(o); E_EXPAND(o); elm_object_style_set(o, "noblock"); evas_object_layer_set(o, E_LAYER_MENU); tb = elm_table_add(o); E_FILL(tb); E_EXPAND(tb); elm_object_content_set(o, tb); evas_object_show(tb); pb = elm_progressbar_add(o); elm_progressbar_span_size_set(pb, 140); E_FILL(pb); E_EXPAND(pb); elm_table_pack(tb, pb, 0, 0, 1, 1); evas_object_data_set(o, "pb_cpu", pb); evas_object_show(pb); pb = elm_progressbar_add(o); elm_progressbar_span_size_set(pb, 140); E_FILL(pb); E_EXPAND(pb); elm_table_pack(tb, pb, 0, 1, 1, 1); evas_object_data_set(o, "pb_mem", pb); evas_object_show(pb); _proc_stats_client_display_update(client); if (client->ec->maximized) { elm_ctxpopup_direction_priority_set(o, ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_UP, ELM_CTXPOPUP_DIRECTION_LEFT, ELM_CTXPOPUP_DIRECTION_RIGHT); evas_object_move(o, ox + (ow / 2), oy + ow); } else { elm_ctxpopup_direction_priority_set(o, ELM_CTXPOPUP_DIRECTION_UP, ELM_CTXPOPUP_DIRECTION_DOWN, ELM_CTXPOPUP_DIRECTION_LEFT, ELM_CTXPOPUP_DIRECTION_RIGHT); evas_object_move(o, ox + (ow / 2), oy); } evas_object_show(o); } static void _proc_stats_client_popup_del(Proc_Stats_Client *client) { if (!client) return; if (client->popup) evas_object_del(client->popup); client->popup = NULL; } static void _proc_stats_client_resize_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Proc_Stats_Client *client; client = data; if ((!client) || (!client->popup)) return; if (client->was_maximized != client->ec->maximized) { _proc_stats_client_popup_del(client); _proc_stats_client_popup_add(client); } client->was_maximized = client->ec->maximized; } static void _proc_stats_client_move_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Proc_Stats_Client *client; Evas_Coord ox, oy, ow, oh; client = data; if ((!client) || (!client->popup)) return; evas_object_geometry_get(client->obj, &ox, &oy, &ow, &oh); evas_object_move(client->popup, ox + (ow / 2), oy); if ((client->ec->hidden) || (client->ec->iconic)) evas_object_hide(client->popup); else evas_object_show(client->popup); } static void _proc_stats_icon_clicked_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Up *ev; Proc_Stats_Client *client; ev = event_info; client = data; if (ev->button != 1) return; if (!client) return; if (client->popup) { _proc_stats_client_popup_del(client); return; } _proc_stats_client_popup_add(client); } static void _proc_stats_client_add(E_Client *ec) { Evas_Object *o; Proc_Stats_Client *client; char buf[PATH_MAX]; Proc_Stats_Module *module = _this_module; if (ec->internal || ec->netwm.pid == -1) return; if (!ec->frame_object) return; if (_proc_stats_client_exists(ec)) return; if (!edje_object_part_exists(ec->frame_object, "e.procstats.swallow")) return; client = calloc(1, sizeof(Proc_Stats_Client)); EINA_SAFETY_ON_NULL_RETURN(client); o = edje_object_add(e_comp->evas); if (!e_theme_edje_object_set(o, "base/theme/modules/procstats", "e/modules/procstats/border")) { evas_object_del(o); snprintf(buf, sizeof(buf), "%s/e-module-procstats.edj", e_module_dir_get(module->e_module)); o = elm_icon_add(e_comp->elm); elm_image_file_set(o, buf, "icon"); } evas_object_size_hint_min_set(o, ELM_SCALE_SIZE(16), ELM_SCALE_SIZE(16)); evas_object_size_hint_max_set(o, ELM_SCALE_SIZE(16), ELM_SCALE_SIZE(16)); E_FILL(o); E_EXPAND(o); client->obj = o; client->frame_obj = ec->frame_object; client->pid = ec->netwm.pid; client->ec = ec; edje_object_part_swallow(ec->frame_object, "e.procstats.swallow", client->obj); evas_object_show(client->obj); edje_object_signal_emit(ec->frame_object, "e,state,procstats,on", "e"); client->delfn = e_object_delfn_add(E_OBJECT(ec), _proc_stats_client_del_cb, client); evas_object_event_callback_add(client->obj, EVAS_CALLBACK_MOVE, _proc_stats_client_move_cb, client); evas_object_event_callback_add(client->obj, EVAS_CALLBACK_MOUSE_UP, _proc_stats_icon_clicked_cb, client); evas_object_event_callback_add(client->frame_obj, EVAS_CALLBACK_RESIZE, _proc_stats_client_resize_cb, client); module->clients = eina_list_append(module->clients, client); } static void _proc_stats_client_remove(Proc_Stats_Client *client) { Eina_List *l, *l_next; Proc_Stats_Client *it; Proc_Stats_Module *module = _this_module; EINA_LIST_FOREACH_SAFE(module->clients, l, l_next, it) { if (it == client) { _proc_stats_client_del(client); module->clients = eina_list_remove_list(module->clients, l); return; } } } static Eina_Bool _proc_stats_client_gone(Proc_Stats_Client *client) { Eina_List *l; E_Client *ec; EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (client->ec == ec) return 0; } return 1; } static void _proc_stats_client_children_update(Eina_List *children, Proc_Stats_Client *client) { Eina_List *l; Proc_Info *child; EINA_LIST_FOREACH(children, l, child) { client->mem_size += child->mem_size; client->cpu_time += child->cpu_time; if (child->children) _proc_stats_client_children_update(child->children, client); } } static char * _size_format(unsigned long long bytes) { char buf[1024]; unsigned long powi = 1; unsigned long long value; unsigned int precision = 2, powj = 1; int i = 0; const char *units[8] = { _("B"), _("KiB"), _("MiB"), _("GiB"), _("TiB"), _("PiB"), _("EiB"), _("ZiB"), }; value = bytes; while (value > 1024) { if ((value / 1024) < powi) break; powi *= 1024; ++i; if (i == 7) break; } if (!i) precision = 0; while (precision > 0) { powj *= 10; if ((value / powi) < powj) break; --precision; } snprintf(buf, sizeof(buf), "%1.*f%s", precision, (double) value / powi, units[i]); return strdup(buf); } static void _proc_stats_client_display_update(Proc_Stats_Client *client) { Edje_Message_Int_Set *msg; Evas_Object *pb; Eina_Strbuf *buf; char *s; double val = 0.0; if (client->cpu_time_prev > client->cpu_time) return; if (!client->cpu_time_prev) client->cpu_time_prev = client->cpu_time; msg = malloc(sizeof(Edje_Message_Int_Set) + (sizeof(int) * 4)); EINA_SAFETY_ON_NULL_RETURN(msg); msg->count = 5; msg->val[0] = eina_cpu_count(); msg->val[1] = (client->cpu_time - client->cpu_time_prev) / POLL_INTERVAL; msg->val[2] = (int) (_mem_total / 4096); msg->val[3] = (int) (client->mem_size / 4096); msg->val[4] = 0; edje_object_message_send(client->frame_obj, EDJE_MESSAGE_INT_SET, 1973, msg); edje_object_message_send(client->obj, EDJE_MESSAGE_INT_SET, 1973, msg); free(msg); if (!client->popup) return; pb = evas_object_data_get(client->popup, "pb_cpu"); val = ((client->cpu_time - client->cpu_time_prev) / POLL_INTERVAL); elm_progressbar_value_set(pb, val / 100.0); buf = eina_strbuf_new(); eina_strbuf_append_printf(buf, "%1.0f%%", val); elm_object_part_text_set(pb, "elm.text.status", eina_strbuf_string_get(buf)); eina_strbuf_reset(buf); pb = evas_object_data_get(client->popup, "pb_mem"); val = client->mem_size / (_mem_total / 100.0); elm_progressbar_value_set(pb, val / 100.0); s = _size_format(client->mem_size); eina_strbuf_append_printf(buf, "%s / ", s); free(s); s = _size_format(_mem_total); eina_strbuf_append(buf, s); free(s); elm_object_part_text_set(pb, "elm.text.status", eina_strbuf_string_get(buf)); eina_strbuf_free(buf); } static void _proc_stats_client_update(Eina_List *procs, Proc_Stats_Client *client) { Proc_Info *proc; Eina_List *l; EINA_LIST_FOREACH(procs, l, proc) { if (proc->pid == client->pid) { client->cpu_time = client->mem_size = 0; client->mem_size += proc->mem_size; client->cpu_time += proc->cpu_time; _proc_stats_client_children_update(proc->children, client); break; } } _proc_stats_client_display_update(client); client->cpu_time_prev = client->cpu_time; } static void _proc_stats_thread_feedback_cb(void *data, Ecore_Thread *thread EINA_UNUSED, void *msgdata) { Proc_Stats_Module *module; Eina_List *procs, *l; E_Client *ec; Proc_Info *proc; Proc_Stats_Client *client; module = data; procs = msgdata; EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (!_proc_stats_client_exists(ec)) _proc_stats_client_add(ec); } EINA_LIST_FOREACH(module->clients, l, client) { if (_proc_stats_client_gone(client)) _proc_stats_client_remove(client); else _proc_stats_client_update(procs, client); } EINA_LIST_FREE(procs, proc) proc_info_free(proc); } static void _proc_stats_thread(void *data, Ecore_Thread *thread) { Proc_Stats_Module *module = data; while (!ecore_thread_check(thread)) { Eina_List *procs = proc_info_all_children_get(); ecore_thread_feedback(thread, procs); for (int i = 0; i < 8 * module->poll_interval; i++) { if (ecore_thread_check(thread)) return; usleep(125000); // e_powersave_sleeper_sleep(module->sleeper, module->poll_interval, EINA_TRUE); } } } E_API E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Procstats" }; E_API int e_modapi_init(E_Module *m) { Proc_Stats_Module *module; if (!_memory_total()) return 0; _this_module = module = calloc(1, sizeof(Proc_Stats_Module)); EINA_SAFETY_ON_NULL_RETURN_VAL(module, 0); module->e_module = m; //module->sleeper = e_powersave_sleeper_new(); module->poll_interval = POLL_INTERVAL; _proc_stats_handler_fullscreen = ecore_event_handler_add (E_EVENT_CLIENT_FULLSCREEN, _proc_stats_handler_fullscreen_check_cb, NULL); _proc_stats_thread_feedback_cb(module, NULL, proc_info_all_children_get()); module->thread = ecore_thread_feedback_run(_proc_stats_thread, _proc_stats_thread_feedback_cb, NULL, NULL, module, 1); return 1; } E_API int e_modapi_shutdown(E_Module *m EINA_UNUSED) { Proc_Stats_Client *client; Proc_Stats_Module *module = _this_module; ecore_thread_cancel(module->thread); ecore_thread_wait(module->thread, 0.5); //e_powersave_sleeper_free(module->sleeper); ecore_event_handler_del(_proc_stats_handler_fullscreen); _proc_stats_handler_fullscreen = NULL; EINA_LIST_FREE(module->clients, client) _proc_stats_client_del(client); free(module); _this_module = NULL; return 1; } E_API int e_modapi_save(E_Module *m EINA_UNUSED) { return 1; }