flist: add flist_first_entry()
[fio.git] / gfio.c
diff --git a/gfio.c b/gfio.c
index d1d1313bdaefc279c5010bff27431138541a7da8..37c1db6918d0f8e25280bceea5ec6735dcabee0d 100644 (file)
--- a/gfio.c
+++ b/gfio.c
@@ -1,7 +1,7 @@
 /*
  * gfio - gui front end for fio - the flexible io tester
  *
- * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 
+ * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
  * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
  *
  * The license below covers all files distributed with fio unless otherwise
@@ -41,7 +41,8 @@ static int gfio_server_running;
 static unsigned int gfio_graph_limit = 100;
 
 GdkColor gfio_color_white;
-const char *gfio_graph_font;
+GdkColor gfio_color_lightyellow;
+const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
 
 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
 
@@ -75,43 +76,49 @@ static struct button_spec {
        },
 };
 
-static struct graph *setup_iops_graph(void)
+static void setup_iops_graph(struct gfio_graphs *gg)
 {
        struct graph *g;
 
        g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
        graph_title(g, "IOPS (IOs/sec)");
        graph_x_title(g, "Time (secs)");
-       graph_add_label(g, "Read IOPS");
-       graph_add_label(g, "Write IOPS");
-       graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
-       graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
+       gg->read_iops = graph_add_label(g, "Read IOPS");
+       gg->write_iops = graph_add_label(g, "Write IOPS");
+       gg->trim_iops = graph_add_label(g, "Trim IOPS");
+       graph_set_color(g, gg->read_iops, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
+       graph_set_color(g, gg->write_iops, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
+       graph_set_color(g, gg->trim_iops, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
        line_graph_set_data_count_limit(g, gfio_graph_limit);
        graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
-       return g;
+       graph_set_graph_all_zeroes(g, 0);
+       gg->iops_graph = g;
 }
 
-static struct graph *setup_bandwidth_graph(void)
+static void setup_bandwidth_graph(struct gfio_graphs *gg)
 {
        struct graph *g;
 
        g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
        graph_title(g, "Bandwidth (bytes/sec)");
        graph_x_title(g, "Time (secs)");
-       graph_add_label(g, "Read Bandwidth");
-       graph_add_label(g, "Write Bandwidth");
-       graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
-       graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
+       gg->read_bw = graph_add_label(g, "Read Bandwidth");
+       gg->write_bw = graph_add_label(g, "Write Bandwidth");
+       gg->trim_bw = graph_add_label(g, "Trim Bandwidth");
+       graph_set_color(g, gg->read_bw, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
+       graph_set_color(g, gg->write_bw, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
+       graph_set_color(g, gg->trim_bw, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
        graph_set_base_offset(g, 1);
        line_graph_set_data_count_limit(g, 100);
        graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
-       return g;
+       graph_set_graph_all_zeroes(g, 0);
+       gg->bandwidth_graph = g;
 }
 
 static void setup_graphs(struct gfio_graphs *g)
 {
-       g->iops_graph = setup_iops_graph();
-       g->bandwidth_graph = setup_bandwidth_graph();
+       setup_iops_graph(g);
+       setup_bandwidth_graph(g);
 }
 
 void clear_ge_ui_info(struct gui_entry *ge)
@@ -291,7 +298,7 @@ static void gfio_ui_setup_log(struct gui *ui)
        GtkListStore *model;
        GtkWidget *tree_view;
 
-       model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
+       model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
 
        tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
        gtk_widget_set_can_focus(tree_view, FALSE);
@@ -313,11 +320,13 @@ static void gfio_ui_setup_log(struct gui *ui)
 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
                                   gpointer data)
 {
+       guint width = gtk_widget_get_allocated_width(w);
+       guint height = gtk_widget_get_allocated_height(w);
        struct gfio_graphs *g = data;
 
-       graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
-       graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
-       graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
+       graph_set_size(g->iops_graph, width / 2.0, height);
+       graph_set_position(g->iops_graph, width / 2.0, 0.0);
+       graph_set_size(g->bandwidth_graph, width / 2.0, height);
        graph_set_position(g->bandwidth_graph, 0, 0);
        return TRUE;
 }
@@ -353,7 +362,7 @@ static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
        struct gfio_graphs *g = p;
        cairo_t *cr;
 
-       cr = gdk_cairo_create(w->window);
+       cr = gdk_cairo_create(gtk_widget_get_window(w));
 
        if (graph_has_tooltips(g->iops_graph) ||
            graph_has_tooltips(g->bandwidth_graph)) {
@@ -376,16 +385,20 @@ static void ge_destroy(struct gui_entry *ge)
 {
        struct gfio_client *gc = ge->client;
 
-       if (gc && gc->client) {
-               if (ge->state >= GE_STATE_CONNECTED)
-                       fio_client_terminate(gc->client);
+       if (gc) {
+               if (gc->client) {
+                       if (ge->state >= GE_STATE_CONNECTED)
+                               fio_client_terminate(gc->client);
 
-               fio_put_client(gc->client);
+                       fio_put_client(gc->client);
+               }
+               free(gc);
        }
 
+       g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
+
        free(ge->job_file);
        free(ge->host);
-       flist_del(&ge->list);
        free(ge);
 }
 
@@ -398,18 +411,11 @@ static void ge_widget_destroy(GtkWidget *w, gpointer data)
 
 static void gfio_quit(struct gui *ui)
 {
-       struct gui_entry *ge;
-
-       while (!flist_empty(&ui->list)) {
-               ge = flist_entry(ui->list.next, struct gui_entry, list);
-               ge_destroy(ge);
-       }
-
-        gtk_main_quit();
+       gtk_main_quit();
 }
 
 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
-                __attribute__((unused)) gpointer data)
+                        gpointer data)
 {
        struct gui *ui = (struct gui *) data;
 
@@ -431,6 +437,18 @@ static int send_job_file(struct gui_entry *ge)
        struct gfio_client *gc = ge->client;
        int ret = 0;
 
+       /*
+        * Prune old options, we are expecting the return options
+        * when the job file is parsed remotely and returned to us.
+        */
+       while (!flist_empty(&gc->o_list)) {
+               struct gfio_client_options *gco;
+
+               gco = flist_first_entry(&gc->o_list, struct gfio_client_options, list);
+               flist_del(&gco->list);
+               free(gco);
+       }
+
        ret = fio_client_send_ini(gc->client, ge->job_file);
        if (!ret)
                return 0;
@@ -458,7 +476,7 @@ static void gfio_start_server(struct gui *ui)
 }
 
 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
-                gpointer data)
+                             gpointer data)
 {
        struct gui_entry *ge = data;
        struct gfio_client *gc = ge->client;
@@ -488,7 +506,7 @@ static void hostname_cb(GtkEntry *entry, gpointer data)
         * or not. Show it if we are a localhost and using network,
         * or using a socket.
         */
-       ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
+       ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
        if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
                uses_net = 1;
        g_free(ctext);
@@ -508,448 +526,463 @@ static void hostname_cb(GtkEntry *entry, gpointer data)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
                gtk_widget_set_sensitive(cw->button, 0);
        }
-       }
+}
 
-       static int get_connection_details(struct gui_entry *ge)
-       {
-               GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
-               struct connection_widgets cw;
-               struct gui *ui = ge->ui;
-               char *typeentry;
-
-               if (ge->host)
-                       return 0;
-
-               dialog = gtk_dialog_new_with_buttons("Connection details",
-                               GTK_WINDOW(ui->window),
-                               GTK_DIALOG_DESTROY_WITH_PARENT,
-                               GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
-                               GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
-
-               frame = gtk_frame_new("Hostname / socket name");
-               /* gtk_dialog_get_content_area() is 2.14 and newer */
-               vbox = GTK_DIALOG(dialog)->vbox;
-               gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
-
-               box = gtk_vbox_new(FALSE, 6);
-               gtk_container_add(GTK_CONTAINER(frame), box);
-
-               hbox = gtk_hbox_new(TRUE, 10);
-               gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-               cw.hentry = gtk_entry_new();
-               gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
-               gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
-
-               frame = gtk_frame_new("Port");
-               gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
-               box = gtk_vbox_new(FALSE, 10);
-               gtk_container_add(GTK_CONTAINER(frame), box);
-
-               hbox = gtk_hbox_new(TRUE, 4);
-               gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-               pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
-
-               frame = gtk_frame_new("Type");
-               gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
-               box = gtk_vbox_new(FALSE, 10);
-               gtk_container_add(GTK_CONTAINER(frame), box);
-
-               hbox = gtk_hbox_new(TRUE, 4);
-               gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-
-               cw.combo = gtk_combo_box_new_text();
-               gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
-               gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
-               gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
-               gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
-
-               gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
-
-               frame = gtk_frame_new("Options");
-               gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
-               box = gtk_vbox_new(FALSE, 10);
-               gtk_container_add(GTK_CONTAINER(frame), box);
-
-               hbox = gtk_hbox_new(TRUE, 4);
-               gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-
-               cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
-               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
-               gtk_widget_set_tooltip_text(cw.button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
-               gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
+static int get_connection_details(struct gui_entry *ge)
+{
+       GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
+       struct connection_widgets cw;
+       struct gui *ui = ge->ui;
+       char *typeentry;
 
-               /*
-                * Connect edit signal, so we can show/not-show the auto start button
-                */
-               g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
-               g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
+       if (ge->host)
+               return 0;
 
-               gtk_widget_show_all(dialog);
+       dialog = gtk_dialog_new_with_buttons("Connection details",
+                       GTK_WINDOW(ui->window),
+                       GTK_DIALOG_DESTROY_WITH_PARENT,
+                       GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+                       GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
 
-               if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
-                       gtk_widget_destroy(dialog);
-                       return 1;
-               }
+       frame = gtk_frame_new("Hostname / socket name");
+       vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+       box = gtk_vbox_new(FALSE, 6);
+       gtk_container_add(GTK_CONTAINER(frame), box);
+
+       hbox = gtk_hbox_new(TRUE, 10);
+       gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
+       cw.hentry = gtk_entry_new();
+       gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
+       gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
+
+       frame = gtk_frame_new("Port");
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+       box = gtk_vbox_new(FALSE, 10);
+       gtk_container_add(GTK_CONTAINER(frame), box);
+
+       hbox = gtk_hbox_new(TRUE, 4);
+       gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
+       pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
+
+       frame = gtk_frame_new("Type");
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+       box = gtk_vbox_new(FALSE, 10);
+       gtk_container_add(GTK_CONTAINER(frame), box);
+
+       hbox = gtk_hbox_new(TRUE, 4);
+       gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
 
-               ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
-               ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
+       cw.combo = gtk_combo_box_text_new();
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
+       gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
 
-               typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
-               if (!typeentry || !strncmp(typeentry, "IPv4", 4))
-                       ge->type = Fio_client_ipv4;
-               else if (!strncmp(typeentry, "IPv6", 4))
-                       ge->type = Fio_client_ipv6;
-               else
-                       ge->type = Fio_client_socket;
-               g_free(typeentry);
+       gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
 
-               ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
+       frame = gtk_frame_new("Options");
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+       box = gtk_vbox_new(FALSE, 10);
+       gtk_container_add(GTK_CONTAINER(frame), box);
 
+       hbox = gtk_hbox_new(TRUE, 4);
+       gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
+
+       cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
+       gtk_widget_set_tooltip_text(cw.button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
+       gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
+
+       /*
+        * Connect edit signal, so we can show/not-show the auto start button
+        */
+       g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
+       g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
+
+       gtk_widget_show_all(dialog);
+
+       if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
                gtk_widget_destroy(dialog);
-               return 0;
+               return 1;
        }
 
-       static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
-       {
-               gc->client = fio_get_client(client);
-               client->client_data = gc;
-       }
+       ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
+       ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
 
-       static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
-       {
-               struct gfio_client *gc;
-
-               gc = malloc(sizeof(*gc));
-               memset(gc, 0, sizeof(*gc));
-               options_default_fill(&gc->o);
-               gc->ge = ge;
-               ge->client = gc;
-               gfio_set_client(gc, client);
-       }
+       typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
+       if (!typeentry || !strncmp(typeentry, "IPv4", 4))
+               ge->type = Fio_client_ipv4;
+       else if (!strncmp(typeentry, "IPv6", 4))
+               ge->type = Fio_client_ipv6;
+       else
+               ge->type = Fio_client_socket;
+       g_free(typeentry);
 
-       static void connect_clicked(GtkWidget *widget, gpointer data)
-       {
-               struct gui_entry *ge = data;
-               struct gfio_client *gc = ge->client;
+       ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
 
-               if (ge->state == GE_STATE_NEW) {
-                       int ret;
+       gtk_widget_destroy(dialog);
+       return 0;
+}
 
-                       if (!ge->job_file)
-                               file_open(widget, ge->ui);
-                       if (!ge->job_file)
-                               return;
+static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
+{
+       gc->client = fio_get_client(client);
+       client->client_data = gc;
+}
 
-                       gc = ge->client;
+static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
+{
+       struct gfio_client_options *gco;
+       struct gfio_client *gc;
 
-                       if (!gc->client) {
-                               struct fio_client *client;
+       gc = calloc(1, sizeof(*gc));
+       INIT_FLIST_HEAD(&gc->o_list);
+       gc->ge = ge;
+       ge->client = gc;
+       gfio_set_client(gc, client);
 
-                               if (get_connection_details(ge)) {
-                                       gfio_report_error(ge, "Failed to get connection details\n");
-                                       return;
-                               }
+       /*
+        * Just add a default set of options, need to consider how best
+        * to handle this
+        */
+       gco = calloc(1, sizeof(*gco));
+       INIT_FLIST_HEAD(&gco->list);
+       options_default_fill(&gco->o);
+       flist_add_tail(&gco->list, &gc->o_list);
+       gc->o_list_nr++;
+}
 
-                               client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
-                               if (!client) {
-                                       gfio_report_error(ge, "Failed to add client %s\n", ge->host);
-                                       free(ge->host);
-                                       ge->host = NULL;
-                                       return;
-                               }
-                               gfio_set_client(gc, client);
+static void gfio_clear_graph_data(struct gfio_graphs *g)
+{
+       graph_clear_values(g->iops_graph);
+       graph_clear_values(g->bandwidth_graph);
+}
+
+static void connect_clicked(GtkWidget *widget, gpointer data)
+{
+       struct gui_entry *ge = data;
+       struct gfio_client *gc = ge->client;
+
+       if (ge->state == GE_STATE_NEW) {
+               int ret;
+
+               if (!ge->job_file)
+                       file_open(widget, ge->ui);
+               if (!ge->job_file)
+                       return;
+
+               gc = ge->client;
+
+               if (!gc->client) {
+                       struct fio_client *client;
+
+                       if (get_connection_details(ge)) {
+                               gfio_report_error(ge, "Failed to get connection details\n");
+                               return;
                        }
 
-                       gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
-                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
-                       ret = fio_client_connect(gc->client);
-                       if (!ret) {
-                               if (!ge->ui->handler_running)
-                                       pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
-                               gfio_set_state(ge, GE_STATE_CONNECTED);
-                       } else {
-                               gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
+                       client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
+                       if (!client) {
+                               gfio_report_error(ge, "Failed to add client %s\n", ge->host);
+                               free(ge->host);
+                               ge->host = NULL;
+                               return;
                        }
+                       gfio_set_client(gc, client);
+               }
+
+               gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
+               gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
+               ret = fio_client_connect(gc->client);
+               if (!ret) {
+                       if (!ge->ui->handler_running)
+                               pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
+                       gfio_set_state(ge, GE_STATE_CONNECTED);
+                       gfio_clear_graph_data(&ge->graphs);
                } else {
-                       fio_client_terminate(gc->client);
-                       gfio_set_state(ge, GE_STATE_NEW);
-                       clear_ge_ui_info(ge);
+                       gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
                }
+       } else {
+               fio_client_terminate(gc->client);
+               gfio_set_state(ge, GE_STATE_NEW);
+               clear_ge_ui_info(ge);
        }
+}
 
-       static void send_clicked(GtkWidget *widget, gpointer data)
-       {
-               struct gui_entry *ge = data;
+static void send_clicked(GtkWidget *widget, gpointer data)
+{
+       struct gui_entry *ge = data;
 
-               if (send_job_file(ge))
-                       gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
-       }
+       if (send_job_file(ge))
+               gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
+}
 
-       static GtkWidget *new_client_page(struct gui_entry *ge);
+static GtkWidget *new_client_page(struct gui_entry *ge);
 
-       static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
-       {
-               struct gui_entry *ge;
-
-               ge = malloc(sizeof(*ge));
-               memset(ge, 0, sizeof(*ge));
-               ge->state = GE_STATE_NEW;
-               INIT_FLIST_HEAD(&ge->list);
-               flist_add_tail(&ge->list, &ui->list);
-               ge->ui = ui;
-               return ge;
-       }
+static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
+{
+       struct gui_entry *ge;
 
-       static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
-       {
-               struct gui_entry *ge;
+       ge = malloc(sizeof(*ge));
+       memset(ge, 0, sizeof(*ge));
+       ge->state = GE_STATE_NEW;
+       ge->ui = ui;
+       return ge;
+}
 
-               ge = alloc_new_gui_entry(ui);
+static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
+{
+       struct gui_entry *ge;
 
-               ge->vbox = new_client_page(ge);
-               g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
+       ge = alloc_new_gui_entry(ui);
 
-               ge->page_label = gtk_label_new(name);
-               ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
+       ge->vbox = new_client_page(ge);
+       g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
 
-               gtk_widget_show_all(ui->window);
-               return ge;
-       }
+       ge->page_label = gtk_label_new(name);
+       ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
 
-       static void file_new(GtkWidget *w, gpointer data)
-       {
-               struct gui *ui = (struct gui *) data;
-               struct gui_entry *ge;
+       g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
 
-               ge = get_new_ge_with_tab(ui, "Untitled");
-               gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
+       gtk_widget_show_all(ui->window);
+       return ge;
+}
+
+static void file_new(GtkWidget *w, gpointer data)
+{
+       struct gui *ui = (struct gui *) data;
+       struct gui_entry *ge;
+
+       ge = get_new_ge_with_tab(ui, "Untitled");
+       gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
+}
+
+/*
+ * Return the 'ge' corresponding to the tab. If the active tab is the
+ * main tab, open a new tab.
+ */
+static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
+                                         int *created)
+{
+       if (!cur_page) {
+               if (created)
+                       *created = 1;
+               return get_new_ge_with_tab(ui, "Untitled");
        }
 
+       if (created)
+               *created = 0;
+
+       return g_hash_table_lookup(ui->ge_hash, &cur_page);
+}
+
+static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
+{
+       gint cur_page;
+
        /*
-        * Return the 'ge' corresponding to the tab. If the active tab is the
-        * main tab, open a new tab.
+        * Main tab is tab 0, so any current page other than 0 holds
+        * a ge entry.
         */
-       static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
-                                                 int *created)
-       {
-               struct flist_head *entry;
-               struct gui_entry *ge;
+       cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
+       if (cur_page)
+               return get_ge_from_page(ui, cur_page, NULL);
 
-               if (!cur_page) {
-                       if (created)
-                               *created = 1;
-                       return get_new_ge_with_tab(ui, "Untitled");
-               }
+       return NULL;
+}
 
-               if (created)
-                       *created = 0;
+static void file_close(GtkWidget *w, gpointer data)
+{
+       struct gui *ui = (struct gui *) data;
+       struct gui_entry *ge;
 
-               flist_for_each(entry, &ui->list) {
-                       ge = flist_entry(entry, struct gui_entry, list);
-                       if (ge->page_num == cur_page)
-                               return ge;
-               }
+       /*
+        * Can't close the main tab
+        */
+       ge = get_ge_from_cur_tab(ui);
+       if (ge) {
+               gtk_widget_destroy(ge->vbox);
+               return;
+       }
 
-               return NULL;
+       if (g_hash_table_size(ui->ge_hash)) {
+               gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
+               return;
        }
 
-       static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
-       {
-               gint cur_page;
+       gfio_quit(ui);
+}
 
-               /*
-                * Main tab is tab 0, so any current page other than 0 holds
-                * a ge entry.
-                */
-               cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
-               if (cur_page)
-                       return get_ge_from_page(ui, cur_page, NULL);
+static void file_add_recent(struct gui *ui, const gchar *uri)
+{
+       GtkRecentData grd;
 
-               return NULL;
-       }
+       memset(&grd, 0, sizeof(grd));
+       grd.display_name = strdup("gfio");
+       grd.description = strdup("Fio job file");
+       grd.mime_type = strdup(GFIO_MIME);
+       grd.app_name = strdup(g_get_application_name());
+       grd.app_exec = strdup("gfio %f/%u");
 
-       static void file_close(GtkWidget *w, gpointer data)
-       {
-               struct gui *ui = (struct gui *) data;
-               struct gui_entry *ge;
+       gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
+}
 
-               /*
-                * Can't close the main tab
-                */
-               ge = get_ge_from_cur_tab(ui);
-               if (ge) {
-                       gtk_widget_destroy(ge->vbox);
-                       return;
-               }
+static gchar *get_filename_from_uri(const gchar *uri)
+{
+       if (strncmp(uri, "file://", 7))
+               return strdup(uri);
 
-               if (!flist_empty(&ui->list)) {
-                       gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
-                       return;
-               }
+       return strdup(uri + 7);
+}
 
-               gfio_quit(ui);
-       }
+static int do_file_open(struct gui_entry *ge, const gchar *uri)
+{
+       struct fio_client *client;
 
-       static void file_add_recent(struct gui *ui, const gchar *uri)
-       {
-               GtkRecentData grd;
+       assert(!ge->job_file);
 
-               memset(&grd, 0, sizeof(grd));
-               grd.display_name = strdup("gfio");
-               grd.description = strdup("Fio job file");
-               grd.mime_type = strdup(GFIO_MIME);
-               grd.app_name = strdup(g_get_application_name());
-               grd.app_exec = strdup("gfio %f/%u");
+       ge->job_file = get_filename_from_uri(uri);
 
-               gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
-       }
+       client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
+       if (client) {
+               char *label = strdup(uri);
 
-       static gchar *get_filename_from_uri(const gchar *uri)
-       {
-               if (strncmp(uri, "file://", 7))
-                       return strdup(uri);
+               basename(label);
+               gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
+               free(label);
 
-               return strdup(uri + 7);
+               gfio_client_added(ge, client);
+               file_add_recent(ge->ui, uri);
+               return 0;
        }
 
-       static int do_file_open(struct gui_entry *ge, const gchar *uri)
-       {
-               struct fio_client *client;
+       gfio_report_error(ge, "Failed to add client %s\n", ge->host);
+       free(ge->host);
+       ge->host = NULL;
+       free(ge->job_file);
+       ge->job_file = NULL;
+       return 1;
+}
+
+static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
+{
+       struct gui_entry *ge;
+       gint cur_page;
+       int ret, ge_is_new = 0;
 
-               assert(!ge->job_file);
+       /*
+        * Creates new tab if current tab is the main window, or the
+        * current tab already has a client.
+        */
+       cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
+       ge = get_ge_from_page(ui, cur_page, &ge_is_new);
+       if (ge->client) {
+               ge = get_new_ge_with_tab(ui, "Untitled");
+               ge_is_new = 1;
+       }
 
-               ge->job_file = get_filename_from_uri(uri);
+       gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
 
-               client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
-               if (client) {
-                       gfio_client_added(ge, client);
-                       file_add_recent(ge->ui, uri);
-                       return 0;
-               }
+       if (get_connection_details(ge)) {
+               if (ge_is_new)
+                       gtk_widget_destroy(ge->vbox);
 
-               gfio_report_error(ge, "Failed to add client %s\n", ge->host);
-               free(ge->host);
-               ge->host = NULL;
                return 1;
        }
 
-       static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
-       {
-               struct gui_entry *ge;
-               gint cur_page;
-               int ret, ge_is_new = 0;
+       ret = do_file_open(ge, uri);
 
-               /*
-                * Creates new tab if current tab is the main window, or the
-                * current tab already has a client.
-                */
-               cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
-               ge = get_ge_from_page(ui, cur_page, &ge_is_new);
-               if (ge->client) {
-                       ge = get_new_ge_with_tab(ui, "Untitled");
-                       ge_is_new = 1;
-               }
+       if (!ret) {
+               if (ge->server_start)
+                       gfio_start_server(ui);
+       } else {
+               if (ge_is_new)
+                       gtk_widget_destroy(ge->vbox);
+       }
 
-               gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
+       return ret;
+}
 
-               if (get_connection_details(ge)) {
-                       if (ge_is_new)
-                               gtk_widget_destroy(ge->vbox);
-                               
-                       return 1;
-               }
+static void recent_open(GtkAction *action, gpointer data)
+{
+       struct gui *ui = (struct gui *) data;
+       GtkRecentInfo *info;
+       const gchar *uri;
 
-               ret = do_file_open(ge, uri);
+       info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
+       uri = gtk_recent_info_get_uri(info);
 
-               if (!ret) {
-                       if (ge->server_start)
-                               gfio_start_server(ui);
-               } else {
-                       if (ge_is_new)
-                               gtk_widget_destroy(ge->vbox);
-               }
+       do_file_open_with_tab(ui, uri);
+}
 
-               return ret;
-       }
+static void file_open(GtkWidget *w, gpointer data)
+{
+       struct gui *ui = data;
+       GtkWidget *dialog;
+       GtkFileFilter *filter;
+       gchar *filename;
 
-       static void recent_open(GtkAction *action, gpointer data)
-       {
-               struct gui *ui = (struct gui *) data;
-               GtkRecentInfo *info;
-               const gchar *uri;
+       dialog = gtk_file_chooser_dialog_new("Open File",
+               GTK_WINDOW(ui->window),
+               GTK_FILE_CHOOSER_ACTION_OPEN,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+               GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+               NULL);
+       gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
 
-               info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
-               uri = gtk_recent_info_get_uri(info);
+       filter = gtk_file_filter_new();
+       gtk_file_filter_add_pattern(filter, "*.fio");
+       gtk_file_filter_add_pattern(filter, "*.job");
+       gtk_file_filter_add_pattern(filter, "*.ini");
+       gtk_file_filter_add_mime_type(filter, GFIO_MIME);
+       gtk_file_filter_set_name(filter, "Fio job file");
+       gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
 
-               do_file_open_with_tab(ui, uri);
+       if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
+               gtk_widget_destroy(dialog);
+               return;
        }
 
-       static void file_open(GtkWidget *w, gpointer data)
-       {
-               struct gui *ui = data;
-               GtkWidget *dialog;
-               GtkFileFilter *filter;
-               gchar *filename;
-
-               dialog = gtk_file_chooser_dialog_new("Open File",
-                       GTK_WINDOW(ui->window),
-                       GTK_FILE_CHOOSER_ACTION_OPEN,
-                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                       NULL);
-               gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
-
-               filter = gtk_file_filter_new();
-               gtk_file_filter_add_pattern(filter, "*.fio");
-               gtk_file_filter_add_pattern(filter, "*.job");
-               gtk_file_filter_add_pattern(filter, "*.ini");
-               gtk_file_filter_add_mime_type(filter, GFIO_MIME);
-               gtk_file_filter_set_name(filter, "Fio job file");
-               gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
-
-               if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
-                       gtk_widget_destroy(dialog);
-                       return;
-               }
-
-               filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 
-               gtk_widget_destroy(dialog);
+       gtk_widget_destroy(dialog);
 
-               do_file_open_with_tab(ui, filename);
-               g_free(filename);
-       }
+       do_file_open_with_tab(ui, filename);
+       g_free(filename);
+}
 
-       static void file_save(GtkWidget *w, gpointer data)
-       {
-               struct gui *ui = data;
-               GtkWidget *dialog;
+static void file_save(GtkWidget *w, gpointer data)
+{
+       struct gui *ui = data;
+       GtkWidget *dialog;
 
-               dialog = gtk_file_chooser_dialog_new("Save File",
-                       GTK_WINDOW(ui->window),
-                       GTK_FILE_CHOOSER_ACTION_SAVE,
-                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-                       NULL);
+       dialog = gtk_file_chooser_dialog_new("Save File",
+               GTK_WINDOW(ui->window),
+               GTK_FILE_CHOOSER_ACTION_SAVE,
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+               GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+               NULL);
 
-               gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
-               gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
+       gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+       gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
 
-               if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
-                       char *filename;
+       if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+               char *filename;
 
-                       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-                       // save_job_file(filename);
-                       g_free(filename);
-               }
-               gtk_widget_destroy(dialog);
+               filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+               // save_job_file(filename);
+               g_free(filename);
        }
+       gtk_widget_destroy(dialog);
+}
 
 static void view_log_destroy(GtkWidget *w, gpointer data)
 {
        struct gui *ui = (struct gui *) data;
 
-       gtk_widget_ref(ui->log_tree);
+       g_object_ref(G_OBJECT(ui->log_tree));
        gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
        gtk_widget_destroy(w);
        ui->log_view = NULL;
@@ -973,12 +1006,12 @@ void gfio_view_log(struct gui *ui)
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
        box = gtk_hbox_new(TRUE, 0);
-       gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
+       gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
        g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
 
        vbox = gtk_vbox_new(TRUE, 5);
-       gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
+       gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
 
        gtk_container_add(GTK_CONTAINER(win), vbox);
        gtk_widget_show_all(win);
@@ -995,7 +1028,7 @@ static void connect_job_entry(GtkWidget *w, gpointer data)
 {
        struct gui *ui = (struct gui *) data;
        struct gui_entry *ge;
-       
+
        ge = get_ge_from_cur_tab(ui);
        if (ge)
                connect_clicked(w, ge);
@@ -1018,7 +1051,7 @@ static void edit_job_entry(GtkWidget *w, gpointer data)
 
        ge = get_ge_from_cur_tab(ui);
        if (ge && ge->client)
-               gopt_get_options_window(ui->window, &ge->client->o);
+               gopt_get_options_window(ui->window, ge->client);
 }
 
 static void start_job_entry(GtkWidget *w, gpointer data)
@@ -1049,23 +1082,38 @@ static void view_results(GtkWidget *w, gpointer data)
                gfio_display_end_results(gc);
 }
 
-static void __update_graph_limits(struct gfio_graphs *g)
+static void __update_graph_settings(struct gfio_graphs *g)
 {
        line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
+       graph_set_font(g->iops_graph, gfio_graph_font);
        line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
+       graph_set_font(g->bandwidth_graph, gfio_graph_font);
+}
+
+static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
+{
+       struct gui_entry *ge = (struct gui_entry *) value;
+       GdkEvent *ev;
+
+       __update_graph_settings(&ge->graphs);
+
+       ev = gdk_event_new(GDK_EXPOSE);
+       g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
+       gdk_event_free(ev);
 }
 
 static void update_graph_limits(void)
 {
-       struct flist_head *entry;
-       struct gui_entry *ge;
+       struct gui *ui = &main_ui;
+       GdkEvent *ev;
 
-       __update_graph_limits(&main_ui.graphs);
+       __update_graph_settings(&ui->graphs);
 
-       flist_for_each(entry, &main_ui.list) {
-               ge = flist_entry(entry, struct gui_entry, list);
-               __update_graph_limits(&ge->graphs);
-       }
+       ev = gdk_event_new(GDK_EXPOSE);
+       g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
+       gdk_event_free(ev);
+
+       g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
 }
 
 static void preferences(GtkWidget *w, gpointer data)
@@ -1083,7 +1131,8 @@ static void preferences(GtkWidget *w, gpointer data)
                NULL);
 
        frame = gtk_frame_new("Graphing");
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+       vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
        vbox = gtk_vbox_new(FALSE, 6);
        gtk_container_add(GTK_CONTAINER(frame), vbox);
 
@@ -1092,7 +1141,7 @@ static void preferences(GtkWidget *w, gpointer data)
        entry = gtk_label_new("Font face to use for graph labels");
        gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
 
-       font = gtk_font_button_new();
+       font = gtk_font_button_new_with_font(gfio_graph_font);
        gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
 
        box = gtk_vbox_new(FALSE, 6);
@@ -1115,7 +1164,8 @@ static void preferences(GtkWidget *w, gpointer data)
 
        spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
        frame = gtk_frame_new("Debug logging");
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+       vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
        vbox = gtk_vbox_new(FALSE, 6);
        gtk_container_add(GTK_CONTAINER(frame), vbox);
 
@@ -1342,10 +1392,12 @@ static GtkWidget *new_client_page(struct gui_entry *ge)
 
        probe_box = gtk_hbox_new(FALSE, 3);
        gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
-       ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
-       ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
-       ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
-       ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
+       ge->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
+       ge->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
+       ge->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
+       ge->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
+       ge->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
+       ge->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
 
        /*
         * Only add this if we have a commit rate
@@ -1367,8 +1419,8 @@ static GtkWidget *new_client_page(struct gui_entry *ge)
        ge->graphs.drawing_area = gtk_drawing_area_new();
        gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
                DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
-       gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
-       g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
+       gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
+       g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
                                G_CALLBACK(on_expose_drawing_area), &ge->graphs);
        g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
                                G_CALLBACK(on_config_drawing_area), &ge->graphs);
@@ -1382,7 +1434,7 @@ static GtkWidget *new_client_page(struct gui_entry *ge)
        setup_graphs(&ge->graphs);
 
        /*
-        * Set up alignments for widgets at the bottom of ui, 
+        * Set up alignments for widgets at the bottom of ui,
         * align bottom left, expand horizontally but not vertically
         */
        bottom_align = gtk_alignment_new(0, 1, 1, 0);
@@ -1390,7 +1442,7 @@ static GtkWidget *new_client_page(struct gui_entry *ge)
        gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
        gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
 
-       add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
+       add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
 
        /*
         * Set up thread status progress bar
@@ -1428,10 +1480,12 @@ static GtkWidget *new_main_page(struct gui *ui)
        probe_box = gtk_hbox_new(FALSE, 3);
        gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
        ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
-       ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
-       ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
-       ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
-       ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
+       ui->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
+       ui->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
+       ui->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
+       ui->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
+       ui->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
+       ui->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
 
        /*
         * Only add this if we have a commit rate
@@ -1453,8 +1507,8 @@ static GtkWidget *new_main_page(struct gui *ui)
        ui->graphs.drawing_area = gtk_drawing_area_new();
        gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
                DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
-       gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
-       g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
+       gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
+       g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
                        G_CALLBACK(on_expose_drawing_area), &ui->graphs);
        g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
                        G_CALLBACK(on_config_drawing_area), &ui->graphs);
@@ -1469,7 +1523,7 @@ static GtkWidget *new_main_page(struct gui *ui)
        setup_graphs(&ui->graphs);
 
        /*
-        * Set up alignments for widgets at the bottom of ui, 
+        * Set up alignments for widgets at the bottom of ui,
         * align bottom left, expand horizontally but not vertically
         */
        bottom_align = gtk_alignment_new(0, 1, 1, 0);
@@ -1623,16 +1677,20 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
         * Without it, the update that happens in gfio_update_thread_status
         * doesn't really happen in a timely fashion, you need expose events
         */
+#if !GTK_CHECK_VERSION(2, 24, 0)
        if (!g_thread_supported())
                g_thread_init(NULL);
+#endif
+
        gdk_threads_init();
 
        gtk_init(argc, argv);
        settings = gtk_settings_get_default();
        gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
        g_type_init();
+       gdk_color_parse("#fffff4", &gfio_color_lightyellow);
        gdk_color_parse("white", &gfio_color_white);
-       
+
        ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
        gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
@@ -1675,13 +1733,19 @@ int main(int argc, char *argv[], char *envp[])
        if (fio_init_options())
                return 1;
 
+       gopt_init();
+
        memset(&main_ui, 0, sizeof(main_ui));
-       INIT_FLIST_HEAD(&main_ui.list);
+       main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
 
        init_ui(&argc, &argv, &main_ui);
 
        gdk_threads_enter();
        gtk_main();
        gdk_threads_leave();
+
+       g_hash_table_destroy(main_ui.ge_hash);
+
+       gopt_exit();
        return 0;
 }