#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <unistd.h>
#ifdef HAVE_SYS_LOADAVG_H
# include <sys/loadavg.h>
#endif
#ifdef WITH_GNOME
# include <gnome.h>
#endif
#include <gtk/gtk.h>
#include "types.h"
#include "distcc.h"
#include "rpc.h"
#include "trace.h"
#include "exitcode.h"
#include "mon.h"
#include "renderer.h"
const char *rs_program_name = "distccmon-gnome";
static GtkWidget *chart_treeview;
static GtkListStore *chart_model;
enum {
COLUMN_HOST,
COLUMN_SLOT,
COLUMN_FILE,
COLUMN_STATE,
COLUMN_HISTORY,
};
GdkGC *dcc_phase_gc[DCC_PHASE_DONE];
#if 0
const GdkColor task_color[] = {
{ 0, 0x2222, 0, 0 },
{ 0, 0x4444, 0, 0 },
{ 0, 0x6666, 0, 0 },
{ 0, 0x8888, 0, 0 },
{ 0, 0xaaaa, 0, 0 },
{ 0, 0xcccc, 0, 0 },
{ 0, 0xeeee, 0, 0 },
{ 0, 0xffff, 0xffff, 0 },
};
#endif
const GdkColor task_color[] = {
{ 0, 0x9999, 0, 0 },
{ 0, 0x9999, 0, 0 },
{ 0, 0xc1c1, 0x6666, 0x5a5a },
{ 0, 0x8888, 0x7f7f, 0xa3a3 },
{ 0, 0xe0e0, 0xc3c3, 0x9e9e },
{ 0, 0x8383, 0xa6a6, 0x7f7f },
{ 0, 0x7575, 0x9090, 0xaeae },
{ 0, 0, 0, 0 },
};
static void
dcc_setup_tree_model (void)
{
chart_model = gtk_list_store_new (5,
G_TYPE_STRING,
G_TYPE_INT,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_POINTER
);
}
static void
dcc_row_history_push (GtkListStore *model,
GtkTreeIter *tree_iter,
enum dcc_phase new_state)
{
struct dcc_history *history;
gtk_tree_model_get(GTK_TREE_MODEL (model), tree_iter,
COLUMN_HISTORY, &history,
-1);
dcc_history_push(history, new_state);
}
static void
dcc_set_row_from_task (GtkListStore *model,
GtkTreeIter *tree_iter,
struct dcc_task_state *task)
{
dcc_row_history_push (model, tree_iter, task->curr_phase);
gtk_list_store_set (model, tree_iter,
COLUMN_HOST, task->host,
COLUMN_SLOT, task->slot,
COLUMN_FILE, task->file,
COLUMN_STATE, dcc_get_phase_name(task->curr_phase),
-1);
}
static void
dcc_insert_row_from_task (GtkListStore *model,
GtkTreeIter *tree_iter,
GtkTreeIter *insert_before,
struct dcc_task_state *task_iter)
{
struct dcc_history *history;
history = dcc_history_new();
dcc_history_push(history, task_iter->curr_phase);
gtk_list_store_insert_before(chart_model, tree_iter, insert_before);
gtk_list_store_set(model, tree_iter,
COLUMN_HOST, task_iter->host,
COLUMN_SLOT, task_iter->slot,
COLUMN_FILE, task_iter->file,
COLUMN_STATE, dcc_get_phase_name(task_iter->curr_phase),
COLUMN_HISTORY, history,
-1);
}
static void
dcc_set_row_idle(GtkListStore *model,
GtkTreeIter *tree_iter)
{
struct dcc_history *history;
gtk_tree_model_get(GTK_TREE_MODEL (model), tree_iter,
COLUMN_HISTORY, &history,
-1);
if (history->past_phases[history->now] != DCC_PHASE_DONE) {
gtk_list_store_set (model, tree_iter,
COLUMN_FILE, NULL,
COLUMN_STATE, NULL,
-1);
} else {
GtkTreePath *path;
path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), tree_iter);
gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, tree_iter);
gtk_tree_path_free(path);
}
dcc_history_push(history, DCC_PHASE_DONE);
}
static void
dcc_update_store_from_tasks (struct dcc_task_state *task_list)
{
struct dcc_task_state *task_iter;
GtkTreeIter tree_iter[1];
gboolean tree_valid;
int cmp;
GtkTreeModel *tree_model = GTK_TREE_MODEL (chart_model);
tree_valid = gtk_tree_model_get_iter_first (tree_model, tree_iter);
for (task_iter = task_list;
task_iter != NULL && tree_valid;
)
{
gchar *row_host;
int row_slot;
if (task_iter->curr_phase == DCC_PHASE_DONE
|| task_iter->host[0] == '\0'
|| task_iter->file[0] == '\0')
{
task_iter = task_iter->next;
continue;
}
gtk_tree_model_get (tree_model, tree_iter,
COLUMN_HOST, &row_host,
COLUMN_SLOT, &row_slot,
-1);
cmp = strcmp (task_iter->host, row_host);
if (cmp == 0)
cmp = task_iter->slot - row_slot;
g_free(row_host);
if (cmp == 0)
{
dcc_set_row_from_task (chart_model, tree_iter, task_iter);
task_iter = task_iter->next;
}
else if (cmp < 0)
{
dcc_insert_row_from_task (chart_model, tree_iter, tree_iter,
task_iter);
task_iter = task_iter->next;
}
else
{
dcc_set_row_idle (chart_model, tree_iter);
}
tree_valid = gtk_tree_model_iter_next (tree_model, tree_iter);
}
for (;
task_iter != NULL;
task_iter = task_iter->next)
{
if (task_iter->curr_phase == DCC_PHASE_DONE)
continue;
if (task_iter->host[0] == '\0'
|| task_iter->file[0] == '\0')
continue;
dcc_insert_row_from_task (chart_model, tree_iter,
NULL,
task_iter);
}
for (;
tree_valid;
tree_valid = gtk_tree_model_iter_next (tree_model, tree_iter))
{
dcc_set_row_idle (chart_model, tree_iter);
}
}
static gint dcc_gnome_update_cb (gpointer UNUSED(view_void))
{
struct dcc_task_state *task_list;
if (dcc_mon_poll (&task_list))
{
rs_log_warning("poll failed");
return TRUE;
}
dcc_update_store_from_tasks (task_list);
dcc_task_state_free (task_list);
return TRUE;
}
static gchar *dcc_gnome_get_title (void)
{
char host[256];
const char *user;
struct passwd *pw;
if (gethostname(host, sizeof host) == -1)
strcpy (host, "localhost");
user = NULL;
pw = getpwuid (getuid ());
if (pw)
user = pw->pw_name;
if (!user)
user = "";
return g_strdup_printf ("distcc Monitor - %s@%s",
user, host);
}
static gint dcc_gnome_load_update_cb (gpointer data)
{
gchar message[200];
double loadavg[3];
guint context_id;
if (getloadavg (loadavg, 3) == -1)
{
rs_log_error ("getloadavg failed: %s", strerror (errno));
return FALSE;
}
snprintf (message, sizeof message,
"Load average: %.2f, %.2f, %.2f",
loadavg[0], loadavg[1], loadavg[2]);
context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR (data), "load");
gtk_statusbar_pop(GTK_STATUSBAR (data), context_id);
gtk_statusbar_push(GTK_STATUSBAR (data), context_id, message);
return TRUE;
}
static void
dcc_create_state_gcs (GtkWidget *widget)
{
enum dcc_phase i_state;
for (i_state = 0; i_state < DCC_PHASE_DONE; i_state++)
{
dcc_phase_gc[i_state] = gdk_gc_new (widget->window);
gdk_gc_set_rgb_fg_color (dcc_phase_gc[i_state],
(GdkColor *) &task_color[i_state]);
}
}
static void dcc_gnome_make_proc_view (GtkTreeModel *proc_model,
GtkWidget **align_return)
{
GtkCellRenderer *text_renderer, *chart_renderer;
GtkTreeSelection *selection;
GtkTreeViewColumn *column;
GtkWidget *align, *proc_scroll;
chart_treeview = gtk_tree_view_new_with_model (proc_model);
gtk_object_set (GTK_OBJECT (chart_treeview),
"headers-visible", TRUE,
NULL);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (chart_treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
g_signal_connect_after (chart_treeview, "realize",
G_CALLBACK (dcc_create_state_gcs), NULL);
text_renderer = gtk_cell_renderer_text_new ();
chart_renderer = dcc_cell_renderer_chart_new ();
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (chart_treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
column = gtk_tree_view_column_new_with_attributes
("Host", text_renderer,
"text", COLUMN_HOST,
NULL);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW (chart_treeview), column);
column = gtk_tree_view_column_new_with_attributes
("Slot", text_renderer,
"text", COLUMN_SLOT,
NULL);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW (chart_treeview), column);
column = gtk_tree_view_column_new_with_attributes
("File", text_renderer,
"text", COLUMN_FILE,
NULL);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW (chart_treeview), column);
column = gtk_tree_view_column_new_with_attributes
("State", text_renderer,
"text", COLUMN_STATE,
NULL);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW (chart_treeview), column);
column = gtk_tree_view_column_new_with_attributes
("Tasks", chart_renderer,
"history", COLUMN_HISTORY,
NULL);
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW (chart_treeview), column);
proc_scroll = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (proc_scroll),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (proc_scroll), chart_treeview);
align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
gtk_container_add (GTK_CONTAINER (align), proc_scroll);
*align_return = align;
}
static GtkWidget * dcc_gnome_make_load_bar (void)
{
GtkWidget *bar;
gint context_id;
bar = gtk_statusbar_new ();
context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR (bar), "load");
gtk_statusbar_push(GTK_STATUSBAR (bar), context_id, "Load: ");
g_timeout_add (2000,
dcc_gnome_load_update_cb,
bar);
dcc_gnome_load_update_cb (bar);
return bar;
}
static GtkWidget * dcc_gnome_make_mainwin (void)
{
GtkWidget *mainwin;
mainwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
{
char *title;
title = dcc_gnome_get_title ();
gtk_window_set_title (GTK_WINDOW (mainwin),
title);
free (title);
}
gtk_window_set_default_size (GTK_WINDOW (mainwin), 500, 300);
g_signal_connect (GTK_OBJECT(mainwin), "delete-event",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (GTK_OBJECT(mainwin), "destroy",
G_CALLBACK (gtk_main_quit), NULL);
#if GTK_CHECK_VERSION(2,2,0)
gtk_window_set_icon_from_file (GTK_WINDOW (mainwin),
PKGDATADIR "/distccmon-gnome-icon.png",
NULL);
#endif
return mainwin;
}
static int dcc_gnome_make_app (void)
{
GtkWidget *topbox, *proc_align, *load_bar;
GtkWidget *mainwin;
mainwin = dcc_gnome_make_mainwin ();
topbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (mainwin),
topbox);
load_bar = dcc_gnome_make_load_bar ();
dcc_setup_tree_model ();
dcc_gnome_make_proc_view (GTK_TREE_MODEL (chart_model),
&proc_align);
gtk_container_add (GTK_CONTAINER (topbox),
proc_align);
gtk_box_pack_end (GTK_BOX (topbox),
load_bar,
FALSE,
FALSE,
0);
g_timeout_add_full (G_PRIORITY_HIGH_IDLE,
500,
dcc_gnome_update_cb,
NULL,
NULL);
gtk_widget_show_all (mainwin);
return 0;
}
int main(int argc, char **argv)
{
nice(5);
#if defined(WITH_GNOME)
gnome_program_init ("distccmon-gnome", PACKAGE_VERSION,
LIBGNOMEUI_MODULE,
argc, argv, NULL);
#elif defined(WITH_GTK)
gtk_init (&argc, &argv);
#else
# error This program must be built with either WITH_GTK or WITH_GNOME
#endif
dcc_gnome_make_app ();
gtk_main ();
return 0;
}