Skip to content

Instantly share code, notes, and snippets.

Created May 20, 2011 14:01
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save anonymous/982939 to your computer and use it in GitHub Desktop.
Save anonymous/982939 to your computer and use it in GitHub Desktop.
indicator-sysload is an Ubuntu appindicator that displays the system load (CPU, memory usage, network traffic).
/*
This is an Ubuntu appindicator that displays the system load
(CPU, memory usage, network traffic).
It looks like this: http://imgur.com/W4PCc .
Compile with:
gcc -Wall `pkg-config --cflags --libs gtk+-2.0 appindicator-0.1 libgtop-2.0` -o indicator-sysload ./indicator-sysload.c
WARNING:
The periodic update makes Compiz leak memory.
12-24 hours of running will cause performance
issues, and you have to restart Gnome.
License: this software is in the public domain.
*/
#include <gtk/gtk.h>
#include <libappindicator/app-indicator.h>
#include <glibtop.h>
#include <glibtop/cpu.h>
#include <glibtop/mem.h>
#include <glibtop/netload.h>
#include <glibtop/netlist.h>
/* update period in seconds */
int period = 1;
AppIndicator *indicator;
GtkWidget *indicator_menu;
GtkWidget *cpu_item;
GtkWidget *memory_item;
GtkWidget *net_down_item;
GtkWidget *net_up_item;
GtkWidget *sysmon_item;
GtkWidget *quit_item;
int get_cpu()
{
static int cpu_total_old = 0;
static int cpu_idle_old = 0;
glibtop_cpu cpu;
glibtop_get_cpu (&cpu);
int diff_total = cpu.total - cpu_total_old;
int diff_idle = cpu.idle - cpu_idle_old;
cpu_total_old = cpu.total;
cpu_idle_old = cpu.idle;
return 100 * (diff_total-diff_idle) / diff_total;
}
int get_memory(void)
{
glibtop_mem mem;
glibtop_get_mem (&mem);
/* used memory in percents */
return 100 - 100 * (mem.free + mem.buffer + mem.cached) / mem.total;
}
gchar* format_net_label(gchar *prefix, int data)
{
gchar *string;
if(data < 1000)
{
string = g_strdup_printf("%s%d B/s", prefix, data);
}
else if(data < 1000000)
{
string = g_strdup_printf("%s%.1f KiB/s", prefix, data/1000.0);
}
else
{
string = g_strdup_printf("%s%.2f MiB/s", prefix, data/1000000.0);
}
return string;
}
void get_net(int traffic[2])
{
static int bytes_in_old = 0;
static int bytes_out_old = 0;
static gboolean first_run = TRUE;
glibtop_netload netload;
glibtop_netlist netlist;
int bytes_in = 0;
int bytes_out = 0;
int i = 0;
gchar **interfaces = glibtop_get_netlist(&netlist);
for(i = 0; i < netlist.number; i++)
{
glibtop_get_netload(&netload, interfaces[i]);
bytes_in += netload.bytes_in;
bytes_out += netload.bytes_out;
}
g_strfreev(interfaces);
if(first_run)
{
bytes_in_old = bytes_in;
bytes_out_old = bytes_out;
first_run = FALSE;
}
traffic[0] = (bytes_in - bytes_in_old) / period;
traffic[1] = (bytes_out - bytes_out_old) / period;
bytes_in_old = bytes_in;
bytes_out_old = bytes_out;
}
gboolean update()
{
int cpu = get_cpu();
int memory = get_memory();
int net_traffic[2] = {0, 0};
get_net(net_traffic);
int net_down = net_traffic[0];
int net_up = net_traffic[1];
static const gchar *separator = "|";
static const gchar *nothing = " ";
/* the half and full block doesn't fit (at least in my system, perhaps a font substitution issue)*/
/*static const gchar *levels[] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};*/
static const gchar *levels[] = {"▁", "▂", "▃", "▅", "▆", "▇"};
int level_num = sizeof(levels) / sizeof("▂");
int cpu_max = 100;
int mem_max = 100;
static int net_down_max = 0;
static int net_up_max = 0;
int weight = 4;
net_down_max = MAX(net_down, (weight*net_down_max + net_down) / (weight+1));
net_up_max = MAX(net_up, (weight*net_up_max + net_up) / (weight+1));
gchar *cpu_level = (gchar *) ((cpu == 0) ? nothing : levels[level_num * cpu / (cpu_max+1)]);
gchar *memory_level = (gchar *) ((memory == 0) ? nothing : levels[level_num * memory / (mem_max+1)]);
gchar *net_down_level = (gchar *) ((net_down == 0) ? nothing : levels[level_num * net_down / (net_down_max+1)]);
gchar *net_up_level = (gchar *) ((net_up == 0) ? nothing : levels[level_num * net_up / (net_up_max+1)]);
gchar *indicator_label = g_strconcat( separator
, cpu_level
, memory_level
, net_down_level
, net_up_level
, separator
, NULL);
static const gchar *label_guide = "|▁▂▃▅|";
app_indicator_set_label(indicator, indicator_label, label_guide);
g_free(indicator_label);
gchar *cpu_label = g_strdup_printf("CPU: %d%%", cpu);
gtk_menu_item_set_label(GTK_MENU_ITEM(cpu_item), cpu_label);
g_free(cpu_label);
gchar *memory_label = g_strdup_printf("Memory: %d%%", memory);
gtk_menu_item_set_label(GTK_MENU_ITEM(memory_item), memory_label);
g_free(memory_label);
gchar *net_down_label = format_net_label("Net ↓: ", net_down);
gtk_menu_item_set_label(GTK_MENU_ITEM(net_down_item), net_down_label);
g_free(net_down_label);
gchar *net_up_label = format_net_label("Net ↑: ", net_up);
gtk_menu_item_set_label(GTK_MENU_ITEM(net_up_item), net_up_label);
g_free(net_up_label);
return TRUE;
}
static void start_sysmon(GtkWidget *widget, gpointer data)
{
GError *err = NULL;
gchar *argv[] = {"gnome-system-monitor"};
if( ! g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err))
{
fprintf(stderr, "Error: %s\n", err->message);
}
}
int main (int argc, char **argv)
{
gtk_init (&argc, &argv);
indicator_menu = gtk_menu_new();
cpu_item = gtk_image_menu_item_new_with_label("");
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), cpu_item);
memory_item = gtk_image_menu_item_new_with_label("");
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), memory_item);
net_down_item = gtk_image_menu_item_new_with_label("");
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), net_down_item);
net_up_item = gtk_image_menu_item_new_with_label("");
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), net_up_item);
GtkWidget *sep = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), sep);
sysmon_item = gtk_menu_item_new_with_label("Gnome System Monitor");
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), sysmon_item);
g_signal_connect(sysmon_item, "activate", G_CALLBACK (start_sysmon), NULL);
/* I don't think it's necessary, uncomment if you want it*/
/* quit_item = gtk_menu_item_new_with_label("Quit");*/
/* gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), quit_item);*/
/* g_signal_connect(quit_item, "activate", G_CALLBACK (gtk_main_quit), NULL);*/
gtk_widget_show_all(indicator_menu);
indicator = app_indicator_new ("sysload", "", APP_INDICATOR_CATEGORY_SYSTEM_SERVICES);
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_label(indicator, "sysload", "sysload");
app_indicator_set_menu(indicator, GTK_MENU (indicator_menu));
update();
/* update period in milliseconds */
g_timeout_add(1000*period, (GtkFunction) update, NULL);
gtk_main ();
return 0;
}
@stefansundin
Copy link

You need to first install some packages:

sudo apt-get install libgtop2-dev libappindicator-dev libgtk2.0-dev

And the compile command above is wrong. It won't link properly. You have to put indicator-sysload.c before the pkg-config command, like this:

gcc -o indicator-sysload indicator-sysload.c -Wall `pkg-config --cflags --libs gtk+-2.0 appindicator-0.1 libgtop-2.0`

Then there is a bug on line 137. It will cause the program to crash when the load gets too high. Change to:

int level_num = sizeof(levels) / sizeof(levels[0]);

You are better off just using my fork: https://gist.github.com/stefansundin/5792671
I also made it use all of the blocks, as all of them worked for me (this gist is 2 years old). I also removed the space when the load is zero, since that caused the label to bounce around too much to my liking (the width of a block != width of a space).

Enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment