This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository evisum.

View the commit online.

commit 292e7afe547e97f0d8390d8e1321cd8e5c8bc9ee
Author: Alastair Poole <[email protected]>
AuthorDate: Mon Apr 27 21:16:08 2026 +0100

    evisum: of all the things I miss, I miss my mind the most.
    
    Holy fucking shit we're in business.
---
 src/bin/engine/evisum_engine.c              | 310 ++++++++++++++++++++++++-
 src/bin/engine/evisum_engine.h              |   6 +
 src/bin/enigmatic/client/Enigmatic_Client.h |  10 +
 src/bin/enigmatic/client/enigmatic_client.c |  33 ++-
 src/bin/ui/evisum_ui_process_list.c         | 343 +++++++++++++++++++++++++++-
 5 files changed, 685 insertions(+), 17 deletions(-)

diff --git a/src/bin/engine/evisum_engine.c b/src/bin/engine/evisum_engine.c
index 5c827e5..9370d30 100644
--- a/src/bin/engine/evisum_engine.c
+++ b/src/bin/engine/evisum_engine.c
@@ -4,9 +4,14 @@
 #include "enigmatic/enigmatic_util.h"
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <strings.h>
 #include <unistd.h>
+#include <limits.h>
+#include <time.h>
+#include <dirent.h>
+#include <sys/stat.h>
 
 #define LOCK() eina_lock_take(&_state.lock)
 #define UNLOCK() eina_lock_release(&_state.lock)
@@ -20,8 +25,17 @@ typedef struct {
     uint64_t snapshot_seq;
 
     Enigmatic_Client *client;
+    Enigmatic_Client *history_client;
+    Eina_Bool history_enabled;
+    uint32_t history_time;
 } Evisum_Engine_State;
 
+typedef struct {
+    char *path;
+    uint32_t start_time;
+    uint32_t end_time;
+} Evisum_Engine_History_Log;
+
 static Evisum_Engine_State _state = {0};
 
 static Eina_Bool
@@ -101,8 +115,14 @@ evisum_engine_shutdown(void)
         enigmatic_client_del(_state.client);
         _state.client = NULL;
     }
+    if (_state.history_client) {
+        enigmatic_client_del(_state.history_client);
+        _state.history_client = NULL;
+    }
 
     _state.started = EINA_FALSE;
+    _state.history_enabled = EINA_FALSE;
+    _state.history_time = 0;
     if (_state.cond_init) eina_condition_broadcast(&_state.cond);
 
     UNLOCK();
@@ -160,6 +180,288 @@ evisum_engine_daemon_running_get(void)
     return enigmatic_running();
 }
 
+static Enigmatic_Client *
+_engine_history_client_for_path_read(char *path, uint32_t time)
+{
+    Enigmatic_Client *client;
+
+    client = enigmatic_client_path_open(path);
+    if (!client) {
+        free(path);
+        return NULL;
+    }
+
+    enigmatic_client_replay_time_start_set(client, 0);
+    if (time) enigmatic_client_replay_time_end_set(client, time);
+    enigmatic_client_read(client);
+
+    return client;
+}
+
+static void
+_engine_history_log_free(Evisum_Engine_History_Log *log)
+{
+    if (!log) return;
+    free(log->path);
+    free(log);
+}
+
+static Eina_Bool
+_engine_history_archive_name_is_valid(const char *name)
+{
+    size_t len;
+    const char *p;
+
+    if (!name || !name[0]) return EINA_FALSE;
+    if (!strcmp(name, ".") || !strcmp(name, "..")) return EINA_FALSE;
+    if (!strcmp(name, "enigmatic.log")) return EINA_FALSE;
+    if (!strcmp(name, "enigmatic.lock") || !strcmp(name, "enigmatic.pid")) return EINA_FALSE;
+
+    len = strlen(name);
+    if (len > 5 && !strcmp(name + len - 5, ".size")) return EINA_FALSE;
+
+    if (len > 4 && !strcmp(name + len - 4, ".lz4")) len -= 4;
+    if (len < 2) return EINA_FALSE;
+
+    for (size_t i = 0; i < len; i++) {
+        if (name[i] == '-') continue;
+        if (name[i] < '0' || name[i] > '9') return EINA_FALSE;
+    }
+
+    p = strchr(name, '-');
+    if (p && (p >= name + len)) return EINA_FALSE;
+
+    return EINA_TRUE;
+}
+
+static int
+_engine_history_log_sort_cb(const void *d1, const void *d2)
+{
+    const Evisum_Engine_History_Log *a = d1;
+    const Evisum_Engine_History_Log *b = d2;
+
+    if (a->start_time < b->start_time) return -1;
+    if (a->start_time > b->start_time) return 1;
+    if (a->end_time < b->end_time) return -1;
+    if (a->end_time > b->end_time) return 1;
+
+    return 0;
+}
+
+static Eina_Bool
+_engine_history_log_add(Eina_List **logs, char *path)
+{
+    Enigmatic_Client *client;
+    Evisum_Engine_History_Log *log;
+    uint32_t start_time = 0, end_time = 0;
+
+    if (!logs || !path) {
+        free(path);
+        return EINA_FALSE;
+    }
+
+    client = _engine_history_client_for_path_read(strdup(path), 0);
+    if (!client) {
+        free(path);
+        return EINA_FALSE;
+    }
+
+    if (!enigmatic_client_time_bounds_get(client, &start_time, &end_time) || !start_time || !end_time) {
+        enigmatic_client_del(client);
+        free(path);
+        return EINA_FALSE;
+    }
+
+    enigmatic_client_del(client);
+
+    log = calloc(1, sizeof(*log));
+    if (!log) {
+        free(path);
+        return EINA_FALSE;
+    }
+
+    log->path = path;
+    log->start_time = start_time;
+    log->end_time = end_time;
+
+    *logs = eina_list_append(*logs, log);
+
+    return EINA_TRUE;
+}
+
+static Eina_List *
+_engine_history_logs_get(void)
+{
+    Eina_List *logs = NULL;
+    DIR *dp;
+    struct dirent *ent;
+    char *dir, *current_path;
+
+    current_path = enigmatic_log_path();
+    _engine_history_log_add(&logs, current_path);
+
+    dir = enigmatic_log_directory();
+    if (!dir) return logs;
+
+    dp = opendir(dir);
+    if (!dp) {
+        free(dir);
+        return logs;
+    }
+
+    while ((ent = readdir(dp))) {
+        char path[PATH_MAX];
+        struct stat st;
+
+        if (!_engine_history_archive_name_is_valid(ent->d_name)) continue;
+
+        snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name);
+        if (stat(path, &st) == -1) continue;
+        if (!S_ISREG(st.st_mode)) continue;
+        if (access(path, R_OK) != 0) continue;
+
+        _engine_history_log_add(&logs, strdup(path));
+    }
+
+    closedir(dp);
+    free(dir);
+
+    return eina_list_sort(logs, eina_list_count(logs), _engine_history_log_sort_cb);
+}
+
+Eina_Bool
+evisum_engine_history_bounds_get(uint32_t *start_time, uint32_t *end_time)
+{
+    Eina_List *logs, *l;
+    Evisum_Engine_History_Log *log;
+    Eina_Bool ok = EINA_FALSE;
+
+    if (start_time) *start_time = 0;
+    if (end_time) *end_time = 0;
+    if (!evisum_engine_ensure_started()) return EINA_FALSE;
+
+    logs = _engine_history_logs_get();
+    if (!logs) return EINA_FALSE;
+
+    EINA_LIST_FOREACH(logs, l, log) {
+        if (!ok) {
+            if (start_time) *start_time = log->start_time;
+            if (end_time) *end_time = log->end_time;
+            ok = EINA_TRUE;
+            continue;
+        }
+        if (start_time && log->start_time < *start_time) *start_time = log->start_time;
+        if (end_time && log->end_time > *end_time) *end_time = log->end_time;
+    }
+
+    EINA_LIST_FREE(logs, log) _engine_history_log_free(log);
+
+    return EINA_TRUE;
+}
+
+Eina_Bool
+evisum_engine_history_time_set(uint32_t time)
+{
+    Enigmatic_Client *client, *old;
+    Eina_List *logs, *l;
+    Evisum_Engine_History_Log *log, *selected = NULL;
+
+    if (!time) return EINA_FALSE;
+    if (!evisum_engine_ensure_started()) return EINA_FALSE;
+
+    logs = _engine_history_logs_get();
+    EINA_LIST_FOREACH(logs, l, log) {
+        if ((time >= log->start_time) && (time <= log->end_time)) {
+            selected = log;
+            break;
+        }
+        if (time > log->end_time) selected = log;
+    }
+
+    if (!selected) {
+        EINA_LIST_FREE(logs, log) _engine_history_log_free(log);
+        return EINA_FALSE;
+    }
+
+    client = _engine_history_client_for_path_read(strdup(selected->path), time);
+    EINA_LIST_FREE(logs, log) _engine_history_log_free(log);
+    if (!client) return EINA_FALSE;
+
+    LOCK();
+    old = _state.history_client;
+    _state.history_client = client;
+    _state.history_enabled = EINA_TRUE;
+    _state.history_time = time;
+    _state.snapshot_seq++;
+    if (_state.cond_init) eina_condition_broadcast(&_state.cond);
+    UNLOCK();
+
+    if (old) enigmatic_client_del(old);
+
+    return EINA_TRUE;
+}
+
+void
+evisum_engine_history_live_set(void)
+{
+    Enigmatic_Client *old = NULL;
+
+    if (!_state.lock_init) return;
+
+    LOCK();
+    old = _state.history_client;
+    _state.history_client = NULL;
+    _state.history_enabled = EINA_FALSE;
+    _state.history_time = 0;
+    _state.snapshot_seq++;
+    if (_state.cond_init) eina_condition_broadcast(&_state.cond);
+    UNLOCK();
+
+    if (old) enigmatic_client_del(old);
+}
+
+Eina_Bool
+evisum_engine_history_live_get(void)
+{
+    Eina_Bool live = EINA_TRUE;
+
+    if (!_state.lock_init) return EINA_TRUE;
+
+    LOCK();
+    live = !_state.history_enabled;
+    UNLOCK();
+
+    return live;
+}
+
+uint32_t
+evisum_engine_history_time_get(void)
+{
+    uint32_t time = 0;
+
+    if (!_state.lock_init) return 0;
+
+    LOCK();
+    time = _state.history_time;
+    UNLOCK();
+
+    return time;
+}
+
+uint32_t
+evisum_engine_live_time_get(void)
+{
+    uint32_t time = 0;
+
+    if (!_state.lock_init) return 0;
+
+    LOCK();
+    if (_state.client) time = _state.client->snapshot.time;
+    UNLOCK();
+
+    return time;
+}
+
 static Eina_Bool
 _engine_snapshot_acquire(const Snapshot **out)
 {
@@ -174,15 +476,17 @@ _engine_snapshot_acquire(const Snapshot **out)
         return EINA_FALSE;
     }
 
-    *out = &_state.client->snapshot;
-    UNLOCK();
+    if (_state.history_enabled && _state.history_client)
+        *out = &_state.history_client->snapshot;
+    else
+        *out = &_state.client->snapshot;
     return EINA_TRUE;
 }
 
 static void
 _engine_snapshot_release(void)
 {
-    (void) 0;
+    if (_state.lock_init) UNLOCK();
 }
 
 static Cpu_Core **
diff --git a/src/bin/engine/evisum_engine.h b/src/bin/engine/evisum_engine.h
index 2353ea3..b847830 100644
--- a/src/bin/engine/evisum_engine.h
+++ b/src/bin/engine/evisum_engine.h
@@ -28,6 +28,12 @@ Eina_Bool evisum_engine_status_get(Evisum_Engine_Status *status);
 uint64_t evisum_engine_update_seq_get(void);
 Eina_Bool evisum_engine_update_wait(uint64_t *seq);
 Eina_Bool evisum_engine_daemon_running_get(void);
+Eina_Bool evisum_engine_history_bounds_get(uint32_t *start_time, uint32_t *end_time);
+Eina_Bool evisum_engine_history_time_set(uint32_t time);
+void evisum_engine_history_live_set(void);
+Eina_Bool evisum_engine_history_live_get(void);
+uint32_t evisum_engine_history_time_get(void);
+uint32_t evisum_engine_live_time_get(void);
 
 int system_cpu_online_count_get(void);
 int system_cpu_count_get(void);
diff --git a/src/bin/enigmatic/client/Enigmatic_Client.h b/src/bin/enigmatic/client/Enigmatic_Client.h
index d98e553..0fe9bcc 100644
--- a/src/bin/enigmatic/client/Enigmatic_Client.h
+++ b/src/bin/enigmatic/client/Enigmatic_Client.h
@@ -96,6 +96,13 @@ struct _Enigmatic_Client
       uint32_t  end_time;
    } replay;
 
+   struct
+   {
+      Eina_Bool valid;
+      uint32_t  start_time;
+      uint32_t  end_time;
+   } bounds;
+
    /* Public */
 
    Event_Snapshot_Data   event_snapshot;
@@ -184,4 +191,7 @@ enigmatic_client_replay_time_end_set(Enigmatic_Client *client, uint32_t secs);
 Eina_Bool
 enigmatic_client_replay(Enigmatic_Client *client);
 
+Eina_Bool
+enigmatic_client_time_bounds_get(Enigmatic_Client *client, uint32_t *start_time, uint32_t *end_time);
+
 #endif
diff --git a/src/bin/enigmatic/client/enigmatic_client.c b/src/bin/enigmatic/client/enigmatic_client.c
index 5f05942..cc5e38f 100644
--- a/src/bin/enigmatic/client/enigmatic_client.c
+++ b/src/bin/enigmatic/client/enigmatic_client.c
@@ -103,6 +103,7 @@ enigmatic_client_reset(Enigmatic_Client *client)
    client->offset = 0;
    client->file_size = 0;
    client->truncated = 0;
+   memset(&client->bounds, 0, sizeof(client->bounds));
    free_snapshot(&client->snapshot);
 }
 
@@ -1253,6 +1254,12 @@ static void
 event_block_end(Enigmatic_Client *client)
 {
    client->snapshot.time = client->header.time;
+   if (!client->bounds.valid)
+     {
+        client->bounds.start_time = client->header.time;
+        client->bounds.valid = 1;
+     }
+   client->bounds.end_time = client->header.time;
 
    if ((!client->follow) && (client->event_snapshot.callback) && (callback_fire(client)))
      {
@@ -1408,6 +1415,7 @@ enigmatic_client_read(Enigmatic_Client *client)
    struct stat st;
    int n;
    Eina_Bool eof = 0;
+   Eina_Bool stop = 0;
    LZ4F_dctx *dctx = NULL;
 
    size_t status = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
@@ -1448,7 +1456,7 @@ enigmatic_client_read(Enigmatic_Client *client)
    client->changes = 0;
    size_t offset = 0;
 
-   while ((!eof) && ((st.st_size - client->offset) > 0))
+   while ((!eof) && (!stop) && ((st.st_size - client->offset) > 0))
      {
         client->zbuf.length = st.st_size - client->offset;
         client->file_size = st.st_size;
@@ -1483,7 +1491,7 @@ enigmatic_client_read(Enigmatic_Client *client)
 
         LZ4F_frameInfo_t info;
 
-        while ((!eof) && (offset < client->zbuf.length))
+        while ((!eof) && (!stop) && (offset < client->zbuf.length))
           {
              uint8_t *src = "" + offset;
              status = LZ4F_getFrameInfo(dctx, &info, src, &compressed_size);
@@ -1523,11 +1531,17 @@ enigmatic_client_read(Enigmatic_Client *client)
              free(dst);
           }
 
-        while ((client->buf.length) && (client->buf.index <= (client->buf.length - sizeof(Header))))
+        while ((!stop) && (client->buf.length) && (client->buf.index <= (client->buf.length - sizeof(Header))))
           {
              memcpy(&client->header, &client->buf.data[client->buf.index], sizeof(Header));
              client->buf.index += sizeof(Header);
              if (client->compressed) client->offset = client->buf.index;
+             if ((client->replay.enabled) && (client->replay.end_time) &&
+                 (client->header.time > client->replay.end_time))
+               {
+                  stop = 1;
+                  break;
+               }
              switch (client->header.event)
                {
                   case EVENT_ERROR:
@@ -1853,3 +1867,16 @@ enigmatic_client_replay_time_end_set(Enigmatic_Client *client, uint32_t secs)
 {
    client->replay.end_time = secs;
 }
+
+Eina_Bool
+enigmatic_client_time_bounds_get(Enigmatic_Client *client, uint32_t *start_time, uint32_t *end_time)
+{
+   if (start_time) *start_time = 0;
+   if (end_time) *end_time = 0;
+   if (!client || !client->bounds.valid) return 0;
+
+   if (start_time) *start_time = client->bounds.start_time;
+   if (end_time) *end_time = client->bounds.end_time;
+
+   return 1;
+}
diff --git a/src/bin/ui/evisum_ui_process_list.c b/src/bin/ui/evisum_ui_process_list.c
index 329aa8f..494f0b8 100644
--- a/src/bin/ui/evisum_ui_process_list.c
+++ b/src/bin/ui/evisum_ui_process_list.c
@@ -13,6 +13,8 @@
 #include <sys/types.h>
 #include <sys/resource.h>
 #include <pwd.h>
+#include <time.h>
+#include <unistd.h>
 
 /* If you're reading this comment, God help you. */
 
@@ -58,8 +60,24 @@ typedef struct {
         Evas_Object *hbx;
         Evas_Object *pb_cpu;
         Evas_Object *pb_mem;
+        Evas_Object *history_slider;
+        Evas_Object *history_live_btn;
     } summary;
 
+    struct {
+        Eina_Lock lock;
+        Eina_Bool lock_init;
+        Eina_Bool live;
+        Eina_Bool pending;
+        Eina_Bool sliding;
+        Eina_Bool slider_updating;
+        Ecore_Timer *timer;
+        Ecore_Timer *apply_timer;
+        uint32_t start_time;
+        uint32_t end_time;
+        uint32_t requested_time;
+    } history;
+
     Elm_Layout *indicator;
     Evisum_Ui *ui;
 
@@ -618,32 +636,270 @@ _evisum_ui_process_list_content_get(void *data, Evas_Object *obj, const char *so
     return row;
 }
 
+static void
+_evisum_ui_process_list_history_time_format(uint32_t t, char *buf, size_t len) {
+    time_t when = (time_t) t;
+    struct tm tm_buf;
+
+    if (!buf || !len) return;
+    if (!t || !localtime_r(&when, &tm_buf)) {
+        snprintf(buf, len, "--:--:--");
+        return;
+    }
+
+    strftime(buf, len, "%Y-%m-%d %H:%M:%S", &tm_buf);
+}
+
+static char *
+_evisum_ui_process_list_history_indicator_format_cb(double value, void *data EINA_UNUSED) {
+    char buf[32];
+
+    _evisum_ui_process_list_history_time_format((uint32_t) (value + 0.5), buf, sizeof(buf));
+
+    return strdup(buf);
+}
+
+static void
+_evisum_ui_process_list_history_indicator_free_cb(char *str) {
+    free(str);
+}
+
+static void
+_evisum_ui_process_list_history_tooltip_set(Evisum_Ui_Process_List_View *view, uint32_t t) {
+    char time_buf[32];
+    char tip[128];
+
+    if (!view->summary.history_slider) return;
+
+    _evisum_ui_process_list_history_time_format(t, time_buf, sizeof(time_buf));
+    snprintf(tip, sizeof(tip), _("Process history: %s"), time_buf);
+    elm_object_tooltip_text_set(view->summary.history_slider, tip);
+}
+
+static Eina_Bool
+_evisum_ui_process_list_history_apply_timer_cb(void *data) {
+    Evisum_Ui_Process_List_View *view = data;
+
+    if (!view->history.lock_init) {
+        view->history.apply_timer = NULL;
+        return EINA_FALSE;
+    }
+
+    eina_lock_take(&view->history.lock);
+    view->history.pending = EINA_TRUE;
+    eina_lock_release(&view->history.lock);
+
+    view->skip_wait = 1;
+    view->skip_update = 0;
+    view->history.apply_timer = NULL;
+
+    return EINA_FALSE;
+}
+
+static void
+_evisum_ui_process_list_history_request_deferred(Evisum_Ui_Process_List_View *view, uint32_t t) {
+    if (!view->history.lock_init) return;
+
+    if (view->history.apply_timer) {
+        ecore_timer_del(view->history.apply_timer);
+        view->history.apply_timer = NULL;
+    }
+
+    eina_lock_take(&view->history.lock);
+    view->history.live = EINA_FALSE;
+    view->history.requested_time = t;
+    view->history.pending = EINA_FALSE;
+    eina_lock_release(&view->history.lock);
+
+    view->history.apply_timer = ecore_timer_add(0.5, _evisum_ui_process_list_history_apply_timer_cb, view);
+}
+
+static void
+_evisum_ui_process_list_history_state_get(Evisum_Ui_Process_List_View *view, Eina_Bool *live, Eina_Bool *pending,
+                                          uint32_t *requested_time) {
+    if (live) *live = EINA_TRUE;
+    if (pending) *pending = EINA_FALSE;
+    if (requested_time) *requested_time = 0;
+    if (!view->history.lock_init) return;
+
+    eina_lock_take(&view->history.lock);
+    if (live) *live = view->history.live;
+    if (pending) *pending = view->history.pending;
+    if (requested_time) *requested_time = view->history.requested_time;
+    eina_lock_release(&view->history.lock);
+}
+
+static void
+_evisum_ui_process_list_history_pending_clear(Evisum_Ui_Process_List_View *view, uint32_t requested_time) {
+    if (!view->history.lock_init) return;
+
+    eina_lock_take(&view->history.lock);
+    if (view->history.requested_time == requested_time) view->history.pending = EINA_FALSE;
+    eina_lock_release(&view->history.lock);
+}
+
+static void
+_evisum_ui_process_list_history_bounds_update(Evisum_Ui_Process_List_View *view) {
+    uint32_t start_time = 0, end_time = 0;
+    uint32_t live_time, now;
+    Eina_Bool live;
+
+    if (!view->summary.history_slider) return;
+
+    now = (uint32_t) time(NULL);
+    live_time = evisum_engine_live_time_get();
+    if ((view->history.start_time) && (now >= view->history.start_time)) {
+        start_time = view->history.start_time;
+        end_time = live_time > now ? live_time : now;
+    } else {
+        if (!evisum_engine_history_bounds_get(&start_time, &end_time)) {
+            elm_object_disabled_set(view->summary.history_slider, 1);
+            elm_object_disabled_set(view->summary.history_live_btn, 1);
+            return;
+        }
+        if ((now >= start_time) && (now > end_time))
+            end_time = now;
+    }
+
+    if (end_time <= start_time) end_time = start_time + 1;
+
+    view->history.start_time = start_time;
+    view->history.end_time = end_time;
+
+    elm_object_disabled_set(view->summary.history_slider, 0);
+    elm_slider_min_max_set(view->summary.history_slider, start_time, end_time);
+
+    _evisum_ui_process_list_history_state_get(view, &live, NULL, NULL);
+    elm_object_disabled_set(view->summary.history_live_btn, live);
+
+    if (live && !view->history.sliding) {
+        view->history.slider_updating = EINA_TRUE;
+        elm_slider_value_set(view->summary.history_slider, end_time);
+        view->history.slider_updating = EINA_FALSE;
+        _evisum_ui_process_list_history_tooltip_set(view, end_time);
+    }
+}
+
+static void
+_evisum_ui_process_list_history_slider_preview_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) {
+    Evisum_Ui_Process_List_View *view = data;
+    uint32_t t;
+
+    if (view->history.slider_updating) return;
+
+    t = (uint32_t) (elm_slider_value_get(obj) + 0.5);
+    if (view->history.start_time && (t < view->history.start_time)) t = view->history.start_time;
+    if (view->history.end_time && (t > view->history.end_time)) t = view->history.end_time;
+
+    elm_object_disabled_set(view->summary.history_live_btn, 0);
+    _evisum_ui_process_list_history_tooltip_set(view, t);
+}
+
+static void
+_evisum_ui_process_list_history_slider_drag_start_cb(void *data, Evas_Object *obj EINA_UNUSED,
+                                                     void *event_info EINA_UNUSED) {
+    Evisum_Ui_Process_List_View *view = data;
+
+    view->history.sliding = EINA_TRUE;
+}
+
+static void
+_evisum_ui_process_list_history_slider_drag_stop_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) {
+    Evisum_Ui_Process_List_View *view = data;
+    uint32_t t;
+
+    view->history.sliding = EINA_FALSE;
+
+    t = (uint32_t) (elm_slider_value_get(obj) + 0.5);
+    if (view->history.start_time && (t < view->history.start_time)) t = view->history.start_time;
+    if (view->history.end_time && (t > view->history.end_time)) t = view->history.end_time;
+
+    elm_object_disabled_set(view->summary.history_live_btn, 0);
+    _evisum_ui_process_list_history_tooltip_set(view, t);
+    _evisum_ui_process_list_history_request_deferred(view, t);
+}
+
+static void
+_evisum_ui_process_list_history_live_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED,
+                                                void *event_info EINA_UNUSED) {
+    Evisum_Ui_Process_List_View *view = data;
+
+    evisum_engine_history_live_set();
+
+    if (view->history.apply_timer) {
+        ecore_timer_del(view->history.apply_timer);
+        view->history.apply_timer = NULL;
+    }
+
+    eina_lock_take(&view->history.lock);
+    view->history.live = EINA_TRUE;
+    view->history.pending = EINA_FALSE;
+    view->history.sliding = EINA_FALSE;
+    view->history.requested_time = 0;
+    eina_lock_release(&view->history.lock);
+
+    elm_object_disabled_set(view->summary.history_live_btn, 1);
+    view->skip_wait = 1;
+    view->skip_update = 0;
+    _evisum_ui_process_list_history_bounds_update(view);
+}
+
+static Eina_Bool
+_evisum_ui_process_list_history_timer_cb(void *data) {
+    Evisum_Ui_Process_List_View *view = data;
+
+    if (!view->summary.visible || !view->summary.history_slider) {
+        view->history.timer = NULL;
+        return EINA_FALSE;
+    }
+
+    _evisum_ui_process_list_history_bounds_update(view);
+
+    return EINA_TRUE;
+}
+
 static void
 _evisum_ui_process_list_summary_update(Evisum_Ui_Process_List_View *view) {
     Evisum_Ui *ui;
+    Evisum_Engine_Status status = {0};
     Eina_Strbuf *buf;
+    uint64_t mem_used, mem_total;
+    double cpu_usage;
 
     ui = view->ui;
 
     if ((!ui->proc.show_statusbar) || (!view->summary.pb_cpu)) return;
 
+    if (evisum_engine_status_get(&status)) {
+        cpu_usage = status.cpu_usage;
+        mem_total = status.memory.total;
+        mem_used = status.memory.used;
+        if (status.zfs_mounted) mem_used += status.memory.zfs_arc_used;
+    } else {
+        cpu_usage = ui->cpu_usage;
+        mem_total = ui->mem_total;
+        mem_used = ui->mem_used;
+    }
+
     buf = eina_strbuf_new();
 
-    elm_progressbar_value_set(view->summary.pb_cpu, ui->cpu_usage / 100.0);
+    elm_progressbar_value_set(view->summary.pb_cpu, cpu_usage / 100.0);
 
-    if (ui->mem_total) elm_progressbar_value_set(view->summary.pb_mem, (double) ui->mem_used / (double) ui->mem_total);
+    if (mem_total) elm_progressbar_value_set(view->summary.pb_mem, (double) mem_used / (double) mem_total);
     else elm_progressbar_value_set(view->summary.pb_mem, 0.0);
-    eina_strbuf_append_printf(buf, "%s / %s ", evisum_size_format(ui->mem_used, 0),
-                              evisum_size_format(ui->mem_total, 0));
+    eina_strbuf_append_printf(buf, "%s / %s ", evisum_size_format(mem_used, 0),
+                              evisum_size_format(mem_total, 0));
     elm_object_part_text_set(view->summary.pb_mem, "elm.text.status", eina_strbuf_string_get(buf));
 
     eina_strbuf_free(buf);
+
+    _evisum_ui_process_list_history_bounds_update(view);
 }
 
 static void
 _evisum_ui_process_list_summary_add(Evisum_Ui_Process_List_View *view) {
     Evisum_Ui *ui = view->ui;
-    Evas_Object *hbx, *ic, *pb, *bx;
+    Evas_Object *hbx, *ic, *pb, *sli, *btn;
 
     if (!ui->proc.show_statusbar) return;
 
@@ -676,11 +932,29 @@ _evisum_ui_process_list_summary_add(Evisum_Ui_Process_List_View *view) {
     evas_object_show(pb);
     elm_box_pack_end(hbx, pb);
 
-    bx = elm_box_add(view->win);
-    evas_object_size_hint_weight_set(bx, EXPAND, EXPAND);
-    evas_object_size_hint_align_set(bx, FILL, FILL);
-    elm_box_pack_end(hbx, bx);
-    evas_object_show(bx);
+    view->summary.history_slider = sli = elm_slider_add(hbx);
+    evas_object_size_hint_weight_set(sli, EXPAND, EXPAND);
+    evas_object_size_hint_align_set(sli, FILL, FILL);
+    elm_slider_indicator_visible_mode_set(sli, ELM_SLIDER_INDICATOR_VISIBLE_MODE_DEFAULT);
+    elm_slider_indicator_format_function_set_full(sli, _evisum_ui_process_list_history_indicator_format_cb,
+                                                  _evisum_ui_process_list_history_indicator_free_cb, view);
+    elm_object_text_set(sli, NULL);
+    elm_slider_unit_format_set(sli, "");
+    evas_object_smart_callback_add(sli, "changed", _evisum_ui_process_list_history_slider_preview_cb, view);
+    evas_object_smart_callback_add(sli, "slider,drag,start", _evisum_ui_process_list_history_slider_drag_start_cb, view);
+    evas_object_smart_callback_add(sli, "slider,drag,stop", _evisum_ui_process_list_history_slider_drag_stop_cb, view);
+    elm_box_pack_end(hbx, sli);
+    evas_object_show(sli);
+
+    view->summary.history_live_btn = btn = evisum_ui_widget_exel_icon_button_add(hbx, "start", _("Live"), 0.0, FILL,
+                                                                                 _evisum_ui_process_list_history_live_clicked_cb,
+                                                                                 view);
+    elm_object_disabled_set(btn, 1);
+    elm_box_pack_end(hbx, btn);
+    evas_object_show(btn);
+
+    _evisum_ui_process_list_history_bounds_update(view);
+    if (!view->history.timer) view->history.timer = ecore_timer_add(1.0, _evisum_ui_process_list_history_timer_cb, view);
 }
 
 static Eina_List *
@@ -840,7 +1114,26 @@ _evisum_ui_process_list_process_list(void *data, Ecore_Thread *thread) {
     ecore_thread_name_set(thread, "process_list");
 
     while (!ecore_thread_check(thread)) {
-        if (!first_run && !view->skip_wait) {
+        Eina_Bool history_live, history_pending;
+        uint32_t history_time;
+
+        _evisum_ui_process_list_history_state_get(view, &history_live, &history_pending, &history_time);
+        if (!history_live) {
+            if (!history_pending && !first_run && !view->skip_wait) {
+                usleep(50000);
+                continue;
+            }
+            if (history_time) {
+                Eina_Bool live_now;
+                if (!evisum_engine_history_time_set(history_time)) {
+                    usleep(50000);
+                    continue;
+                }
+                _evisum_ui_process_list_history_state_get(view, &live_now, NULL, NULL);
+                if (live_now) evisum_engine_history_live_set();
+                else _evisum_ui_process_list_history_pending_clear(view, history_time);
+            }
+        } else if (!first_run && !view->skip_wait) {
             int delay_secs = view->ui->proc.poll_delay;
             int target_ticks;
             if (delay_secs < 1) delay_secs = 1;
@@ -1544,8 +1837,27 @@ _evisum_ui_process_list_config_changed_cb(void *data, int type EINA_UNUSED, void
 
     if ((!view->summary.visible) && (ui->proc.show_statusbar)) _evisum_ui_process_list_summary_add(view);
     else if ((view->summary.visible) && (!ui->proc.show_statusbar)) {
+        evisum_engine_history_live_set();
+        eina_lock_take(&view->history.lock);
+        view->history.live = EINA_TRUE;
+        view->history.pending = EINA_FALSE;
+        view->history.sliding = EINA_FALSE;
+        view->history.requested_time = 0;
+        eina_lock_release(&view->history.lock);
+        if (view->history.timer) {
+            ecore_timer_del(view->history.timer);
+            view->history.timer = NULL;
+        }
+        if (view->history.apply_timer) {
+            ecore_timer_del(view->history.apply_timer);
+            view->history.apply_timer = NULL;
+        }
         elm_box_clear(view->summary.hbx);
         view->summary.visible = 0;
+        view->summary.pb_cpu = NULL;
+        view->summary.pb_mem = NULL;
+        view->summary.history_slider = NULL;
+        view->summary.history_live_btn = NULL;
     }
 
     _evisum_ui_process_list_win_alpha_set(view);
@@ -1580,6 +1892,8 @@ _evisum_ui_process_list_win_del_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
     if (view->search.timer) ecore_timer_del(view->search.timer);
     if (view->resize_timer) ecore_timer_del(view->resize_timer);
     if (view->main_menu_timer) ecore_timer_del(view->main_menu_timer);
+    if (view->history.timer) ecore_timer_del(view->history.timer);
+    if (view->history.apply_timer) ecore_timer_del(view->history.apply_timer);
 
     if (view->menu) evas_object_del(view->menu);
     if (view->main_menu) evas_object_del(view->main_menu);
@@ -1588,6 +1902,8 @@ _evisum_ui_process_list_win_del_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
 
     if (view->thread) ecore_thread_wait(view->thread, 0.5);
 
+    evisum_engine_history_live_set();
+
     if (view->handler) ecore_event_handler_del(view->handler);
     if (view->icon_cache) evisum_icon_cache_del(view->icon_cache);
 
@@ -1599,6 +1915,7 @@ _evisum_ui_process_list_win_del_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
     if (view->widget_exel) evisum_ui_widget_exel_free(view->widget_exel);
 
     if (view->proc_usage_cache) eina_hash_free(view->proc_usage_cache);
+    if (view->history.lock_init) eina_lock_free(&view->history.lock);
 
     free(view);
     view = NULL;
@@ -1641,6 +1958,10 @@ evisum_ui_process_list_win_add(Evisum_Ui *ui) {
 
     view->selected_pid = -1;
     view->ui = ui;
+    eina_lock_new(&view->history.lock);
+    view->history.lock_init = EINA_TRUE;
+    view->history.live = EINA_TRUE;
+    evisum_engine_history_live_set();
     view->handler
             = ecore_event_handler_add(EVISUM_EVENT_CONFIG_CHANGED, _evisum_ui_process_list_config_changed_cb, view);
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to