diff --git a/CMakeLists.txt b/CMakeLists.txt
index 79e688837ccbf644b2bc2182c47d16d6396b59ba..6323f01e41086d288b5205c9084f4c5baa19bbc1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,6 +106,7 @@ include_directories( ${PROJECT_BINARY_DIR}
                      src
                      src/battery
                      src/clock
+                     src/graphs
                      src/systray
                      src/taskbar
                      src/launcher
@@ -136,6 +137,7 @@ set( SOURCES src/config.c
              src/drag_and_drop.c
              src/default_icon.c
              src/clock/clock.c
+             src/graphs/graphs.c
              src/systray/systraybar.c
              src/launcher/launcher.c
              src/launcher/apps-common.c
diff --git a/src/config.c b/src/config.c
index b23857c1dfe48049e3c22e3257e022360e73d368..dd527f3128a38d75322a436d78c57c1d7f22ec5b 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,60 +1,60 @@
 /**************************************************************************
-*
-* Tint2 : read/write config file
-*
-* Copyright (C) 2007 Pål Staurland (staura@gmail.com)
-* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
-*
-* This program is free software; you can redistribute it and/or
-* modify it under the terms of the GNU General Public License version 2
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-**************************************************************************/
+ *
+ * Tint2 : read/write config file
+ *
+ * Copyright (C) 2007 Pål Staurland (staura@gmail.com)
+ * Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **************************************************************************/
 
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <cairo.h>
-#include <cairo-xlib.h>
+#include <Imlib2.h>
+#include <X11/Xatom.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cairo-xlib.h>
+#include <cairo.h>
 #include <ctype.h>
 #include <glib/gstdio.h>
 #include <pango/pango-font.h>
-#include <Imlib2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
 
 #include "config.h"
 
 #ifndef TINT2CONF
 
-#include "tint2rc.h"
+#include "clock.h"
 #include "common.h"
+#include "execplugin.h"
+#include "graphs.h"
+#include "launcher.h"
+#include "panel.h"
+#include "separator.h"
 #include "server.h"
 #include "strnatcmp.h"
-#include "panel.h"
+#include "systraybar.h"
 #include "task.h"
 #include "taskbar.h"
 #include "taskbarname.h"
-#include "systraybar.h"
-#include "launcher.h"
-#include "clock.h"
-#include "window.h"
-#include "tooltip.h"
 #include "timer.h"
-#include "separator.h"
-#include "execplugin.h"
-
+#include "tint2rc.h"
+#include "tooltip.h"
+#include "window.h"
 #ifdef ENABLE_BATTERY
 #include "battery.h"
 #endif
@@ -453,6 +453,8 @@ void add_entry(char *key, char *value)
             }
             if (panel_items_order[j] == 'C')
                 clock_enabled = 1;
+            if (panel_items_order[j] == 'G')
+                graphs_enabled = 1;
         }
     } else if (strcmp(key, "panel_margin") == 0) {
         extract_values(value, &value1, &value2, &value3);
@@ -943,6 +945,70 @@ void add_entry(char *key, char *value)
             clock_dwheel_command = strdup(value);
     }
 
+    /* Graphs */
+    else if (strcmp(key, "graphs_items") == 0) {
+        int j;
+        for (j = 0; j < strlen(value); j++) {
+            if (value[j] == 'C' && graphs_cpu_pos == -1) {
+                graphs_cpu_pos = graphs_ncurves;
+                graphs_curves_per_pos[graphs_ngraphs] = 2;
+                graphs_ngraphs++;
+                graphs_ncurves += 2;
+            } else if (value[j] == 'M' && graphs_mem_pos == -1) {
+                graphs_mem_pos = graphs_ncurves;
+                graphs_curves_per_pos[graphs_ngraphs] = 2;
+                graphs_ngraphs++;
+                graphs_ncurves += 2;
+            } else if (value[j] == 'N' && graphs_net_pos == -1) {
+                graphs_net_pos = graphs_ncurves;
+                graphs_curves_per_pos[graphs_ngraphs] = 2;
+                graphs_ngraphs++;
+                graphs_ncurves += 2;
+            }
+        }
+    } else if (strcmp(key, "graphs_use_bars") == 0) {
+        graphs_use_bars = atoi(value);
+    } else if (strcmp(key, "graphs_graph_width") == 0) {
+        int w = atoi(value);
+        graphs_graph_width = (w <= 200 && w >= 10) ? w : 50;
+    } else if (strcmp(key, "graphs_netiface") == 0) {
+        if (strlen(value) > 0)
+            graphs_netiface = strdup(value);
+    } else if (strcmp(key, "graphs_netmaxup") == 0) {
+        int s = atoi(value);
+        graphs_netmaxup = s;
+    } else if (strcmp(key, "graphs_netmaxdown") == 0) {
+        int s = atoi(value);
+        graphs_netmaxdown = s;
+    } else if (strncmp(key, "graphs_color", 12) == 0) {
+        int idx = key[12] - 48;
+        if (idx >= 0 && idx < MAXCURVES) {
+            extract_values(value, &value1, &value2, &value3);
+            get_color(value1, panel_config.graphs.g[idx].rgb);
+            if (value2)
+                panel_config.graphs.g[idx].alpha = (atoi(value2) / 100.0);
+            else
+                panel_config.graphs.g[idx].alpha = 0.5;
+        }
+    } else if (strcmp(key, "graphs_padding") == 0) {
+        extract_values(value, &value1, &value2, &value3);
+        panel_config.graphs.area.paddingxlr = panel_config.graphs.area.paddingx = atoi(value1);
+        if (value2)
+            panel_config.graphs.area.paddingy = atoi(value2);
+        if (value3)
+            panel_config.graphs.area.paddingx = atoi(value3);
+    } else if (strcmp(key, "graphs_background_id") == 0) {
+        int id = atoi(value);
+        id = (id < backgrounds->len && id >= 0) ? id : 0;
+        panel_config.graphs.area.bg = &g_array_index(backgrounds, Background, id);
+    } else if (strcmp(key, "graphs_lclick_command") == 0) {
+        if (strlen(value) > 0)
+            graphs_lclick_command = strdup(value);
+    } else if (strcmp(key, "graphs_rclick_command") == 0) {
+        if (strlen(value) > 0)
+            graphs_rclick_command = strdup(value);
+    }
+
     /* Taskbar */
     else if (strcmp(key, "taskbar_mode") == 0) {
         if (strcmp(value, "multi_desktop") == 0)
@@ -1155,8 +1221,9 @@ void add_entry(char *key, char *value)
         systray_monitor = MAX(0, config_get_monitor(value));
     } else if (strcmp(key, "systray_name_filter") == 0) {
         if (systray_hide_name_filter) {
-            fprintf(stderr, "tint2: Error: duplicate option 'systray_name_filter'. Please use it only once. See "
-                            "https://gitlab.com/o9000/tint2/issues/652\n");
+            fprintf(stderr,
+                    "tint2: Error: duplicate option 'systray_name_filter'. Please use it only once. See "
+                    "https://gitlab.com/o9000/tint2/issues/652\n");
             free(systray_hide_name_filter);
         }
         systray_hide_name_filter = strdup(value);
diff --git a/src/graphs/graphs.c b/src/graphs/graphs.c
new file mode 100644
index 0000000000000000000000000000000000000000..e89d84969fe9b61b0a49a092044533b1ec237ef5
--- /dev/null
+++ b/src/graphs/graphs.c
@@ -0,0 +1,439 @@
+/**************************************************************************
+ *
+ * Tint2 : graphs
+ *
+ * Copyright (C) 2011 Francisco J. Vazquez (fjvazquezaraujo@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **************************************************************************/
+
+#include <cairo-xlib.h>
+#include <cairo.h>
+#include <ctype.h>
+#include <pango/pangocairo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "graphs.h"
+#include "panel.h"
+#include "server.h"
+#include "timer.h"
+#include "window.h"
+
+#define CPU_SAMPLE_COUNT 5
+#define NET_SAMPLE_COUNT 5
+
+int graphs_enabled;
+int graphs_ngraphs;
+int graphs_ncurves;
+int graphs_cpu_pos;
+int graphs_mem_pos;
+int graphs_net_pos;
+int graphs_curves_per_pos[MAXGRAPHS];
+int graphs_use_bars;
+int graphs_graph_width;
+int graphs_cpu_nsamples_avg;
+int graphs_net_nsamples_avg;
+char *graphs_netiface;
+int graphs_netmaxup;
+int graphs_netmaxdown;
+char *graphs_lclick_command;
+char *graphs_rclick_command;
+
+float **graph_values; // circular array list
+static int pos_x = 0; // current position in graph_values
+// static timeout* graphs_timeout;
+static Timer graphs_timer;
+
+void default_graphs()
+{
+    graphs_enabled = 0;
+    graphs_ngraphs = 0;
+    graphs_ncurves = 0;
+    graphs_cpu_pos = -1;
+    graphs_mem_pos = -1;
+    graphs_net_pos = -1;
+    graphs_use_bars = 0;
+    graphs_graph_width = 50;
+    graphs_cpu_nsamples_avg = 1;
+    graphs_net_nsamples_avg = 2;
+    graphs_netiface = 0;
+    graphs_netmaxup = 0;
+    graphs_netmaxdown = 0;
+    // graphs_timeout = 0;
+    INIT_TIMER(graphs_timer);
+    graphs_lclick_command = 0;
+    graphs_rclick_command = 0;
+}
+
+void cleanup_graphs()
+{
+    if (graphs_netiface)
+        g_free(graphs_netiface);
+    if (graphs_lclick_command)
+        g_free(graphs_lclick_command);
+    if (graphs_rclick_command)
+        g_free(graphs_rclick_command);
+    // if (graphs_timeout) stop_timeout(graphs_timeout);
+    destroy_timer(&graphs_timer);
+    int i;
+    for (i = 0; i < graphs_ncurves; i++) {
+        free(graph_values[i]);
+    }
+    free(graph_values);
+}
+
+int update_cpugraph()
+{
+    if (graphs_cpu_pos == -1)
+        return 0;
+
+    struct cpu_info {
+        unsigned long long last_total;
+        unsigned long long last_active_total, last_wait_total;
+        int cur_idx;
+        double active[CPU_SAMPLE_COUNT], wait[CPU_SAMPLE_COUNT];
+        float active_perc, wait_perc;
+    };
+    static struct cpu_info cpu = {.cur_idx = 0};
+
+    FILE *fp;
+    if ((fp = fopen("/proc/stat", "r")) == NULL) {
+        return 0;
+    }
+    char buf[256];
+    while (!feof(fp)) {
+        if (fgets(buf, 255, fp) == NULL) {
+            break;
+        }
+        if (strncmp(buf, "cpu", 3) == 0) {
+            unsigned long long cpu_user;
+            unsigned long long cpu_system;
+            unsigned long long cpu_nice;
+            unsigned long long cpu_idle;
+            unsigned long long cpu_iowait;
+            unsigned long long cpu_irq;
+            unsigned long long cpu_softirq;
+            unsigned long long cpu_steal;
+            unsigned long long cpu_total;
+            unsigned long long cpu_active_total;
+            unsigned long long cpu_wait_total;
+            sscanf(buf,
+                   "%*s %llu %llu %llu %llu %llu %llu %llu %llu",
+                   &cpu_user,
+                   &cpu_nice,
+                   &cpu_system,
+                   &cpu_idle,
+                   &cpu_iowait,
+                   &cpu_irq,
+                   &cpu_softirq,
+                   &cpu_steal);
+            cpu_total = cpu_user + cpu_nice + cpu_system + cpu_idle + cpu_iowait + cpu_irq + cpu_softirq + cpu_steal;
+            cpu_active_total = cpu_user + cpu_nice + cpu_system + cpu_steal;
+            cpu_wait_total = cpu_total - cpu_active_total - cpu_idle;
+            double time = (cpu_total - cpu.last_total);
+            cpu.active[cpu.cur_idx] = (cpu_active_total - cpu.last_active_total) / time;
+            cpu.wait[cpu.cur_idx] = (cpu_wait_total - cpu.last_wait_total) / time;
+            cpu.last_total = cpu_total;
+            cpu.last_active_total = cpu_active_total;
+            cpu.last_wait_total = cpu_wait_total;
+            double curtmp1 = 0;
+            double curtmp2 = 0;
+            int i;
+            // Average the samples
+            for (i = 0; i < graphs_cpu_nsamples_avg; i++) {
+                int idx2 = (cpu.cur_idx + CPU_SAMPLE_COUNT - i) % CPU_SAMPLE_COUNT;
+                curtmp1 += cpu.active[idx2];
+                curtmp2 += cpu.wait[idx2];
+            }
+            cpu.active_perc = curtmp1 / (float)graphs_cpu_nsamples_avg;
+            cpu.wait_perc = curtmp2 / (float)graphs_cpu_nsamples_avg;
+            cpu.cur_idx = (cpu.cur_idx + 1) % CPU_SAMPLE_COUNT;
+            break; // Ignore the rest
+        }
+    }
+    fclose(fp);
+
+    graph_values[graphs_cpu_pos][pos_x] = cpu.active_perc;
+    graph_values[graphs_cpu_pos + 1][pos_x] = cpu.active_perc + cpu.wait_perc;
+
+    return 0;
+}
+
+int update_memgraph()
+{
+    if (graphs_mem_pos == -1)
+        return 0;
+
+    long long unsigned int memtotal = 0, memfree = 0, buffers = 0, cached = 0;
+
+    FILE *fp;
+    if ((fp = fopen("/proc/meminfo", "r")) == NULL) {
+        return 0;
+    }
+    char buf[256];
+    while (!feof(fp)) {
+        if (fgets(buf, 255, fp) == NULL) {
+            break;
+        }
+        if (strncmp(buf, "MemTotal:", 9) == 0) {
+            sscanf(buf, "%*s %llu", &memtotal);
+        } else if (strncmp(buf, "MemFree:", 8) == 0) {
+            sscanf(buf, "%*s %llu", &memfree);
+        } else if (strncmp(buf, "Buffers:", 8) == 0) {
+            sscanf(buf, "%*s %llu", &buffers);
+        } else if (strncmp(buf, "Cached:", 7) == 0) {
+            sscanf(buf, "%*s %llu", &cached);
+        }
+    }
+    fclose(fp);
+
+    long long unsigned int used = memtotal - memfree;
+    long long unsigned int bufcach = buffers + cached;
+
+    graph_values[graphs_mem_pos][pos_x] = (used - bufcach) / (float)memtotal;
+    graph_values[graphs_mem_pos + 1][pos_x] = used / (float)memtotal;
+
+    return 0;
+}
+
+int update_netgraph()
+{
+    if (graphs_net_pos == -1 || graphs_netiface == NULL)
+        return 0;
+
+    struct net_stat {
+        long long last_down, last_up;
+        int cur_idx;
+        long long down[NET_SAMPLE_COUNT], up[NET_SAMPLE_COUNT];
+        double down_rate, up_rate;
+        double max_down, max_up;
+    };
+    static struct net_stat net = {.cur_idx = 0, .max_down = 0, .max_up = 0};
+
+    double max(double v1, double v2)
+    {
+        return v1 > v2 ? v1 : v2;
+    }
+    double min(double v1, double v2)
+    {
+        return v1 < v2 ? v1 : v2;
+    }
+
+    FILE *fp;
+    if (!(fp = fopen("/proc/net/dev", "r"))) {
+        return 0;
+    }
+    char buf[256];
+    // Ignore first two lines
+    fgets(buf, 255, fp);
+    fgets(buf, 255, fp);
+    static int first_run = 1;
+    while (!feof(fp)) {
+        if (fgets(buf, 255, fp) == NULL) {
+            break;
+        }
+        char *p = buf;
+        while (isspace((int)*p))
+            p++;
+        char *curdev = p;
+        while (*p && *p != ':')
+            p++;
+        if (*p == '\0')
+            continue;
+        *p = '\0';
+
+        if (strcmp(curdev, graphs_netiface))
+            continue;
+
+        long long down, up;
+        sscanf(p + 1, "%lld %*d %*d %*d %*d %*d %*d %*d %lld", &down, &up);
+        if (down < net.last_down)
+            net.last_down = 0; // Overflow
+        if (up < net.last_up)
+            net.last_up = 0; // Overflow
+        net.down[net.cur_idx] = (down - net.last_down);
+        net.up[net.cur_idx] = (up - net.last_up);
+        net.last_down = down;
+        net.last_up = up;
+        if (first_run) {
+            first_run = 0;
+            break;
+        }
+
+        unsigned int curtmp1 = 0;
+        unsigned int curtmp2 = 0;
+        // Average the samples
+        int i;
+        for (i = 0; i < graphs_net_nsamples_avg; i++) {
+            curtmp1 += net.down[(net.cur_idx + NET_SAMPLE_COUNT - i) % NET_SAMPLE_COUNT];
+            curtmp2 += net.up[(net.cur_idx + NET_SAMPLE_COUNT - i) % NET_SAMPLE_COUNT];
+        }
+        net.down_rate = curtmp1 / (double)graphs_net_nsamples_avg;
+        net.up_rate = curtmp2 / (double)graphs_net_nsamples_avg;
+        if (graphs_netmaxdown > 0) {
+            net.down_rate /= (float)graphs_netmaxdown;
+            net.down_rate = min(1.0, net.down_rate);
+        } else {
+            // Normalize by maximum speed (a priori unknown,
+            // so we must do this all the time).
+            if (net.max_down < net.down_rate) {
+                for (i = 0; i < graphs_graph_width; i++) {
+                    graph_values[graphs_net_pos][i] *= (net.max_down / net.down_rate);
+                }
+                net.max_down = net.down_rate;
+                net.down_rate = 1.0;
+            } else if (net.max_down != 0)
+                net.down_rate /= net.max_down;
+        }
+        if (graphs_netmaxup > 0) {
+            net.up_rate /= (float)graphs_netmaxup;
+            net.up_rate = min(1.0, net.up_rate);
+        } else {
+            if (net.max_up < net.up_rate) {
+                for (i = 0; i < graphs_graph_width; i++) {
+                    graph_values[graphs_net_pos + 1][i] *= (net.max_up / net.up_rate);
+                }
+                net.max_up = net.up_rate;
+                net.up_rate = 1.0;
+            } else if (net.max_up != 0)
+                net.up_rate /= net.max_up;
+        }
+        net.cur_idx = (net.cur_idx + 1) % NET_SAMPLE_COUNT;
+        break; // Ignore the rest
+    }
+    fclose(fp);
+
+    graph_values[graphs_net_pos][pos_x] = net.down_rate;
+    graph_values[graphs_net_pos + 1][pos_x] = net.up_rate;
+
+    return 0;
+}
+
+void update_graphs(void *arg)
+{
+    update_cpugraph();
+    update_memgraph();
+    update_netgraph();
+    int i;
+    for (i = 0; i < num_panels; i++)
+        panels[i].graphs.area._redraw_needed = TRUE;
+
+    // panel1[i].graphs.area.redraw = 1;
+    // schedule_redraw(&
+    panel_refresh = 1;
+}
+
+void init_graphs()
+{
+    if (!graphs_enabled || !graphs_ngraphs)
+        return;
+
+    if (!graphs_timer.enabled_) {
+        change_timer(&graphs_timer, true, 10, 1000, update_graphs, &graphs_timer);
+    }
+    int i;
+    graph_values = (float **)malloc(graphs_ncurves * sizeof(float *));
+    for (i = 0; i < graphs_ncurves; i++) {
+        unsigned int gsize = graphs_graph_width * sizeof(float);
+        graph_values[i] = malloc(gsize);
+        memset(graph_values[i], 0, gsize);
+    }
+}
+
+void init_graphs_panel(void *p)
+{
+    if (!graphs_enabled || !graphs_ngraphs)
+        return;
+
+    Panel *panel = (Panel *)p;
+    Graphs *graphs = &panel->graphs;
+
+    if (graphs->area.bg == 0)
+        graphs->area.bg = &g_array_index(backgrounds, Background, 0);
+    graphs->area.parent = p;
+    graphs->area.panel = p;
+    graphs->area._draw_foreground = draw_graphs;
+    graphs->area.size_mode = LAYOUT_FIXED;
+
+    graphs->area._resize = resize_graphs;
+
+    graphs->area.resize_needed = 1;
+    graphs->area.on_screen = 1;
+}
+
+void draw_graphs(void *obj, cairo_t *c)
+{
+    Graphs *graphs = obj;
+
+    cairo_set_line_width(c, 1.0);
+    int i, i2, g, cv;
+    int x = graphs->area.paddingxlr + graphs->area.bg->border.width + 1;
+    int y1 = graphs->area.height - graphs->area.bg->border.width - graphs->area.paddingy;
+    for (g = 0, cv = 0; g < graphs_ngraphs; g++) {
+        for (i2 = 0; i2 < graphs_curves_per_pos[g]; i2++, cv++) {
+            cairo_set_source_rgba(c,
+                                  graphs->g[cv].rgb[0],
+                                  graphs->g[cv].rgb[1],
+                                  graphs->g[cv].rgb[2],
+                                  graphs->g[cv].alpha);
+            for (i = 0; i < graphs_graph_width; i++) {
+                int idx = (pos_x + 1 + i + graphs_graph_width) % graphs_graph_width;
+                int y2 = y1 - graph_values[cv][idx] * (y1 - graphs->area.bg->border.width - graphs->area.paddingy);
+                if (graphs_use_bars)
+                    cairo_move_to(c, x + i, y1);
+                cairo_line_to(c, x + i, y2);
+            }
+            cairo_stroke(c);
+        }
+        x += graphs->area.paddingx + graphs_graph_width;
+    }
+    pos_x = (pos_x + 1) % graphs_graph_width;
+}
+
+int resize_graphs(void *obj)
+{
+    Graphs *graphs = obj;
+    int ret = 0;
+
+    graphs->area._redraw_needed = TRUE;
+
+    if (panel_horizontal) {
+        int new_size = (2 * graphs->area.paddingxlr) + (2 * graphs->area.bg->border.width) +
+                       graphs_ngraphs * (graphs_graph_width) + (graphs_ngraphs - 1) * (graphs->area.paddingx);
+        if (new_size != graphs->area.width) {
+            graphs->area.width = new_size + 1;
+            ret = 1;
+        }
+    }
+
+    return ret;
+}
+
+void graphs_action(void *obj, int button, int x, int y, Time time)
+{
+
+    char *command = NULL;
+    switch (button) {
+    case 1:
+        command = graphs_lclick_command;
+        break;
+    case 3:
+        command = graphs_rclick_command;
+        break;
+    }
+
+    tint_exec(command, NULL, NULL, time, obj, x, y, FALSE, TRUE);
+}
diff --git a/src/graphs/graphs.h b/src/graphs/graphs.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ab203daf553153d3267b5905149381bb3a0825f
--- /dev/null
+++ b/src/graphs/graphs.h
@@ -0,0 +1,52 @@
+/**************************************************************************
+ * Copyright (C) 2011 Francisco J. Vazquez (fjvazquezaraujo@gmail.com)
+ *
+ **************************************************************************/
+
+#ifndef GRAPHS_H
+#define GRAPHS_H
+
+#include "area.h"
+#include "common.h"
+#include <sys/time.h>
+
+#define MAXGRAPHS 3
+#define MAXCURVES 6
+
+typedef struct Graphs {
+    // always start with area
+    Area area;
+
+    char *netdev;
+    Color g[MAXCURVES];
+} Graphs;
+
+extern int graphs_enabled;
+extern int graphs_ngraphs;
+extern int graphs_ncurves;
+extern int graphs_cpu_pos;
+extern int graphs_mem_pos;
+extern int graphs_net_pos;
+extern int graphs_curves_per_pos[MAXGRAPHS];
+extern int graphs_use_bars;
+extern int graphs_graph_width;
+extern char *graphs_netiface;
+extern int graphs_netmaxup;
+extern int graphs_netmaxdown;
+extern char *graphs_lclick_command;
+extern char *graphs_rclick_command;
+
+void default_graphs();
+
+void cleanup_graphs();
+
+void init_graphs();
+void init_graphs_panel(void *panel);
+
+void draw_graphs(void *obj, cairo_t *c);
+
+int resize_graphs(void *obj);
+
+void graphs_action(void *obj, int button, int x, int y, Time time);
+
+#endif
diff --git a/src/init.c b/src/init.c
index 772997a2fef67c8a14d0ae1594f47ae318051529..7f660edab3f10f1b1e6d9040f7f089abf0f27959 100644
--- a/src/init.c
+++ b/src/init.c
@@ -5,16 +5,18 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <pango/pango-font.h>
 
+#include <X11/Xatom.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
-#include <X11/Xatom.h>
 #include <X11/extensions/XShm.h>
 
 #include "config.h"
 #include "default_icon.h"
 #include "drag_and_drop.h"
 #include "fps_distribution.h"
+#include "graphs.h"
 #include "panel.h"
 #include "server.h"
 #include "signals.h"
@@ -57,7 +59,7 @@ void handle_cli_arguments(int argc, char **argv)
             run_all_tests(true);
             exit(0);
         } else if (strcmp(argv[i], "--dump-image-data") == 0) {
-            dump_image_data(argv[i+1], argv[i+2]);
+            dump_image_data(argv[i + 1], argv[i + 2]);
             exit(0);
         } else if (strcmp(argv[i], "-c") == 0) {
             if (i + 1 < argc) {
@@ -164,6 +166,7 @@ void create_default_elements()
     default_battery();
 #endif
     default_clock();
+    default_graphs();
     default_launcher();
     default_taskbar();
     default_tooltip();
@@ -182,9 +185,7 @@ void load_default_task_icon()
         g_free(path);
     }
     if (!default_icon) {
-        default_icon = imlib_create_image_using_data(default_icon_width,
-                                                     default_icon_height,
-                                                     default_icon_data);
+        default_icon = imlib_create_image_using_data(default_icon_width, default_icon_height, default_icon_data);
     }
 }
 
@@ -273,6 +274,7 @@ void cleanup()
     cleanup_systray();
     cleanup_tooltip();
     cleanup_clock();
+    cleanup_graphs();
     cleanup_launcher();
 #ifdef ENABLE_BATTERY
     cleanup_battery();
diff --git a/src/mouse_actions.c b/src/mouse_actions.c
index 2f791fd8c70f720c12a815364a47fd3101947028..108df173ec6f3e16d03370fad20ac81190771208 100644
--- a/src/mouse_actions.c
+++ b/src/mouse_actions.c
@@ -1,18 +1,18 @@
 /**************************************************************************
-* Copyright (C) 2017 tint2 authors
-*
-* This program is free software; you can redistribute it and/or
-* modify it under the terms of the GNU General Public License version 2
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-**************************************************************************/
+ * Copyright (C) 2017 tint2 authors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **************************************************************************/
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -195,7 +195,24 @@ void handle_mouse_release_event(XEvent *e)
 
     Clock *clock = click_clock(panel, e->xbutton.x, e->xbutton.y);
     if (clock) {
-        clock_action(clock, e->xbutton.button, e->xbutton.x - clock->area.posx, e->xbutton.y - clock->area.posy, e->xbutton.time);
+        clock_action(clock,
+                     e->xbutton.button,
+                     e->xbutton.x - clock->area.posx,
+                     e->xbutton.y - clock->area.posy,
+                     e->xbutton.time);
+        if (panel_layer == BOTTOM_LAYER)
+            XLowerWindow(server.display, panel->main_win);
+        task_drag = 0;
+        return;
+    }
+
+    Graphs *graphs = click_graphs(panel, e->xbutton.x, e->xbutton.y);
+    if (graphs) {
+        graphs_action(graphs,
+                      e->xbutton.button,
+                      e->xbutton.x - graphs->area.posx,
+                      e->xbutton.y - graphs->area.posy,
+                      e->xbutton.time);
         if (panel_layer == BOTTOM_LAYER)
             XLowerWindow(server.display, panel->main_win);
         task_drag = 0;
@@ -205,7 +222,11 @@ void handle_mouse_release_event(XEvent *e)
 #ifdef ENABLE_BATTERY
     Battery *battery = click_battery(panel, e->xbutton.x, e->xbutton.y);
     if (battery) {
-        battery_action(battery, e->xbutton.button, e->xbutton.x - battery->area.posx, e->xbutton.y - battery->area.posy, e->xbutton.time);
+        battery_action(battery,
+                       e->xbutton.button,
+                       e->xbutton.x - battery->area.posx,
+                       e->xbutton.y - battery->area.posy,
+                       e->xbutton.time);
         if (panel_layer == BOTTOM_LAYER)
             XLowerWindow(server.display, panel->main_win);
         task_drag = 0;
@@ -215,7 +236,11 @@ void handle_mouse_release_event(XEvent *e)
 
     Execp *execp = click_execp(panel, e->xbutton.x, e->xbutton.y);
     if (execp) {
-        execp_action(execp, e->xbutton.button, e->xbutton.x - execp->area.posx, e->xbutton.y - execp->area.posy, e->xbutton.time);
+        execp_action(execp,
+                     e->xbutton.button,
+                     e->xbutton.x - execp->area.posx,
+                     e->xbutton.y - execp->area.posy,
+                     e->xbutton.time);
         if (panel_layer == BOTTOM_LAYER)
             XLowerWindow(server.display, panel->main_win);
         task_drag = 0;
@@ -224,7 +249,11 @@ void handle_mouse_release_event(XEvent *e)
 
     Button *button = click_button(panel, e->xbutton.x, e->xbutton.y);
     if (button) {
-        button_action(button, e->xbutton.button, e->xbutton.x - button->area.posx, e->xbutton.y - button->area.posy, e->xbutton.time);
+        button_action(button,
+                      e->xbutton.button,
+                      e->xbutton.x - button->area.posx,
+                      e->xbutton.y - button->area.posy,
+                      e->xbutton.time);
         if (panel_layer == BOTTOM_LAYER)
             XLowerWindow(server.display, panel->main_win);
         task_drag = 0;
diff --git a/src/panel.c b/src/panel.c
index 542c08797459c9b766454aab0b203d78d04a0a34..9dc1165f7ce41a84c47912e209f9cbb77356165b 100644
--- a/src/panel.c
+++ b/src/panel.c
@@ -1,40 +1,40 @@
 /**************************************************************************
-*
-* Copyright (C) 2008 Pål Staurland (staura@gmail.com)
-* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
-*
-* This program is free software; you can redistribute it and/or
-* modify it under the terms of the GNU General Public License version 2
-* as published by the Free Software Foundation.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-**************************************************************************/
+ *
+ * Copyright (C) 2008 Pål Staurland (staura@gmail.com)
+ * Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ **************************************************************************/
 
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <cairo-xlib.h>
+#include <cairo.h>
 #include <glib.h>
+#include <pango/pangocairo.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <cairo.h>
-#include <cairo-xlib.h>
-#include <pango/pangocairo.h>
 
-#include "server.h"
 #include "config.h"
-#include "window.h"
-#include "task.h"
 #include "panel.h"
+#include "server.h"
+#include "task.h"
 #include "tooltip.h"
+#include "window.h"
 
 void panel_clear_background(void *obj);
 
@@ -103,7 +103,7 @@ void default_panel()
     panel_autohide_height = 5; // for vertical panels this is of course the width
     panel_shrink = FALSE;
     panel_strut_policy = STRUT_FOLLOW_SIZE;
-    panel_dock = FALSE;         // default not in the dock
+    panel_dock = FALSE; // default not in the dock
     panel_pivot_struts = FALSE;
     panel_layer = BOTTOM_LAYER; // default is bottom layer
     panel_window_name = strdup("tint2");
@@ -195,6 +195,7 @@ void init_panel()
     init_systray();
     init_launcher();
     init_clock();
+    init_graphs();
 #ifdef ENABLE_BATTERY
     init_battery();
 #endif
@@ -231,8 +232,11 @@ void init_panel()
             p->scale = 1;
         if (ui_scale_monitor_size_ref > 0)
             p->scale *= server.monitors[p->monitor].height / ui_scale_monitor_size_ref;
-        if (p->scale > 8 || p->scale < 1./8) {
-            fprintf(stderr, RED "tint2: panel %d having scale %g outside bounds, resetting to 1.0" RESET "\n", i + 1, p->scale);
+        if (p->scale > 8 || p->scale < 1. / 8) {
+            fprintf(stderr,
+                    RED "tint2: panel %d having scale %g outside bounds, resetting to 1.0" RESET "\n",
+                    i + 1,
+                    p->scale);
             p->scale = 1;
         }
         fprintf(stderr, BLUE "tint2: panel %d uses scale %g " RESET "\n", i + 1, p->scale);
@@ -255,6 +259,8 @@ void init_panel()
                 init_launcher_panel(p);
             if (panel_items_order[k] == 'T')
                 init_taskbar_panel(p);
+            if (panel_items_order[k] == 'G')
+                init_graphs_panel(p);
 #ifdef ENABLE_BATTERY
             if (panel_items_order[k] == 'B')
                 init_battery_panel(p);
@@ -432,8 +438,8 @@ void panel_compute_position(Panel *panel)
         panel->hidden_width = panel->area.width - diff;
         panel->hidden_height = panel->area.height;
     }
-    // fprintf(stderr, "tint2: panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width,
-    // panel->area.height);
+    // fprintf(stderr, "tint2: panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy,
+    // panel->area.width, panel->area.height);
 }
 
 void init_panel_size_and_position(Panel *panel)
@@ -528,7 +534,9 @@ gboolean resize_panel(void *obj)
         if (num_tasks > 0) {
             int task_size = total_size / num_tasks;
             if (taskbar_alignment != ALIGN_LEFT)
-                task_size = MIN(task_size, panel_horizontal ? panel_config.g_task.maximum_width : panel_config.g_task.maximum_height);
+                task_size =
+                    MIN(task_size,
+                        panel_horizontal ? panel_config.g_task.maximum_width : panel_config.g_task.maximum_height);
             for (int i = 0; i < panel->num_desktops; i++) {
                 Taskbar *taskbar = &panel->taskbar[i];
                 if (!taskbar->area.on_screen)
@@ -612,19 +620,19 @@ gboolean resize_panel(void *obj)
     return FALSE;
 }
 
-#define STRUT_LEFT      0
-#define STRUT_RIGHT     1
-#define STRUT_TOP       2
-#define STRUT_BOTTOM    3
-#define STRUT_LEFT_Y1   4
-#define STRUT_LEFT_Y2   5
-#define STRUT_RIGHT_Y1  6
-#define STRUT_RIGHT_Y2  7
-#define STRUT_TOP_X1    8
-#define STRUT_TOP_X2    9
+#define STRUT_LEFT 0
+#define STRUT_RIGHT 1
+#define STRUT_TOP 2
+#define STRUT_BOTTOM 3
+#define STRUT_LEFT_Y1 4
+#define STRUT_LEFT_Y2 5
+#define STRUT_RIGHT_Y1 6
+#define STRUT_RIGHT_Y2 7
+#define STRUT_TOP_X1 8
+#define STRUT_TOP_X2 9
 #define STRUT_BOTTOM_X1 10
 #define STRUT_BOTTOM_X2 11
-#define STRUT_COUNT     12
+#define STRUT_COUNT 12
 #define STRUT_COUNT_OLD 4
 
 void update_strut(Panel *p)
@@ -644,7 +652,8 @@ void update_strut(Panel *p)
     long struts[STRUT_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     if (panel_horizontal ^ panel_pivot_struts) {
         int height = p->area.height + p->marginy;
-        if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
+        if (panel_strut_policy == STRUT_MINIMUM ||
+            (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
             height = p->hidden_height;
         if (panel_position & TOP) {
             struts[STRUT_TOP] = height + monitor.y;
@@ -659,7 +668,8 @@ void update_strut(Panel *p)
         }
     } else {
         int width = p->area.width + p->marginx;
-        if (panel_strut_policy == STRUT_MINIMUM || (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
+        if (panel_strut_policy == STRUT_MINIMUM ||
+            (panel_strut_policy == STRUT_FOLLOW_SIZE && panel_autohide && p->is_hidden))
             width = p->hidden_width;
         if (panel_position & LEFT) {
             struts[STRUT_LEFT] = width + monitor.x;
@@ -712,6 +722,10 @@ void set_panel_items_order(Panel *p)
             for (int j = 0; j < p->num_desktops; j++)
                 p->area.children = g_list_append(p->area.children, &p->taskbar[j]);
         }
+
+        if (panel_items_order[k] == 'G')
+            p->area.children = g_list_append(p->area.children, &p->graphs);
+
 #ifdef ENABLE_BATTERY
         if (panel_items_order[k] == 'B')
             p->area.children = g_list_append(p->area.children, &p->battery);
@@ -1073,6 +1087,14 @@ LauncherIcon *click_launcher_icon(Panel *panel, int x, int y)
     return NULL;
 }
 
+Graphs *click_graphs(Panel *panel, int x, int y)
+{
+    Graphs *gr = &panel->graphs;
+    if (area_is_under_mouse(gr, x, y))
+        return gr;
+    return NULL;
+}
+
 Clock *click_clock(Panel *panel, int x, int y)
 {
     Clock *clock = &panel->clock;
diff --git a/src/panel.h b/src/panel.h
index cf1373c897a45a736020cf4eaf3901c5e59d162a..0217144501d0396e3b9f7b4eb0a0e8e4fa3a6511 100644
--- a/src/panel.h
+++ b/src/panel.h
@@ -1,12 +1,12 @@
 /**************************************************************************
-* Copyright (C) 2008 Pål Staurland (staura@gmail.com)
-* Modified (C) 2008/2009 thierry lorthiois (lorthiois@bbsoft.fr)
-*
-* panel :
-* - draw panel and all objects according to panel_layout
-*
-*
-**************************************************************************/
+ * Copyright (C) 2008 Pål Staurland (staura@gmail.com)
+ * Modified (C) 2008/2009 thierry lorthiois (lorthiois@bbsoft.fr)
+ *
+ * panel :
+ * - draw panel and all objects according to panel_layout
+ *
+ *
+ **************************************************************************/
 
 #ifndef PANEL_H
 #define PANEL_H
@@ -14,16 +14,17 @@
 #include <pango/pangocairo.h>
 #include <sys/time.h>
 
-#include "common.h"
+#include "button.h"
 #include "clock.h"
-#include "task.h"
-#include "taskbar.h"
-#include "systraybar.h"
-#include "launcher.h"
-#include "freespace.h"
+#include "common.h"
 #include "execplugin.h"
+#include "freespace.h"
+#include "graphs.h"
+#include "launcher.h"
 #include "separator.h"
-#include "button.h"
+#include "systraybar.h"
+#include "task.h"
+#include "taskbar.h"
 
 #ifdef ENABLE_BATTERY
 #include "battery.h"
@@ -136,6 +137,8 @@ typedef struct Panel {
 
     Clock clock;
 
+    Graphs graphs;
+
 #ifdef ENABLE_BATTERY
     Battery battery;
 #endif
@@ -192,6 +195,7 @@ Task *click_task(Panel *panel, int x, int y);
 Launcher *click_launcher(Panel *panel, int x, int y);
 LauncherIcon *click_launcher_icon(Panel *panel, int x, int y);
 Clock *click_clock(Panel *panel, int x, int y);
+Graphs *click_graphs(Panel *panel, int x, int y);
 
 #ifdef ENABLE_BATTERY
 Battery *click_battery(Panel *panel, int x, int y);
