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;
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 struct graph *setup_iops_graph(void)
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 graph_add_label(g, "Read IOPS");
86 graph_add_label(g, "Write IOPS");
87 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
88 graph_set_color(g, "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);
94 static struct graph *setup_bandwidth_graph(void)
98 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
99 graph_title(g, "Bandwidth (bytes/sec)");
100 graph_x_title(g, "Time (secs)");
101 graph_add_label(g, "Read Bandwidth");
102 graph_add_label(g, "Write Bandwidth");
103 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
104 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
105 graph_set_base_offset(g, 1);
106 line_graph_set_data_count_limit(g, 100);
107 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
111 static void setup_graphs(struct gfio_graphs *g)
113 g->iops_graph = setup_iops_graph();
114 g->bandwidth_graph = setup_bandwidth_graph();
117 void clear_ge_ui_info(struct gui_entry *ge)
119 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
120 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
121 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
122 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
124 /* should we empty it... */
125 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
127 multitext_update_entry(&ge->eta.iotype, 0, "");
128 multitext_update_entry(&ge->eta.bs, 0, "");
129 multitext_update_entry(&ge->eta.ioengine, 0, "");
130 multitext_update_entry(&ge->eta.iodepth, 0, "");
131 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
132 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
133 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
134 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
139 static void set_menu_entry_text(struct gui *ui, const char *path,
144 w = gtk_ui_manager_get_widget(ui->uimanager, path);
146 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
148 fprintf(stderr, "gfio: can't find path %s\n", path);
152 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
156 w = gtk_ui_manager_get_widget(ui->uimanager, path);
158 gtk_widget_set_sensitive(w, show);
160 fprintf(stderr, "gfio: can't find path %s\n", path);
163 static void set_job_menu_visible(struct gui *ui, int visible)
165 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
168 static void set_view_results_visible(struct gui *ui, int visible)
170 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
173 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
175 if (s->tooltiptext[sensitive])
176 return s->tooltiptext[sensitive];
178 return s->tooltiptext[0];
181 static GtkWidget *add_button(GtkWidget *buttonbox,
182 struct button_spec *buttonspec, gpointer data)
184 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
185 gboolean sens = buttonspec->start_sensitive;
187 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
188 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
190 sens = buttonspec->start_sensitive;
191 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
192 gtk_widget_set_sensitive(button, sens);
197 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
202 for (i = 0; i < nbuttons; i++)
203 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
207 * Update sensitivity of job buttons and job menu items, based on the
208 * state of the client.
210 static void update_button_states(struct gui *ui, struct gui_entry *ge)
212 unsigned int connect_state, send_state, start_state, edit_state;
213 const char *connect_str = NULL;
217 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
218 /* fall through to new state */
222 connect_str = "Connect";
226 case GE_STATE_CONNECTED:
229 connect_str = "Disconnect";
233 case GE_STATE_JOB_SENT:
236 connect_str = "Disconnect";
240 case GE_STATE_JOB_STARTED:
243 connect_str = "Disconnect";
247 case GE_STATE_JOB_RUNNING:
250 connect_str = "Disconnect";
254 case GE_STATE_JOB_DONE:
257 connect_str = "Connect";
263 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
264 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
265 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
266 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
267 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
269 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
270 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
272 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
273 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
274 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
276 if (ge->client && ge->client->nr_results)
277 set_view_results_visible(ui, 1);
279 set_view_results_visible(ui, 0);
282 void gfio_set_state(struct gui_entry *ge, unsigned int state)
285 update_button_states(ge->ui, ge);
288 static void gfio_ui_setup_log(struct gui *ui)
290 GtkTreeSelection *selection;
292 GtkWidget *tree_view;
294 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
296 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
297 gtk_widget_set_can_focus(tree_view, FALSE);
299 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
300 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
301 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
302 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
304 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
305 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
306 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
307 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
309 ui->log_model = model;
310 ui->log_tree = tree_view;
313 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
316 struct gfio_graphs *g = data;
318 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
319 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
320 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
321 graph_set_position(g->bandwidth_graph, 0, 0);
325 static void draw_graph(struct graph *g, cairo_t *cr)
327 line_graph_draw(g, cr);
331 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
332 gboolean keyboard_mode, GtkTooltip *tooltip,
335 struct gfio_graphs *g = data;
336 const char *text = NULL;
338 if (graph_contains_xy(g->iops_graph, x, y))
339 text = graph_find_tooltip(g->iops_graph, x, y);
340 else if (graph_contains_xy(g->bandwidth_graph, x, y))
341 text = graph_find_tooltip(g->bandwidth_graph, x, y);
344 gtk_tooltip_set_text(tooltip, text);
351 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
353 struct gfio_graphs *g = p;
356 cr = gdk_cairo_create(w->window);
358 if (graph_has_tooltips(g->iops_graph) ||
359 graph_has_tooltips(g->bandwidth_graph)) {
360 g_object_set(w, "has-tooltip", TRUE, NULL);
361 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
364 cairo_set_source_rgb(cr, 0, 0, 0);
365 draw_graph(g->iops_graph, cr);
366 draw_graph(g->bandwidth_graph, cr);
373 * FIXME: need more handling here
375 static void ge_destroy(struct gui_entry *ge)
377 struct gfio_client *gc = ge->client;
381 if (ge->state >= GE_STATE_CONNECTED)
382 fio_client_terminate(gc->client);
384 fio_put_client(gc->client);
389 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
396 static void ge_widget_destroy(GtkWidget *w, gpointer data)
398 struct gui_entry *ge = (struct gui_entry *) data;
403 static void gfio_quit(struct gui *ui)
408 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
409 __attribute__((unused)) gpointer data)
411 struct gui *ui = (struct gui *) data;
416 static void *job_thread(void *arg)
418 struct gui *ui = arg;
420 ui->handler_running = 1;
421 fio_handle_clients(&gfio_client_ops);
422 ui->handler_running = 0;
426 static int send_job_file(struct gui_entry *ge)
428 struct gfio_client *gc = ge->client;
431 ret = fio_client_send_ini(gc->client, ge->job_file);
435 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
439 static void *server_thread(void *arg)
442 gfio_server_running = 1;
443 fio_start_server(NULL);
444 gfio_server_running = 0;
448 static void gfio_start_server(struct gui *ui)
450 if (!gfio_server_running) {
451 gfio_server_running = 1;
452 pthread_create(&ui->server_t, NULL, server_thread, NULL);
453 pthread_detach(ui->server_t);
457 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
460 struct gui_entry *ge = data;
461 struct gfio_client *gc = ge->client;
464 fio_start_client(gc->client);
467 static void file_open(GtkWidget *w, gpointer data);
469 struct connection_widgets
476 static void hostname_cb(GtkEntry *entry, gpointer data)
478 struct connection_widgets *cw = data;
479 int uses_net = 0, is_localhost = 0;
484 * Check whether to display the 'auto start backend' box
485 * or not. Show it if we are a localhost and using network,
488 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
489 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
494 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
495 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
496 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
497 !strcmp(text, "ip6-loopback"))
501 if (!uses_net || is_localhost) {
502 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
503 gtk_widget_set_sensitive(cw->button, 1);
505 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
506 gtk_widget_set_sensitive(cw->button, 0);
510 static int get_connection_details(struct gui_entry *ge)
512 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
513 struct connection_widgets cw;
514 struct gui *ui = ge->ui;
520 dialog = gtk_dialog_new_with_buttons("Connection details",
521 GTK_WINDOW(ui->window),
522 GTK_DIALOG_DESTROY_WITH_PARENT,
523 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
524 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
526 frame = gtk_frame_new("Hostname / socket name");
527 /* gtk_dialog_get_content_area() is 2.14 and newer */
528 vbox = GTK_DIALOG(dialog)->vbox;
529 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
531 box = gtk_vbox_new(FALSE, 6);
532 gtk_container_add(GTK_CONTAINER(frame), box);
534 hbox = gtk_hbox_new(TRUE, 10);
535 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
536 cw.hentry = gtk_entry_new();
537 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
538 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
540 frame = gtk_frame_new("Port");
541 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
542 box = gtk_vbox_new(FALSE, 10);
543 gtk_container_add(GTK_CONTAINER(frame), box);
545 hbox = gtk_hbox_new(TRUE, 4);
546 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
547 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
549 frame = gtk_frame_new("Type");
550 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
551 box = gtk_vbox_new(FALSE, 10);
552 gtk_container_add(GTK_CONTAINER(frame), box);
554 hbox = gtk_hbox_new(TRUE, 4);
555 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
557 cw.combo = gtk_combo_box_new_text();
558 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
559 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
560 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
561 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
563 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
565 frame = gtk_frame_new("Options");
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.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
574 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
575 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.");
576 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
579 * Connect edit signal, so we can show/not-show the auto start button
581 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
582 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
584 gtk_widget_show_all(dialog);
586 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
587 gtk_widget_destroy(dialog);
591 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
592 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
594 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
595 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
596 ge->type = Fio_client_ipv4;
597 else if (!strncmp(typeentry, "IPv6", 4))
598 ge->type = Fio_client_ipv6;
600 ge->type = Fio_client_socket;
603 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
605 gtk_widget_destroy(dialog);
609 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
611 gc->client = fio_get_client(client);
612 client->client_data = gc;
615 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
617 struct gfio_client *gc;
619 gc = malloc(sizeof(*gc));
620 memset(gc, 0, sizeof(*gc));
621 options_default_fill(&gc->o);
624 gfio_set_client(gc, client);
627 static void connect_clicked(GtkWidget *widget, gpointer data)
629 struct gui_entry *ge = data;
630 struct gfio_client *gc = ge->client;
632 if (ge->state == GE_STATE_NEW) {
636 file_open(widget, ge->ui);
643 struct fio_client *client;
645 if (get_connection_details(ge)) {
646 gfio_report_error(ge, "Failed to get connection details\n");
650 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
652 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
657 gfio_set_client(gc, client);
660 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
661 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
662 ret = fio_client_connect(gc->client);
664 if (!ge->ui->handler_running)
665 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
666 gfio_set_state(ge, GE_STATE_CONNECTED);
668 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
671 fio_client_terminate(gc->client);
672 gfio_set_state(ge, GE_STATE_NEW);
673 clear_ge_ui_info(ge);
677 static void send_clicked(GtkWidget *widget, gpointer data)
679 struct gui_entry *ge = data;
681 if (send_job_file(ge))
682 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
685 static GtkWidget *new_client_page(struct gui_entry *ge);
687 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
689 struct gui_entry *ge;
691 ge = malloc(sizeof(*ge));
692 memset(ge, 0, sizeof(*ge));
693 ge->state = GE_STATE_NEW;
698 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
700 struct gui_entry *ge;
702 ge = alloc_new_gui_entry(ui);
704 ge->vbox = new_client_page(ge);
705 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
707 ge->page_label = gtk_label_new(name);
708 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
710 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
712 gtk_widget_show_all(ui->window);
716 static void file_new(GtkWidget *w, gpointer data)
718 struct gui *ui = (struct gui *) data;
719 struct gui_entry *ge;
721 ge = get_new_ge_with_tab(ui, "Untitled");
722 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
726 * Return the 'ge' corresponding to the tab. If the active tab is the
727 * main tab, open a new tab.
729 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
735 return get_new_ge_with_tab(ui, "Untitled");
741 return g_hash_table_lookup(ui->ge_hash, &cur_page);
744 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
749 * Main tab is tab 0, so any current page other than 0 holds
752 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
754 return get_ge_from_page(ui, cur_page, NULL);
759 static void file_close(GtkWidget *w, gpointer data)
761 struct gui *ui = (struct gui *) data;
762 struct gui_entry *ge;
765 * Can't close the main tab
767 ge = get_ge_from_cur_tab(ui);
769 gtk_widget_destroy(ge->vbox);
773 if (g_hash_table_size(ui->ge_hash)) {
774 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
781 static void file_add_recent(struct gui *ui, const gchar *uri)
785 memset(&grd, 0, sizeof(grd));
786 grd.display_name = strdup("gfio");
787 grd.description = strdup("Fio job file");
788 grd.mime_type = strdup(GFIO_MIME);
789 grd.app_name = strdup(g_get_application_name());
790 grd.app_exec = strdup("gfio %f/%u");
792 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
795 static gchar *get_filename_from_uri(const gchar *uri)
797 if (strncmp(uri, "file://", 7))
800 return strdup(uri + 7);
803 static int do_file_open(struct gui_entry *ge, const gchar *uri)
805 struct fio_client *client;
807 assert(!ge->job_file);
809 ge->job_file = get_filename_from_uri(uri);
811 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
813 char *label = strdup(uri);
816 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
819 gfio_client_added(ge, client);
820 file_add_recent(ge->ui, uri);
824 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
832 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
834 struct gui_entry *ge;
836 int ret, ge_is_new = 0;
839 * Creates new tab if current tab is the main window, or the
840 * current tab already has a client.
842 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
843 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
845 ge = get_new_ge_with_tab(ui, "Untitled");
849 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
851 if (get_connection_details(ge)) {
853 gtk_widget_destroy(ge->vbox);
858 ret = do_file_open(ge, uri);
861 if (ge->server_start)
862 gfio_start_server(ui);
865 gtk_widget_destroy(ge->vbox);
871 static void recent_open(GtkAction *action, gpointer data)
873 struct gui *ui = (struct gui *) data;
877 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
878 uri = gtk_recent_info_get_uri(info);
880 do_file_open_with_tab(ui, uri);
883 static void file_open(GtkWidget *w, gpointer data)
885 struct gui *ui = data;
887 GtkFileFilter *filter;
890 dialog = gtk_file_chooser_dialog_new("Open File",
891 GTK_WINDOW(ui->window),
892 GTK_FILE_CHOOSER_ACTION_OPEN,
893 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
894 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
896 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
898 filter = gtk_file_filter_new();
899 gtk_file_filter_add_pattern(filter, "*.fio");
900 gtk_file_filter_add_pattern(filter, "*.job");
901 gtk_file_filter_add_pattern(filter, "*.ini");
902 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
903 gtk_file_filter_set_name(filter, "Fio job file");
904 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
906 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
907 gtk_widget_destroy(dialog);
911 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
913 gtk_widget_destroy(dialog);
915 do_file_open_with_tab(ui, filename);
919 static void file_save(GtkWidget *w, gpointer data)
921 struct gui *ui = data;
924 dialog = gtk_file_chooser_dialog_new("Save File",
925 GTK_WINDOW(ui->window),
926 GTK_FILE_CHOOSER_ACTION_SAVE,
927 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
928 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
931 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
932 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
934 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
937 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
938 // save_job_file(filename);
941 gtk_widget_destroy(dialog);
944 static void view_log_destroy(GtkWidget *w, gpointer data)
946 struct gui *ui = (struct gui *) data;
948 gtk_widget_ref(ui->log_tree);
949 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
950 gtk_widget_destroy(w);
954 void gfio_view_log(struct gui *ui)
956 GtkWidget *win, *scroll, *vbox, *box;
961 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
962 gtk_window_set_title(GTK_WINDOW(win), "Log");
963 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
965 scroll = gtk_scrolled_window_new(NULL, NULL);
967 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
969 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
971 box = gtk_hbox_new(TRUE, 0);
972 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
973 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
974 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
976 vbox = gtk_vbox_new(TRUE, 5);
977 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
979 gtk_container_add(GTK_CONTAINER(win), vbox);
980 gtk_widget_show_all(win);
983 static void view_log(GtkWidget *w, gpointer data)
985 struct gui *ui = (struct gui *) data;
990 static void connect_job_entry(GtkWidget *w, gpointer data)
992 struct gui *ui = (struct gui *) data;
993 struct gui_entry *ge;
995 ge = get_ge_from_cur_tab(ui);
997 connect_clicked(w, ge);
1000 static void send_job_entry(GtkWidget *w, gpointer data)
1002 struct gui *ui = (struct gui *) data;
1003 struct gui_entry *ge;
1005 ge = get_ge_from_cur_tab(ui);
1007 send_clicked(w, ge);
1010 static void edit_job_entry(GtkWidget *w, gpointer data)
1012 struct gui *ui = (struct gui *) data;
1013 struct gui_entry *ge;
1015 ge = get_ge_from_cur_tab(ui);
1016 if (ge && ge->client)
1017 gopt_get_options_window(ui->window, &ge->client->o);
1020 static void start_job_entry(GtkWidget *w, gpointer data)
1022 struct gui *ui = (struct gui *) data;
1023 struct gui_entry *ge;
1025 ge = get_ge_from_cur_tab(ui);
1027 start_job_clicked(w, ge);
1030 static void view_results(GtkWidget *w, gpointer data)
1032 struct gui *ui = (struct gui *) data;
1033 struct gfio_client *gc;
1034 struct gui_entry *ge;
1036 ge = get_ge_from_cur_tab(ui);
1040 if (ge->results_window)
1044 if (gc && gc->nr_results)
1045 gfio_display_end_results(gc);
1048 static void __update_graph_limits(struct gfio_graphs *g)
1050 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1051 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1054 static void ge_update_lim_fn(gpointer key, gpointer value, gpointer data)
1056 struct gui_entry *ge = (struct gui_entry *) value;
1058 __update_graph_limits(&ge->graphs);
1061 static void update_graph_limits(void)
1063 struct gui *ui = &main_ui;
1065 __update_graph_limits(&ui->graphs);
1067 g_hash_table_foreach(ui->ge_hash, ge_update_lim_fn, NULL);
1070 static void preferences(GtkWidget *w, gpointer data)
1072 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1073 GtkWidget *hbox, *spin, *entry, *spin_int;
1074 struct gui *ui = (struct gui *) data;
1077 dialog = gtk_dialog_new_with_buttons("Preferences",
1078 GTK_WINDOW(ui->window),
1079 GTK_DIALOG_DESTROY_WITH_PARENT,
1080 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1081 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1084 frame = gtk_frame_new("Graphing");
1085 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1086 vbox = gtk_vbox_new(FALSE, 6);
1087 gtk_container_add(GTK_CONTAINER(frame), vbox);
1089 hbox = gtk_hbox_new(FALSE, 5);
1090 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1091 entry = gtk_label_new("Font face to use for graph labels");
1092 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1094 font = gtk_font_button_new();
1095 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1097 box = gtk_vbox_new(FALSE, 6);
1098 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1100 hbox = gtk_hbox_new(FALSE, 5);
1101 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1102 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1103 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1105 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1107 box = gtk_vbox_new(FALSE, 6);
1108 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1110 hbox = gtk_hbox_new(FALSE, 5);
1111 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1112 entry = gtk_label_new("Client ETA request interval (msec)");
1113 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1115 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
1116 frame = gtk_frame_new("Debug logging");
1117 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1118 vbox = gtk_vbox_new(FALSE, 6);
1119 gtk_container_add(GTK_CONTAINER(frame), vbox);
1121 box = gtk_hbox_new(FALSE, 6);
1122 gtk_container_add(GTK_CONTAINER(vbox), box);
1124 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1126 for (i = 0; i < FD_DEBUG_MAX; i++) {
1128 box = gtk_hbox_new(FALSE, 6);
1129 gtk_container_add(GTK_CONTAINER(vbox), box);
1133 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1134 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1135 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1138 gtk_widget_show_all(dialog);
1140 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1141 gtk_widget_destroy(dialog);
1145 for (i = 0; i < FD_DEBUG_MAX; i++) {
1148 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1150 fio_debug |= (1UL << i);
1153 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1154 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1155 update_graph_limits();
1156 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1158 gtk_widget_destroy(dialog);
1161 static void about_dialog(GtkWidget *w, gpointer data)
1163 const char *authors[] = {
1164 "Jens Axboe <axboe@kernel.dk>",
1165 "Stephen Carmeron <stephenmcameron@gmail.com>",
1168 const char *license[] = {
1169 "Fio is free software; you can redistribute it and/or modify "
1170 "it under the terms of the GNU General Public License as published by "
1171 "the Free Software Foundation; either version 2 of the License, or "
1172 "(at your option) any later version.\n",
1173 "Fio is distributed in the hope that it will be useful, "
1174 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1175 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1176 "GNU General Public License for more details.\n",
1177 "You should have received a copy of the GNU General Public License "
1178 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1179 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1181 char *license_trans;
1183 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1184 license[2], "\n", NULL);
1186 gtk_show_about_dialog(NULL,
1187 "program-name", "gfio",
1188 "comments", "Gtk2 UI for fio",
1189 "license", license_trans,
1190 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1192 "version", fio_version_string,
1193 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1194 "logo-icon-name", "fio",
1196 "wrap-license", TRUE,
1199 g_free(license_trans);
1202 static GtkActionEntry menu_items[] = {
1203 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1204 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1205 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1206 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1207 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1208 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1209 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1210 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1211 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1212 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1213 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1214 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1215 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1216 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1217 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1218 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1219 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1221 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1223 static const gchar *ui_string = " \
1225 <menubar name=\"MainMenu\"> \
1226 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1227 <menuitem name=\"New\" action=\"NewFile\" /> \
1228 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1229 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1230 <separator name=\"Separator1\"/> \
1231 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1232 <separator name=\"Separator2\"/> \
1233 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1234 <separator name=\"Separator3\"/> \
1235 <placeholder name=\"FileRecentFiles\"/> \
1236 <separator name=\"Separator4\"/> \
1237 <menuitem name=\"Quit\" action=\"Quit\" /> \
1239 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1240 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1241 <separator name=\"Separator5\"/> \
1242 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1243 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1244 <separator name=\"Separator6\"/> \
1245 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1247 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1248 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1249 <separator name=\"Separator7\"/> \
1250 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1252 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1253 <menuitem name=\"About\" action=\"About\" /> \
1259 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1262 GtkActionGroup *action_group;
1265 action_group = gtk_action_group_new("Menu");
1266 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1268 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1269 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1271 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1273 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1276 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1277 GtkWidget *vbox, GtkUIManager *ui_manager)
1279 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1282 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1284 struct gui_entry *ge = (struct gui_entry *) data;
1287 index = gtk_combo_box_get_active(box);
1289 multitext_set_entry(&ge->eta.iotype, index);
1290 multitext_set_entry(&ge->eta.bs, index);
1291 multitext_set_entry(&ge->eta.ioengine, index);
1292 multitext_set_entry(&ge->eta.iodepth, index);
1295 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1297 struct gui_entry *ge = (struct gui_entry *) data;
1299 multitext_free(&ge->eta.iotype);
1300 multitext_free(&ge->eta.bs);
1301 multitext_free(&ge->eta.ioengine);
1302 multitext_free(&ge->eta.iodepth);
1305 static GtkWidget *new_client_page(struct gui_entry *ge)
1307 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1308 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1310 main_vbox = gtk_vbox_new(FALSE, 3);
1312 top_align = gtk_alignment_new(0, 0, 1, 0);
1313 top_vbox = gtk_vbox_new(FALSE, 3);
1314 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1315 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1317 probe = gtk_frame_new("Job");
1318 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1319 probe_frame = gtk_vbox_new(FALSE, 3);
1320 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1322 probe_box = gtk_hbox_new(FALSE, 3);
1323 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1324 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1325 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1326 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1327 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1329 probe_box = gtk_hbox_new(FALSE, 3);
1330 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1332 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1333 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1334 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1335 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1336 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1337 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1338 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1339 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1340 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1342 probe_box = gtk_hbox_new(FALSE, 3);
1343 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1344 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1345 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1346 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1347 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1350 * Only add this if we have a commit rate
1353 probe_box = gtk_hbox_new(FALSE, 3);
1354 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1356 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1357 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1359 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1360 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1364 * Set up a drawing area and IOPS and bandwidth graphs
1366 ge->graphs.drawing_area = gtk_drawing_area_new();
1367 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1368 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1369 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1370 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1371 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1372 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1373 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1374 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1375 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1376 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1377 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1378 ge->graphs.drawing_area);
1379 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1381 setup_graphs(&ge->graphs);
1384 * Set up alignments for widgets at the bottom of ui,
1385 * align bottom left, expand horizontally but not vertically
1387 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1388 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1389 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1390 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1392 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
1395 * Set up thread status progress bar
1397 ge->thread_status_pb = gtk_progress_bar_new();
1398 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1399 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1400 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1406 static GtkWidget *new_main_page(struct gui *ui)
1408 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1409 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1411 main_vbox = gtk_vbox_new(FALSE, 3);
1414 * Set up alignments for widgets at the top of ui,
1415 * align top left, expand horizontally but not vertically
1417 top_align = gtk_alignment_new(0, 0, 1, 0);
1418 top_vbox = gtk_vbox_new(FALSE, 0);
1419 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1420 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1422 probe = gtk_frame_new("Run statistics");
1423 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1424 probe_frame = gtk_vbox_new(FALSE, 3);
1425 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1427 probe_box = gtk_hbox_new(FALSE, 3);
1428 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1429 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1430 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1431 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1432 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1433 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1436 * Only add this if we have a commit rate
1439 probe_box = gtk_hbox_new(FALSE, 3);
1440 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1442 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1443 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1445 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1446 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1450 * Set up a drawing area and IOPS and bandwidth graphs
1452 ui->graphs.drawing_area = gtk_drawing_area_new();
1453 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1454 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1455 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1456 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1457 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1458 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1459 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1460 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1461 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1462 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1463 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1464 ui->graphs.drawing_area);
1465 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1468 setup_graphs(&ui->graphs);
1471 * Set up alignments for widgets at the bottom of ui,
1472 * align bottom left, expand horizontally but not vertically
1474 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1475 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1476 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1477 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1480 * Set up thread status progress bar
1482 ui->thread_status_pb = gtk_progress_bar_new();
1483 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1484 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1485 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1490 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1491 guint page, gpointer data)
1494 struct gui *ui = (struct gui *) data;
1495 struct gui_entry *ge;
1498 set_job_menu_visible(ui, 0);
1499 set_view_results_visible(ui, 0);
1503 set_job_menu_visible(ui, 1);
1504 ge = get_ge_from_page(ui, page, NULL);
1506 update_button_states(ui, ge);
1511 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1513 time_t time_a = gtk_recent_info_get_visited(a);
1514 time_t time_b = gtk_recent_info_get_visited(b);
1516 return time_b - time_a;
1519 static void add_recent_file_items(struct gui *ui)
1521 const gchar *gfio = g_get_application_name();
1522 GList *items, *item;
1525 if (ui->recent_ui_id) {
1526 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1527 gtk_ui_manager_ensure_update(ui->uimanager);
1529 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1531 if (ui->actiongroup) {
1532 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1533 g_object_unref(ui->actiongroup);
1535 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1537 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1539 items = gtk_recent_manager_get_items(ui->recentmanager);
1540 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1542 for (item = items; item && item->data; item = g_list_next(item)) {
1543 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1548 if (!gtk_recent_info_has_application(info, gfio))
1552 * We only support local files for now
1554 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1557 action_name = g_strdup_printf("RecentFile%u", i++);
1558 label = gtk_recent_info_get_display_name(info);
1560 action = g_object_new(GTK_TYPE_ACTION,
1561 "name", action_name,
1562 "label", label, NULL);
1564 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1565 gtk_recent_info_ref(info),
1566 (GDestroyNotify) gtk_recent_info_unref);
1569 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1571 gtk_action_group_add_action(ui->actiongroup, action);
1572 g_object_unref(action);
1574 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1575 "/MainMenu/FileMenu/FileRecentFiles",
1577 GTK_UI_MANAGER_MENUITEM, FALSE);
1579 g_free(action_name);
1585 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1589 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1590 gint x, gint y, GtkSelectionData *seldata,
1591 guint info, guint time, gpointer *data)
1593 struct gui *ui = (struct gui *) data;
1597 source = gtk_drag_get_source_widget(ctx);
1598 if (source && widget == gtk_widget_get_toplevel(source)) {
1599 gtk_drag_finish(ctx, FALSE, FALSE, time);
1603 uris = gtk_selection_data_get_uris(seldata);
1605 gtk_drag_finish(ctx, FALSE, FALSE, time);
1610 do_file_open_with_tab(ui, uris[0]);
1612 gtk_drag_finish(ctx, TRUE, FALSE, time);
1616 static void init_ui(int *argc, char **argv[], struct gui *ui)
1618 GtkSettings *settings;
1621 /* Magical g*thread incantation, you just need this thread stuff.
1622 * Without it, the update that happens in gfio_update_thread_status
1623 * doesn't really happen in a timely fashion, you need expose events
1625 if (!g_thread_supported())
1626 g_thread_init(NULL);
1629 gtk_init(argc, argv);
1630 settings = gtk_settings_get_default();
1631 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1633 gdk_color_parse("white", &gfio_color_white);
1635 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1636 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1637 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1639 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1640 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1642 ui->vbox = gtk_vbox_new(FALSE, 0);
1643 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1645 ui->uimanager = gtk_ui_manager_new();
1646 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1647 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1649 ui->recentmanager = gtk_recent_manager_get_default();
1650 add_recent_file_items(ui);
1652 ui->notebook = gtk_notebook_new();
1653 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1654 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1655 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1656 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1658 vbox = new_main_page(ui);
1659 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1660 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1661 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1663 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1665 gfio_ui_setup_log(ui);
1667 gtk_widget_show_all(ui->window);
1670 int main(int argc, char *argv[], char *envp[])
1672 if (initialize_fio(envp))
1674 if (fio_init_options())
1677 memset(&main_ui, 0, sizeof(main_ui));
1678 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
1680 init_ui(&argc, &argv, &main_ui);
1682 gdk_threads_enter();
1684 gdk_threads_leave();
1686 g_hash_table_destroy(main_ui.ge_hash);