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_text_get_active_text(GTK_COMBO_BOX_TEXT(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 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
542 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
544 box = gtk_vbox_new(FALSE, 6);
545 gtk_container_add(GTK_CONTAINER(frame), box);
547 hbox = gtk_hbox_new(TRUE, 10);
548 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
549 cw.hentry = gtk_entry_new();
550 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
551 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
553 frame = gtk_frame_new("Port");
554 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
555 box = gtk_vbox_new(FALSE, 10);
556 gtk_container_add(GTK_CONTAINER(frame), box);
558 hbox = gtk_hbox_new(TRUE, 4);
559 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
560 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
562 frame = gtk_frame_new("Type");
563 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
564 box = gtk_vbox_new(FALSE, 10);
565 gtk_container_add(GTK_CONTAINER(frame), box);
567 hbox = gtk_hbox_new(TRUE, 4);
568 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
570 cw.combo = gtk_combo_box_text_new();
571 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
572 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
573 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
574 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
576 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
578 frame = gtk_frame_new("Options");
579 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
580 box = gtk_vbox_new(FALSE, 10);
581 gtk_container_add(GTK_CONTAINER(frame), box);
583 hbox = gtk_hbox_new(TRUE, 4);
584 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
586 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
587 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
588 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.");
589 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
592 * Connect edit signal, so we can show/not-show the auto start button
594 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
595 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
597 gtk_widget_show_all(dialog);
599 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
600 gtk_widget_destroy(dialog);
604 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
605 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
607 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
608 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
609 ge->type = Fio_client_ipv4;
610 else if (!strncmp(typeentry, "IPv6", 4))
611 ge->type = Fio_client_ipv6;
613 ge->type = Fio_client_socket;
616 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
618 gtk_widget_destroy(dialog);
622 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
624 gc->client = fio_get_client(client);
625 client->client_data = gc;
628 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
630 struct gfio_client_options *gco;
631 struct gfio_client *gc;
633 gc = calloc(1, sizeof(*gc));
634 INIT_FLIST_HEAD(&gc->o_list);
637 gfio_set_client(gc, client);
640 * Just add a default set of options, need to consider how best
643 gco = calloc(1, sizeof(*gco));
644 INIT_FLIST_HEAD(&gco->list);
645 options_default_fill(&gco->o);
646 flist_add_tail(&gco->list, &gc->o_list);
650 static void connect_clicked(GtkWidget *widget, gpointer data)
652 struct gui_entry *ge = data;
653 struct gfio_client *gc = ge->client;
655 if (ge->state == GE_STATE_NEW) {
659 file_open(widget, ge->ui);
666 struct fio_client *client;
668 if (get_connection_details(ge)) {
669 gfio_report_error(ge, "Failed to get connection details\n");
673 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
675 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
680 gfio_set_client(gc, client);
683 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
684 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
685 ret = fio_client_connect(gc->client);
687 if (!ge->ui->handler_running)
688 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
689 gfio_set_state(ge, GE_STATE_CONNECTED);
691 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
694 fio_client_terminate(gc->client);
695 gfio_set_state(ge, GE_STATE_NEW);
696 clear_ge_ui_info(ge);
700 static void send_clicked(GtkWidget *widget, gpointer data)
702 struct gui_entry *ge = data;
704 if (send_job_file(ge))
705 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
708 static GtkWidget *new_client_page(struct gui_entry *ge);
710 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
712 struct gui_entry *ge;
714 ge = malloc(sizeof(*ge));
715 memset(ge, 0, sizeof(*ge));
716 ge->state = GE_STATE_NEW;
721 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
723 struct gui_entry *ge;
725 ge = alloc_new_gui_entry(ui);
727 ge->vbox = new_client_page(ge);
728 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
730 ge->page_label = gtk_label_new(name);
731 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
733 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
735 gtk_widget_show_all(ui->window);
739 static void file_new(GtkWidget *w, gpointer data)
741 struct gui *ui = (struct gui *) data;
742 struct gui_entry *ge;
744 ge = get_new_ge_with_tab(ui, "Untitled");
745 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
749 * Return the 'ge' corresponding to the tab. If the active tab is the
750 * main tab, open a new tab.
752 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
758 return get_new_ge_with_tab(ui, "Untitled");
764 return g_hash_table_lookup(ui->ge_hash, &cur_page);
767 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
772 * Main tab is tab 0, so any current page other than 0 holds
775 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
777 return get_ge_from_page(ui, cur_page, NULL);
782 static void file_close(GtkWidget *w, gpointer data)
784 struct gui *ui = (struct gui *) data;
785 struct gui_entry *ge;
788 * Can't close the main tab
790 ge = get_ge_from_cur_tab(ui);
792 gtk_widget_destroy(ge->vbox);
796 if (g_hash_table_size(ui->ge_hash)) {
797 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
804 static void file_add_recent(struct gui *ui, const gchar *uri)
808 memset(&grd, 0, sizeof(grd));
809 grd.display_name = strdup("gfio");
810 grd.description = strdup("Fio job file");
811 grd.mime_type = strdup(GFIO_MIME);
812 grd.app_name = strdup(g_get_application_name());
813 grd.app_exec = strdup("gfio %f/%u");
815 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
818 static gchar *get_filename_from_uri(const gchar *uri)
820 if (strncmp(uri, "file://", 7))
823 return strdup(uri + 7);
826 static int do_file_open(struct gui_entry *ge, const gchar *uri)
828 struct fio_client *client;
830 assert(!ge->job_file);
832 ge->job_file = get_filename_from_uri(uri);
834 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
836 char *label = strdup(uri);
839 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
842 gfio_client_added(ge, client);
843 file_add_recent(ge->ui, uri);
847 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
855 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
857 struct gui_entry *ge;
859 int ret, ge_is_new = 0;
862 * Creates new tab if current tab is the main window, or the
863 * current tab already has a client.
865 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
866 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
868 ge = get_new_ge_with_tab(ui, "Untitled");
872 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
874 if (get_connection_details(ge)) {
876 gtk_widget_destroy(ge->vbox);
881 ret = do_file_open(ge, uri);
884 if (ge->server_start)
885 gfio_start_server(ui);
888 gtk_widget_destroy(ge->vbox);
894 static void recent_open(GtkAction *action, gpointer data)
896 struct gui *ui = (struct gui *) data;
900 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
901 uri = gtk_recent_info_get_uri(info);
903 do_file_open_with_tab(ui, uri);
906 static void file_open(GtkWidget *w, gpointer data)
908 struct gui *ui = data;
910 GtkFileFilter *filter;
913 dialog = gtk_file_chooser_dialog_new("Open File",
914 GTK_WINDOW(ui->window),
915 GTK_FILE_CHOOSER_ACTION_OPEN,
916 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
917 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
919 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
921 filter = gtk_file_filter_new();
922 gtk_file_filter_add_pattern(filter, "*.fio");
923 gtk_file_filter_add_pattern(filter, "*.job");
924 gtk_file_filter_add_pattern(filter, "*.ini");
925 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
926 gtk_file_filter_set_name(filter, "Fio job file");
927 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
929 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
930 gtk_widget_destroy(dialog);
934 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
936 gtk_widget_destroy(dialog);
938 do_file_open_with_tab(ui, filename);
942 static void file_save(GtkWidget *w, gpointer data)
944 struct gui *ui = data;
947 dialog = gtk_file_chooser_dialog_new("Save File",
948 GTK_WINDOW(ui->window),
949 GTK_FILE_CHOOSER_ACTION_SAVE,
950 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
951 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
954 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
955 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
957 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
960 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
961 // save_job_file(filename);
964 gtk_widget_destroy(dialog);
967 static void view_log_destroy(GtkWidget *w, gpointer data)
969 struct gui *ui = (struct gui *) data;
971 g_object_ref(G_OBJECT(ui->log_tree));
972 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
973 gtk_widget_destroy(w);
977 void gfio_view_log(struct gui *ui)
979 GtkWidget *win, *scroll, *vbox, *box;
984 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
985 gtk_window_set_title(GTK_WINDOW(win), "Log");
986 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
988 scroll = gtk_scrolled_window_new(NULL, NULL);
990 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
992 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
994 box = gtk_hbox_new(TRUE, 0);
995 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
996 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
997 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
999 vbox = gtk_vbox_new(TRUE, 5);
1000 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
1002 gtk_container_add(GTK_CONTAINER(win), vbox);
1003 gtk_widget_show_all(win);
1006 static void view_log(GtkWidget *w, gpointer data)
1008 struct gui *ui = (struct gui *) data;
1013 static void connect_job_entry(GtkWidget *w, gpointer data)
1015 struct gui *ui = (struct gui *) data;
1016 struct gui_entry *ge;
1018 ge = get_ge_from_cur_tab(ui);
1020 connect_clicked(w, ge);
1023 static void send_job_entry(GtkWidget *w, gpointer data)
1025 struct gui *ui = (struct gui *) data;
1026 struct gui_entry *ge;
1028 ge = get_ge_from_cur_tab(ui);
1030 send_clicked(w, ge);
1033 static void edit_job_entry(GtkWidget *w, gpointer data)
1035 struct gui *ui = (struct gui *) data;
1036 struct gui_entry *ge;
1038 ge = get_ge_from_cur_tab(ui);
1039 if (ge && ge->client)
1040 gopt_get_options_window(ui->window, ge->client);
1043 static void start_job_entry(GtkWidget *w, gpointer data)
1045 struct gui *ui = (struct gui *) data;
1046 struct gui_entry *ge;
1048 ge = get_ge_from_cur_tab(ui);
1050 start_job_clicked(w, ge);
1053 static void view_results(GtkWidget *w, gpointer data)
1055 struct gui *ui = (struct gui *) data;
1056 struct gfio_client *gc;
1057 struct gui_entry *ge;
1059 ge = get_ge_from_cur_tab(ui);
1063 if (ge->results_window)
1067 if (gc && gc->nr_results)
1068 gfio_display_end_results(gc);
1071 static void __update_graph_settings(struct gfio_graphs *g)
1073 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1074 graph_set_font(g->iops_graph, gfio_graph_font);
1075 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1076 graph_set_font(g->bandwidth_graph, gfio_graph_font);
1079 static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
1081 struct gui_entry *ge = (struct gui_entry *) value;
1084 __update_graph_settings(&ge->graphs);
1086 ev = gdk_event_new(GDK_EXPOSE);
1087 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), "expose_event", GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1091 static void update_graph_limits(void)
1093 struct gui *ui = &main_ui;
1096 __update_graph_settings(&ui->graphs);
1098 ev = gdk_event_new(GDK_EXPOSE);
1099 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), "expose_event", GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1102 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
1105 static void preferences(GtkWidget *w, gpointer data)
1107 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1108 GtkWidget *hbox, *spin, *entry, *spin_int;
1109 struct gui *ui = (struct gui *) data;
1112 dialog = gtk_dialog_new_with_buttons("Preferences",
1113 GTK_WINDOW(ui->window),
1114 GTK_DIALOG_DESTROY_WITH_PARENT,
1115 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1116 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1119 frame = gtk_frame_new("Graphing");
1120 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1121 gtk_box_pack_start(GTK_BOX(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 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1154 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1155 vbox = gtk_vbox_new(FALSE, 6);
1156 gtk_container_add(GTK_CONTAINER(frame), vbox);
1158 box = gtk_hbox_new(FALSE, 6);
1159 gtk_container_add(GTK_CONTAINER(vbox), box);
1161 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1163 for (i = 0; i < FD_DEBUG_MAX; i++) {
1165 box = gtk_hbox_new(FALSE, 6);
1166 gtk_container_add(GTK_CONTAINER(vbox), box);
1170 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1171 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1172 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1175 gtk_widget_show_all(dialog);
1177 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1178 gtk_widget_destroy(dialog);
1182 for (i = 0; i < FD_DEBUG_MAX; i++) {
1185 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1187 fio_debug |= (1UL << i);
1190 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1191 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1192 update_graph_limits();
1193 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1195 gtk_widget_destroy(dialog);
1198 static void about_dialog(GtkWidget *w, gpointer data)
1200 const char *authors[] = {
1201 "Jens Axboe <axboe@kernel.dk>",
1202 "Stephen Carmeron <stephenmcameron@gmail.com>",
1205 const char *license[] = {
1206 "Fio is free software; you can redistribute it and/or modify "
1207 "it under the terms of the GNU General Public License as published by "
1208 "the Free Software Foundation; either version 2 of the License, or "
1209 "(at your option) any later version.\n",
1210 "Fio is distributed in the hope that it will be useful, "
1211 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1212 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1213 "GNU General Public License for more details.\n",
1214 "You should have received a copy of the GNU General Public License "
1215 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1216 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1218 char *license_trans;
1220 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1221 license[2], "\n", NULL);
1223 gtk_show_about_dialog(NULL,
1224 "program-name", "gfio",
1225 "comments", "Gtk2 UI for fio",
1226 "license", license_trans,
1227 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1229 "version", fio_version_string,
1230 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1231 "logo-icon-name", "fio",
1233 "wrap-license", TRUE,
1236 g_free(license_trans);
1239 static GtkActionEntry menu_items[] = {
1240 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1241 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1242 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1243 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1244 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1245 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1246 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1247 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1248 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1249 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1250 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1251 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1252 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1253 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1254 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1255 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1256 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1258 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1260 static const gchar *ui_string = " \
1262 <menubar name=\"MainMenu\"> \
1263 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1264 <menuitem name=\"New\" action=\"NewFile\" /> \
1265 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1266 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1267 <separator name=\"Separator1\"/> \
1268 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1269 <separator name=\"Separator2\"/> \
1270 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1271 <separator name=\"Separator3\"/> \
1272 <placeholder name=\"FileRecentFiles\"/> \
1273 <separator name=\"Separator4\"/> \
1274 <menuitem name=\"Quit\" action=\"Quit\" /> \
1276 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1277 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1278 <separator name=\"Separator5\"/> \
1279 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1280 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1281 <separator name=\"Separator6\"/> \
1282 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1284 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1285 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1286 <separator name=\"Separator7\"/> \
1287 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1289 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1290 <menuitem name=\"About\" action=\"About\" /> \
1296 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1299 GtkActionGroup *action_group;
1302 action_group = gtk_action_group_new("Menu");
1303 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1305 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1306 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1308 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1310 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1313 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1314 GtkWidget *vbox, GtkUIManager *ui_manager)
1316 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1319 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1321 struct gui_entry *ge = (struct gui_entry *) data;
1324 index = gtk_combo_box_get_active(box);
1326 multitext_set_entry(&ge->eta.iotype, index);
1327 multitext_set_entry(&ge->eta.bs, index);
1328 multitext_set_entry(&ge->eta.ioengine, index);
1329 multitext_set_entry(&ge->eta.iodepth, index);
1332 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1334 struct gui_entry *ge = (struct gui_entry *) data;
1336 multitext_free(&ge->eta.iotype);
1337 multitext_free(&ge->eta.bs);
1338 multitext_free(&ge->eta.ioengine);
1339 multitext_free(&ge->eta.iodepth);
1342 static GtkWidget *new_client_page(struct gui_entry *ge)
1344 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1345 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1347 main_vbox = gtk_vbox_new(FALSE, 3);
1349 top_align = gtk_alignment_new(0, 0, 1, 0);
1350 top_vbox = gtk_vbox_new(FALSE, 3);
1351 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1352 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1354 probe = gtk_frame_new("Job");
1355 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1356 probe_frame = gtk_vbox_new(FALSE, 3);
1357 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1359 probe_box = gtk_hbox_new(FALSE, 3);
1360 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1361 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1362 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1363 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1364 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1366 probe_box = gtk_hbox_new(FALSE, 3);
1367 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1369 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1370 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1371 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1372 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1373 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1374 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1375 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1376 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1377 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1379 probe_box = gtk_hbox_new(FALSE, 3);
1380 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1381 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1382 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1383 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1384 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1387 * Only add this if we have a commit rate
1390 probe_box = gtk_hbox_new(FALSE, 3);
1391 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1393 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1394 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1396 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1397 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1401 * Set up a drawing area and IOPS and bandwidth graphs
1403 ge->graphs.drawing_area = gtk_drawing_area_new();
1404 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1405 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1406 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1407 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1408 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1409 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1410 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1411 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1412 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1413 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1414 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1415 ge->graphs.drawing_area);
1416 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1418 setup_graphs(&ge->graphs);
1421 * Set up alignments for widgets at the bottom of ui,
1422 * align bottom left, expand horizontally but not vertically
1424 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1425 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1426 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1427 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1429 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
1432 * Set up thread status progress bar
1434 ge->thread_status_pb = gtk_progress_bar_new();
1435 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1436 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1437 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1443 static GtkWidget *new_main_page(struct gui *ui)
1445 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1446 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1448 main_vbox = gtk_vbox_new(FALSE, 3);
1451 * Set up alignments for widgets at the top of ui,
1452 * align top left, expand horizontally but not vertically
1454 top_align = gtk_alignment_new(0, 0, 1, 0);
1455 top_vbox = gtk_vbox_new(FALSE, 0);
1456 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1457 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1459 probe = gtk_frame_new("Run statistics");
1460 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1461 probe_frame = gtk_vbox_new(FALSE, 3);
1462 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1464 probe_box = gtk_hbox_new(FALSE, 3);
1465 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1466 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1467 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1468 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1469 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1470 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1473 * Only add this if we have a commit rate
1476 probe_box = gtk_hbox_new(FALSE, 3);
1477 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1479 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1480 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1482 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1483 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1487 * Set up a drawing area and IOPS and bandwidth graphs
1489 ui->graphs.drawing_area = gtk_drawing_area_new();
1490 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1491 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1492 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1493 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1494 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1495 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1496 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1497 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1498 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1499 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1500 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1501 ui->graphs.drawing_area);
1502 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1505 setup_graphs(&ui->graphs);
1508 * Set up alignments for widgets at the bottom of ui,
1509 * align bottom left, expand horizontally but not vertically
1511 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1512 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1513 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1514 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1517 * Set up thread status progress bar
1519 ui->thread_status_pb = gtk_progress_bar_new();
1520 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1521 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1522 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1527 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1528 guint page, gpointer data)
1531 struct gui *ui = (struct gui *) data;
1532 struct gui_entry *ge;
1535 set_job_menu_visible(ui, 0);
1536 set_view_results_visible(ui, 0);
1540 set_job_menu_visible(ui, 1);
1541 ge = get_ge_from_page(ui, page, NULL);
1543 update_button_states(ui, ge);
1548 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1550 time_t time_a = gtk_recent_info_get_visited(a);
1551 time_t time_b = gtk_recent_info_get_visited(b);
1553 return time_b - time_a;
1556 static void add_recent_file_items(struct gui *ui)
1558 const gchar *gfio = g_get_application_name();
1559 GList *items, *item;
1562 if (ui->recent_ui_id) {
1563 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1564 gtk_ui_manager_ensure_update(ui->uimanager);
1566 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1568 if (ui->actiongroup) {
1569 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1570 g_object_unref(ui->actiongroup);
1572 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1574 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1576 items = gtk_recent_manager_get_items(ui->recentmanager);
1577 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1579 for (item = items; item && item->data; item = g_list_next(item)) {
1580 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1585 if (!gtk_recent_info_has_application(info, gfio))
1589 * We only support local files for now
1591 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1594 action_name = g_strdup_printf("RecentFile%u", i++);
1595 label = gtk_recent_info_get_display_name(info);
1597 action = g_object_new(GTK_TYPE_ACTION,
1598 "name", action_name,
1599 "label", label, NULL);
1601 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1602 gtk_recent_info_ref(info),
1603 (GDestroyNotify) gtk_recent_info_unref);
1606 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1608 gtk_action_group_add_action(ui->actiongroup, action);
1609 g_object_unref(action);
1611 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1612 "/MainMenu/FileMenu/FileRecentFiles",
1614 GTK_UI_MANAGER_MENUITEM, FALSE);
1616 g_free(action_name);
1622 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1626 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1627 gint x, gint y, GtkSelectionData *seldata,
1628 guint info, guint time, gpointer *data)
1630 struct gui *ui = (struct gui *) data;
1634 source = gtk_drag_get_source_widget(ctx);
1635 if (source && widget == gtk_widget_get_toplevel(source)) {
1636 gtk_drag_finish(ctx, FALSE, FALSE, time);
1640 uris = gtk_selection_data_get_uris(seldata);
1642 gtk_drag_finish(ctx, FALSE, FALSE, time);
1647 do_file_open_with_tab(ui, uris[0]);
1649 gtk_drag_finish(ctx, TRUE, FALSE, time);
1653 static void init_ui(int *argc, char **argv[], struct gui *ui)
1655 GtkSettings *settings;
1658 /* Magical g*thread incantation, you just need this thread stuff.
1659 * Without it, the update that happens in gfio_update_thread_status
1660 * doesn't really happen in a timely fashion, you need expose events
1662 if (!g_thread_supported())
1663 g_thread_init(NULL);
1666 gtk_init(argc, argv);
1667 settings = gtk_settings_get_default();
1668 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1670 gdk_color_parse("white", &gfio_color_white);
1672 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1673 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1674 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1676 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1677 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1679 ui->vbox = gtk_vbox_new(FALSE, 0);
1680 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1682 ui->uimanager = gtk_ui_manager_new();
1683 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1684 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1686 ui->recentmanager = gtk_recent_manager_get_default();
1687 add_recent_file_items(ui);
1689 ui->notebook = gtk_notebook_new();
1690 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1691 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1692 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1693 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1695 vbox = new_main_page(ui);
1696 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1697 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1698 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1700 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1702 gfio_ui_setup_log(ui);
1704 gtk_widget_show_all(ui->window);
1707 int main(int argc, char *argv[], char *envp[])
1709 if (initialize_fio(envp))
1711 if (fio_init_options())
1716 memset(&main_ui, 0, sizeof(main_ui));
1717 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
1719 init_ui(&argc, &argv, &main_ui);
1721 gdk_threads_enter();
1723 gdk_threads_leave();
1725 g_hash_table_destroy(main_ui.ge_hash);