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 GdkColor gfio_color_lightyellow;
45 const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
47 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
49 static void connect_clicked(GtkWidget *widget, gpointer data);
50 static void start_job_clicked(GtkWidget *widget, gpointer data);
51 static void send_clicked(GtkWidget *widget, gpointer data);
53 static struct button_spec {
54 const char *buttontext;
56 const char *tooltiptext[2];
57 const int start_sensitive;
58 } buttonspeclist[] = {
60 .buttontext = "Connect",
62 .tooltiptext = { "Disconnect from host", "Connect to host" },
68 .tooltiptext = { "Send job description to host", NULL },
72 .buttontext = "Start Job",
73 .f = start_job_clicked,
74 .tooltiptext = { "Start the current job on the server", NULL },
79 static void setup_iops_graph(struct gfio_graphs *gg)
83 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
84 graph_title(g, "IOPS (IOs/sec)");
85 graph_x_title(g, "Time (secs)");
86 gg->read_iops = graph_add_label(g, "Read IOPS");
87 gg->write_iops = graph_add_label(g, "Write IOPS");
88 graph_set_color(g, gg->read_iops, 0.13, 0.54, 0.13);
89 graph_set_color(g, gg->write_iops, 1.0, 0.0, 0.0);
90 line_graph_set_data_count_limit(g, gfio_graph_limit);
91 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
92 graph_set_graph_all_zeroes(g, 0);
96 static void setup_bandwidth_graph(struct gfio_graphs *gg)
100 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
101 graph_title(g, "Bandwidth (bytes/sec)");
102 graph_x_title(g, "Time (secs)");
103 gg->read_bw = graph_add_label(g, "Read Bandwidth");
104 gg->write_bw = graph_add_label(g, "Write Bandwidth");
105 graph_set_color(g, gg->read_bw, 0.13, 0.54, 0.13);
106 graph_set_color(g, gg->write_bw, 1.0, 0.0, 0.0);
107 graph_set_base_offset(g, 1);
108 line_graph_set_data_count_limit(g, 100);
109 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
110 graph_set_graph_all_zeroes(g, 0);
111 gg->bandwidth_graph = g;
114 static void setup_graphs(struct gfio_graphs *g)
117 setup_bandwidth_graph(g);
120 void clear_ge_ui_info(struct gui_entry *ge)
122 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
123 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
124 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
125 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
127 /* should we empty it... */
128 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
130 multitext_update_entry(&ge->eta.iotype, 0, "");
131 multitext_update_entry(&ge->eta.bs, 0, "");
132 multitext_update_entry(&ge->eta.ioengine, 0, "");
133 multitext_update_entry(&ge->eta.iodepth, 0, "");
134 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
137 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
138 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
139 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
142 static void set_menu_entry_text(struct gui *ui, const char *path,
147 w = gtk_ui_manager_get_widget(ui->uimanager, path);
149 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
151 fprintf(stderr, "gfio: can't find path %s\n", path);
155 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
159 w = gtk_ui_manager_get_widget(ui->uimanager, path);
161 gtk_widget_set_sensitive(w, show);
163 fprintf(stderr, "gfio: can't find path %s\n", path);
166 static void set_job_menu_visible(struct gui *ui, int visible)
168 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
171 static void set_view_results_visible(struct gui *ui, int visible)
173 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
176 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
178 if (s->tooltiptext[sensitive])
179 return s->tooltiptext[sensitive];
181 return s->tooltiptext[0];
184 static GtkWidget *add_button(GtkWidget *buttonbox,
185 struct button_spec *buttonspec, gpointer data)
187 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
188 gboolean sens = buttonspec->start_sensitive;
190 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
191 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
193 sens = buttonspec->start_sensitive;
194 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
195 gtk_widget_set_sensitive(button, sens);
200 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
205 for (i = 0; i < nbuttons; i++)
206 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
210 * Update sensitivity of job buttons and job menu items, based on the
211 * state of the client.
213 static void update_button_states(struct gui *ui, struct gui_entry *ge)
215 unsigned int connect_state, send_state, start_state, edit_state;
216 const char *connect_str = NULL;
220 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
221 /* fall through to new state */
225 connect_str = "Connect";
229 case GE_STATE_CONNECTED:
232 connect_str = "Disconnect";
236 case GE_STATE_JOB_SENT:
239 connect_str = "Disconnect";
243 case GE_STATE_JOB_STARTED:
246 connect_str = "Disconnect";
250 case GE_STATE_JOB_RUNNING:
253 connect_str = "Disconnect";
257 case GE_STATE_JOB_DONE:
260 connect_str = "Connect";
266 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
267 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
268 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
269 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
270 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
272 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
273 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
275 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
276 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
277 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
279 if (ge->client && ge->client->nr_results)
280 set_view_results_visible(ui, 1);
282 set_view_results_visible(ui, 0);
285 void gfio_set_state(struct gui_entry *ge, unsigned int state)
288 update_button_states(ge->ui, ge);
291 static void gfio_ui_setup_log(struct gui *ui)
293 GtkTreeSelection *selection;
295 GtkWidget *tree_view;
297 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
299 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
300 gtk_widget_set_can_focus(tree_view, FALSE);
302 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
303 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
304 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
305 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
307 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
308 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
309 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
310 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
312 ui->log_model = model;
313 ui->log_tree = tree_view;
316 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
319 guint width = gtk_widget_get_allocated_width(w);
320 guint height = gtk_widget_get_allocated_height(w);
321 struct gfio_graphs *g = data;
323 graph_set_size(g->iops_graph, width / 2.0, height);
324 graph_set_position(g->iops_graph, width / 2.0, 0.0);
325 graph_set_size(g->bandwidth_graph, width / 2.0, height);
326 graph_set_position(g->bandwidth_graph, 0, 0);
330 static void draw_graph(struct graph *g, cairo_t *cr)
332 line_graph_draw(g, cr);
336 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
337 gboolean keyboard_mode, GtkTooltip *tooltip,
340 struct gfio_graphs *g = data;
341 const char *text = NULL;
343 if (graph_contains_xy(g->iops_graph, x, y))
344 text = graph_find_tooltip(g->iops_graph, x, y);
345 else if (graph_contains_xy(g->bandwidth_graph, x, y))
346 text = graph_find_tooltip(g->bandwidth_graph, x, y);
349 gtk_tooltip_set_text(tooltip, text);
356 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
358 struct gfio_graphs *g = p;
361 cr = gdk_cairo_create(gtk_widget_get_window(w));
363 if (graph_has_tooltips(g->iops_graph) ||
364 graph_has_tooltips(g->bandwidth_graph)) {
365 g_object_set(w, "has-tooltip", TRUE, NULL);
366 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
369 cairo_set_source_rgb(cr, 0, 0, 0);
370 draw_graph(g->iops_graph, cr);
371 draw_graph(g->bandwidth_graph, cr);
378 * FIXME: need more handling here
380 static void ge_destroy(struct gui_entry *ge)
382 struct gfio_client *gc = ge->client;
386 if (ge->state >= GE_STATE_CONNECTED)
387 fio_client_terminate(gc->client);
389 fio_put_client(gc->client);
394 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
401 static void ge_widget_destroy(GtkWidget *w, gpointer data)
403 struct gui_entry *ge = (struct gui_entry *) data;
408 static void gfio_quit(struct gui *ui)
413 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
416 struct gui *ui = (struct gui *) data;
421 static void *job_thread(void *arg)
423 struct gui *ui = arg;
425 ui->handler_running = 1;
426 fio_handle_clients(&gfio_client_ops);
427 ui->handler_running = 0;
431 static int send_job_file(struct gui_entry *ge)
433 struct gfio_client *gc = ge->client;
437 * Prune old options, we are expecting the return options
438 * when the job file is parsed remotely and returned to us.
440 while (!flist_empty(&gc->o_list)) {
441 struct gfio_client_options *gco;
443 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list);
444 flist_del(&gco->list);
448 ret = fio_client_send_ini(gc->client, ge->job_file);
452 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
456 static void *server_thread(void *arg)
459 gfio_server_running = 1;
460 fio_start_server(NULL);
461 gfio_server_running = 0;
465 static void gfio_start_server(struct gui *ui)
467 if (!gfio_server_running) {
468 gfio_server_running = 1;
469 pthread_create(&ui->server_t, NULL, server_thread, NULL);
470 pthread_detach(ui->server_t);
474 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
477 struct gui_entry *ge = data;
478 struct gfio_client *gc = ge->client;
481 fio_start_client(gc->client);
484 static void file_open(GtkWidget *w, gpointer data);
486 struct connection_widgets
493 static void hostname_cb(GtkEntry *entry, gpointer data)
495 struct connection_widgets *cw = data;
496 int uses_net = 0, is_localhost = 0;
501 * Check whether to display the 'auto start backend' box
502 * or not. Show it if we are a localhost and using network,
505 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
506 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
511 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
512 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
513 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
514 !strcmp(text, "ip6-loopback"))
518 if (!uses_net || is_localhost) {
519 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
520 gtk_widget_set_sensitive(cw->button, 1);
522 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
523 gtk_widget_set_sensitive(cw->button, 0);
527 static int get_connection_details(struct gui_entry *ge)
529 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
530 struct connection_widgets cw;
531 struct gui *ui = ge->ui;
537 dialog = gtk_dialog_new_with_buttons("Connection details",
538 GTK_WINDOW(ui->window),
539 GTK_DIALOG_DESTROY_WITH_PARENT,
540 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
541 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
543 frame = gtk_frame_new("Hostname / socket name");
544 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
545 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
547 box = gtk_vbox_new(FALSE, 6);
548 gtk_container_add(GTK_CONTAINER(frame), box);
550 hbox = gtk_hbox_new(TRUE, 10);
551 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
552 cw.hentry = gtk_entry_new();
553 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
554 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
556 frame = gtk_frame_new("Port");
557 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
558 box = gtk_vbox_new(FALSE, 10);
559 gtk_container_add(GTK_CONTAINER(frame), box);
561 hbox = gtk_hbox_new(TRUE, 4);
562 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
563 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
565 frame = gtk_frame_new("Type");
566 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
567 box = gtk_vbox_new(FALSE, 10);
568 gtk_container_add(GTK_CONTAINER(frame), box);
570 hbox = gtk_hbox_new(TRUE, 4);
571 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
573 cw.combo = gtk_combo_box_text_new();
574 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
575 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
576 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
577 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
579 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
581 frame = gtk_frame_new("Options");
582 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
583 box = gtk_vbox_new(FALSE, 10);
584 gtk_container_add(GTK_CONTAINER(frame), box);
586 hbox = gtk_hbox_new(TRUE, 4);
587 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
589 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
591 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.");
592 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
595 * Connect edit signal, so we can show/not-show the auto start button
597 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
598 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
600 gtk_widget_show_all(dialog);
602 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
603 gtk_widget_destroy(dialog);
607 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
608 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
610 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
611 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
612 ge->type = Fio_client_ipv4;
613 else if (!strncmp(typeentry, "IPv6", 4))
614 ge->type = Fio_client_ipv6;
616 ge->type = Fio_client_socket;
619 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
621 gtk_widget_destroy(dialog);
625 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
627 gc->client = fio_get_client(client);
628 client->client_data = gc;
631 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
633 struct gfio_client_options *gco;
634 struct gfio_client *gc;
636 gc = calloc(1, sizeof(*gc));
637 INIT_FLIST_HEAD(&gc->o_list);
640 gfio_set_client(gc, client);
643 * Just add a default set of options, need to consider how best
646 gco = calloc(1, sizeof(*gco));
647 INIT_FLIST_HEAD(&gco->list);
648 options_default_fill(&gco->o);
649 flist_add_tail(&gco->list, &gc->o_list);
653 static void connect_clicked(GtkWidget *widget, gpointer data)
655 struct gui_entry *ge = data;
656 struct gfio_client *gc = ge->client;
658 if (ge->state == GE_STATE_NEW) {
662 file_open(widget, ge->ui);
669 struct fio_client *client;
671 if (get_connection_details(ge)) {
672 gfio_report_error(ge, "Failed to get connection details\n");
676 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
678 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
683 gfio_set_client(gc, client);
686 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
687 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
688 ret = fio_client_connect(gc->client);
690 if (!ge->ui->handler_running)
691 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
692 gfio_set_state(ge, GE_STATE_CONNECTED);
694 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
697 fio_client_terminate(gc->client);
698 gfio_set_state(ge, GE_STATE_NEW);
699 clear_ge_ui_info(ge);
703 static void send_clicked(GtkWidget *widget, gpointer data)
705 struct gui_entry *ge = data;
707 if (send_job_file(ge))
708 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
711 static GtkWidget *new_client_page(struct gui_entry *ge);
713 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
715 struct gui_entry *ge;
717 ge = malloc(sizeof(*ge));
718 memset(ge, 0, sizeof(*ge));
719 ge->state = GE_STATE_NEW;
724 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
726 struct gui_entry *ge;
728 ge = alloc_new_gui_entry(ui);
730 ge->vbox = new_client_page(ge);
731 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
733 ge->page_label = gtk_label_new(name);
734 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
736 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
738 gtk_widget_show_all(ui->window);
742 static void file_new(GtkWidget *w, gpointer data)
744 struct gui *ui = (struct gui *) data;
745 struct gui_entry *ge;
747 ge = get_new_ge_with_tab(ui, "Untitled");
748 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
752 * Return the 'ge' corresponding to the tab. If the active tab is the
753 * main tab, open a new tab.
755 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
761 return get_new_ge_with_tab(ui, "Untitled");
767 return g_hash_table_lookup(ui->ge_hash, &cur_page);
770 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
775 * Main tab is tab 0, so any current page other than 0 holds
778 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
780 return get_ge_from_page(ui, cur_page, NULL);
785 static void file_close(GtkWidget *w, gpointer data)
787 struct gui *ui = (struct gui *) data;
788 struct gui_entry *ge;
791 * Can't close the main tab
793 ge = get_ge_from_cur_tab(ui);
795 gtk_widget_destroy(ge->vbox);
799 if (g_hash_table_size(ui->ge_hash)) {
800 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
807 static void file_add_recent(struct gui *ui, const gchar *uri)
811 memset(&grd, 0, sizeof(grd));
812 grd.display_name = strdup("gfio");
813 grd.description = strdup("Fio job file");
814 grd.mime_type = strdup(GFIO_MIME);
815 grd.app_name = strdup(g_get_application_name());
816 grd.app_exec = strdup("gfio %f/%u");
818 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
821 static gchar *get_filename_from_uri(const gchar *uri)
823 if (strncmp(uri, "file://", 7))
826 return strdup(uri + 7);
829 static int do_file_open(struct gui_entry *ge, const gchar *uri)
831 struct fio_client *client;
833 assert(!ge->job_file);
835 ge->job_file = get_filename_from_uri(uri);
837 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
839 char *label = strdup(uri);
842 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
845 gfio_client_added(ge, client);
846 file_add_recent(ge->ui, uri);
850 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
858 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
860 struct gui_entry *ge;
862 int ret, ge_is_new = 0;
865 * Creates new tab if current tab is the main window, or the
866 * current tab already has a client.
868 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
869 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
871 ge = get_new_ge_with_tab(ui, "Untitled");
875 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
877 if (get_connection_details(ge)) {
879 gtk_widget_destroy(ge->vbox);
884 ret = do_file_open(ge, uri);
887 if (ge->server_start)
888 gfio_start_server(ui);
891 gtk_widget_destroy(ge->vbox);
897 static void recent_open(GtkAction *action, gpointer data)
899 struct gui *ui = (struct gui *) data;
903 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
904 uri = gtk_recent_info_get_uri(info);
906 do_file_open_with_tab(ui, uri);
909 static void file_open(GtkWidget *w, gpointer data)
911 struct gui *ui = data;
913 GtkFileFilter *filter;
916 dialog = gtk_file_chooser_dialog_new("Open File",
917 GTK_WINDOW(ui->window),
918 GTK_FILE_CHOOSER_ACTION_OPEN,
919 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
920 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
922 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
924 filter = gtk_file_filter_new();
925 gtk_file_filter_add_pattern(filter, "*.fio");
926 gtk_file_filter_add_pattern(filter, "*.job");
927 gtk_file_filter_add_pattern(filter, "*.ini");
928 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
929 gtk_file_filter_set_name(filter, "Fio job file");
930 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
932 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
933 gtk_widget_destroy(dialog);
937 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
939 gtk_widget_destroy(dialog);
941 do_file_open_with_tab(ui, filename);
945 static void file_save(GtkWidget *w, gpointer data)
947 struct gui *ui = data;
950 dialog = gtk_file_chooser_dialog_new("Save File",
951 GTK_WINDOW(ui->window),
952 GTK_FILE_CHOOSER_ACTION_SAVE,
953 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
954 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
957 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
958 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
960 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
963 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
964 // save_job_file(filename);
967 gtk_widget_destroy(dialog);
970 static void view_log_destroy(GtkWidget *w, gpointer data)
972 struct gui *ui = (struct gui *) data;
974 g_object_ref(G_OBJECT(ui->log_tree));
975 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
976 gtk_widget_destroy(w);
980 void gfio_view_log(struct gui *ui)
982 GtkWidget *win, *scroll, *vbox, *box;
987 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
988 gtk_window_set_title(GTK_WINDOW(win), "Log");
989 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
991 scroll = gtk_scrolled_window_new(NULL, NULL);
993 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
995 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
997 box = gtk_hbox_new(TRUE, 0);
998 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
999 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1000 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1002 vbox = gtk_vbox_new(TRUE, 5);
1003 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
1005 gtk_container_add(GTK_CONTAINER(win), vbox);
1006 gtk_widget_show_all(win);
1009 static void view_log(GtkWidget *w, gpointer data)
1011 struct gui *ui = (struct gui *) data;
1016 static void connect_job_entry(GtkWidget *w, gpointer data)
1018 struct gui *ui = (struct gui *) data;
1019 struct gui_entry *ge;
1021 ge = get_ge_from_cur_tab(ui);
1023 connect_clicked(w, ge);
1026 static void send_job_entry(GtkWidget *w, gpointer data)
1028 struct gui *ui = (struct gui *) data;
1029 struct gui_entry *ge;
1031 ge = get_ge_from_cur_tab(ui);
1033 send_clicked(w, ge);
1036 static void edit_job_entry(GtkWidget *w, gpointer data)
1038 struct gui *ui = (struct gui *) data;
1039 struct gui_entry *ge;
1041 ge = get_ge_from_cur_tab(ui);
1042 if (ge && ge->client)
1043 gopt_get_options_window(ui->window, ge->client);
1046 static void start_job_entry(GtkWidget *w, gpointer data)
1048 struct gui *ui = (struct gui *) data;
1049 struct gui_entry *ge;
1051 ge = get_ge_from_cur_tab(ui);
1053 start_job_clicked(w, ge);
1056 static void view_results(GtkWidget *w, gpointer data)
1058 struct gui *ui = (struct gui *) data;
1059 struct gfio_client *gc;
1060 struct gui_entry *ge;
1062 ge = get_ge_from_cur_tab(ui);
1066 if (ge->results_window)
1070 if (gc && gc->nr_results)
1071 gfio_display_end_results(gc);
1074 static void __update_graph_settings(struct gfio_graphs *g)
1076 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1077 graph_set_font(g->iops_graph, gfio_graph_font);
1078 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1079 graph_set_font(g->bandwidth_graph, gfio_graph_font);
1082 static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
1084 struct gui_entry *ge = (struct gui_entry *) value;
1087 __update_graph_settings(&ge->graphs);
1089 ev = gdk_event_new(GDK_EXPOSE);
1090 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1094 static void update_graph_limits(void)
1096 struct gui *ui = &main_ui;
1099 __update_graph_settings(&ui->graphs);
1101 ev = gdk_event_new(GDK_EXPOSE);
1102 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1105 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
1108 static void preferences(GtkWidget *w, gpointer data)
1110 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1111 GtkWidget *hbox, *spin, *entry, *spin_int;
1112 struct gui *ui = (struct gui *) data;
1115 dialog = gtk_dialog_new_with_buttons("Preferences",
1116 GTK_WINDOW(ui->window),
1117 GTK_DIALOG_DESTROY_WITH_PARENT,
1118 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1119 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1122 frame = gtk_frame_new("Graphing");
1123 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1124 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1125 vbox = gtk_vbox_new(FALSE, 6);
1126 gtk_container_add(GTK_CONTAINER(frame), vbox);
1128 hbox = gtk_hbox_new(FALSE, 5);
1129 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1130 entry = gtk_label_new("Font face to use for graph labels");
1131 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1133 font = gtk_font_button_new_with_font(gfio_graph_font);
1134 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1136 box = gtk_vbox_new(FALSE, 6);
1137 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1139 hbox = gtk_hbox_new(FALSE, 5);
1140 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1141 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1142 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1144 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1146 box = gtk_vbox_new(FALSE, 6);
1147 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1149 hbox = gtk_hbox_new(FALSE, 5);
1150 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1151 entry = gtk_label_new("Client ETA request interval (msec)");
1152 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1154 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
1155 frame = gtk_frame_new("Debug logging");
1156 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1157 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1158 vbox = gtk_vbox_new(FALSE, 6);
1159 gtk_container_add(GTK_CONTAINER(frame), vbox);
1161 box = gtk_hbox_new(FALSE, 6);
1162 gtk_container_add(GTK_CONTAINER(vbox), box);
1164 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1166 for (i = 0; i < FD_DEBUG_MAX; i++) {
1168 box = gtk_hbox_new(FALSE, 6);
1169 gtk_container_add(GTK_CONTAINER(vbox), box);
1173 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1174 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1175 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1178 gtk_widget_show_all(dialog);
1180 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1181 gtk_widget_destroy(dialog);
1185 for (i = 0; i < FD_DEBUG_MAX; i++) {
1188 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1190 fio_debug |= (1UL << i);
1193 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1194 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1195 update_graph_limits();
1196 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1198 gtk_widget_destroy(dialog);
1201 static void about_dialog(GtkWidget *w, gpointer data)
1203 const char *authors[] = {
1204 "Jens Axboe <axboe@kernel.dk>",
1205 "Stephen Carmeron <stephenmcameron@gmail.com>",
1208 const char *license[] = {
1209 "Fio is free software; you can redistribute it and/or modify "
1210 "it under the terms of the GNU General Public License as published by "
1211 "the Free Software Foundation; either version 2 of the License, or "
1212 "(at your option) any later version.\n",
1213 "Fio is distributed in the hope that it will be useful, "
1214 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1215 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1216 "GNU General Public License for more details.\n",
1217 "You should have received a copy of the GNU General Public License "
1218 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1219 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1221 char *license_trans;
1223 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1224 license[2], "\n", NULL);
1226 gtk_show_about_dialog(NULL,
1227 "program-name", "gfio",
1228 "comments", "Gtk2 UI for fio",
1229 "license", license_trans,
1230 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1232 "version", fio_version_string,
1233 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1234 "logo-icon-name", "fio",
1236 "wrap-license", TRUE,
1239 g_free(license_trans);
1242 static GtkActionEntry menu_items[] = {
1243 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1244 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1245 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1246 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1247 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1248 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1249 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1250 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1251 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1252 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1253 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1254 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1255 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1256 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1257 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1258 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1259 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1261 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1263 static const gchar *ui_string = " \
1265 <menubar name=\"MainMenu\"> \
1266 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1267 <menuitem name=\"New\" action=\"NewFile\" /> \
1268 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1269 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1270 <separator name=\"Separator1\"/> \
1271 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1272 <separator name=\"Separator2\"/> \
1273 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1274 <separator name=\"Separator3\"/> \
1275 <placeholder name=\"FileRecentFiles\"/> \
1276 <separator name=\"Separator4\"/> \
1277 <menuitem name=\"Quit\" action=\"Quit\" /> \
1279 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1280 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1281 <separator name=\"Separator5\"/> \
1282 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1283 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1284 <separator name=\"Separator6\"/> \
1285 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1287 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1288 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1289 <separator name=\"Separator7\"/> \
1290 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1292 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1293 <menuitem name=\"About\" action=\"About\" /> \
1299 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1302 GtkActionGroup *action_group;
1305 action_group = gtk_action_group_new("Menu");
1306 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1308 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1309 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1311 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1313 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1316 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1317 GtkWidget *vbox, GtkUIManager *ui_manager)
1319 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1322 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1324 struct gui_entry *ge = (struct gui_entry *) data;
1327 index = gtk_combo_box_get_active(box);
1329 multitext_set_entry(&ge->eta.iotype, index);
1330 multitext_set_entry(&ge->eta.bs, index);
1331 multitext_set_entry(&ge->eta.ioengine, index);
1332 multitext_set_entry(&ge->eta.iodepth, index);
1335 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1337 struct gui_entry *ge = (struct gui_entry *) data;
1339 multitext_free(&ge->eta.iotype);
1340 multitext_free(&ge->eta.bs);
1341 multitext_free(&ge->eta.ioengine);
1342 multitext_free(&ge->eta.iodepth);
1345 static GtkWidget *new_client_page(struct gui_entry *ge)
1347 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1348 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1350 main_vbox = gtk_vbox_new(FALSE, 3);
1352 top_align = gtk_alignment_new(0, 0, 1, 0);
1353 top_vbox = gtk_vbox_new(FALSE, 3);
1354 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1355 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1357 probe = gtk_frame_new("Job");
1358 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1359 probe_frame = gtk_vbox_new(FALSE, 3);
1360 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1362 probe_box = gtk_hbox_new(FALSE, 3);
1363 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1364 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1365 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1366 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1367 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1369 probe_box = gtk_hbox_new(FALSE, 3);
1370 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1372 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1373 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1374 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1375 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1376 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1377 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1378 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1379 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1380 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1382 probe_box = gtk_hbox_new(FALSE, 3);
1383 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1384 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1385 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1386 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1387 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1390 * Only add this if we have a commit rate
1393 probe_box = gtk_hbox_new(FALSE, 3);
1394 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1396 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1397 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1399 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1400 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1404 * Set up a drawing area and IOPS and bandwidth graphs
1406 ge->graphs.drawing_area = gtk_drawing_area_new();
1407 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1408 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1409 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
1410 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
1411 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1412 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1413 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1414 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1415 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1416 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1417 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1418 ge->graphs.drawing_area);
1419 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1421 setup_graphs(&ge->graphs);
1424 * Set up alignments for widgets at the bottom of ui,
1425 * align bottom left, expand horizontally but not vertically
1427 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1428 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1429 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1430 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1432 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
1435 * Set up thread status progress bar
1437 ge->thread_status_pb = gtk_progress_bar_new();
1438 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1439 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1440 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1446 static GtkWidget *new_main_page(struct gui *ui)
1448 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1449 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1451 main_vbox = gtk_vbox_new(FALSE, 3);
1454 * Set up alignments for widgets at the top of ui,
1455 * align top left, expand horizontally but not vertically
1457 top_align = gtk_alignment_new(0, 0, 1, 0);
1458 top_vbox = gtk_vbox_new(FALSE, 0);
1459 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1460 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1462 probe = gtk_frame_new("Run statistics");
1463 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1464 probe_frame = gtk_vbox_new(FALSE, 3);
1465 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1467 probe_box = gtk_hbox_new(FALSE, 3);
1468 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1469 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1470 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1471 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1472 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1473 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1476 * Only add this if we have a commit rate
1479 probe_box = gtk_hbox_new(FALSE, 3);
1480 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1482 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1483 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1485 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1486 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1490 * Set up a drawing area and IOPS and bandwidth graphs
1492 ui->graphs.drawing_area = gtk_drawing_area_new();
1493 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1494 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1495 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
1496 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
1497 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1498 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1499 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1500 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1501 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1502 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1503 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1504 ui->graphs.drawing_area);
1505 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1508 setup_graphs(&ui->graphs);
1511 * Set up alignments for widgets at the bottom of ui,
1512 * align bottom left, expand horizontally but not vertically
1514 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1515 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1516 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1517 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1520 * Set up thread status progress bar
1522 ui->thread_status_pb = gtk_progress_bar_new();
1523 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1524 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1525 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1530 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1531 guint page, gpointer data)
1534 struct gui *ui = (struct gui *) data;
1535 struct gui_entry *ge;
1538 set_job_menu_visible(ui, 0);
1539 set_view_results_visible(ui, 0);
1543 set_job_menu_visible(ui, 1);
1544 ge = get_ge_from_page(ui, page, NULL);
1546 update_button_states(ui, ge);
1551 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1553 time_t time_a = gtk_recent_info_get_visited(a);
1554 time_t time_b = gtk_recent_info_get_visited(b);
1556 return time_b - time_a;
1559 static void add_recent_file_items(struct gui *ui)
1561 const gchar *gfio = g_get_application_name();
1562 GList *items, *item;
1565 if (ui->recent_ui_id) {
1566 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1567 gtk_ui_manager_ensure_update(ui->uimanager);
1569 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1571 if (ui->actiongroup) {
1572 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1573 g_object_unref(ui->actiongroup);
1575 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1577 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1579 items = gtk_recent_manager_get_items(ui->recentmanager);
1580 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1582 for (item = items; item && item->data; item = g_list_next(item)) {
1583 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1588 if (!gtk_recent_info_has_application(info, gfio))
1592 * We only support local files for now
1594 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1597 action_name = g_strdup_printf("RecentFile%u", i++);
1598 label = gtk_recent_info_get_display_name(info);
1600 action = g_object_new(GTK_TYPE_ACTION,
1601 "name", action_name,
1602 "label", label, NULL);
1604 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1605 gtk_recent_info_ref(info),
1606 (GDestroyNotify) gtk_recent_info_unref);
1609 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1611 gtk_action_group_add_action(ui->actiongroup, action);
1612 g_object_unref(action);
1614 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1615 "/MainMenu/FileMenu/FileRecentFiles",
1617 GTK_UI_MANAGER_MENUITEM, FALSE);
1619 g_free(action_name);
1625 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1629 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1630 gint x, gint y, GtkSelectionData *seldata,
1631 guint info, guint time, gpointer *data)
1633 struct gui *ui = (struct gui *) data;
1637 source = gtk_drag_get_source_widget(ctx);
1638 if (source && widget == gtk_widget_get_toplevel(source)) {
1639 gtk_drag_finish(ctx, FALSE, FALSE, time);
1643 uris = gtk_selection_data_get_uris(seldata);
1645 gtk_drag_finish(ctx, FALSE, FALSE, time);
1650 do_file_open_with_tab(ui, uris[0]);
1652 gtk_drag_finish(ctx, TRUE, FALSE, time);
1656 static void init_ui(int *argc, char **argv[], struct gui *ui)
1658 GtkSettings *settings;
1661 /* Magical g*thread incantation, you just need this thread stuff.
1662 * Without it, the update that happens in gfio_update_thread_status
1663 * doesn't really happen in a timely fashion, you need expose events
1665 if (!g_thread_supported())
1666 g_thread_init(NULL);
1669 gtk_init(argc, argv);
1670 settings = gtk_settings_get_default();
1671 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1673 gdk_color_parse("#ffffee", &gfio_color_lightyellow);
1674 gdk_color_parse("white", &gfio_color_white);
1676 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1677 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1678 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1680 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1681 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1683 ui->vbox = gtk_vbox_new(FALSE, 0);
1684 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1686 ui->uimanager = gtk_ui_manager_new();
1687 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1688 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1690 ui->recentmanager = gtk_recent_manager_get_default();
1691 add_recent_file_items(ui);
1693 ui->notebook = gtk_notebook_new();
1694 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1695 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1696 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1697 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1699 vbox = new_main_page(ui);
1700 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1701 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1702 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1704 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1706 gfio_ui_setup_log(ui);
1708 gtk_widget_show_all(ui->window);
1711 int main(int argc, char *argv[], char *envp[])
1713 if (initialize_fio(envp))
1715 if (fio_init_options())
1720 memset(&main_ui, 0, sizeof(main_ui));
1721 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
1723 init_ui(&argc, &argv, &main_ui);
1725 gdk_threads_enter();
1727 gdk_threads_leave();
1729 g_hash_table_destroy(main_ui.ge_hash);