gfio: lessen use of main_ui directly
[fio.git] / gfio.c
diff --git a/gfio.c b/gfio.c
index bbeb229996272aa079c11d26856fda7f78600588..cd0e35a00088c5519df45cdbc6f6ff43ce46f9af 100644 (file)
--- a/gfio.c
+++ b/gfio.c
 #include <gtk/gtk.h>
 
 #include "fio.h"
+#include "gfio.h"
+#include "ghelpers.h"
+#include "goptions.h"
 #include "graph.h"
 
-#define GFIO_MIME      "text/fio"
-
 static int gfio_server_running;
 static const char *gfio_graph_font;
 static unsigned int gfio_graph_limit = 100;
@@ -41,8 +42,6 @@ static GdkColor white;
 
 static void view_log(GtkWidget *w, gpointer data);
 
-#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
-
 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
 
 static void connect_clicked(GtkWidget *widget, gpointer data);
@@ -55,152 +54,29 @@ static struct button_spec {
        const char *tooltiptext[2];
        const int start_sensitive;
 } buttonspeclist[] = {
-#define CONNECT_BUTTON 0
-#define SEND_BUTTON 1
-#define START_JOB_BUTTON 2
-       { "Connect", connect_clicked, { "Disconnect from host", "Connect to host" }, 1 },
-       { "Send", send_clicked, { "Send job description to host", NULL }, 0 },
-       { "Start Job", start_job_clicked,
-               { "Start the current job on the server", NULL }, 0 },
-};
-
-struct probe_widget {
-       GtkWidget *hostname;
-       GtkWidget *os;
-       GtkWidget *arch;
-       GtkWidget *fio_ver;
-};
-
-struct multitext_widget {
-       GtkWidget *entry;
-       char **text;
-       unsigned int cur_text;
-       unsigned int max_text;
-};
-
-struct eta_widget {
-       GtkWidget *names;
-       struct multitext_widget iotype;
-       struct multitext_widget ioengine;
-       struct multitext_widget iodepth;
-       GtkWidget *jobs;
-       GtkWidget *files;
-       GtkWidget *read_bw;
-       GtkWidget *read_iops;
-       GtkWidget *cr_bw;
-       GtkWidget *cr_iops;
-       GtkWidget *write_bw;
-       GtkWidget *write_iops;
-       GtkWidget *cw_bw;
-       GtkWidget *cw_iops;
-};
-
-struct gfio_graphs {
-#define DRAWING_AREA_XDIM 1000
-#define DRAWING_AREA_YDIM 400
-       GtkWidget *drawing_area;
-       struct graph *iops_graph;
-       struct graph *bandwidth_graph;
-};
-
-/*
- * Main window widgets and data
- */
-struct gui {
-       GtkUIManager *uimanager;
-       GtkRecentManager *recentmanager;
-       GtkActionGroup *actiongroup;
-       guint recent_ui_id;
-       GtkWidget *menu;
-       GtkWidget *window;
-       GtkWidget *vbox;
-       GtkWidget *thread_status_pb;
-       GtkWidget *buttonbox;
-       GtkWidget *notebook;
-       GtkWidget *error_info_bar;
-       GtkWidget *error_label;
-       GtkListStore *log_model;
-       GtkWidget *log_tree;
-       GtkWidget *log_view;
-       struct gfio_graphs graphs;
-       struct probe_widget probe;
-       struct eta_widget eta;
-       pthread_t server_t;
-
-       pthread_t t;
-       int handler_running;
-
-       struct flist_head list;
-} main_ui;
-
-enum {
-       GE_STATE_NEW = 1,
-       GE_STATE_CONNECTED,
-       GE_STATE_JOB_SENT,
-       GE_STATE_JOB_STARTED,
-       GE_STATE_JOB_RUNNING,
-       GE_STATE_JOB_DONE,
-};
-
-/*
- * Notebook entry
- */
-struct gui_entry {
-       struct flist_head list;
-       struct gui *ui;
-
-       GtkWidget *vbox;
-       GtkWidget *job_notebook;
-       GtkWidget *thread_status_pb;
-       GtkWidget *buttonbox;
-       GtkWidget *button[ARRAYSIZE(buttonspeclist)];
-       GtkWidget *notebook;
-       GtkWidget *error_info_bar;
-       GtkWidget *error_label;
-       GtkWidget *results_window;
-       GtkWidget *results_notebook;
-       GtkUIManager *results_uimanager;
-       GtkWidget *results_menu;
-       GtkWidget *disk_util_vbox;
-       GtkListStore *log_model;
-       GtkWidget *log_tree;
-       GtkWidget *log_view;
-       struct gfio_graphs graphs;
-       struct probe_widget probe;
-       struct eta_widget eta;
-       GtkWidget *page_label;
-       gint page_num;
-       unsigned int state;
-
-       struct graph *clat_graph;
-       struct graph *lat_bucket_graph;
-
-       struct gfio_client *client;
-       int nr_job_files;
-       char **job_files;
-};
-
-struct end_results {
-       struct group_run_stats gs;
-       struct thread_stat ts;
-};
-
-struct gfio_client {
-       struct gui_entry *ge;
-       struct fio_client *client;
-       GtkWidget *err_entry;
-       struct thread_options o;
-
-       struct end_results *results;
-       unsigned int nr_results;
-
-       struct cmd_du_pdu *du;
-       unsigned int nr_du;
+       {
+         .buttontext           = "Connect",
+         .f                    = connect_clicked,
+         .tooltiptext          = { "Disconnect from host", "Connect to host" },
+         .start_sensitive      = 1,
+       },
+       {
+         .buttontext           = "Send",
+         .f                    = send_clicked,
+         .tooltiptext          = { "Send job description to host", NULL },
+         .start_sensitive      = 0,
+       },
+       {
+         .buttontext           = "Start Job",
+         .f                    = start_job_clicked,
+         .tooltiptext          = { "Start the current job on the server", NULL },
+         .start_sensitive      = 0,
+       },
 };
 
 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
-static void gfio_update_thread_status_all(char *status_message, double perc);
-void report_error(GError *error);
+static void gfio_update_thread_status_all(struct gui *ui, char *status_message, double perc);
+static void report_error(GError *error);
 
 static struct graph *setup_iops_graph(void)
 {
@@ -241,54 +117,6 @@ static void setup_graphs(struct gfio_graphs *g)
        g->bandwidth_graph = setup_bandwidth_graph();
 }
 
-static void multitext_add_entry(struct multitext_widget *mt, const char *text)
-{
-       mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
-       mt->text[mt->max_text] = strdup(text);
-       mt->max_text++;
-}
-
-static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
-{
-       if (index >= mt->max_text)
-               return;
-       if (!mt->text || !mt->text[index])
-               return;
-
-       mt->cur_text = index;
-       gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
-}
-
-static void multitext_update_entry(struct multitext_widget *mt,
-                                  unsigned int index, const char *text)
-{
-       if (!mt->text)
-               return;
-
-       if (mt->text[index])
-               free(mt->text[index]);
-
-       mt->text[index] = strdup(text);
-       if (mt->cur_text == index)
-               gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
-}
-
-static void multitext_free(struct multitext_widget *mt)
-{
-       int i;
-
-       gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
-
-       for (i = 0; i < mt->max_text; i++) {
-               if (mt->text[i])
-                       free(mt->text[i]);
-       }
-
-       free(mt->text);
-       mt->cur_text = -1;
-       mt->max_text = 0;
-}
-
 static void clear_ge_ui_info(struct gui_entry *ge)
 {
        gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
@@ -300,6 +128,7 @@ static void clear_ge_ui_info(struct gui_entry *ge)
        gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
 #endif
        multitext_update_entry(&ge->eta.iotype, 0, "");
+       multitext_update_entry(&ge->eta.bs, 0, "");
        multitext_update_entry(&ge->eta.ioengine, 0, "");
        multitext_update_entry(&ge->eta.iodepth, 0, "");
        gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
@@ -310,76 +139,6 @@ static void clear_ge_ui_info(struct gui_entry *ge)
        gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
 }
 
-static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
-{
-       GtkWidget *entry, *frame;
-
-       frame = gtk_frame_new(label);
-       entry = gtk_combo_box_new_text();
-       gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
-       gtk_container_add(GTK_CONTAINER(frame), entry);
-
-       return entry;
-}
-
-static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
-{
-       GtkWidget *entry, *frame;
-
-       frame = gtk_frame_new(label);
-       entry = gtk_entry_new();
-       gtk_entry_set_editable(GTK_ENTRY(entry), 0);
-       gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
-       gtk_container_add(GTK_CONTAINER(frame), entry);
-
-       return entry;
-}
-
-static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
-{
-       GtkWidget *label_widget;
-       GtkWidget *frame;
-
-       frame = gtk_frame_new(label);
-       label_widget = gtk_label_new(NULL);
-       gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
-       gtk_container_add(GTK_CONTAINER(frame), label_widget);
-
-       return label_widget;
-}
-
-static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
-{
-       GtkWidget *button, *box;
-
-       box = gtk_hbox_new(FALSE, 3);
-       gtk_container_add(GTK_CONTAINER(hbox), box);
-
-       button = gtk_spin_button_new_with_range(min, max, 1.0);
-       gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
-
-       gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
-       gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
-
-       return button;
-}
-
-static void label_set_int_value(GtkWidget *entry, unsigned int val)
-{
-       char tmp[80];
-
-       sprintf(tmp, "%u", val);
-       gtk_label_set_text(GTK_LABEL(entry), tmp);
-}
-
-static void entry_set_int_value(GtkWidget *entry, unsigned int val)
-{
-       char tmp[80];
-
-       sprintf(tmp, "%u", val);
-       gtk_entry_set_text(GTK_ENTRY(entry), tmp);
-}
-
 static void show_info_dialog(struct gui *ui, const char *title,
                             const char *message)
 {
@@ -485,21 +244,21 @@ static void update_button_states(struct gui *ui, struct gui_entry *ge)
 
        case GE_STATE_NEW:
                connect_state = 1;
-               edit_state = 0;
+               edit_state = 1;
                connect_str = "Connect";
                send_state = 0;
                start_state = 0;
                break;
        case GE_STATE_CONNECTED:
                connect_state = 1;
-               edit_state = 0;
+               edit_state = 1;
                connect_str = "Disconnect";
                send_state = 1;
                start_state = 0;
                break;
        case GE_STATE_JOB_SENT:
                connect_state = 1;
-               edit_state = 0;
+               edit_state = 1;
                connect_str = "Disconnect";
                send_state = 0;
                start_state = 1;
@@ -527,11 +286,11 @@ static void update_button_states(struct gui *ui, struct gui_entry *ge)
                break;
        }
 
-       gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
-       gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
-       gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
-       gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
-       gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state));
+       gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
+       gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
+       gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
+       gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
+       gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
 
        set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
        set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
@@ -552,51 +311,6 @@ static void gfio_set_state(struct gui_entry *ge, unsigned int state)
        update_button_states(ge->ui, ge);
 }
 
-#define ALIGN_LEFT 1
-#define ALIGN_RIGHT 2
-#define INVISIBLE 4
-#define UNSORTABLE 8
-
-GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
-{
-       GtkCellRenderer *renderer;
-       GtkTreeViewColumn *col;
-       double xalign = 0.0; /* left as default */
-       PangoAlignment align;
-       gboolean visible;
-
-       align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
-               (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
-               PANGO_ALIGN_CENTER;
-       visible = !(flags & INVISIBLE);
-
-       renderer = gtk_cell_renderer_text_new();
-       col = gtk_tree_view_column_new();
-
-       gtk_tree_view_column_set_title(col, title);
-       if (!(flags & UNSORTABLE))
-               gtk_tree_view_column_set_sort_column_id(col, index);
-       gtk_tree_view_column_set_resizable(col, TRUE);
-       gtk_tree_view_column_pack_start(col, renderer, TRUE);
-       gtk_tree_view_column_add_attribute(col, renderer, "text", index);
-       gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
-       switch (align) {
-       case PANGO_ALIGN_LEFT:
-               xalign = 0.0;
-               break;
-       case PANGO_ALIGN_CENTER:
-               xalign = 0.5;
-               break;
-       case PANGO_ALIGN_RIGHT:
-               xalign = 1.0;
-               break;
-       }
-       gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
-       gtk_tree_view_column_set_visible(col, visible);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
-       return col;
-}
-
 static void gfio_ui_setup_log(struct gui *ui)
 {
        GtkTreeSelection *selection;
@@ -633,6 +347,7 @@ static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
        g = graph_new(xdim, ydim, gfio_graph_font);
        graph_title(g, title);
        graph_x_title(g, "Percentile");
+       graph_y_title(g, "Time");
 
        for (i = 0; i < len; i++) {
                char fbuf[8];
@@ -943,6 +658,7 @@ static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
        g = graph_new(xdim, ydim, gfio_graph_font);
        graph_title(g, title);
        graph_x_title(g, "Buckets");
+       graph_y_title(g, "Percent");
 
        for (i = 0; i < len; i++) {
                graph_add_label(g, labels[i]);
@@ -1310,7 +1026,7 @@ static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
        scroll = get_scrolled_window(5);
        vbox = gtk_vbox_new(FALSE, 3);
        box = gtk_hbox_new(FALSE, 0);
-       gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
+       gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
 
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
        res_notebook = get_results_window(ge);
@@ -1503,7 +1219,9 @@ static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
        struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
-       struct gui *ui = &main_ui;
+       struct gfio_client *gc = client->client_data;
+       struct gui_entry *ge = gc->ge;
+       struct gui *ui = ge->ui;
        GtkTreeIter iter;
        struct tm *tm;
        time_t sec;
@@ -1818,7 +1536,7 @@ static void gfio_update_all_eta(struct jobs_eta *je)
                sprintf(dst, " - %s", eta_str);
        }
                
-       gfio_update_thread_status_all(output, perc);
+       gfio_update_thread_status_all(ui, output, perc);
        gdk_threads_leave();
 }
 
@@ -1863,12 +1581,12 @@ static void gfio_update_thread_status(struct gui_entry *ge,
        strncpy(message, status_message, sizeof(message) - 1);
        gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
        gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
-       gtk_widget_queue_draw(main_ui.window);
+       gtk_widget_queue_draw(ge->ui->window);
 }
 
-static void gfio_update_thread_status_all(char *status_message, double perc)
+static void gfio_update_thread_status_all(struct gui *ui, char *status_message,
+                                         double perc)
 {
-       struct gui *ui = &main_ui;
        static char message[100];
        const char *m = message;
 
@@ -1893,7 +1611,8 @@ static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
        struct gfio_client *gc = client->client_data;
        struct thread_options *o = &gc->o;
        struct gui_entry *ge = gc->ge;
-       char tmp[8];
+       char *c1, *c2, *c3, *c4;
+       char tmp[80];
 
        p->thread_number = le32_to_cpu(p->thread_number);
        p->groupid = le32_to_cpu(p->groupid);
@@ -1906,13 +1625,27 @@ static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
        gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
        gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
 
-       multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
+       sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
+       multitext_add_entry(&ge->eta.iotype, tmp);
+
+       c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
+       c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
+       c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
+       c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
+       sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
+       free(c1);
+       free(c2);
+       free(c3);
+       free(c4);
+       multitext_add_entry(&ge->eta.bs, tmp);
+
        multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
 
        sprintf(tmp, "%u", o->iodepth);
        multitext_add_entry(&ge->eta.iodepth, tmp);
 
        multitext_set_entry(&ge->eta.iotype, 0);
+       multitext_set_entry(&ge->eta.bs, 0);
        multitext_set_entry(&ge->eta.ioengine, 0);
        multitext_set_entry(&ge->eta.iodepth, 0);
 
@@ -2015,6 +1748,9 @@ static void ge_destroy(struct gui_entry *ge)
 
 static void ge_widget_destroy(GtkWidget *w, gpointer data)
 {
+       struct gui_entry *ge = (struct gui_entry *) data;
+
+       ge_destroy(ge);
 }
 
 static void gfio_quit(struct gui *ui)
@@ -2032,7 +1768,9 @@ static void gfio_quit(struct gui *ui)
 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
                 __attribute__((unused)) gpointer data)
 {
-       gfio_quit(data);
+       struct gui *ui = (struct gui *) data;
+
+       gfio_quit(ui);
 }
 
 static void *job_thread(void *arg)
@@ -2086,10 +1824,8 @@ static void *server_thread(void *arg)
        return NULL;
 }
 
-static void gfio_start_server(void)
+static void gfio_start_server(struct gui *ui)
 {
-       struct gui *ui = &main_ui;
-
        if (!gfio_server_running) {
                gfio_server_running = 1;
                pthread_create(&ui->server_t, NULL, server_thread, NULL);
@@ -2156,7 +1892,7 @@ static void send_clicked(GtkWidget *widget, gpointer data)
                report_error(error);
                g_error_free(error);
 
-               gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
+               gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
        }
 }
 
@@ -2171,7 +1907,7 @@ static void on_info_bar_response(GtkWidget *widget, gint response,
        }
 }
 
-void report_error(GError *error)
+static void report_error(GError *error)
 {
        struct gui *ui = &main_ui;
 
@@ -2339,6 +2075,7 @@ static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
 
        gc = malloc(sizeof(*gc));
        memset(gc, 0, sizeof(*gc));
+       options_default_fill(&gc->o);
        gc->ge = ge;
        gc->client = fio_get_client(client);
 
@@ -2391,7 +2128,8 @@ static void file_new(GtkWidget *w, gpointer data)
  * 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(gint cur_page, int *created)
+static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
+                                         int *created)
 {
        struct flist_head *entry;
        struct gui_entry *ge;
@@ -2405,7 +2143,7 @@ static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
        if (created)
                *created = 0;
 
-       flist_for_each(entry, &main_ui.list) {
+       flist_for_each(entry, &ui->list) {
                ge = flist_entry(entry, struct gui_entry, list);
                if (ge->page_num == cur_page)
                        return ge;
@@ -2424,7 +2162,7 @@ static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
         */
        cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
        if (cur_page)
-               return get_ge_from_page(cur_page, NULL);
+               return get_ge_from_page(ui, cur_page, NULL);
 
        return NULL;
 }
@@ -2514,7 +2252,7 @@ static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
         * current tab already has a client.
         */
        cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
-       ge = get_ge_from_page(cur_page, &ge_is_new);
+       ge = get_ge_from_page(ui, cur_page, &ge_is_new);
        if (ge->client) {
                ge = get_new_ge_with_tab("Untitled");
                ge_is_new = 1;
@@ -2535,7 +2273,7 @@ static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
 
        if (!ret) {
                if (server_start)
-                       gfio_start_server();
+                       gfio_start_server(ui);
        } else {
                if (ge_is_new)
                        gtk_widget_destroy(ge->vbox);
@@ -2686,6 +2424,12 @@ static void send_job_entry(GtkWidget *w, gpointer data)
 
 static void edit_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 && ge->client)
+               gopt_get_options_window(ui->window, &ge->client->o);
 }
 
 static void start_job_entry(GtkWidget *w, gpointer data)
@@ -2739,10 +2483,11 @@ static void preferences(GtkWidget *w, gpointer data)
 {
        GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
        GtkWidget *hbox, *spin, *entry, *spin_int;
+       struct gui *ui = (struct gui *) data;
        int i;
 
        dialog = gtk_dialog_new_with_buttons("Preferences",
-               GTK_WINDOW(main_ui.window),
+               GTK_WINDOW(ui->window),
                GTK_DIALOG_DESTROY_WITH_PARENT,
                GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
@@ -2878,7 +2623,7 @@ static GtkActionEntry menu_items[] = {
        { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
        { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
        { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
-       { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
+       { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
        { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
        { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
        { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
@@ -2954,6 +2699,7 @@ static void combo_entry_changed(GtkComboBox *box, gpointer data)
        index = gtk_combo_box_get_active(box);
 
        multitext_set_entry(&ge->eta.iotype, index);
+       multitext_set_entry(&ge->eta.bs, index);
        multitext_set_entry(&ge->eta.ioengine, index);
        multitext_set_entry(&ge->eta.iodepth, index);
 }
@@ -2963,6 +2709,7 @@ static void combo_entry_destroy(GtkWidget *widget, gpointer data)
        struct gui_entry *ge = (struct gui_entry *) data;
 
        multitext_free(&ge->eta.iotype);
+       multitext_free(&ge->eta.bs);
        multitext_free(&ge->eta.ioengine);
        multitext_free(&ge->eta.iodepth);
 }
@@ -2998,6 +2745,7 @@ static GtkWidget *new_client_page(struct gui_entry *ge)
        g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
        g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
        ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
+       ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
        ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
        ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
        ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
@@ -3165,7 +2913,7 @@ static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
        }
 
        set_job_menu_visible(ui, 1);
-       ge = get_ge_from_page(page, NULL);
+       ge = get_ge_from_page(ui, page, NULL);
        if (ge)
                update_button_states(ui, ge);
 
@@ -3251,10 +2999,10 @@ static void add_recent_file_items(struct gui *ui)
 }
 
 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
-                                  gint x, gint y, GtkSelectionData *data,
-                                  guint info, guint time)
+                                  gint x, gint y, GtkSelectionData *seldata,
+                                  guint info, guint time, gpointer *data)
 {
-       struct gui *ui = &main_ui;
+       struct gui *ui = (struct gui *) data;
        gchar **uris;
        GtkWidget *source;
        int i;
@@ -3265,7 +3013,7 @@ static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
                return;
        }
 
-       uris = gtk_selection_data_get_uris(data);
+       uris = gtk_selection_data_get_uris(seldata);
        if (!uris) {
                gtk_drag_finish(ctx, FALSE, FALSE, time);
                return;
@@ -3305,8 +3053,8 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
        gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
        gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
 
-       g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
-       g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
+       g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
+       g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
 
        ui->vbox = gtk_vbox_new(FALSE, 0);
        gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);