2 * gfio - gui front end for fio - the flexible io tester
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40 static int gfio_server_running;
41 static unsigned int gfio_graph_limit = 100;
43 GdkColor gfio_color_white;
44 const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
46 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
48 static void connect_clicked(GtkWidget *widget, gpointer data);
49 static void start_job_clicked(GtkWidget *widget, gpointer data);
50 static void send_clicked(GtkWidget *widget, gpointer data);
52 static struct button_spec {
53 const char *buttontext;
55 const char *tooltiptext[2];
56 const int start_sensitive;
57 } buttonspeclist[] = {
59 .buttontext = "Connect",
61 .tooltiptext = { "Disconnect from host", "Connect to host" },
67 .tooltiptext = { "Send job description to host", NULL },
71 .buttontext = "Start Job",
72 .f = start_job_clicked,
73 .tooltiptext = { "Start the current job on the server", NULL },
78 static void setup_iops_graph(struct gfio_graphs *gg)
82 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
83 graph_title(g, "IOPS (IOs/sec)");
84 graph_x_title(g, "Time (secs)");
85 gg->read_iops = graph_add_label(g, "Read IOPS");
86 gg->write_iops = graph_add_label(g, "Write IOPS");
87 graph_set_color(g, gg->read_iops, 0.13, 0.54, 0.13);
88 graph_set_color(g, gg->write_iops, 1.0, 0.0, 0.0);
89 line_graph_set_data_count_limit(g, gfio_graph_limit);
90 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
91 graph_set_graph_all_zeroes(g, 0);
95 static void setup_bandwidth_graph(struct gfio_graphs *gg)
99 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
100 graph_title(g, "Bandwidth (bytes/sec)");
101 graph_x_title(g, "Time (secs)");
102 gg->read_bw = graph_add_label(g, "Read Bandwidth");
103 gg->write_bw = graph_add_label(g, "Write Bandwidth");
104 graph_set_color(g, gg->read_bw, 0.13, 0.54, 0.13);
105 graph_set_color(g, gg->write_bw, 1.0, 0.0, 0.0);
106 graph_set_base_offset(g, 1);
107 line_graph_set_data_count_limit(g, 100);
108 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
109 graph_set_graph_all_zeroes(g, 0);
110 gg->bandwidth_graph = g;
113 static void setup_graphs(struct gfio_graphs *g)
116 setup_bandwidth_graph(g);
119 void clear_ge_ui_info(struct gui_entry *ge)
121 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
122 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
123 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
124 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
126 /* should we empty it... */
127 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
129 multitext_update_entry(&ge->eta.iotype, 0, "");
130 multitext_update_entry(&ge->eta.bs, 0, "");
131 multitext_update_entry(&ge->eta.ioengine, 0, "");
132 multitext_update_entry(&ge->eta.iodepth, 0, "");
133 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
134 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
137 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
138 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
141 static void set_menu_entry_text(struct gui *ui, const char *path,
146 w = gtk_ui_manager_get_widget(ui->uimanager, path);
148 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
150 fprintf(stderr, "gfio: can't find path %s\n", path);
154 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
158 w = gtk_ui_manager_get_widget(ui->uimanager, path);
160 gtk_widget_set_sensitive(w, show);
162 fprintf(stderr, "gfio: can't find path %s\n", path);
165 static void set_job_menu_visible(struct gui *ui, int visible)
167 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
170 static void set_view_results_visible(struct gui *ui, int visible)
172 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
175 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
177 if (s->tooltiptext[sensitive])
178 return s->tooltiptext[sensitive];
180 return s->tooltiptext[0];
183 static GtkWidget *add_button(GtkWidget *buttonbox,
184 struct button_spec *buttonspec, gpointer data)
186 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
187 gboolean sens = buttonspec->start_sensitive;
189 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
190 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
192 sens = buttonspec->start_sensitive;
193 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
194 gtk_widget_set_sensitive(button, sens);
199 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
204 for (i = 0; i < nbuttons; i++)
205 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
209 * Update sensitivity of job buttons and job menu items, based on the
210 * state of the client.
212 static void update_button_states(struct gui *ui, struct gui_entry *ge)
214 unsigned int connect_state, send_state, start_state, edit_state;
215 const char *connect_str = NULL;
219 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
220 /* fall through to new state */
224 connect_str = "Connect";
228 case GE_STATE_CONNECTED:
231 connect_str = "Disconnect";
235 case GE_STATE_JOB_SENT:
238 connect_str = "Disconnect";
242 case GE_STATE_JOB_STARTED:
245 connect_str = "Disconnect";
249 case GE_STATE_JOB_RUNNING:
252 connect_str = "Disconnect";
256 case GE_STATE_JOB_DONE:
259 connect_str = "Connect";
265 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
266 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
267 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
268 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
269 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
271 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
272 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
274 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
275 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
276 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
278 if (ge->client && ge->client->nr_results)
279 set_view_results_visible(ui, 1);
281 set_view_results_visible(ui, 0);
284 void gfio_set_state(struct gui_entry *ge, unsigned int state)
287 update_button_states(ge->ui, ge);
290 static void gfio_ui_setup_log(struct gui *ui)
292 GtkTreeSelection *selection;
294 GtkWidget *tree_view;
296 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
298 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
299 gtk_widget_set_can_focus(tree_view, FALSE);
301 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
302 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
303 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
304 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
306 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
307 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
308 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
309 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
311 ui->log_model = model;
312 ui->log_tree = tree_view;
315 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
318 struct gfio_graphs *g = data;
320 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
321 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
322 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
323 graph_set_position(g->bandwidth_graph, 0, 0);
327 static void draw_graph(struct graph *g, cairo_t *cr)
329 line_graph_draw(g, cr);
333 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
334 gboolean keyboard_mode, GtkTooltip *tooltip,
337 struct gfio_graphs *g = data;
338 const char *text = NULL;
340 if (graph_contains_xy(g->iops_graph, x, y))
341 text = graph_find_tooltip(g->iops_graph, x, y);
342 else if (graph_contains_xy(g->bandwidth_graph, x, y))
343 text = graph_find_tooltip(g->bandwidth_graph, x, y);
346 gtk_tooltip_set_text(tooltip, text);
353 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
355 struct gfio_graphs *g = p;
358 cr = gdk_cairo_create(w->window);
360 if (graph_has_tooltips(g->iops_graph) ||
361 graph_has_tooltips(g->bandwidth_graph)) {
362 g_object_set(w, "has-tooltip", TRUE, NULL);
363 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
366 cairo_set_source_rgb(cr, 0, 0, 0);
367 draw_graph(g->iops_graph, cr);
368 draw_graph(g->bandwidth_graph, cr);
375 * FIXME: need more handling here
377 static void ge_destroy(struct gui_entry *ge)
379 struct gfio_client *gc = ge->client;
383 if (ge->state >= GE_STATE_CONNECTED)
384 fio_client_terminate(gc->client);
386 fio_put_client(gc->client);
391 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
398 static void ge_widget_destroy(GtkWidget *w, gpointer data)
400 struct gui_entry *ge = (struct gui_entry *) data;
405 static void gfio_quit(struct gui *ui)
410 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
411 __attribute__((unused)) gpointer data)
413 struct gui *ui = (struct gui *) data;
418 static void *job_thread(void *arg)
420 struct gui *ui = arg;
422 ui->handler_running = 1;
423 fio_handle_clients(&gfio_client_ops);
424 ui->handler_running = 0;
428 static int send_job_file(struct gui_entry *ge)
430 struct gfio_client *gc = ge->client;
434 * Prune old options, we are expecting the return options
435 * when the job file is parsed remotely and returned to us.
437 while (!flist_empty(&gc->o_list)) {
438 struct gfio_client_options *gco;
440 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list);
441 flist_del(&gco->list);
445 ret = fio_client_send_ini(gc->client, ge->job_file);
449 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
453 static void *server_thread(void *arg)
456 gfio_server_running = 1;
457 fio_start_server(NULL);
458 gfio_server_running = 0;
462 static void gfio_start_server(struct gui *ui)
464 if (!gfio_server_running) {
465 gfio_server_running = 1;
466 pthread_create(&ui->server_t, NULL, server_thread, NULL);
467 pthread_detach(ui->server_t);
471 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
474 struct gui_entry *ge = data;
475 struct gfio_client *gc = ge->client;
478 fio_start_client(gc->client);
481 static void file_open(GtkWidget *w, gpointer data);
483 struct connection_widgets
490 static void hostname_cb(GtkEntry *entry, gpointer data)
492 struct connection_widgets *cw = data;
493 int uses_net = 0, is_localhost = 0;
498 * Check whether to display the 'auto start backend' box
499 * or not. Show it if we are a localhost and using network,
502 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
503 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
508 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
509 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
510 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
511 !strcmp(text, "ip6-loopback"))
515 if (!uses_net || is_localhost) {
516 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
517 gtk_widget_set_sensitive(cw->button, 1);
519 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
520 gtk_widget_set_sensitive(cw->button, 0);
524 static int get_connection_details(struct gui_entry *ge)
526 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
527 struct connection_widgets cw;
528 struct gui *ui = ge->ui;
534 dialog = gtk_dialog_new_with_buttons("Connection details",
535 GTK_WINDOW(ui->window),
536 GTK_DIALOG_DESTROY_WITH_PARENT,
537 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
538 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
540 frame = gtk_frame_new("Hostname / socket name");
541 /* gtk_dialog_get_content_area() is 2.14 and newer */
542 vbox = GTK_DIALOG(dialog)->vbox;
543 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
545 box = gtk_vbox_new(FALSE, 6);
546 gtk_container_add(GTK_CONTAINER(frame), box);
548 hbox = gtk_hbox_new(TRUE, 10);
549 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
550 cw.hentry = gtk_entry_new();
551 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
552 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
554 frame = gtk_frame_new("Port");
555 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
556 box = gtk_vbox_new(FALSE, 10);
557 gtk_container_add(GTK_CONTAINER(frame), box);
559 hbox = gtk_hbox_new(TRUE, 4);
560 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
561 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
563 frame = gtk_frame_new("Type");
564 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
565 box = gtk_vbox_new(FALSE, 10);
566 gtk_container_add(GTK_CONTAINER(frame), box);
568 hbox = gtk_hbox_new(TRUE, 4);
569 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
571 cw.combo = gtk_combo_box_new_text();
572 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
573 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
574 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
575 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
577 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
579 frame = gtk_frame_new("Options");
580 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
581 box = gtk_vbox_new(FALSE, 10);
582 gtk_container_add(GTK_CONTAINER(frame), box);
584 hbox = gtk_hbox_new(TRUE, 4);
585 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
587 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
588 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
589 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.");
590 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
593 * Connect edit signal, so we can show/not-show the auto start button
595 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
596 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
598 gtk_widget_show_all(dialog);
600 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
601 gtk_widget_destroy(dialog);
605 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
606 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
608 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
609 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
610 ge->type = Fio_client_ipv4;
611 else if (!strncmp(typeentry, "IPv6", 4))
612 ge->type = Fio_client_ipv6;
614 ge->type = Fio_client_socket;
617 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
619 gtk_widget_destroy(dialog);
623 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
625 gc->client = fio_get_client(client);
626 client->client_data = gc;
629 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
631 struct gfio_client_options *gco;
632 struct gfio_client *gc;
634 gc = calloc(1, sizeof(*gc));
635 INIT_FLIST_HEAD(&gc->o_list);
638 gfio_set_client(gc, client);
641 * Just add a default set of options, need to consider how best
644 gco = calloc(1, sizeof(*gco));
645 INIT_FLIST_HEAD(&gco->list);
646 options_default_fill(&gco->o);
647 flist_add_tail(&gco->list, &gc->o_list);
651 static void connect_clicked(GtkWidget *widget, gpointer data)
653 struct gui_entry *ge = data;
654 struct gfio_client *gc = ge->client;
656 if (ge->state == GE_STATE_NEW) {
660 file_open(widget, ge->ui);
667 struct fio_client *client;
669 if (get_connection_details(ge)) {
670 gfio_report_error(ge, "Failed to get connection details\n");
674 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
676 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
681 gfio_set_client(gc, client);
684 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
685 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
686 ret = fio_client_connect(gc->client);
688 if (!ge->ui->handler_running)
689 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
690 gfio_set_state(ge, GE_STATE_CONNECTED);
692 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
695 fio_client_terminate(gc->client);
696 gfio_set_state(ge, GE_STATE_NEW);
697 clear_ge_ui_info(ge);
701 static void send_clicked(GtkWidget *widget, gpointer data)
703 struct gui_entry *ge = data;
705 if (send_job_file(ge))
706 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
709 static GtkWidget *new_client_page(struct gui_entry *ge);
711 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
713 struct gui_entry *ge;
715 ge = malloc(sizeof(*ge));
716 memset(ge, 0, sizeof(*ge));
717 ge->state = GE_STATE_NEW;
722 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
724 struct gui_entry *ge;
726 ge = alloc_new_gui_entry(ui);
728 ge->vbox = new_client_page(ge);
729 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
731 ge->page_label = gtk_label_new(name);
732 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
734 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
736 gtk_widget_show_all(ui->window);
740 static void file_new(GtkWidget *w, gpointer data)
742 struct gui *ui = (struct gui *) data;
743 struct gui_entry *ge;
745 ge = get_new_ge_with_tab(ui, "Untitled");
746 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
750 * Return the 'ge' corresponding to the tab. If the active tab is the
751 * main tab, open a new tab.
753 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
759 return get_new_ge_with_tab(ui, "Untitled");
765 return g_hash_table_lookup(ui->ge_hash, &cur_page);
768 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
773 * Main tab is tab 0, so any current page other than 0 holds
776 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
778 return get_ge_from_page(ui, cur_page, NULL);
783 static void file_close(GtkWidget *w, gpointer data)
785 struct gui *ui = (struct gui *) data;
786 struct gui_entry *ge;
789 * Can't close the main tab
791 ge = get_ge_from_cur_tab(ui);
793 gtk_widget_destroy(ge->vbox);
797 if (g_hash_table_size(ui->ge_hash)) {
798 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
805 static void file_add_recent(struct gui *ui, const gchar *uri)
809 memset(&grd, 0, sizeof(grd));
810 grd.display_name = strdup("gfio");
811 grd.description = strdup("Fio job file");
812 grd.mime_type = strdup(GFIO_MIME);
813 grd.app_name = strdup(g_get_application_name());
814 grd.app_exec = strdup("gfio %f/%u");
816 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
819 static gchar *get_filename_from_uri(const gchar *uri)
821 if (strncmp(uri, "file://", 7))
824 return strdup(uri + 7);
827 static int do_file_open(struct gui_entry *ge, const gchar *uri)
829 struct fio_client *client;
831 assert(!ge->job_file);
833 ge->job_file = get_filename_from_uri(uri);
835 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
837 char *label = strdup(uri);
840 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
843 gfio_client_added(ge, client);
844 file_add_recent(ge->ui, uri);
848 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
856 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
858 struct gui_entry *ge;
860 int ret, ge_is_new = 0;
863 * Creates new tab if current tab is the main window, or the
864 * current tab already has a client.
866 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
867 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
869 ge = get_new_ge_with_tab(ui, "Untitled");
873 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
875 if (get_connection_details(ge)) {
877 gtk_widget_destroy(ge->vbox);
882 ret = do_file_open(ge, uri);
885 if (ge->server_start)
886 gfio_start_server(ui);
889 gtk_widget_destroy(ge->vbox);
895 static void recent_open(GtkAction *action, gpointer data)
897 struct gui *ui = (struct gui *) data;
901 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
902 uri = gtk_recent_info_get_uri(info);
904 do_file_open_with_tab(ui, uri);
907 static void file_open(GtkWidget *w, gpointer data)
909 struct gui *ui = data;
911 GtkFileFilter *filter;
914 dialog = gtk_file_chooser_dialog_new("Open File",
915 GTK_WINDOW(ui->window),
916 GTK_FILE_CHOOSER_ACTION_OPEN,
917 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
918 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
920 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
922 filter = gtk_file_filter_new();
923 gtk_file_filter_add_pattern(filter, "*.fio");
924 gtk_file_filter_add_pattern(filter, "*.job");
925 gtk_file_filter_add_pattern(filter, "*.ini");
926 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
927 gtk_file_filter_set_name(filter, "Fio job file");
928 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
930 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
931 gtk_widget_destroy(dialog);
935 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
937 gtk_widget_destroy(dialog);
939 do_file_open_with_tab(ui, filename);
943 static void file_save(GtkWidget *w, gpointer data)
945 struct gui *ui = data;
948 dialog = gtk_file_chooser_dialog_new("Save File",
949 GTK_WINDOW(ui->window),
950 GTK_FILE_CHOOSER_ACTION_SAVE,
951 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
952 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
955 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
956 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
958 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
961 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
962 // save_job_file(filename);
965 gtk_widget_destroy(dialog);
968 static void view_log_destroy(GtkWidget *w, gpointer data)
970 struct gui *ui = (struct gui *) data;
972 gtk_widget_ref(ui->log_tree);
973 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
974 gtk_widget_destroy(w);
978 void gfio_view_log(struct gui *ui)
980 GtkWidget *win, *scroll, *vbox, *box;
985 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
986 gtk_window_set_title(GTK_WINDOW(win), "Log");
987 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
989 scroll = gtk_scrolled_window_new(NULL, NULL);
991 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
993 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
995 box = gtk_hbox_new(TRUE, 0);
996 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
997 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
998 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1000 vbox = gtk_vbox_new(TRUE, 5);
1001 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1003 gtk_container_add(GTK_CONTAINER(win), vbox);
1004 gtk_widget_show_all(win);
1007 static void view_log(GtkWidget *w, gpointer data)
1009 struct gui *ui = (struct gui *) data;
1014 static void connect_job_entry(GtkWidget *w, gpointer data)
1016 struct gui *ui = (struct gui *) data;
1017 struct gui_entry *ge;
1019 ge = get_ge_from_cur_tab(ui);
1021 connect_clicked(w, ge);
1024 static void send_job_entry(GtkWidget *w, gpointer data)
1026 struct gui *ui = (struct gui *) data;
1027 struct gui_entry *ge;
1029 ge = get_ge_from_cur_tab(ui);
1031 send_clicked(w, ge);
1034 static void edit_job_entry(GtkWidget *w, gpointer data)
1036 struct gui *ui = (struct gui *) data;
1037 struct gui_entry *ge;
1039 ge = get_ge_from_cur_tab(ui);
1040 if (ge && ge->client)
1041 gopt_get_options_window(ui->window, ge->client);
1044 static void start_job_entry(GtkWidget *w, gpointer data)
1046 struct gui *ui = (struct gui *) data;
1047 struct gui_entry *ge;
1049 ge = get_ge_from_cur_tab(ui);
1051 start_job_clicked(w, ge);
1054 static void view_results(GtkWidget *w, gpointer data)
1056 struct gui *ui = (struct gui *) data;
1057 struct gfio_client *gc;
1058 struct gui_entry *ge;
1060 ge = get_ge_from_cur_tab(ui);
1064 if (ge->results_window)
1068 if (gc && gc->nr_results)
1069 gfio_display_end_results(gc);
1072 static void __update_graph_settings(struct gfio_graphs *g)
1074 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1075 graph_set_font(g->iops_graph, gfio_graph_font);
1076 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1077 graph_set_font(g->bandwidth_graph, gfio_graph_font);
1080 static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
1082 struct gui_entry *ge = (struct gui_entry *) value;
1085 __update_graph_settings(&ge->graphs);
1087 ev = gdk_event_new(GDK_EXPOSE);
1088 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), "expose_event", GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1092 static void update_graph_limits(void)
1094 struct gui *ui = &main_ui;
1097 __update_graph_settings(&ui->graphs);
1099 ev = gdk_event_new(GDK_EXPOSE);
1100 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), "expose_event", GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1103 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
1106 static void preferences(GtkWidget *w, gpointer data)
1108 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1109 GtkWidget *hbox, *spin, *entry, *spin_int;
1110 struct gui *ui = (struct gui *) data;
1113 dialog = gtk_dialog_new_with_buttons("Preferences",
1114 GTK_WINDOW(ui->window),
1115 GTK_DIALOG_DESTROY_WITH_PARENT,
1116 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1117 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1120 frame = gtk_frame_new("Graphing");
1121 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1122 vbox = gtk_vbox_new(FALSE, 6);
1123 gtk_container_add(GTK_CONTAINER(frame), vbox);
1125 hbox = gtk_hbox_new(FALSE, 5);
1126 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1127 entry = gtk_label_new("Font face to use for graph labels");
1128 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1130 font = gtk_font_button_new_with_font(gfio_graph_font);
1131 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1133 box = gtk_vbox_new(FALSE, 6);
1134 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1136 hbox = gtk_hbox_new(FALSE, 5);
1137 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1138 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1139 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1141 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1143 box = gtk_vbox_new(FALSE, 6);
1144 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1146 hbox = gtk_hbox_new(FALSE, 5);
1147 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1148 entry = gtk_label_new("Client ETA request interval (msec)");
1149 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1151 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
1152 frame = gtk_frame_new("Debug logging");
1153 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1154 vbox = gtk_vbox_new(FALSE, 6);
1155 gtk_container_add(GTK_CONTAINER(frame), vbox);
1157 box = gtk_hbox_new(FALSE, 6);
1158 gtk_container_add(GTK_CONTAINER(vbox), box);
1160 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1162 for (i = 0; i < FD_DEBUG_MAX; i++) {
1164 box = gtk_hbox_new(FALSE, 6);
1165 gtk_container_add(GTK_CONTAINER(vbox), box);
1169 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1170 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1171 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1174 gtk_widget_show_all(dialog);
1176 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1177 gtk_widget_destroy(dialog);
1181 for (i = 0; i < FD_DEBUG_MAX; i++) {
1184 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1186 fio_debug |= (1UL << i);
1189 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1190 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1191 update_graph_limits();
1192 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1194 gtk_widget_destroy(dialog);
1197 static void about_dialog(GtkWidget *w, gpointer data)
1199 const char *authors[] = {
1200 "Jens Axboe <axboe@kernel.dk>",
1201 "Stephen Carmeron <stephenmcameron@gmail.com>",
1204 const char *license[] = {
1205 "Fio is free software; you can redistribute it and/or modify "
1206 "it under the terms of the GNU General Public License as published by "
1207 "the Free Software Foundation; either version 2 of the License, or "
1208 "(at your option) any later version.\n",
1209 "Fio is distributed in the hope that it will be useful, "
1210 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1211 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1212 "GNU General Public License for more details.\n",
1213 "You should have received a copy of the GNU General Public License "
1214 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1215 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1217 char *license_trans;
1219 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1220 license[2], "\n", NULL);
1222 gtk_show_about_dialog(NULL,
1223 "program-name", "gfio",
1224 "comments", "Gtk2 UI for fio",
1225 "license", license_trans,
1226 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1228 "version", fio_version_string,
1229 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1230 "logo-icon-name", "fio",
1232 "wrap-license", TRUE,
1235 g_free(license_trans);
1238 static GtkActionEntry menu_items[] = {
1239 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1240 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1241 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1242 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1243 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1244 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1245 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1246 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1247 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1248 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1249 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1250 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1251 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1252 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1253 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1254 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1255 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1257 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1259 static const gchar *ui_string = " \
1261 <menubar name=\"MainMenu\"> \
1262 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1263 <menuitem name=\"New\" action=\"NewFile\" /> \
1264 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1265 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1266 <separator name=\"Separator1\"/> \
1267 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1268 <separator name=\"Separator2\"/> \
1269 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1270 <separator name=\"Separator3\"/> \
1271 <placeholder name=\"FileRecentFiles\"/> \
1272 <separator name=\"Separator4\"/> \
1273 <menuitem name=\"Quit\" action=\"Quit\" /> \
1275 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1276 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1277 <separator name=\"Separator5\"/> \
1278 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1279 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1280 <separator name=\"Separator6\"/> \
1281 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1283 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1284 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1285 <separator name=\"Separator7\"/> \
1286 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1288 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1289 <menuitem name=\"About\" action=\"About\" /> \
1295 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1298 GtkActionGroup *action_group;
1301 action_group = gtk_action_group_new("Menu");
1302 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1304 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1305 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1307 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1309 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1312 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1313 GtkWidget *vbox, GtkUIManager *ui_manager)
1315 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1318 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1320 struct gui_entry *ge = (struct gui_entry *) data;
1323 index = gtk_combo_box_get_active(box);
1325 multitext_set_entry(&ge->eta.iotype, index);
1326 multitext_set_entry(&ge->eta.bs, index);
1327 multitext_set_entry(&ge->eta.ioengine, index);
1328 multitext_set_entry(&ge->eta.iodepth, index);
1331 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1333 struct gui_entry *ge = (struct gui_entry *) data;
1335 multitext_free(&ge->eta.iotype);
1336 multitext_free(&ge->eta.bs);
1337 multitext_free(&ge->eta.ioengine);
1338 multitext_free(&ge->eta.iodepth);
1341 static GtkWidget *new_client_page(struct gui_entry *ge)
1343 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1344 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1346 main_vbox = gtk_vbox_new(FALSE, 3);
1348 top_align = gtk_alignment_new(0, 0, 1, 0);
1349 top_vbox = gtk_vbox_new(FALSE, 3);
1350 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1351 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1353 probe = gtk_frame_new("Job");
1354 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1355 probe_frame = gtk_vbox_new(FALSE, 3);
1356 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1358 probe_box = gtk_hbox_new(FALSE, 3);
1359 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1360 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1361 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1362 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1363 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1365 probe_box = gtk_hbox_new(FALSE, 3);
1366 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1368 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1369 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1370 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1371 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1372 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1373 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1374 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1375 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1376 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1378 probe_box = gtk_hbox_new(FALSE, 3);
1379 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1380 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1381 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1382 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1383 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1386 * Only add this if we have a commit rate
1389 probe_box = gtk_hbox_new(FALSE, 3);
1390 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1392 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1393 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1395 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1396 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1400 * Set up a drawing area and IOPS and bandwidth graphs
1402 ge->graphs.drawing_area = gtk_drawing_area_new();
1403 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1404 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1405 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1406 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1407 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1408 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1409 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1410 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1411 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1412 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1413 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1414 ge->graphs.drawing_area);
1415 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1417 setup_graphs(&ge->graphs);
1420 * Set up alignments for widgets at the bottom of ui,
1421 * align bottom left, expand horizontally but not vertically
1423 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1424 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1425 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1426 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1428 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
1431 * Set up thread status progress bar
1433 ge->thread_status_pb = gtk_progress_bar_new();
1434 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1435 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1436 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1442 static GtkWidget *new_main_page(struct gui *ui)
1444 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1445 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1447 main_vbox = gtk_vbox_new(FALSE, 3);
1450 * Set up alignments for widgets at the top of ui,
1451 * align top left, expand horizontally but not vertically
1453 top_align = gtk_alignment_new(0, 0, 1, 0);
1454 top_vbox = gtk_vbox_new(FALSE, 0);
1455 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1456 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1458 probe = gtk_frame_new("Run statistics");
1459 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1460 probe_frame = gtk_vbox_new(FALSE, 3);
1461 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1463 probe_box = gtk_hbox_new(FALSE, 3);
1464 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1465 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1466 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1467 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1468 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1469 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1472 * Only add this if we have a commit rate
1475 probe_box = gtk_hbox_new(FALSE, 3);
1476 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1478 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1479 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1481 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1482 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1486 * Set up a drawing area and IOPS and bandwidth graphs
1488 ui->graphs.drawing_area = gtk_drawing_area_new();
1489 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1490 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1491 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1492 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1493 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1494 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1495 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1496 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1497 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1498 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1499 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1500 ui->graphs.drawing_area);
1501 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1504 setup_graphs(&ui->graphs);
1507 * Set up alignments for widgets at the bottom of ui,
1508 * align bottom left, expand horizontally but not vertically
1510 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1511 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1512 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1513 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1516 * Set up thread status progress bar
1518 ui->thread_status_pb = gtk_progress_bar_new();
1519 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1520 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1521 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1526 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1527 guint page, gpointer data)
1530 struct gui *ui = (struct gui *) data;
1531 struct gui_entry *ge;
1534 set_job_menu_visible(ui, 0);
1535 set_view_results_visible(ui, 0);
1539 set_job_menu_visible(ui, 1);
1540 ge = get_ge_from_page(ui, page, NULL);
1542 update_button_states(ui, ge);
1547 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1549 time_t time_a = gtk_recent_info_get_visited(a);
1550 time_t time_b = gtk_recent_info_get_visited(b);
1552 return time_b - time_a;
1555 static void add_recent_file_items(struct gui *ui)
1557 const gchar *gfio = g_get_application_name();
1558 GList *items, *item;
1561 if (ui->recent_ui_id) {
1562 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1563 gtk_ui_manager_ensure_update(ui->uimanager);
1565 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1567 if (ui->actiongroup) {
1568 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1569 g_object_unref(ui->actiongroup);
1571 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1573 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1575 items = gtk_recent_manager_get_items(ui->recentmanager);
1576 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1578 for (item = items; item && item->data; item = g_list_next(item)) {
1579 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1584 if (!gtk_recent_info_has_application(info, gfio))
1588 * We only support local files for now
1590 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1593 action_name = g_strdup_printf("RecentFile%u", i++);
1594 label = gtk_recent_info_get_display_name(info);
1596 action = g_object_new(GTK_TYPE_ACTION,
1597 "name", action_name,
1598 "label", label, NULL);
1600 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1601 gtk_recent_info_ref(info),
1602 (GDestroyNotify) gtk_recent_info_unref);
1605 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1607 gtk_action_group_add_action(ui->actiongroup, action);
1608 g_object_unref(action);
1610 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1611 "/MainMenu/FileMenu/FileRecentFiles",
1613 GTK_UI_MANAGER_MENUITEM, FALSE);
1615 g_free(action_name);
1621 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1625 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1626 gint x, gint y, GtkSelectionData *seldata,
1627 guint info, guint time, gpointer *data)
1629 struct gui *ui = (struct gui *) data;
1633 source = gtk_drag_get_source_widget(ctx);
1634 if (source && widget == gtk_widget_get_toplevel(source)) {
1635 gtk_drag_finish(ctx, FALSE, FALSE, time);
1639 uris = gtk_selection_data_get_uris(seldata);
1641 gtk_drag_finish(ctx, FALSE, FALSE, time);
1646 do_file_open_with_tab(ui, uris[0]);
1648 gtk_drag_finish(ctx, TRUE, FALSE, time);
1652 static void init_ui(int *argc, char **argv[], struct gui *ui)
1654 GtkSettings *settings;
1657 /* Magical g*thread incantation, you just need this thread stuff.
1658 * Without it, the update that happens in gfio_update_thread_status
1659 * doesn't really happen in a timely fashion, you need expose events
1661 if (!g_thread_supported())
1662 g_thread_init(NULL);
1665 gtk_init(argc, argv);
1666 settings = gtk_settings_get_default();
1667 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1669 gdk_color_parse("white", &gfio_color_white);
1671 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1672 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1673 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1675 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1676 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1678 ui->vbox = gtk_vbox_new(FALSE, 0);
1679 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1681 ui->uimanager = gtk_ui_manager_new();
1682 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1683 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1685 ui->recentmanager = gtk_recent_manager_get_default();
1686 add_recent_file_items(ui);
1688 ui->notebook = gtk_notebook_new();
1689 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1690 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1691 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1692 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1694 vbox = new_main_page(ui);
1695 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1696 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1697 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1699 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1701 gfio_ui_setup_log(ui);
1703 gtk_widget_show_all(ui->window);
1706 int main(int argc, char *argv[], char *envp[])
1708 if (initialize_fio(envp))
1710 if (fio_init_options())
1715 memset(&main_ui, 0, sizeof(main_ui));
1716 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
1718 init_ui(&argc, &argv, &main_ui);
1720 gdk_threads_enter();
1722 gdk_threads_leave();
1724 g_hash_table_destroy(main_ui.ge_hash);