commit de2818a3947eb9bdc7e7a294db1eb253cfb370eb Author: Alastair Poole Date: Mon Jun 4 11:11:21 2018 +0100 First commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..de6c88e --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ + + Copyright (c) 2018, Al Poole + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1ec6db --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# esysinfo +System Information (EFL) + +This is a process monitor. + +Currently have full engines for Linux, FreeBSD, OpenBSD and MacOS. + diff --git a/makefile b/makefile new file mode 100644 index 0000000..845aac0 --- /dev/null +++ b/makefile @@ -0,0 +1,22 @@ +LIBS= + +OSNAME := $(shell uname -s) + +ifeq ($(OSNAME), OpenBSD) + LIBS += -lkvm + LDFLAGS += -I/usr/local/include -L/usr/local/lib -L/usr/X11R6/lib +endif + +export CFLAGS = -g -ggdb3 -O + +export PKGS = eina elementary + +export LIBS + +export LDFLAGS + +default: + $(MAKE) -C src + +clean: + $(MAKE) -C src clean diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..49ccd1b --- /dev/null +++ b/src/main.c @@ -0,0 +1,59 @@ +/* Copyright 2018. Alastair Poole + See LICENSE file for details. +*/ + +#include "process.h" +#include "system.h" +#include "ui.h" + +static void +_win_del_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + evas_object_del(obj); + ecore_main_loop_quit(); +} + +static Evas_Object * +_win_add(void) +{ + Evas_Object *win, *icon; + + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + + win = elm_win_util_standard_add("esysinfo", "esysinfo"); + icon = elm_icon_add(win); + elm_icon_standard_set(icon, "system-preferences"); + elm_win_icon_object_set(win, icon); + + evas_object_resize(win, 768 * elm_config_scale_get(), 420 * elm_config_scale_get()); + evas_object_smart_callback_add(win, "delete,request", _win_del_cb, NULL); + + elm_win_title_set(win, "System Information"); + + return win; +} + +int +main(int argc, char **argv) +{ + Evas_Object *win; + + eina_init(); + ecore_init(); + elm_init(argc, argv); + + win = _win_add(); + ui_add(win); + + elm_win_center(win, EINA_TRUE, EINA_TRUE); + evas_object_show(win); + + ecore_main_loop_begin(); + + eina_shutdown(); + ecore_shutdown(); + elm_shutdown(); + + return 0; +} + diff --git a/src/makefile b/src/makefile new file mode 100644 index 0000000..1bb054a --- /dev/null +++ b/src/makefile @@ -0,0 +1,24 @@ +TARGET = ../esysinfo + +OBJECTS = system.o process.o ui.o main.o + +default: $(TARGET) + +$(TARGET) : $(OBJECTS) + $(CC) $(OBJECTS) $(shell pkg-config --libs $(PKGS)) $(LIBS) $(LDFLAGS) -o $@ + +main.o: main.c + $(CC) -c $(CFLAGS) $(shell pkg-config --cflags $(PKGS)) main.c -o $@ + +system.o: system.c + $(CC) -c $(CFLAGS) system.c -o $@ + +process.o: process.c + $(CC) -c $(CFLAGS) $(shell pkg-config --cflags $(PKGS)) process.c -o $@ + +ui.o: ui.c + $(CC) -c $(CFLAGS) $(shell pkg-config --cflags $(PKGS)) ui.c -o $@ + +clean: + -rm $(OBJECTS) + -rm $(TARGET) diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..ab1d4a8 --- /dev/null +++ b/src/process.c @@ -0,0 +1,568 @@ +#if defined(__MACH__) && defined(__APPLE__) +# define __MacOS__ +#endif + +#if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +# include +# include +# include +# include +#endif + +#if defined(__OpenBSD__) +# include +# include +# include +# include +# include +#endif + +#if defined(__MacOS__) +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "process.h" +#include +#include +#include + +static const char * +_process_state_name(char state) +{ + const char *statename = NULL; +#if defined(__linux__) + + switch (state) + { + case 'D': + statename = "DSLEEP"; + break; + + case 'I': + statename = "IDLE"; + break; + + case 'R': + statename = "RUN"; + break; + + case 'S': + statename = "SLEEP"; + break; + + case 'T': + case 't': + statename = "STOP"; + break; + + case 'X': + statename = "DEAD"; + break; + + case 'Z': + statename = "ZOMB"; + break; + } +#else + switch (state) + { + case SIDL: + statename = "IDLE"; + break; + + case SRUN: + statename = "RUN"; + break; + + case SSLEEP: + statename = "SLEEP"; + break; + + case SSTOP: + statename = "STOP"; + break; + +#if !defined(__MacOS__) +#if !defined(__OpenBSD__) + case SWAIT: + statename = "WAIT"; + break; + + case SLOCK: + statename = "LOCK"; + break; + +#endif + case SZOMB: + statename = "ZOMB"; + break; + +#endif +#if defined(__OpenBSD__) + case SDEAD: + statename = "DEAD"; + break; + + case SONPROC: + statename = "ONPROC"; + break; +#endif + } +#endif + return statename; +} + +#if defined(__linux__) + +static unsigned long +_parse_line(const char *line) +{ + char *p, *tok; + + p = strchr(line, ':') + 1; + while (isspace(*p)) + p++; + tok = strtok(p, " "); + + return atol(tok); +} + +static Eina_List * +_process_list_linux_get(void) +{ + char *name; + Eina_List *files, *list = NULL; + FILE *f; + char path[PATH_MAX], line[4096], program_name[1024], state; + int pid, res, utime, stime, cutime, cstime, uid, psr, pri, nice, numthreads; + unsigned int mem_size, mem_rss; + + int pagesize = getpagesize(); + + files = ecore_file_ls("/proc"); + EINA_LIST_FREE(files, name) + { + pid = atoi(name); + free(name); + + if (!pid) continue; + + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + + f = fopen(path, "r"); + if (!f) continue; + + if (fgets(line, sizeof(line), f)) + { + int dummy; + char *end, *start = strchr(line, '(') + 1; + end = strchr(line, ')'); + strncpy(program_name, start, end - start); + program_name[end - start] = '\0'; + + res = sscanf(end + 2, "%c %d %d %d %d %d %u %u %u %u %u %d %d %d %d %d %d %u %u %d %u %u %u %u %u %u %u %u %d %d %d %d %u %d %d %d %d %d %d %d %d %d", + &state, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &utime, &stime, &cutime, &cstime, + &pri, &nice, &numthreads, &dummy, &dummy, &mem_size, &mem_rss, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &psr, &dummy, &dummy, &dummy, &dummy, &dummy); + } + + fclose(f); + + if (res != 42) continue; + + snprintf(path, sizeof(path), "/proc/%d/status", pid); + + f = fopen(path, "r"); + if (!f) continue; + + while ((fgets(line, sizeof(line), f)) != NULL) + { + if (!strncmp(line, "Uid:", 4)) + { + uid = _parse_line(line); + break; + } + } + + fclose(f); + + Proc_Stats *p = calloc(1, sizeof(Proc_Stats)); + + p->pid = pid; + p->uid = uid; + p->cpu_id = psr; + snprintf(p->command, sizeof(p->command), "%s", program_name); + p->state = _process_state_name(state); + p->cpu_time = utime + stime; + p->mem_size = mem_size; + p->mem_rss = mem_rss * pagesize; + p->nice = nice; + p->priority = pri; + p->numthreads = numthreads; + + list = eina_list_append(list, p); + } + + if (files) + eina_list_free(files); + + return list; +} + +Proc_Stats * +proc_info_by_pid(int pid) +{ + FILE *f; + char path[PATH_MAX]; + char line[4096]; + char state, program_name[1024]; + int res, dummy, utime, stime, cutime, cstime, uid, psr; + unsigned int mem_size, mem_rss, pri, nice, numthreads; + + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + if (!ecore_file_exists(path)) + return NULL; + + f = fopen(path, "r"); + if (!f) return NULL; + + if (fgets(line, sizeof(line), f)) + { + char *end, *start = strchr(line, '(') + 1; + end = strchr(line, ')'); + strncpy(program_name, start, end - start); + program_name[end - start] = '\0'; + + res = sscanf(end + 2, "%c %d %d %d %d %d %u %u %u %u %u %d %d %d %d %d %d %u %u %d %u %u %u %u %u %u %u %u %d %d %d %d %u %d %d %d %d %d %d %d %d %d", + &state, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &utime, &stime, &cutime, &cstime, + &pri, &nice, &numthreads, &dummy, &dummy, &mem_size, &mem_rss, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &psr, &dummy, &dummy, &dummy, &dummy, &dummy); + } + fclose(f); + + if (res != 42) return NULL; + + snprintf(path, sizeof(path), "/proc/%d/status", pid); + + f = fopen(path, "r"); + if (!f) return NULL; + + while ((fgets(line, sizeof(line), f)) != NULL) + { + if (!strncmp(line, "Uid:", 4)) + { + uid = _parse_line(line); + break; + } + } + fclose(f); + + Proc_Stats *p = calloc(1, sizeof(Proc_Stats)); + p->pid = pid; + p->uid = uid; + p->cpu_id = psr; + snprintf(p->command, sizeof(p->command), "%s", program_name); + p->state = _process_state_name(state); + p->cpu_time = utime + stime; + p->mem_size = mem_size; + p->mem_rss = mem_rss * getpagesize(); + p->priority = pri; + p->nice = nice; + p->numthreads = numthreads; + + return p; +} + +#endif + +#if defined(__OpenBSD__) + +Proc_Stats * +proc_info_by_pid(int pid) +{ + struct kinfo_proc *kp; + kvm_t *kern; + char errbuf[4096]; + int count, pagesize, pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kern) return NULL; + + kp = kvm_getprocs(kern, KERN_PROC_PID, pid, sizeof(*kp), &count); + if (!kp) return NULL; + + if (count == 0) return NULL; + pagesize = getpagesize(); + + Proc_Stats *p = malloc(sizeof(Proc_Stats)); + p->pid = kp->p_pid; + p->uid = kp->p_uid; + p->cpu_id = kp->p_cpuid; + snprintf(p->command, sizeof(p->command), "%s", kp->p_comm); + p->state = _process_state_name(kp->p_stat); + p->cpu_time = kp->p_uticks + kp->p_sticks + kp->p_iticks; + p->mem_size = (kp->p_vm_tsize * pagesize) + (kp->p_vm_dsize * pagesize) + (kp->p_vm_ssize * pagesize); + p->mem_rss = kp->p_vm_rssize * pagesize; + p->priority = kp->p_priority - PZERO; + p->nice = kp->p_nice - NZERO; + p->numthreads = -1; + + kp = kvm_getprocs(kern, KERN_PROC_SHOW_THREADS, 0, sizeof(*kp), &pid_count); + + for (int i = 0; i < pid_count; i++) + { + if (kp[i].p_pid == p->pid) + p->numthreads++; + } + + kvm_close(kern); + + return p; +} + +static Eina_List * +_process_list_openbsd_get(void) +{ + struct kinfo_proc *kp; + Proc_Stats *p; + char errbuf[4096]; + kvm_t *kern; + int pid_count, pagesize; + Eina_List *l, *list = NULL; + + kern = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kern) return NULL; + + kp = kvm_getprocs(kern, KERN_PROC_ALL, 0, sizeof(*kp), &pid_count); + if (!kp) return NULL; + + pagesize = getpagesize(); + + for (int i = 0; i < pid_count; i++) + { + p = malloc(sizeof(Proc_Stats)); + p->pid = kp[i].p_pid; + p->uid = kp[i].p_uid; + p->cpu_id = kp[i].p_cpuid; + snprintf(p->command, sizeof(p->command), "%s", kp[i].p_comm); + p->state = _process_state_name(kp[i].p_stat); + p->cpu_time = kp[i].p_uticks + kp[i].p_sticks + kp[i].p_iticks; + p->mem_size = (kp[i].p_vm_tsize * pagesize) + (kp[i].p_vm_dsize * pagesize) + (kp[i].p_vm_ssize * pagesize); + p->mem_rss = kp[i].p_vm_rssize * pagesize; + p->priority = kp[i].p_priority - PZERO; + p->nice = kp[i].p_nice - NZERO; + p->numthreads = -1; + list = eina_list_append(list, p); + } + + kp = kvm_getprocs(kern, KERN_PROC_SHOW_THREADS, 0, sizeof(*kp), &pid_count); + + EINA_LIST_FOREACH (list, l, p) + { + for (int i = 0; i < pid_count; i++) + { + if (kp[i].p_pid == p->pid) + p->numthreads++; + } + } + + kvm_close(kern); + + return list; +} + +#endif + +#if defined(__MacOS__) +static Eina_List * +_process_list_macos_get(void) +{ + Eina_List *list = NULL; + + for (int i = 1; i <= PID_MAX; i++) + { + struct proc_taskallinfo taskinfo; + int size = proc_pidinfo(i, PROC_PIDTASKALLINFO, 0, &taskinfo, sizeof(taskinfo)); + if (size != sizeof(taskinfo)) continue; + + Proc_Stats *p = calloc(1, sizeof(Proc_Stats)); + p->pid = i; + p->uid = taskinfo.pbsd.pbi_uid; + p->cpu_id = -1; + snprintf(p->command, sizeof(p->command), "%s", taskinfo.pbsd.pbi_comm); + p->cpu_time = taskinfo.ptinfo.pti_total_user + taskinfo.ptinfo.pti_total_system; + p->cpu_time /= 10000000; + p->state = _process_state_name(taskinfo.pbsd.pbi_status); + p->mem_size = taskinfo.ptinfo.pti_virtual_size; + p->mem_rss = taskinfo.ptinfo.pti_resident_size; + p->priority = taskinfo.ptinfo.pti_priority; + p->nice = taskinfo.pbsd.pbi_nice; + p->numthreads = taskinfo.ptinfo.pti_threadnum; + + list = eina_list_append(list, p); + } + + return list; +} + +Proc_Stats * +proc_info_by_pid(int pid) +{ + struct kinfo_proc kp; + struct proc_taskallinfo taskinfo; + struct proc_workqueueinfo workqueue; + size_t size; + + size = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &taskinfo, sizeof(taskinfo)); + if (size != sizeof(taskinfo)) + return NULL; + + size = proc_pidinfo(pid, PROC_PIDWORKQUEUEINFO, 0, &workqueue, sizeof(workqueue)); + if (size != sizeof(workqueue)) + return NULL; + + Proc_Stats *p = calloc(1, sizeof(Proc_Stats)); + p->pid = pid; + p->uid = taskinfo.pbsd.pbi_uid; + p->cpu_id = workqueue.pwq_nthreads; + snprintf(p->command, sizeof(p->command), "%s", taskinfo.pbsd.pbi_comm); + p->cpu_time = taskinfo.ptinfo.pti_total_user + taskinfo.ptinfo.pti_total_system; + p->cpu_time /= 10000000; + p->state = _process_state_name(taskinfo.pbsd.pbi_status); + p->mem_size = taskinfo.ptinfo.pti_virtual_size; + p->mem_rss = taskinfo.ptinfo.pti_resident_size; + p->priority = taskinfo.ptinfo.pti_priority; + p->nice = taskinfo.pbsd.pbi_nice; + p->numthreads = taskinfo.ptinfo.pti_threadnum; + + return p; +} + +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +static Eina_List * +_process_list_freebsd_get(void) +{ + Eina_List *list; + struct rusage *usage; + struct kinfo_proc kp; + int mib[4]; + size_t len; + int pagesize = getpagesize(); + + list = NULL; + + len = sizeof(int); + if (sysctlnametomib("kern.proc.pid", mib, &len) == -1) + return NULL; + + for (int i = 1; i <= PID_MAX; i++) + { + mib[3] = i; + len = sizeof(kp); + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) + { + continue; + } + + if (kp.ki_flag & P_SYSTEM) + continue; + + Proc_Stats *p = calloc(1, sizeof(Proc_Stats)); + + p->pid = kp.ki_pid; + p->uid = kp.ki_uid; + snprintf(p->command, sizeof(p->command), "%s", kp.ki_comm); + p->cpu_id = kp.ki_oncpu; + if (p->cpu_id == -1) + p->cpu_id = kp.ki_lastcpu; + + usage = &kp.ki_rusage; + + p->cpu_time = (usage->ru_utime.tv_sec * 1000000) + usage->ru_utime.tv_usec + (usage->ru_stime.tv_sec * 1000000) + usage->ru_stime.tv_usec; + p->cpu_time /= 10000; + p->state = _process_state_name(kp.ki_stat); + p->mem_size = kp.ki_size; + p->mem_rss = kp.ki_rssize * pagesize; + p->nice = kp.ki_nice - NZERO; + p->priority = kp.ki_pri.pri_level - PZERO; + p->numthreads = kp.ki_numthreads; + + list = eina_list_append(list, p); + } + + return list; +} + +Proc_Stats * +proc_info_by_pid(int pid) +{ + struct rusage *usage; + struct kinfo_proc kp; + int mib[4]; + size_t len; + int pagesize = getpagesize(); + + len = sizeof(int); + if (sysctlnametomib("kern.proc.pid", mib, &len) == -1) + return NULL; + + mib[3] = pid; + + len = sizeof(kp); + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) + return NULL; + + Proc_Stats *p = calloc(1, sizeof(Proc_Stats)); + p->pid = kp.ki_pid; + p->uid = kp.ki_uid; + snprintf(p->command, sizeof(p->command), "%s", kp.ki_comm); + p->cpu_id = kp.ki_oncpu; + if (p->cpu_id == -1) + p->cpu_id = kp.ki_lastcpu; + + usage = &kp.ki_rusage; + + p->cpu_time = (usage->ru_utime.tv_sec * 1000000) + usage->ru_utime.tv_usec + (usage->ru_stime.tv_sec * 1000000) + usage->ru_stime.tv_usec; + p->cpu_time /= 10000; + p->state = _process_state_name(kp.ki_stat); + p->mem_size = kp.ki_size; + p->mem_rss = kp.ki_rssize * pagesize; + p->nice = kp.ki_nice = NZERO; + p->priority = kp.ki_pri.pri_level - PZERO; + p->numthreads = kp.ki_numthreads; + + return p; +} + +#endif + +Eina_List * +proc_info_all_get(void) +{ + Eina_List *processes; + +#if defined(__linux__) + processes = _process_list_linux_get(); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + processes = _process_list_freebsd_get(); +#elif defined(__MacOS__) + processes = _process_list_macos_get(); +#elif defined(__OpenBSD__) + processes = _process_list_openbsd_get(); +#else + processes = NULL; +#endif + + return processes; +} + diff --git a/src/process.h b/src/process.h new file mode 100644 index 0000000..78c20fa --- /dev/null +++ b/src/process.h @@ -0,0 +1,69 @@ +#ifndef __PROC_H__ +#define __PROC_H__ + +/** + * @file + * @brief Routines for querying processes. + */ + +/** + * @brief Querying Processes + * @defgroup Proc + * + * @{ + * + * Query processes. + * + */ + +#include +#include +#include + +#if !defined(PID_MAX) +# define PID_MAX 99999 +#endif + +#define CMD_NAME_MAX 256 + +typedef struct _Proc_Stats +{ + pid_t pid; + uid_t uid; + int8_t nice; + int8_t priority; + int cpu_id; + int32_t numthreads; + int64_t mem_size; + int64_t mem_rss; + double cpu_usage; + char command[CMD_NAME_MAX]; + const char *state; + + // Not used yet in UI. + long cpu_time; +} Proc_Stats; + +/** + * Query a full list of running processes and return a list. + * + * @return A list of proc_t members for all processes. + */ +Eina_List * +proc_info_all_get(void); + +/** + * Query a process for its current state. + * + * @param pid The process ID to query. + * + * @return A proc_t pointer containing the process information. + */ +Proc_Stats * +proc_info_by_pid(int pid); + +/** + * @} + */ + +#endif diff --git a/src/system.c b/src/system.c new file mode 100644 index 0000000..86b0601 --- /dev/null +++ b/src/system.c @@ -0,0 +1,1184 @@ +/* + Copyright (c) 2017, Al Poole + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +#define __MacOS__ +# include +# include +# include +# include +# include +# include +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) +# include +# include +# include +# include +# include +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +# include +# include +#endif + +#define CPU_STATES 5 + +#define MAX_BATTERIES 5 +#define INVALID_TEMP -999 + +/* Filter requests and results */ +#define RESULTS_CPU 0x01 +#define RESULTS_MEM 0x02 +#define RESULTS_PWR 0x04 +#define RESULTS_TMP 0x08 +#define RESULTS_AUD 0x10 +#define RESULTS_NET 0x20 +#define RESULTS_DEFAULT 0x3f +#define RESULTS_MEM_MB 0x40 +#define RESULTS_MEM_GB 0x80 +#define RESULTS_CPU_CORES 0x100 + +typedef struct +{ + float percent; + unsigned long total; + unsigned long idle; +} cpu_core_t; + +typedef struct +{ + unsigned long total; + unsigned long used; + unsigned long cached; + unsigned long buffered; + unsigned long shared; + unsigned long swap_total; + unsigned long swap_used; +} meminfo_t; + +typedef struct +{ + bool have_ac; + int battery_count; + + double charge_full; + double charge_current; + uint8_t percent; + + char battery_names[256]; + int *bat_mibs[MAX_BATTERIES]; + int ac_mibs[5]; +} power_t; + +typedef struct results_t results_t; +struct results_t +{ + int cpu_count; + cpu_core_t **cores; + + meminfo_t memory; + + power_t power; + + unsigned long incoming; + unsigned long outgoing; + + int temperature; +}; + +static void +_memsize_bytes_to_kb(unsigned long *bytes) +{ + *bytes = (unsigned int)*bytes >> 10; +} + +#define _memsize_kb_to_mb _memsize_bytes_to_kb + +static void +_memsize_kb_to_gb(unsigned long *bytes) +{ + *bytes = (unsigned int)*bytes >> 20; +} + +#if defined(__linux__) +static char * +Fcontents(const char *path) +{ + char *buf; + char byte[1]; + size_t count, bytes = 0; + FILE *f = fopen(path, "r"); + if (!f) return NULL; + + int n = 1024; + + buf = malloc(n * sizeof(byte) + 1); + if (!buf) exit(0 << 1); + + while ((count = (fread(byte, sizeof(byte), 1, f))) > 0) + { + bytes += sizeof(byte); + if (bytes == (n * sizeof(byte))) + { + n *= 2; + char *tmp = realloc(buf, n * sizeof(byte) + 1); + if (!tmp) exit(1 << 1); + buf = tmp; + } + memcpy(&buf[bytes - sizeof(byte)], byte, sizeof(byte)); + } + + if (!feof(f)) exit(2 << 1); + fclose(f); + + buf[bytes] = 0; + + return buf; +} + +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +static long int +_sysctlfromname(const char *name, void *mib, int depth, size_t *len) +{ + long int result; + + if (sysctlnametomib(name, mib, len) < 0) + return -1; + + *len = sizeof(result); + if (sysctl(mib, depth, &result, len, NULL, 0) < 0) + return -1; + + return result; +} + +#endif + +static int +cpu_count(void) +{ + int cores = 0; +#if defined(__linux__) + char buf[4096]; + FILE *f; + int line = 0; + + f = fopen("/proc/stat", "r"); + if (!f) return 0; + + while (fgets(buf, sizeof(buf), f)) + { + if (line) + { + if (!strncmp(buf, "cpu", 3)) + cores++; + else + break; + } + line++; + } + + fclose(f); +#elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) + size_t len; + int mib[2] = { CTL_HW, HW_NCPU }; + + len = sizeof(cores); + if (sysctl(mib, 2, &cores, &len, NULL, 0) < 0) + return 0; +#endif + return cores; +} + +static void +_cpu_state_get(cpu_core_t **cores, int ncpu) +{ + int diff_total, diff_idle; + double ratio, percent; + unsigned long total, idle, used; + cpu_core_t *core; +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) + size_t size; + int i, j; +#endif +#if defined(__FreeBSD__) || defined(__DragonFly__) + if (!ncpu) + return; + size = sizeof(unsigned long) * (CPU_STATES * ncpu); + unsigned long cpu_times[ncpu][CPU_STATES]; + + if (sysctlbyname("kern.cp_times", cpu_times, &size, NULL, 0) < 0) + return; + + for (i = 0; i < ncpu; i++) { + core = cores[i]; + unsigned long *cpu = cpu_times[i]; + + total = 0; + for (j = 0; j < CPU_STATES; j++) + total += cpu[j]; + + idle = cpu[4]; + + diff_total = total - core->total; + diff_idle = idle - core->idle; + if (diff_total == 0) diff_total = 1; + + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } +#elif defined(__OpenBSD__) + unsigned long cpu_times[CPU_STATES]; + if (!ncpu) + return; + if (ncpu == 1) + { + core = cores[0]; + int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME }; + size = CPU_STATES * sizeof(unsigned long); + if (sysctl(cpu_time_mib, 2, &cpu_times, &size, NULL, 0) < 0) + return; + + total = 0; + for (j = 0; j < CPU_STATES; j++) + total += cpu_times[j]; + + idle = cpu_times[4]; + + diff_total = total - core->total; + diff_idle = idle - core->idle; + if (diff_total == 0) diff_total = 1; + + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } + else if (ncpu > 1) + { + for (i = 0; i < ncpu; i++) { + core = cores[i]; + int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME2, 0 }; + size = CPU_STATES * sizeof(unsigned long); + cpu_time_mib[2] = i; + if (sysctl(cpu_time_mib, 3, &cpu_times, &size, NULL, 0) < 0) + return; + + total = 0; + for (j = 0; j < CPU_STATES; j++) + total += cpu_times[j]; + + idle = cpu_times[4]; + + diff_total = total - core->total; + if (diff_total == 0) diff_total = 1; + + diff_idle = idle - core->idle; + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } + } +#elif defined(__linux__) + char *buf, name[128]; + int i; + + buf = Fcontents("/proc/stat"); + if (!buf) return; + + for (i = 0; i < ncpu; i++) { + core = cores[i]; + snprintf(name, sizeof(name), "cpu%d", i); + char *line = strstr(buf, name); + if (line) + { + line = strchr(line, ' ') + 1; + unsigned long cpu_times[4] = { 0 }; + + if (4 != sscanf(line, "%lu %lu %lu %lu", &cpu_times[0], &cpu_times[1], &cpu_times[2], &cpu_times[3])) + return; + + total = cpu_times[0] + cpu_times[1] + cpu_times[2] + cpu_times[3]; + idle = cpu_times[3]; + diff_total = total - core->total; + if (diff_total == 0) diff_total = 1; + + diff_idle = idle - core->idle; + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } + } + free(buf); +#elif defined(__MacOS__) + mach_msg_type_number_t count; + processor_cpu_load_info_t load; + mach_port_t mach_port; + unsigned int cpu_count; + int i; + + cpu_count = ncpu; + + count = HOST_CPU_LOAD_INFO_COUNT; + mach_port = mach_host_self(); + if (host_processor_info(mach_port, PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)&load, &count) != KERN_SUCCESS) + exit(4 << 1); + + for (i = 0; i < ncpu; i++) { + core = cores[i]; + + total = load[i].cpu_ticks[CPU_STATE_USER] + load[i].cpu_ticks[CPU_STATE_SYSTEM] + load[i].cpu_ticks[CPU_STATE_IDLE] + load[i].cpu_ticks[CPU_STATE_NICE]; + idle = load[i].cpu_ticks[CPU_STATE_IDLE]; + + diff_total = total - core->total; + if (diff_total == 0) diff_total = 1; + diff_idle = idle - core->idle; + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } +#endif +} + +static cpu_core_t ** +_cpu_cores_state_get(int *ncpu) +{ + cpu_core_t **cores; + int i; + + *ncpu = cpu_count(); + + cores = malloc((*ncpu) * sizeof(cpu_core_t *)); + + for (i = 0; i < *ncpu; i++) + cores[i] = calloc(1, sizeof(cpu_core_t)); + + _cpu_state_get(cores, *ncpu); + usleep(1000000); + _cpu_state_get(cores, *ncpu); + + return cores; +} + +#if defined(__linux__) +static unsigned long +_meminfo_parse_line(const char *line) +{ + char *p, *tok; + + p = strchr(line, ':') + 1; + while (isspace(*p)) + p++; + tok = strtok(p, " "); + + return atol(tok); +} + +#endif + +static void +_memory_usage_get(meminfo_t *memory) +{ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) + size_t len = 0; + int i = 0; +#endif + memset(memory, 0, sizeof(meminfo_t)); +#if defined(__linux__) + FILE *f; + unsigned long swap_free = 0, tmp_free = 0, tmp_slab = 0; + char line[256]; + int fields = 0; + + f = fopen("/proc/meminfo", "r"); + if (!f) return; + + while (fgets(line, sizeof(line), f) != NULL) + { + if (!strncmp("MemTotal:", line, 9)) + { + memory->total = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("MemFree:", line, 8)) + { + tmp_free = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Cached:", line, 7)) + { + memory->cached = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Slab:", line, 5)) + { + tmp_slab = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Buffers:", line, 8)) + { + memory->buffered = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Shmem:", line, 6)) + { + memory->shared = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("SwapTotal:", line, 10)) + { + memory->swap_total = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("SwapFree:", line, 9)) + { + swap_free = _meminfo_parse_line(line); + fields++; + } + + if (fields >= 8) + break; + } + + memory->cached += tmp_slab; + memory->used = memory->total - tmp_free - memory->cached - memory->buffered; + memory->swap_used = memory->swap_total = swap_free; + + fclose(f); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + int total_pages = 0, free_pages = 0, inactive_pages = 0; + long int result = 0; + int page_size = getpagesize(); + int mib[4] = { CTL_HW, HW_PHYSMEM, 0, 0 }; + + len = sizeof(memory->total); + if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1) + return; + memory->total /= 1024; + + total_pages = + _sysctlfromname("vm.stats.vm.v_page_count", mib, 4, &len); + if (total_pages < 0) + return; + + free_pages = _sysctlfromname("vm.stats.vm.v_free_count", mib, 4, &len); + if (free_pages < 0) + return; + + inactive_pages = + _sysctlfromname("vm.stats.vm.v_inactive_count", mib, 4, &len); + if (inactive_pages < 0) + return; + + memory->used = (total_pages - free_pages - inactive_pages) * page_size; + _memsize_bytes_to_kb(&memory->used); + + result = _sysctlfromname("vfs.bufspace", mib, 2, &len); + if (result < 0) + return; + memory->buffered = (result); + _memsize_bytes_to_kb(&memory->buffered); + + result = _sysctlfromname("vm.stats.vm.v_active_count", mib, 4, &len); + if (result < 0) + return; + memory->cached = (result * page_size); + _memsize_bytes_to_kb(&memory->cached); + + result = _sysctlfromname("vm.stats.vm.v_cache_count", mib, 4, &len); + if (result < 0) + return; + memory->shared = (result * page_size); + _memsize_bytes_to_kb(&memory->shared); + + result = _sysctlfromname("vm.swap_total", mib, 2, &len); + if (result < 0) + return; + memory->swap_total = (result / 1024); + + struct xswdev xsw; + /* previous mib is important for this one... */ + + while (i++) + { + mib[2] = i; + len = sizeof(xsw); + if (sysctl(mib, 3, &xsw, &len, NULL, 0) == -1) + break; + + memory->swap_used += xsw.xsw_used * page_size; + } + + memory->swap_used >>= 10; + +#elif defined(__OpenBSD__) + static int mib[] = { CTL_HW, HW_PHYSMEM64 }; + static int bcstats_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; + struct bcachestats bcstats; + static int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; + struct uvmexp uvmexp; + int nswap, rnswap; + struct swapent *swdev = NULL; + + len = sizeof(memory->total); + if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1) + return; + + len = sizeof(uvmexp); + if (sysctl(uvmexp_mib, 2, &uvmexp, &len, NULL, 0) == -1) + return; + + len = sizeof(bcstats); + if (sysctl(bcstats_mib, 3, &bcstats, &len, NULL, 0) == -1) + return; + + /* Don't fail if there's not swap! */ + nswap = swapctl(SWAP_NSWAP, 0, 0); + if (nswap == 0) + goto swap_out; + + swdev = calloc(nswap, sizeof(*swdev)); + if (swdev == NULL) + goto swap_out; + + rnswap = swapctl(SWAP_STATS, swdev, nswap); + if (rnswap == -1) + goto swap_out; + + for (i = 0; i < nswap; i++) { + if (swdev[i].se_flags & SWF_ENABLE) + { + memory->swap_used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); + memory->swap_total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); + } + } +swap_out: + if (swdev) + free(swdev); + + memory->total /= 1024; + + memory->cached = (uvmexp.pagesize * bcstats.numbufpages); + _memsize_bytes_to_kb(&memory->cached); + + memory->used = (uvmexp.active * uvmexp.pagesize); + _memsize_bytes_to_kb(&memory->used); + + memory->buffered = (uvmexp.pagesize * (uvmexp.npages - uvmexp.free)); + _memsize_bytes_to_kb(&memory->buffered); + + memory->shared = (uvmexp.pagesize * uvmexp.wired); + _memsize_bytes_to_kb(&memory->shared); +#elif defined(__MacOS__) + int mib[2] = { CTL_HW, HW_MEMSIZE }; + size_t total; + vm_size_t page_size; + mach_port_t mach_port; + mach_msg_type_number_t count; + vm_statistics64_data_t vm_stats; + struct xsw_usage xsu; + + size_t len = sizeof(size_t); + if (sysctl(mib, 2, &total, &len, NULL, 0) == -1) + return; + mach_port = mach_host_self(); + count = sizeof(vm_stats) / sizeof(natural_t); + + total >>= 10; + memory->total = total; + + if (host_page_size(mach_port, &page_size) == KERN_SUCCESS && + host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t)&vm_stats, &count) == KERN_SUCCESS) + { + memory->used = vm_stats.active_count + vm_stats.inactive_count + vm_stats.wire_count * page_size; + memory->used >>= 10; + memory->cached = vm_stats.active_count * page_size; + memory->cached >>= 10; + memory->shared = vm_stats.wire_count * page_size; + memory->shared >>= 10; + memory->buffered = vm_stats.inactive_count * page_size; + memory->buffered >>= 10; + } + + total = sizeof(xsu); + if (sysctlbyname("vm.swapusage", &xsu, &total, NULL, 0) != -1) + { + memory->swap_total = xsu.xsu_total; + memory->swap_used = xsu.xsu_used; + } +#endif +} + +static void +_temperature_cpu_get(int *temperature) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + int mibs[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; + int devn, numt; + struct sensor snsr; + size_t slen = sizeof(struct sensor); + struct sensordev snsrdev; + size_t sdlen = sizeof(struct sensordev); + + for (devn = 0;; devn++) { + mibs[2] = devn; + + if (sysctl(mibs, 3, &snsrdev, &sdlen, NULL, 0) == -1) + { + if (errno == ENOENT) + break; + else + continue; + } + if (!strcmp("cpu0", snsrdev.xname)) + break; + else if (!strcmp("km0", snsrdev.xname)) + break; + } + + for (numt = 0; numt < snsrdev.maxnumt[SENSOR_TEMP]; numt++) { + mibs[4] = numt; + + if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) == -1) + continue; + + if (slen > 0 && (snsr.flags & SENSOR_FINVALID) == 0) + break; + } + + if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) + != -1) + { + *temperature = (snsr.value - 273150000) / 1000000.0; + } + else + *temperature = INVALID_TEMP; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + unsigned int value; + size_t len = sizeof(value); + if ((sysctlbyname + ("hw.acpi.thermal.tz0.temperature", &value, &len, NULL, + 0)) != -1) + { + *temperature = (value - 2732) / 10; + } + else + *temperature = INVALID_TEMP; +#elif defined(__linux__) + struct dirent *dh; + DIR *dir; + char path[PATH_MAX]; + + *temperature = INVALID_TEMP; + + dir = opendir("/sys/class/thermal"); + if (!dir) return; + + while ((dh = readdir(dir)) != NULL) + { + if (!strncmp(dh->d_name, "thermal_zone", 12)) + { + snprintf(path, sizeof(path), "/sys/class/thermal/%s/type", dh->d_name); + char *type = Fcontents(path); + if (type) + { + /* This should ensure we get the highest available core temperature */ + if (strstr(type, "_pkg_temp")) + { + snprintf(path, sizeof(path), "/sys/class/thermal/%s/temp", dh->d_name); + char *value = Fcontents(path); + if (value) + { + *temperature = atoi(value) / 1000; + free(value); + free(type); + break; + } + } + free(type); + } + } + } + + closedir(dir); +#elif defined(__MacOS__) + *temperature = INVALID_TEMP; +#endif +} + +static int +_power_battery_count_get(power_t *power) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + struct sensordev snsrdev; + size_t sdlen = sizeof(struct sensordev); + int mib[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; + int i, devn; + for (devn = 0;; devn++) { + mib[2] = devn; + if (sysctl(mib, 3, &snsrdev, &sdlen, NULL, 0) == -1) + { + if (errno == ENXIO) + continue; + if (errno == ENOENT) + break; + } + + for (i = 0; i < MAX_BATTERIES; i++) { + char buf[64]; + snprintf(buf, sizeof(buf), "acpibat%d", i); + if (!strcmp(buf, snsrdev.xname)) + { + power->bat_mibs[power->battery_count] = + malloc(sizeof(int) * 5); + int *tmp = power->bat_mibs[power->battery_count++]; + tmp[0] = mib[0]; + tmp[1] = mib[1]; + tmp[2] = mib[2]; + } + } + + if (!strcmp("acpiac0", snsrdev.xname)) + { + power->ac_mibs[0] = mib[0]; + power->ac_mibs[1] = mib[1]; + power->ac_mibs[2] = mib[2]; + } + } +#elif defined(__FreeBSD__) || defined(__DragonFly__) + size_t len; + if ((sysctlbyname("hw.acpi.battery.life", NULL, &len, NULL, 0)) != -1) + { + power->bat_mibs[power->battery_count] = malloc(sizeof(int) * 5); + sysctlnametomib("hw.acpi.battery.life", + power->bat_mibs[power->battery_count], &len); + power->battery_count = 1; + } + + if ((sysctlbyname("hw.acpi.acline", NULL, &len, NULL, 0)) != -1) + { + sysctlnametomib("hw.acpi.acline", power->ac_mibs, &len); + } +#elif defined(__linux__) + struct dirent *dh; + DIR *dir; + + dir = opendir("/sys/class/power_supply"); + if (!dir) return 0; + + while ((dh = readdir(dir)) != NULL) + { + if (dh->d_name[0] == '.') continue; + if (!strncmp(dh->d_name, "BAT", 3)) + { + power->battery_names[power->battery_count++] = (char)dh->d_name[3]; + } + } + + closedir(dir); +#endif + return power->battery_count; +} + +static void +_battery_state_get(power_t *power, int *mib) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + double charge_full = 0; + double charge_current = 0; + size_t slen = sizeof(struct sensor); + struct sensor snsr; + + mib[3] = 7; + mib[4] = 0; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_full = (double)snsr.value; + + mib[3] = 7; + mib[4] = 3; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_current = (double)snsr.value; + + /* ACPI bug workaround... */ + if (charge_current == 0 || charge_full == 0) + { + mib[3] = 8; + mib[4] = 0; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_full = (double)snsr.value; + + mib[3] = 8; + mib[4] = 3; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_current = (double)snsr.value; + } + + power->charge_full += charge_full; + power->charge_current += charge_current; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + unsigned int value; + size_t len = sizeof(value); + if ((sysctl(mib, 4, &value, &len, NULL, 0)) != -1) + power->percent = value; +#elif defined(__linux__) + char path[PATH_MAX]; + struct dirent *dh; + DIR *dir; + char *buf, *naming = NULL; + int i = 0; + unsigned long charge_full = 0; + unsigned long charge_current = 0; + + while (power->battery_names[i] != '\0') + { + snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c", power->battery_names[i]); + dir = opendir(path); + if (!dir) return; + while ((dh = readdir(dir)) != NULL) + { + if (!strcmp(dh->d_name, "energy_full")) + { + naming = "energy"; break; + } + else if (!strcmp(dh->d_name, "capacity_full")) + { + naming = "capacity"; break; + } + } + closedir(dir); + if (!naming) continue; + snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c/%s_full", power->battery_names[i], naming); + buf = Fcontents(path); + if (buf) + { + charge_full = atol(buf); + free(buf); + } + snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c/%s_now", power->battery_names[i], naming); + buf = Fcontents(path); + if (buf) + { + charge_current = atol(buf); + free(buf); + } + power->charge_full += charge_full; + power->charge_current += charge_current; + naming = NULL; + i++; + } +#endif +} + +static void +_power_state_get(power_t *power) +{ + int i; +#if defined(__OpenBSD__) || defined(__NetBSD__) + struct sensor snsr; + int have_ac = 0; + size_t slen = sizeof(struct sensor); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + unsigned int value; + size_t len; +#elif defined(__linux__) + char *buf; + int have_ac = 0; +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) + power->ac_mibs[3] = 9; + power->ac_mibs[4] = 0; + + if (sysctl(power->ac_mibs, 5, &snsr, &slen, NULL, 0) != -1) + have_ac = (int)snsr.value; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(value); + if ((sysctl(power->ac_mibs, 3, &value, &len, NULL, 0)) == -1) + { + return; + } + power->have_ac = value; +#elif defined(__linux__) + buf = Fcontents("/sys/class/power_supply/AC/online"); + if (buf) + { + have_ac = atoi(buf); + free(buf); + } +#endif + + for (i = 0; i < power->battery_count; i++) + _battery_state_get(power, power->bat_mibs[i]); + +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__linux__) + double percent = + 100 * (power->charge_current / power->charge_full); + + power->percent = percent; + power->have_ac = have_ac; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(value); + if ((sysctl(power->bat_mibs[0], 4, &value, &len, NULL, 0)) == -1) + { + return; + } + + power->percent = value; + +#endif + for (i = 0; i < power->battery_count; i++) + if (power->bat_mibs[i]) free(power->bat_mibs[i]); +} + +#if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) +static void +_freebsd_generic_network_status(unsigned long int *in, + unsigned long int *out) +{ + struct ifmibdata *ifmd; + size_t len; + int i, count; + len = sizeof(count); + + if (sysctlbyname + ("net.link.generic.system.ifcount", &count, &len, NULL, 0) < 0) + return; + + ifmd = malloc(sizeof(struct ifmibdata)); + if (!ifmd) + return; + + for (i = 1; i <= count; i++) { + int mib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL }; + len = sizeof(*ifmd); + if (sysctl(mib, 6, ifmd, &len, NULL, 0) < 0) continue; + if (!strcmp(ifmd->ifmd_name, "lo0")) + continue; + *in += ifmd->ifmd_data.ifi_ibytes; + *out += ifmd->ifmd_data.ifi_obytes; + } + free(ifmd); +} + +#endif + +#if defined(__OpenBSD__) +static void +_openbsd_generic_network_status(unsigned long int *in, + unsigned long int *out) +{ + struct ifaddrs *interfaces, *ifa; + + if (getifaddrs(&interfaces) < 0) + return; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + return; + + for (ifa = interfaces; ifa; ifa = ifa->ifa_next) { + struct ifreq ifreq; + struct if_data if_data; + + ifreq.ifr_data = (void *)&if_data; + strncpy(ifreq.ifr_name, ifa->ifa_name, IFNAMSIZ - 1); + if (ioctl(sock, SIOCGIFDATA, &ifreq) < 0) + return; + + struct if_data *const ifi = &if_data; + if (ifi->ifi_type == IFT_ETHER || + ifi->ifi_type == IFT_FASTETHER || + ifi->ifi_type == IFT_GIGABITETHERNET || + ifi->ifi_type == IFT_IEEE80211) + { + if (ifi->ifi_ibytes) + *in += ifi->ifi_ibytes; + + if (ifi->ifi_obytes) + *out += ifi->ifi_obytes; + } + } + close(sock); +} + +#endif + +#if defined(__linux__) +static void +_linux_generic_network_status(unsigned long int *in, + unsigned long int *out) +{ + FILE *f; + char buf[4096], dummy_s[256]; + unsigned long int tmp_in, tmp_out, dummy; + + f = fopen("/proc/net/dev", "r"); + if (!f) return; + + while (fgets(buf, sizeof(buf), f)) + { + if (17 == sscanf(buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " + "%lu %lu %lu %lu\n", dummy_s, &tmp_in, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &tmp_out, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy)) + { + *in += tmp_in; + *out += tmp_out; + } + } + + fclose(f); +} + +#endif + +static void +_network_transfer_get(results_t *results) +{ + unsigned long first_in = 0, first_out = 0; + unsigned long last_in = 0, last_out = 0; +#if defined(__linux__) + _linux_generic_network_status(&first_in, &first_out); + usleep(1000000); + _linux_generic_network_status(&last_in, &last_out); +#elif defined(__OpenBSD__) + _openbsd_generic_network_status(&first_in, &first_out); + usleep(1000000); + _openbsd_generic_network_status(&last_in, &last_out); +#elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) + _freebsd_generic_network_status(&first_in, &first_out); + usleep(1000000); + _freebsd_generic_network_status(&last_in, &last_out); +#endif + results->incoming = last_in - first_in; + results->outgoing = last_out - first_out; +} + +static double +_results_cpu(cpu_core_t **cores, int cpu_count) +{ + double total = 0; + for (int i = 0; i < cpu_count; i++) + total += cores[i]->percent; + + total = total / cpu_count; + + return total; +} + +int +system_cpu_memory_get(double *percent_cpu, long *memory_total, long *memory_used) +{ + results_t results; + + memset(&results, 0, sizeof(results)); + + results.cores = _cpu_cores_state_get(&results.cpu_count); + + _memory_usage_get(&results.memory); + + *percent_cpu = _results_cpu(results.cores, results.cpu_count); + *memory_total = results.memory.total; + *memory_used = results.memory.used; + + for (int i = 0; i < results.cpu_count; i++) + { + free(results.cores[i]); + } + + free(results.cores); + + return results.cpu_count; +} + diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000..9491aa0 --- /dev/null +++ b/src/system.h @@ -0,0 +1,7 @@ +#ifndef __SYSTEM_H__ +#define __SYSTEM_H__ + +int +system_cpu_memory_get(double *percent_cpu, long *memory_total, long *memory_used); + +#endif diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..264b854 --- /dev/null +++ b/src/ui.c @@ -0,0 +1,1401 @@ +#include "system.h" +#include "process.h" +#include "ui.h" +#include +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +# define __MacOS__ +#endif + +static Eina_Lock _lock; + +static long _memory_total = 0; +static long _memory_used = 0; + +static void +_system_stats(void *data, Ecore_Thread *thread) +{ + Ui *ui; + Sys_Stats *sys; + int i; + + ui = data; + + while (1) + { + sys = malloc(sizeof(Sys_Stats)); + sys->cpu_count = system_cpu_memory_get(&sys->cpu_usage, &sys->mem_total, &sys->mem_used); + + ecore_thread_feedback(thread, sys); + + for (i = 0; i < ui->poll_delay * 2; i++) + { + if (ecore_thread_check(thread)) + return; + + usleep(500000); + } + } +} + +static void +_system_stats_feedback_cb(void *data, Ecore_Thread *thread, void *msg) +{ + Ui *ui; + Sys_Stats *sys; + + ui = data; + sys = msg; + + if (ecore_thread_check(thread)) + goto out; + + _memory_total = sys->mem_total >>= 10; + _memory_used = sys->mem_used >>= 10; + + elm_progressbar_value_set(ui->progress_cpu, (double)sys->cpu_usage / 100); + elm_progressbar_value_set(ui->progress_mem, (double)((sys->mem_total / 100.0) * sys->mem_used) / 1000000); + +out: + free(sys); +} + +static int +_sort_by_pid(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->pid - inf2->pid; +} + +static int +_sort_by_uid(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->uid - inf2->uid; +} + +static int +_sort_by_nice(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->nice - inf2->nice; +} + +static int +_sort_by_pri(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->priority - inf2->priority; +} + +static int +_sort_by_cpu(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->cpu_id - inf2->cpu_id; +} + +static int +_sort_by_threads(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->numthreads - inf2->numthreads; +} + +static int +_sort_by_size(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + int64_t size1, size2; + + inf1 = p1; inf2 = p2; + + size1 = inf1->mem_size; + size2 = inf2->mem_size; + + if (size1 < size2) + return -1; + if (size2 > size1) + return 1; + + return 0; +} + +static int +_sort_by_rss(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + int64_t size1, size2; + + inf1 = p1; inf2 = p2; + + size1 = inf1->mem_rss; + size2 = inf2->mem_rss; + + if (size1 < size2) + return -1; + if (size2 > size1) + return 1; + + return 0; +} + +static int +_sort_by_cpu_usage(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + double one, two; + + inf1 = p1; inf2 = p2; + + one = inf1->cpu_usage; + two = inf2->cpu_usage; + + if (one < two) + return -1; + if (two > one) + return 1; + + return 0; +} + +static int +_sort_by_cmd(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return strcasecmp(inf1->command, inf2->command); +} + +static int +_sort_by_state(const void *p1, const void *p2) +{ + const Proc_Stats *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return strcmp(inf1->state, inf2->state); +} + +static void +_fields_append(Ui *ui, Proc_Stats *proc) +{ + // FIXME: hiding self from the list until more efficient. + // It's not too bad but it pollutes a lovely list. + if (ui->program_pid == proc->pid) + return; + + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_PID], eina_slstr_printf("%d
", proc->pid), TEXT_FIELD_MAX); + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_UID], eina_slstr_printf("%d
", proc->uid), TEXT_FIELD_MAX); + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_SIZE], eina_slstr_printf("%lld K
", proc->mem_size >> 10), TEXT_FIELD_MAX); + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_RSS], eina_slstr_printf("%lld K
", proc->mem_rss >> 10), TEXT_FIELD_MAX); + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_COMMAND], eina_slstr_printf("%s
", proc->command), TEXT_FIELD_MAX); + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_STATE], eina_slstr_printf("%s
", proc->state), TEXT_FIELD_MAX); + eina_strlcat(ui->fields[PROCESS_INFO_FIELD_CPU_USAGE], eina_slstr_printf("%.1f%%
", proc->cpu_usage), TEXT_FIELD_MAX); +} + +static void +_fields_show(Ui *ui) +{ + elm_object_text_set(ui->entry_pid, ui->fields[PROCESS_INFO_FIELD_PID]); + elm_object_text_set(ui->entry_uid, ui->fields[PROCESS_INFO_FIELD_UID]); + elm_object_text_set(ui->entry_size, ui->fields[PROCESS_INFO_FIELD_SIZE]); + elm_object_text_set(ui->entry_rss, ui->fields[PROCESS_INFO_FIELD_RSS]); + elm_object_text_set(ui->entry_cmd, ui->fields[PROCESS_INFO_FIELD_COMMAND]); + elm_object_text_set(ui->entry_state, ui->fields[PROCESS_INFO_FIELD_STATE]); + elm_object_text_set(ui->entry_cpu_usage, ui->fields[PROCESS_INFO_FIELD_CPU_USAGE]); +} + +static void +_fields_clear(Ui *ui) +{ + for (int i = 0; i < PROCESS_INFO_FIELDS; i++) + { + ui->fields[i][0] = '\0'; + } +} + +static void +_fields_free(Ui *ui) +{ + for (int i = 0; i < PROCESS_INFO_FIELDS; i++) + { + free(ui->fields[i]); + } +} + +static Eina_List * +_list_sort(Ui *ui, Eina_List *list) +{ + switch (ui->sort_type) + { + case SORT_BY_NONE: + case SORT_BY_PID: + list = eina_list_sort(list, eina_list_count(list), _sort_by_pid); + break; + + case SORT_BY_UID: + list = eina_list_sort(list, eina_list_count(list), _sort_by_uid); + break; + + case SORT_BY_NICE: + list = eina_list_sort(list, eina_list_count(list), _sort_by_nice); + break; + + case SORT_BY_PRI: + list = eina_list_sort(list, eina_list_count(list), _sort_by_pri); + break; + + case SORT_BY_CPU: + list = eina_list_sort(list, eina_list_count(list), _sort_by_cpu); + break; + + case SORT_BY_THREADS: + list = eina_list_sort(list, eina_list_count(list), _sort_by_threads); + break; + + case SORT_BY_SIZE: + list = eina_list_sort(list, eina_list_count(list), _sort_by_size); + break; + + case SORT_BY_RSS: + list = eina_list_sort(list, eina_list_count(list), _sort_by_rss); + break; + + case SORT_BY_CMD: + list = eina_list_sort(list, eina_list_count(list), _sort_by_cmd); + break; + + case SORT_BY_STATE: + list = eina_list_sort(list, eina_list_count(list), _sort_by_state); + break; + + case SORT_BY_CPU_USAGE: + list = eina_list_sort(list, eina_list_count(list), _sort_by_cpu_usage); + break; + } + + if (ui->sort_reverse) + list = eina_list_reverse(list); + + return list; +} + +static void +_system_process_list_feedback_cb(void *data, Ecore_Thread *thread EINA_UNUSED, void *msg EINA_UNUSED) +{ + Ui *ui; + Eina_List *list, *l; + Proc_Stats *proc; + + eina_lock_take(&_lock); + + ui = data; + + list = proc_info_all_get(); + + EINA_LIST_FOREACH (list, l, proc) + { + int64_t time_prev = ui->cpu_times[proc->pid]; + proc->cpu_usage = 0; + if (!ui->first_run && proc->cpu_time > time_prev) + { + proc->cpu_usage = (double) (proc->cpu_time - time_prev) / ui->poll_delay; + } + } + + list = _list_sort(ui, list); + + EINA_LIST_FREE (list, proc) + { + _fields_append(ui, proc); + ui->first_run = EINA_FALSE; + ui->cpu_times[proc->pid] = proc->cpu_time; + + free(proc); + } + + if (list) + eina_list_free(list); + + _fields_show(ui); + _fields_clear(ui); + + eina_lock_release(&_lock); +} + +static void +_system_process_list_update(Ui *ui) +{ + _system_process_list_feedback_cb(ui, NULL, NULL); +} + +static void +_system_process_list(void *data, Ecore_Thread *thread) +{ + Ui *ui; + int i; + + ui = data; + + while (1) + { + ecore_thread_feedback(thread, ui); + for (i = 0; i < ui->poll_delay * 2; i++) + { + if (ecore_thread_check(thread)) + return; + usleep(500000); + } + } +} + +static void +_thread_end_cb(void *data EINA_UNUSED, Ecore_Thread *thread) +{ + thread = NULL; +} + +static void +_thread_error_cb(void *data EINA_UNUSED, Ecore_Thread *thread) +{ + thread = NULL; +} + +static char * +_progress_mem_format_cb(double val) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "%ld M out of %ld M", _memory_used, _memory_total); + + return strdup(buf); +} + +static void +_progress_mem_format_free_cb(char *str) +{ + if (str) + free(str); +} + +static void +_btn_icon_state_set(Evas_Object *button, Eina_Bool reverse) +{ + Evas_Object *icon = elm_icon_add(button); + if (reverse) + elm_icon_standard_set(icon, "go-down"); + else + elm_icon_standard_set(icon, "go-up"); + + elm_object_part_content_set(button, "icon", icon); + evas_object_show(icon); +} + +static void +_btn_pid_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_PID) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_pid, ui->sort_reverse); + + ui->sort_type = SORT_BY_PID; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_btn_uid_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_UID) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_uid, ui->sort_reverse); + + ui->sort_type = SORT_BY_UID; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + + +static void +_btn_cpu_usage_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_CPU_USAGE) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_cpu_usage, ui->sort_reverse); + + ui->sort_type = SORT_BY_CPU_USAGE; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_btn_size_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_SIZE) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_size, ui->sort_reverse); + + ui->sort_type = SORT_BY_SIZE; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_btn_rss_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_RSS) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_rss, ui->sort_reverse); + + ui->sort_type = SORT_BY_RSS; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_btn_cmd_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_CMD) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_cmd, ui->sort_reverse); + + ui->sort_type = SORT_BY_CMD; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_btn_state_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->sort_type == SORT_BY_STATE) + ui->sort_reverse = !ui->sort_reverse; + + _btn_icon_state_set(ui->btn_state, ui->sort_reverse); + + ui->sort_type = SORT_BY_STATE; + + _system_process_list_update(ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_btn_quit_clicked_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + ecore_main_loop_quit(); + + elm_exit(); +} + +static void +_btn_about_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui; + Evas_Object *win; + + ui = data; + win = ui->win; + + printf("(c) Copyright 2018. Alastair Poole \n"); +} + +static void +_list_item_del_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + pid_t *pid = data; + + free(pid); +} + +static void +_process_panel_pids_update(Ui *ui) +{ + Proc_Stats *proc; + Elm_Widget_Item *item; + Eina_List *list; + pid_t *pid; + char buf[64]; + + if (!ui->panel_visible) + return; + + // FIXME: something fishy going on here (mem-wise). + list = proc_info_all_get(); + list = eina_list_sort(list, eina_list_count(list), _sort_by_pid); + + elm_list_clear(ui->list_pid); + + EINA_LIST_FREE(list, proc) + { + snprintf(buf, sizeof(buf), "%d", proc->pid); + + pid = malloc(sizeof(pid_t)); + *pid = proc->pid; + + item = elm_list_item_append(ui->list_pid, buf, NULL, NULL, NULL, pid); + elm_object_item_del_cb_set(item, _list_item_del_cb); + + free(proc); + } + + elm_list_go(ui->list_pid); + + if (list) + eina_list_free(list); +} + +static Eina_Bool +_process_panel_update(void *data) +{ + Ui *ui; + const Eina_List *l, *list; + Elm_Widget_Item *it; + struct passwd *pwd_entry; + Proc_Stats *proc; + double cpu_usage = 0.0; + + ui = data; + + proc = proc_info_by_pid(ui->selected_pid); + if (!proc) + { + _process_panel_pids_update(ui); + + return ECORE_CALLBACK_CANCEL; + } + + list = elm_list_items_get(ui->list_pid); + EINA_LIST_FOREACH(list, l, it) + { + pid_t *pid = elm_object_item_data_get(it); + if (pid && *pid == ui->selected_pid) + { + elm_list_item_selected_set(it, EINA_TRUE); + break; + } + } + + elm_object_text_set(ui->entry_pid_cmd, proc->command); + + pwd_entry = getpwuid(proc->uid); + if (pwd_entry) + elm_object_text_set(ui->entry_pid_user, pwd_entry->pw_name); + + elm_object_text_set(ui->entry_pid_pid, eina_slstr_printf("%d", proc->pid)); + elm_object_text_set(ui->entry_pid_uid, eina_slstr_printf("%d", proc->uid)); + elm_object_text_set(ui->entry_pid_cpu, eina_slstr_printf("%d", proc->cpu_id)); + elm_object_text_set(ui->entry_pid_threads, eina_slstr_printf("%d", proc->numthreads)); + elm_object_text_set(ui->entry_pid_size, eina_slstr_printf("%lld bytes", proc->mem_size)); + elm_object_text_set(ui->entry_pid_rss, eina_slstr_printf("%lld bytes", proc->mem_rss)); + elm_object_text_set(ui->entry_pid_nice, eina_slstr_printf("%d", proc->nice)); + elm_object_text_set(ui->entry_pid_pri, eina_slstr_printf("%d", proc->priority)); + elm_object_text_set(ui->entry_pid_state, proc->state); + + if (proc->cpu_time > ui->pid_cpu_time) + { + cpu_usage = (double) (proc->cpu_time - ui->pid_cpu_time) / ui->poll_delay; + } + + ui->pid_cpu_time = proc->cpu_time; + elm_object_text_set(ui->entry_pid_cpu_usage, eina_slstr_printf("%.1f%%", cpu_usage)); + + free(proc); + + return ECORE_CALLBACK_RENEW; +} + +static void +_process_panel_list_selected_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Elm_Object_Item *it; + Ui *ui; + const char *text; + + ui = data; + + it = elm_list_selected_item_get(obj); + + text = elm_object_item_text_get(it); + + if (ui->timer_pid) + { + ecore_timer_del(ui->timer_pid); + ui->timer_pid = NULL; + } + + ui->selected_pid = atoi(text); + + _process_panel_update(ui); + + ui->timer_pid = ecore_timer_add(ui->poll_delay, _process_panel_update, ui); + + elm_scroller_page_bring_in(ui->scroller, 0, 0); +} + +static void +_panel_scrolled_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + ui->panel_visible = !ui->panel_visible; +} + +static void +_btn_start_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->selected_pid == -1) + return; + + kill(ui->selected_pid, SIGCONT); +} + +static void +_btn_stop_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->selected_pid == -1) + return; + + kill(ui->selected_pid, SIGSTOP); +} + +static void +_btn_kill_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui = data; + + if (ui->selected_pid == -1) + return; + + kill(ui->selected_pid, SIGKILL); +} + +static void +_entry_pid_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ui *ui; + Evas_Object *textblock; + Evas_Textblock_Cursor *pos; + const char *text; + char *pid_text, *start, *end; + + ui = data; + + textblock = elm_entry_textblock_get(obj); + if (!textblock) + return; + + pos = evas_object_textblock_cursor_get(textblock); + if (!pos) + return; + + text = evas_textblock_cursor_paragraph_text_get(pos); + if (!text) + return; + + pid_text = strdup(text); + + start = strchr(pid_text, '>') + 1; + if (start) + { + end = strchr(start, '<'); + if (end) + *end = '\0'; + } + else + { + free(pid_text); + return; + } + + ui->selected_pid = atol(start); + + free(pid_text); + + _process_panel_update(ui); + + if (ui->timer_pid) + { + ecore_timer_del(ui->timer_pid); + ui->timer_pid = NULL; + } + + ui->timer_pid = ecore_timer_add(ui->poll_delay, _process_panel_update, ui); + + elm_panel_toggle(ui->panel); + ui->panel_visible = EINA_TRUE; +} + +static void +_ui_main_view_add(Evas_Object *parent, Ui *ui) +{ + Evas_Object *box, *hbox, *frame, *table; + Evas_Object *progress, *button, *entry; + Evas_Object *scroller; + + box = elm_box_add(parent); + evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(box); + elm_object_content_set(parent, box); + + hbox = elm_box_add(box); + evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, 0); + elm_box_horizontal_set(hbox, EINA_TRUE); + elm_box_pack_end(box, hbox); + evas_object_show(hbox); + + frame = elm_frame_add(hbox); + evas_object_size_hint_weight_set(frame, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(frame, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(frame, "System CPU"); + elm_box_pack_end(hbox, frame); + evas_object_show(frame); + + ui->progress_cpu = progress = elm_progressbar_add(parent); + evas_object_size_hint_align_set(progress, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(progress, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_progressbar_span_size_set(progress, 1.0); + elm_progressbar_unit_format_set(progress, "%1.2f%%"); + elm_object_content_set(frame, progress); + evas_object_show(progress); + + frame = elm_frame_add(hbox); + evas_object_size_hint_weight_set(frame, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(frame, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(frame, "System Memory"); + elm_box_pack_end(hbox, frame); + evas_object_show(frame); + + ui->progress_mem = progress = elm_progressbar_add(parent); + evas_object_size_hint_align_set(progress, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_weight_set(progress, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + elm_progressbar_span_size_set(progress, 1.0); + elm_progressbar_unit_format_function_set(progress, _progress_mem_format_cb, _progress_mem_format_free_cb); + elm_object_content_set(frame, progress); + evas_object_show(progress); + + table = elm_table_add(parent); + evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(table, EVAS_HINT_FILL, 0); + elm_table_padding_set(table, 0, 0); + evas_object_show(table); + elm_box_pack_end(box, table); + + ui->btn_pid = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "PID"); + evas_object_show(button); + elm_table_pack(table, button, 0, 0, 1, 1); + + ui->btn_uid = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "UID"); + evas_object_show(button); + elm_table_pack(table, button, 1, 0, 1, 1); + + ui->btn_size = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Size"); + evas_object_show(button); + elm_table_pack(table, button, 2, 0, 1, 1); + + ui->btn_rss = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Res"); + evas_object_show(button); + elm_table_pack(table, button, 3, 0, 1, 1); + + ui->btn_cmd = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Command"); + evas_object_show(button); + elm_table_pack(table, button, 4, 0, 1, 1); + + ui->btn_state = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "State"); + evas_object_show(button); + elm_table_pack(table, button, 5, 0, 1, 1); + + ui->btn_cpu_usage = button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "CPU %"); + evas_object_show(button); + elm_table_pack(table, button, 6, 0, 1, 1); + + table = elm_table_add(parent); + evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(table, EVAS_HINT_FILL, EVAS_HINT_EXPAND); + elm_table_padding_set(table, 0, 0); + evas_object_show(table); + + ui->scroller = scroller = elm_scroller_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_scroller_policy_set(scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON); + elm_scroller_bounce_set(scroller, EINA_FALSE, EINA_FALSE); + elm_scroller_gravity_set(scroller, 0.0, 0.0); + elm_scroller_wheel_disabled_set(scroller, 1); + elm_scroller_page_relative_set(scroller, 1, 0); + evas_object_show(scroller); + elm_object_content_set(scroller, table); + + frame = elm_frame_add(box); + evas_object_size_hint_weight_set(frame, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(frame, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(frame, "System Overview"); + elm_object_style_set(frame, "pad_small"); + elm_box_pack_end(box, frame); + evas_object_show(frame); + elm_object_content_set(frame, scroller); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "PID"); + elm_table_pack(table, button, 0, 0, 1, 1); + + ui->entry_pid = entry = elm_entry_add(parent); + elm_entry_text_style_user_push(entry, "DEFAULT='font=default:style=default size=12 align=center'"); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_scrollable_set(entry, 0); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_table_pack(table, entry, 0, 0, 1, 1); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "UID"); + elm_table_pack(table, button, 1, 0, 1, 1); + + ui->entry_uid = entry = elm_entry_add(parent); + elm_entry_text_style_user_push(entry, "DEFAULT='font=default:style=default size=12 align=center'"); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_table_pack(table, entry, 1, 0, 1, 1); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Size"); + elm_table_pack(table, button, 2, 0, 1, 1); + + ui->entry_size = entry = elm_entry_add(parent); + elm_entry_text_style_user_push(entry, "DEFAULT='font=default:style=default size=12 align=right'"); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_scrollable_set(entry, 0); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_table_pack(table, entry, 2, 0, 1, 1); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Res"); + elm_table_pack(table, button, 3, 0, 1, 1); + + ui->entry_rss = entry = elm_entry_add(parent); + elm_entry_text_style_user_push(entry, "DEFAULT='font=default:style=default size=12 align=right'"); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_scrollable_set(entry, 0); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_table_pack(table, entry, 3, 0, 1, 1); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Command"); + elm_table_pack(table, button, 4, 0, 1, 1); + + ui->entry_cmd = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_scrollable_set(entry, 0); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_table_pack(table, entry, 4, 0, 1, 1); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "State"); + elm_table_pack(table, button, 5, 0, 1, 1); + + ui->entry_state = entry = elm_entry_add(parent); + elm_entry_text_style_user_push(entry, "DEFAULT='font=default:style=default size=12 align=center'"); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 0); + elm_entry_editable_set(entry, 0); + elm_entry_line_wrap_set(entry, 1); + evas_object_show(entry); + elm_table_pack(table, entry, 5, 0, 1, 1); + + button = elm_button_add(parent); + _btn_icon_state_set(button, EINA_FALSE); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "CPU %"); + elm_table_pack(table, button, 6, 0, 1, 1); + + ui->entry_cpu_usage = entry = elm_entry_add(parent); + elm_entry_text_style_user_push(entry, "DEFAULT='font=default:style=default size=12 align=center'"); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 0); + elm_entry_editable_set(entry, 0); + elm_entry_line_wrap_set(entry, 1); + evas_object_show(entry); + elm_table_pack(table, entry, 6, 0, 1, 1); + + hbox = elm_box_add(parent); + evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_box_horizontal_set(hbox, EINA_TRUE); + evas_object_show(hbox); + elm_box_pack_end(box, hbox); + + box = elm_box_add(parent); + evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_box_horizontal_set(box, EINA_TRUE); + elm_box_pack_end(hbox, box); + + button = elm_button_add(parent); + evas_object_size_hint_weight_set(button, 0.1, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0); + elm_object_text_set(button, "Close"); + elm_box_pack_end(hbox, button); + evas_object_show(button); + evas_object_smart_callback_add(button, "clicked", _btn_quit_clicked_cb, ui); + + evas_object_smart_callback_add(ui->btn_pid, "clicked", _btn_pid_clicked_cb, ui); + evas_object_smart_callback_add(ui->btn_uid, "clicked", _btn_uid_clicked_cb, ui); + evas_object_smart_callback_add(ui->btn_size, "clicked", _btn_size_clicked_cb, ui); + evas_object_smart_callback_add(ui->btn_rss, "clicked", _btn_rss_clicked_cb, ui); + evas_object_smart_callback_add(ui->btn_cmd, "clicked", _btn_cmd_clicked_cb, ui); + evas_object_smart_callback_add(ui->btn_state, "clicked", _btn_state_clicked_cb, ui); + evas_object_smart_callback_add(ui->btn_cpu_usage, "clicked", _btn_cpu_usage_clicked_cb, ui); + evas_object_smart_callback_add(ui->entry_pid, "clicked", _entry_pid_clicked_cb, ui); +} + +static void +_ui_process_panel_add(Evas_Object *parent, Ui *ui) +{ + Evas_Object *panel, *box, *hbox, *frame, *scroller, *table; + Evas_Object *label, *list, *entry, *button; + + ui->panel = panel = elm_panel_add(parent); + evas_object_size_hint_weight_set(panel, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(panel, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_panel_orient_set(panel, ELM_PANEL_ORIENT_BOTTOM); + elm_panel_toggle(panel); + elm_object_content_set(parent, panel); + evas_object_show(panel); + evas_object_smart_callback_add(ui->panel, "scroll", _panel_scrolled_cb, ui); + + hbox = elm_box_add(parent); + evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_box_horizontal_set(hbox, EINA_TRUE); + elm_object_content_set(panel, hbox); + evas_object_show(hbox); + + frame = elm_frame_add(hbox); + evas_object_size_hint_weight_set(frame, 0.2, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(frame, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(frame, "PID"); + elm_box_pack_end(hbox, frame); + evas_object_show(frame); + + ui->list_pid = list = elm_list_add(frame); + evas_object_size_hint_weight_set(list, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_size_hint_align_set(list, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(list); + elm_object_content_set(frame, list); + evas_object_smart_callback_add(ui->list_pid, "selected", _process_panel_list_selected_cb, ui); + + frame = elm_frame_add(hbox); + evas_object_size_hint_weight_set(frame, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(frame, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(frame, "Process Statistics"); + elm_box_pack_end(hbox, frame); + evas_object_show(frame); + + table = elm_table_add(frame); + evas_object_size_hint_weight_set(table, 0.5, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(table, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(table); + + scroller = elm_scroller_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_scroller_policy_set(scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_ON); + evas_object_show(scroller); + elm_object_content_set(scroller, table); + elm_object_content_set(frame, scroller); + elm_box_pack_end(hbox, frame); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "Name:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 0, 1, 1); + + ui->entry_pid_cmd = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 0, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "PID:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 1, 1, 1); + + ui->entry_pid_pid = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 1, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "Username:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 2, 1, 1); + + ui->entry_pid_user = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 2, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "UID:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 3, 1, 1); + + ui->entry_pid_uid = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 3, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); +#if defined(__MacOS__) + elm_object_text_set(label, "WQ #:"); +#else + elm_object_text_set(label, "CPU #:"); +#endif + evas_object_show(label); + elm_table_pack(table, label, 0, 4, 1, 1); + + ui->entry_pid_cpu = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 4, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "Threads:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 5, 1, 1); + + ui->entry_pid_threads = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 5, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "Total memory:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 6, 1, 1); + + ui->entry_pid_size = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 6, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, " Reserved memory:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 7, 1, 1); + + ui->entry_pid_rss = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 7, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "Nice:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 8, 1, 1); + + ui->entry_pid_nice = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 8, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "Priority:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 9, 1, 1); + + ui->entry_pid_pri = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 9, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "State:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 10, 1, 1); + + ui->entry_pid_state = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 10, 1, 1); + + label = elm_label_add(parent); + evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_text_set(label, "CPU %:"); + evas_object_show(label); + elm_table_pack(table, label, 0, 11, 1, 1); + + ui->entry_pid_cpu_usage = entry = elm_entry_add(parent); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_single_line_set(entry, 1); + elm_entry_scrollable_set(entry, 1); + elm_entry_editable_set(entry, 0); + evas_object_show(entry); + elm_entry_line_wrap_set(entry, 1); + elm_table_pack(table, entry, 1, 11, 1, 1); + + hbox = elm_box_add(parent); + evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_box_horizontal_set(hbox, EINA_TRUE); + evas_object_show(hbox); + elm_table_pack(table, hbox, 1, 12, 1, 1); + + button = elm_button_add(parent); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Stop Process"); + elm_box_pack_end(hbox, button); + evas_object_show(button); + evas_object_smart_callback_add(button, "clicked", _btn_stop_clicked_cb, ui); + + button = elm_button_add(parent); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Start Process"); + elm_box_pack_end(hbox, button); + evas_object_smart_callback_add(button, "clicked", _btn_start_clicked_cb, ui); + evas_object_show(button); + + button = elm_button_add(parent); + evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(button, EVAS_HINT_FILL, 0.5); + elm_object_text_set(button, "Kill Process"); + elm_box_pack_end(hbox, button); + evas_object_show(button); + evas_object_smart_callback_add(button, "clicked", _btn_kill_clicked_cb, ui); +} + +void +ui_add(Evas_Object *parent) +{ + Ui *ui; + int i; + + ui = calloc(1, sizeof(Ui)); + ui->win = parent; + ui->first_run = EINA_TRUE; + ui->poll_delay = 3; + ui->sort_reverse = EINA_FALSE; + ui->sort_type = SORT_BY_PID; + ui->selected_pid = -1; + ui->program_pid = getpid(); + ui->panel_visible = EINA_TRUE; + + memset(ui->cpu_times, 0, PID_MAX * sizeof(int64_t)); + + for (i = 0; i < PROCESS_INFO_FIELDS; i++) + { + ui->fields[i] = malloc(TEXT_FIELD_MAX * sizeof(char)); + ui->fields[i][0] = '\0'; + } + + eina_lock_new(&_lock); + + _ui_main_view_add(parent, ui); + _ui_process_panel_add(parent, ui); + + _process_panel_update(ui); + ecore_thread_feedback_run(_system_stats, _system_stats_feedback_cb, _thread_end_cb, _thread_error_cb, ui, EINA_FALSE); + ecore_thread_feedback_run(_system_process_list, _system_process_list_feedback_cb, _thread_end_cb, _thread_error_cb, ui, EINA_FALSE); +} + diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..3712c51 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,114 @@ +#ifndef __UI_H__ +#define __UI_H__ + +#include + +typedef enum +{ + PROCESS_INFO_FIELD_PID, + PROCESS_INFO_FIELD_UID, + PROCESS_INFO_FIELD_SIZE, + PROCESS_INFO_FIELD_RSS, + PROCESS_INFO_FIELD_COMMAND, + PROCESS_INFO_FIELD_STATE, + PROCESS_INFO_FIELD_CPU_USAGE, + + // Not displayed in the main UI. + PROCESS_INFO_FIELD_NICE, + PROCESS_INFO_FIELD_PRI, + PROCESS_INFO_FIELD_CPU, + PROCESS_INFO_FIELD_THREADS, + // Not used yet in UI. + PROCESS_INFO_FIELD_CPU_TIME, +} Proc_Stats_Field; + +#define PROCESS_INFO_FIELDS 7 + +typedef enum +{ + SORT_BY_NONE, + SORT_BY_PID, + SORT_BY_UID, + SORT_BY_NICE, + SORT_BY_PRI, + SORT_BY_CPU, + SORT_BY_THREADS, + SORT_BY_SIZE, + SORT_BY_RSS, + SORT_BY_CMD, + SORT_BY_STATE, + SORT_BY_CPU_USAGE, +} Sort_Type; + +typedef struct Ui +{ + Evas_Object *win; + Evas_Object *panel; + Evas_Object *scroller; + + Evas_Object *progress_cpu; + Evas_Object *progress_mem; + + Evas_Object *entry_pid; + Evas_Object *entry_uid; + Evas_Object *entry_size; + Evas_Object *entry_rss; + Evas_Object *entry_cmd; + Evas_Object *entry_state; + Evas_Object *entry_cpu_usage; + + Evas_Object *btn_pid; + Evas_Object *btn_uid; + Evas_Object *btn_size; + Evas_Object *btn_rss; + Evas_Object *btn_cmd; + Evas_Object *btn_state; + Evas_Object *btn_cpu_usage; + + Evas_Object *entry_pid_cmd; + Evas_Object *entry_pid_user; + Evas_Object *entry_pid_pid; + Evas_Object *entry_pid_uid; + Evas_Object *entry_pid_cpu; + Evas_Object *entry_pid_threads; + Evas_Object *entry_pid_size; + Evas_Object *entry_pid_rss; + Evas_Object *entry_pid_nice; + Evas_Object *entry_pid_pri; + Evas_Object *entry_pid_state; + Evas_Object *entry_pid_cpu_usage; + + Ecore_Timer *timer_pid; + pid_t selected_pid; + pid_t program_pid; + +#define TEXT_FIELD_MAX 65535 + char *fields[PROCESS_INFO_FIELDS]; + + Evas_Object *list_pid; + + Eina_Bool first_run; + + int64_t cpu_times[PID_MAX]; + int64_t pid_cpu_time; + + int poll_delay; + + Sort_Type sort_type; + Eina_Bool sort_reverse; + Eina_Bool panel_visible; + +} Ui; + +typedef struct Sys_Stats +{ + int cpu_count; + double cpu_usage; + long mem_total; + long mem_used; +} Sys_Stats; + +void +ui_add(Evas_Object *win); + +#endif