gfio: add font selection and resize original window
[fio.git] / gfio.c
diff --git a/gfio.c b/gfio.c
index 9c26fececdafafdbcfeda2a63e29fedf62acee09..833cd93daef2883e462fad5ae6fbc4f3a7db1286 100644 (file)
--- a/gfio.c
+++ b/gfio.c
 #include <malloc.h>
 
 #include <glib.h>
+#include <cairo.h>
 #include <gtk/gtk.h>
 
 #include "fio.h"
+#include "graph.h"
 
 static int gfio_server_running;
+static const char *gfio_graph_font;
 
 static void gfio_update_thread_status(char *status_message, double perc);
 
@@ -88,7 +91,9 @@ struct gui {
        GtkWidget *buttonbox;
        GtkWidget *button[ARRAYSIZE(buttonspeclist)];
        GtkWidget *scrolled_window;
-       GtkWidget *textview;
+#define DRAWING_AREA_XDIM 1000
+#define DRAWING_AREA_YDIM 400
+       GtkWidget *drawing_area;
        GtkWidget *error_info_bar;
        GtkWidget *error_label;
        GtkWidget *results_notebook;
@@ -103,6 +108,8 @@ struct gui {
        pthread_t t;
        pthread_t server_t;
 
+       struct graph *iops_graph;
+       struct graph *bandwidth_graph;
        struct fio_client *client;
        int nr_job_files;
        char **job_files;
@@ -114,6 +121,36 @@ struct gfio_client {
        GtkWidget *disk_util_frame;
 };
 
+static void setup_iops_graph(struct gui *ui)
+{
+       if (ui->iops_graph)
+               graph_free(ui->iops_graph);
+       ui->iops_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
+                                       DRAWING_AREA_YDIM, gfio_graph_font);
+       graph_title(ui->iops_graph, "IOPS");
+       graph_x_title(ui->iops_graph, "Time");
+       graph_y_title(ui->iops_graph, "IOPS");
+       graph_add_label(ui->iops_graph, "Read IOPS");
+       graph_add_label(ui->iops_graph, "Write IOPS");
+       graph_set_color(ui->iops_graph, "Read IOPS", 0.7, 0.0, 0.0);
+       graph_set_color(ui->iops_graph, "Write IOPS", 0.0, 0.0, 0.7);
+}
+
+static void setup_bandwidth_graph(struct gui *ui)
+{
+       if (ui->bandwidth_graph)
+               graph_free(ui->bandwidth_graph);
+       ui->bandwidth_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
+                                       DRAWING_AREA_YDIM, gfio_graph_font);
+       graph_title(ui->bandwidth_graph, "Bandwidth");
+       graph_x_title(ui->bandwidth_graph, "Time");
+       graph_y_title(ui->bandwidth_graph, "Bandwidth");
+       graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
+       graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
+       graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.7, 0.0, 0.0);
+       graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 0.0, 0.0, 0.7);
+}
+
 static void clear_ui_info(struct gui *ui)
 {
        gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
@@ -960,6 +997,32 @@ static void gfio_group_stats_op(struct fio_client *client,
        gdk_threads_leave();
 }
 
+static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
+{
+       struct gui *ui = (struct gui *) p;
+       cairo_t *cr;
+
+       cr = gdk_cairo_create(w->window);
+
+       cairo_set_source_rgb(cr, 0, 0, 0);
+
+       cairo_save(cr);
+       cairo_translate(cr, 0, 0);
+       line_graph_draw(ui->bandwidth_graph, cr);
+       cairo_stroke(cr);
+       cairo_restore(cr);
+
+       cairo_save(cr);
+       cairo_translate(cr, DRAWING_AREA_XDIM / 2.0, 0);
+                               // DRAWING_AREA_YDIM * 0.05);
+       line_graph_draw(ui->iops_graph, cr);
+       cairo_stroke(cr);
+       cairo_restore(cr);
+       cairo_destroy(cr);
+
+       return FALSE;
+}
+
 static void gfio_update_eta(struct jobs_eta *je)
 {
        static int eta_good;
@@ -1027,6 +1090,11 @@ static void gfio_update_eta(struct jobs_eta *je)
                gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
                gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
 
+               graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
+               graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
+               graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
+               graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
+
                free(rate_str[0]);
                free(rate_str[1]);
                free(iops_str[0]);
@@ -1314,11 +1382,52 @@ void report_error(GError *error)
        }
 }
 
+struct connection_widgets
+{
+       GtkWidget *hentry;
+       GtkWidget *combo;
+       GtkWidget *button;
+};
+
+static void hostname_cb(GtkEntry *entry, gpointer data)
+{
+       struct connection_widgets *cw = data;
+       int uses_net = 0, is_localhost = 0;
+       const gchar *text;
+       gchar *ctext;
+
+       /*
+        * Check whether to display the 'auto start backend' box
+        * 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));
+       if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
+               uses_net = 1;
+       g_free(ctext);
+
+       if (uses_net) {
+               text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
+               if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
+                   !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
+                   !strcmp(text, "ip6-loopback"))
+                       is_localhost = 1;
+       }
+
+       if (!uses_net || is_localhost) {
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
+               gtk_widget_set_sensitive(cw->button, 1);
+       } else {
+               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
+               gtk_widget_set_sensitive(cw->button, 0);
+       }
+}
+
 static int get_connection_details(char **host, int *port, int *type,
                                  int *server_start)
 {
-       GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
-       GtkWidget *button;
+       GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
+       struct connection_widgets cw;
        char *typeentry;
 
        dialog = gtk_dialog_new_with_buttons("Connection details",
@@ -1336,9 +1445,9 @@ static int get_connection_details(char **host, int *port, int *type,
 
        hbox = gtk_hbox_new(TRUE, 10);
        gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-       hentry = gtk_entry_new();
-       gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
-       gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 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);
@@ -1357,13 +1466,13 @@ static int get_connection_details(char **host, int *port, int *type,
        hbox = gtk_hbox_new(TRUE, 4);
        gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
 
-       combo = gtk_combo_box_new_text();
-       gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
-       gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
-       gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
-       gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 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), combo);
+       gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
 
        frame = gtk_frame_new("Options");
        gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
@@ -1373,10 +1482,16 @@ static int get_connection_details(char **host, int *port, int *type,
        hbox = gtk_hbox_new(TRUE, 4);
        gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
 
-       button = gtk_check_button_new_with_label("Auto-spawn fio backend");
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
-       gtk_widget_set_tooltip_text(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), button, FALSE, FALSE, 6);
+       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(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
+       g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
 
        gtk_widget_show_all(dialog);
 
@@ -1385,10 +1500,10 @@ static int get_connection_details(char **host, int *port, int *type,
                return 1;
        }
 
-       *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
+       *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
        *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
 
-       typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
+       typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
        if (!typeentry || !strncmp(typeentry, "IPv4", 4))
                *type = Fio_client_ipv4;
        else if (!strncmp(typeentry, "IPv6", 4))
@@ -1397,7 +1512,7 @@ static int get_connection_details(char **host, int *port, int *type,
                *type = Fio_client_socket;
        g_free(typeentry);
 
-       *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+       *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
 
        gtk_widget_destroy(dialog);
        return 0;
@@ -1434,6 +1549,7 @@ static void file_open(GtkWidget *w, gpointer data)
        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, "text/fio");
        gtk_file_filter_set_name(filter, "Fio job file");
        gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
@@ -1547,7 +1663,7 @@ static void view_log(GtkWidget *w, gpointer data)
 
 static void preferences(GtkWidget *w, gpointer data)
 {
-       GtkWidget *dialog, *frame, *box, **buttons;
+       GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
        int i;
 
        dialog = gtk_dialog_new_with_buttons("Preferences",
@@ -1559,17 +1675,35 @@ static void preferences(GtkWidget *w, gpointer data)
 
        frame = gtk_frame_new("Debug logging");
        gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+
+       vbox = gtk_vbox_new(FALSE, 6);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+
        box = gtk_hbox_new(FALSE, 6);
-       gtk_container_add(GTK_CONTAINER(frame), box);
+       gtk_container_add(GTK_CONTAINER(vbox), box);
 
        buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
 
        for (i = 0; i < FD_DEBUG_MAX; i++) {
+               if (i == 7) {
+                       box = gtk_hbox_new(FALSE, 6);
+                       gtk_container_add(GTK_CONTAINER(vbox), box);
+               }
+
+
                buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
                gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
                gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
        }
 
+       frame = gtk_frame_new("Graph font");
+       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+       vbox = gtk_vbox_new(FALSE, 6);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+
+       font = gtk_font_button_new();
+       gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
+
        gtk_widget_show_all(dialog);
 
        if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
@@ -1585,6 +1719,9 @@ static void preferences(GtkWidget *w, gpointer data)
                        fio_debug |= (1UL << i);
        }
 
+       gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
+       printf("got font %s\n", gfio_graph_font);
+
        gtk_widget_destroy(dialog);
 }
 
@@ -1681,7 +1818,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
        
        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), 700, 500);
+       gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 700);
 
        g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
        g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
@@ -1746,20 +1883,24 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
 #endif
 
        /*
-        * Add a text box for text op messages 
+        * Set up a drawing area and IOPS and bandwidth graphs
         */
-       ui->textview = gtk_text_view_new();
-       ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
-       gtk_text_buffer_set_text(ui->text, "", -1);
-       gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
-       gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
+       ui->drawing_area = gtk_drawing_area_new();
+       gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area), 
+                       DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
+       g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
+                               G_CALLBACK (on_expose_drawing_area), ui);
        ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-       gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
+       gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
+                                       ui->drawing_area);
        gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
                        TRUE, TRUE, 0);
 
+       setup_iops_graph(ui);
+       setup_bandwidth_graph(ui);
+
        /*
         * Set up alignments for widgets at the bottom of ui, 
         * align bottom left, expand horizontally but not vertically