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;
379 if (gc && gc->client) {
380 if (ge->state >= GE_STATE_CONNECTED)
381 fio_client_terminate(gc->client);
383 fio_put_client(gc->client);
388 flist_del(&ge->list);
392 static void ge_widget_destroy(GtkWidget *w, gpointer data)
394 struct gui_entry *ge = (struct gui_entry *) data;
399 static void gfio_quit(struct gui *ui)
401 struct gui_entry *ge;
403 while (!flist_empty(&ui->list)) {
404 ge = flist_entry(ui->list.next, struct gui_entry, list);
411 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
412 __attribute__((unused)) gpointer data)
414 struct gui *ui = (struct gui *) data;
419 static void *job_thread(void *arg)
421 struct gui *ui = arg;
423 ui->handler_running = 1;
424 fio_handle_clients(&gfio_client_ops);
425 ui->handler_running = 0;
429 static int send_job_file(struct gui_entry *ge)
431 struct gfio_client *gc = ge->client;
434 ret = fio_client_send_ini(gc->client, ge->job_file);
438 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
442 static void *server_thread(void *arg)
445 gfio_server_running = 1;
446 fio_start_server(NULL);
447 gfio_server_running = 0;
451 static void gfio_start_server(struct gui *ui)
453 if (!gfio_server_running) {
454 gfio_server_running = 1;
455 pthread_create(&ui->server_t, NULL, server_thread, NULL);
456 pthread_detach(ui->server_t);
460 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
463 struct gui_entry *ge = data;
464 struct gfio_client *gc = ge->client;
467 fio_start_client(gc->client);
470 static void file_open(GtkWidget *w, gpointer data);
472 struct connection_widgets
479 static void hostname_cb(GtkEntry *entry, gpointer data)
481 struct connection_widgets *cw = data;
482 int uses_net = 0, is_localhost = 0;
487 * Check whether to display the 'auto start backend' box
488 * or not. Show it if we are a localhost and using network,
491 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
492 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
497 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
498 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
499 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
500 !strcmp(text, "ip6-loopback"))
504 if (!uses_net || is_localhost) {
505 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
506 gtk_widget_set_sensitive(cw->button, 1);
508 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
509 gtk_widget_set_sensitive(cw->button, 0);
513 static int get_connection_details(struct gui_entry *ge)
515 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
516 struct connection_widgets cw;
517 struct gui *ui = ge->ui;
523 dialog = gtk_dialog_new_with_buttons("Connection details",
524 GTK_WINDOW(ui->window),
525 GTK_DIALOG_DESTROY_WITH_PARENT,
526 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
527 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
529 frame = gtk_frame_new("Hostname / socket name");
530 /* gtk_dialog_get_content_area() is 2.14 and newer */
531 vbox = GTK_DIALOG(dialog)->vbox;
532 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
534 box = gtk_vbox_new(FALSE, 6);
535 gtk_container_add(GTK_CONTAINER(frame), box);
537 hbox = gtk_hbox_new(TRUE, 10);
538 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
539 cw.hentry = gtk_entry_new();
540 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
541 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
543 frame = gtk_frame_new("Port");
544 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
545 box = gtk_vbox_new(FALSE, 10);
546 gtk_container_add(GTK_CONTAINER(frame), box);
548 hbox = gtk_hbox_new(TRUE, 4);
549 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
550 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
552 frame = gtk_frame_new("Type");
553 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
554 box = gtk_vbox_new(FALSE, 10);
555 gtk_container_add(GTK_CONTAINER(frame), box);
557 hbox = gtk_hbox_new(TRUE, 4);
558 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
560 cw.combo = gtk_combo_box_new_text();
561 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
562 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
563 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
564 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
566 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
568 frame = gtk_frame_new("Options");
569 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
570 box = gtk_vbox_new(FALSE, 10);
571 gtk_container_add(GTK_CONTAINER(frame), box);
573 hbox = gtk_hbox_new(TRUE, 4);
574 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
576 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
577 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
578 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.");
579 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
582 * Connect edit signal, so we can show/not-show the auto start button
584 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
585 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
587 gtk_widget_show_all(dialog);
589 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
590 gtk_widget_destroy(dialog);
594 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
595 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
597 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
598 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
599 ge->type = Fio_client_ipv4;
600 else if (!strncmp(typeentry, "IPv6", 4))
601 ge->type = Fio_client_ipv6;
603 ge->type = Fio_client_socket;
606 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
608 gtk_widget_destroy(dialog);
612 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
614 gc->client = fio_get_client(client);
615 client->client_data = gc;
618 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
620 struct gfio_client *gc;
622 gc = malloc(sizeof(*gc));
623 memset(gc, 0, sizeof(*gc));
624 options_default_fill(&gc->o);
627 gfio_set_client(gc, client);
630 static void connect_clicked(GtkWidget *widget, gpointer data)
632 struct gui_entry *ge = data;
633 struct gfio_client *gc = ge->client;
635 if (ge->state == GE_STATE_NEW) {
639 file_open(widget, ge->ui);
646 struct fio_client *client;
648 if (get_connection_details(ge)) {
649 gfio_report_error(ge, "Failed to get connection details\n");
653 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
655 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
660 gfio_set_client(gc, client);
663 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
664 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
665 ret = fio_client_connect(gc->client);
667 if (!ge->ui->handler_running)
668 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
669 gfio_set_state(ge, GE_STATE_CONNECTED);
671 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
674 fio_client_terminate(gc->client);
675 gfio_set_state(ge, GE_STATE_NEW);
676 clear_ge_ui_info(ge);
680 static void send_clicked(GtkWidget *widget, gpointer data)
682 struct gui_entry *ge = data;
684 if (send_job_file(ge))
685 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
688 static GtkWidget *new_client_page(struct gui_entry *ge);
690 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
692 struct gui_entry *ge;
694 ge = malloc(sizeof(*ge));
695 memset(ge, 0, sizeof(*ge));
696 ge->state = GE_STATE_NEW;
697 INIT_FLIST_HEAD(&ge->list);
698 flist_add_tail(&ge->list, &ui->list);
703 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
705 struct gui_entry *ge;
707 ge = alloc_new_gui_entry(ui);
709 ge->vbox = new_client_page(ge);
710 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
712 ge->page_label = gtk_label_new(name);
713 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
715 gtk_widget_show_all(ui->window);
719 static void file_new(GtkWidget *w, gpointer data)
721 struct gui *ui = (struct gui *) data;
722 struct gui_entry *ge;
724 ge = get_new_ge_with_tab(ui, "Untitled");
725 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
729 * Return the 'ge' corresponding to the tab. If the active tab is the
730 * main tab, open a new tab.
732 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
735 struct flist_head *entry;
736 struct gui_entry *ge;
741 return get_new_ge_with_tab(ui, "Untitled");
747 flist_for_each(entry, &ui->list) {
748 ge = flist_entry(entry, struct gui_entry, list);
749 if (ge->page_num == cur_page)
756 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
761 * Main tab is tab 0, so any current page other than 0 holds
764 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
766 return get_ge_from_page(ui, cur_page, NULL);
771 static void file_close(GtkWidget *w, gpointer data)
773 struct gui *ui = (struct gui *) data;
774 struct gui_entry *ge;
777 * Can't close the main tab
779 ge = get_ge_from_cur_tab(ui);
781 gtk_widget_destroy(ge->vbox);
785 if (!flist_empty(&ui->list)) {
786 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
793 static void file_add_recent(struct gui *ui, const gchar *uri)
797 memset(&grd, 0, sizeof(grd));
798 grd.display_name = strdup("gfio");
799 grd.description = strdup("Fio job file");
800 grd.mime_type = strdup(GFIO_MIME);
801 grd.app_name = strdup(g_get_application_name());
802 grd.app_exec = strdup("gfio %f/%u");
804 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
807 static gchar *get_filename_from_uri(const gchar *uri)
809 if (strncmp(uri, "file://", 7))
812 return strdup(uri + 7);
815 static int do_file_open(struct gui_entry *ge, const gchar *uri)
817 struct fio_client *client;
819 assert(!ge->job_file);
821 ge->job_file = get_filename_from_uri(uri);
823 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
825 gfio_client_added(ge, client);
826 file_add_recent(ge->ui, uri);
830 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
836 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
838 struct gui_entry *ge;
840 int ret, ge_is_new = 0;
843 * Creates new tab if current tab is the main window, or the
844 * current tab already has a client.
846 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
847 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
849 ge = get_new_ge_with_tab(ui, "Untitled");
853 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
855 if (get_connection_details(ge)) {
857 gtk_widget_destroy(ge->vbox);
862 ret = do_file_open(ge, uri);
865 if (ge->server_start)
866 gfio_start_server(ui);
869 gtk_widget_destroy(ge->vbox);
875 static void recent_open(GtkAction *action, gpointer data)
877 struct gui *ui = (struct gui *) data;
881 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
882 uri = gtk_recent_info_get_uri(info);
884 do_file_open_with_tab(ui, uri);
887 static void file_open(GtkWidget *w, gpointer data)
889 struct gui *ui = data;
891 GtkFileFilter *filter;
894 dialog = gtk_file_chooser_dialog_new("Open File",
895 GTK_WINDOW(ui->window),
896 GTK_FILE_CHOOSER_ACTION_OPEN,
897 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
898 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
900 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
902 filter = gtk_file_filter_new();
903 gtk_file_filter_add_pattern(filter, "*.fio");
904 gtk_file_filter_add_pattern(filter, "*.job");
905 gtk_file_filter_add_pattern(filter, "*.ini");
906 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
907 gtk_file_filter_set_name(filter, "Fio job file");
908 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
910 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
911 gtk_widget_destroy(dialog);
915 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
917 gtk_widget_destroy(dialog);
919 do_file_open_with_tab(ui, filename);
923 static void file_save(GtkWidget *w, gpointer data)
925 struct gui *ui = data;
928 dialog = gtk_file_chooser_dialog_new("Save File",
929 GTK_WINDOW(ui->window),
930 GTK_FILE_CHOOSER_ACTION_SAVE,
931 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
932 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
935 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
936 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
938 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
941 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
942 // save_job_file(filename);
945 gtk_widget_destroy(dialog);
948 static void view_log_destroy(GtkWidget *w, gpointer data)
950 struct gui *ui = (struct gui *) data;
952 gtk_widget_ref(ui->log_tree);
953 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
954 gtk_widget_destroy(w);
958 void gfio_view_log(struct gui *ui)
960 GtkWidget *win, *scroll, *vbox, *box;
965 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
966 gtk_window_set_title(GTK_WINDOW(win), "Log");
967 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
969 scroll = gtk_scrolled_window_new(NULL, NULL);
971 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
973 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
975 box = gtk_hbox_new(TRUE, 0);
976 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
977 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
978 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
980 vbox = gtk_vbox_new(TRUE, 5);
981 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
983 gtk_container_add(GTK_CONTAINER(win), vbox);
984 gtk_widget_show_all(win);
987 static void view_log(GtkWidget *w, gpointer data)
989 struct gui *ui = (struct gui *) data;
994 static void connect_job_entry(GtkWidget *w, gpointer data)
996 struct gui *ui = (struct gui *) data;
997 struct gui_entry *ge;
999 ge = get_ge_from_cur_tab(ui);
1001 connect_clicked(w, ge);
1004 static void send_job_entry(GtkWidget *w, gpointer data)
1006 struct gui *ui = (struct gui *) data;
1007 struct gui_entry *ge;
1009 ge = get_ge_from_cur_tab(ui);
1011 send_clicked(w, ge);
1014 static void edit_job_entry(GtkWidget *w, gpointer data)
1016 struct gui *ui = (struct gui *) data;
1017 struct gui_entry *ge;
1019 ge = get_ge_from_cur_tab(ui);
1020 if (ge && ge->client)
1021 gopt_get_options_window(ui->window, &ge->client->o);
1024 static void start_job_entry(GtkWidget *w, gpointer data)
1026 struct gui *ui = (struct gui *) data;
1027 struct gui_entry *ge;
1029 ge = get_ge_from_cur_tab(ui);
1031 start_job_clicked(w, ge);
1034 static void view_results(GtkWidget *w, gpointer data)
1036 struct gui *ui = (struct gui *) data;
1037 struct gfio_client *gc;
1038 struct gui_entry *ge;
1040 ge = get_ge_from_cur_tab(ui);
1044 if (ge->results_window)
1048 if (gc && gc->nr_results)
1049 gfio_display_end_results(gc);
1052 static void __update_graph_limits(struct gfio_graphs *g)
1054 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1055 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1058 static void update_graph_limits(void)
1060 struct flist_head *entry;
1061 struct gui_entry *ge;
1063 __update_graph_limits(&main_ui.graphs);
1065 flist_for_each(entry, &main_ui.list) {
1066 ge = flist_entry(entry, struct gui_entry, list);
1067 __update_graph_limits(&ge->graphs);
1071 static void preferences(GtkWidget *w, gpointer data)
1073 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1074 GtkWidget *hbox, *spin, *entry, *spin_int;
1075 struct gui *ui = (struct gui *) data;
1078 dialog = gtk_dialog_new_with_buttons("Preferences",
1079 GTK_WINDOW(ui->window),
1080 GTK_DIALOG_DESTROY_WITH_PARENT,
1081 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1082 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1085 frame = gtk_frame_new("Graphing");
1086 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1087 vbox = gtk_vbox_new(FALSE, 6);
1088 gtk_container_add(GTK_CONTAINER(frame), vbox);
1090 hbox = gtk_hbox_new(FALSE, 5);
1091 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1092 entry = gtk_label_new("Font face to use for graph labels");
1093 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1095 font = gtk_font_button_new();
1096 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1098 box = gtk_vbox_new(FALSE, 6);
1099 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1101 hbox = gtk_hbox_new(FALSE, 5);
1102 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1103 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1104 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1106 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1108 box = gtk_vbox_new(FALSE, 6);
1109 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1111 hbox = gtk_hbox_new(FALSE, 5);
1112 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1113 entry = gtk_label_new("Client ETA request interval (msec)");
1114 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1116 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
1117 frame = gtk_frame_new("Debug logging");
1118 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1119 vbox = gtk_vbox_new(FALSE, 6);
1120 gtk_container_add(GTK_CONTAINER(frame), vbox);
1122 box = gtk_hbox_new(FALSE, 6);
1123 gtk_container_add(GTK_CONTAINER(vbox), box);
1125 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1127 for (i = 0; i < FD_DEBUG_MAX; i++) {
1129 box = gtk_hbox_new(FALSE, 6);
1130 gtk_container_add(GTK_CONTAINER(vbox), box);
1134 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1135 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1136 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1139 gtk_widget_show_all(dialog);
1141 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1142 gtk_widget_destroy(dialog);
1146 for (i = 0; i < FD_DEBUG_MAX; i++) {
1149 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1151 fio_debug |= (1UL << i);
1154 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1155 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1156 update_graph_limits();
1157 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1159 gtk_widget_destroy(dialog);
1162 static void about_dialog(GtkWidget *w, gpointer data)
1164 const char *authors[] = {
1165 "Jens Axboe <axboe@kernel.dk>",
1166 "Stephen Carmeron <stephenmcameron@gmail.com>",
1169 const char *license[] = {
1170 "Fio is free software; you can redistribute it and/or modify "
1171 "it under the terms of the GNU General Public License as published by "
1172 "the Free Software Foundation; either version 2 of the License, or "
1173 "(at your option) any later version.\n",
1174 "Fio is distributed in the hope that it will be useful, "
1175 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1176 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1177 "GNU General Public License for more details.\n",
1178 "You should have received a copy of the GNU General Public License "
1179 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1180 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1182 char *license_trans;
1184 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1185 license[2], "\n", NULL);
1187 gtk_show_about_dialog(NULL,
1188 "program-name", "gfio",
1189 "comments", "Gtk2 UI for fio",
1190 "license", license_trans,
1191 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1193 "version", fio_version_string,
1194 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1195 "logo-icon-name", "fio",
1197 "wrap-license", TRUE,
1200 g_free(license_trans);
1203 static GtkActionEntry menu_items[] = {
1204 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1205 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1206 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1207 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1208 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1209 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1210 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1211 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1212 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1213 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1214 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1215 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1216 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1217 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1218 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1219 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1220 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1222 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1224 static const gchar *ui_string = " \
1226 <menubar name=\"MainMenu\"> \
1227 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1228 <menuitem name=\"New\" action=\"NewFile\" /> \
1229 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1230 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1231 <separator name=\"Separator1\"/> \
1232 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1233 <separator name=\"Separator2\"/> \
1234 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1235 <separator name=\"Separator3\"/> \
1236 <placeholder name=\"FileRecentFiles\"/> \
1237 <separator name=\"Separator4\"/> \
1238 <menuitem name=\"Quit\" action=\"Quit\" /> \
1240 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1241 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1242 <separator name=\"Separator5\"/> \
1243 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1244 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1245 <separator name=\"Separator6\"/> \
1246 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1248 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1249 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1250 <separator name=\"Separator7\"/> \
1251 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1253 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1254 <menuitem name=\"About\" action=\"About\" /> \
1260 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1263 GtkActionGroup *action_group;
1266 action_group = gtk_action_group_new("Menu");
1267 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1269 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1270 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1272 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1274 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1277 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1278 GtkWidget *vbox, GtkUIManager *ui_manager)
1280 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1283 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1285 struct gui_entry *ge = (struct gui_entry *) data;
1288 index = gtk_combo_box_get_active(box);
1290 multitext_set_entry(&ge->eta.iotype, index);
1291 multitext_set_entry(&ge->eta.bs, index);
1292 multitext_set_entry(&ge->eta.ioengine, index);
1293 multitext_set_entry(&ge->eta.iodepth, index);
1296 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1298 struct gui_entry *ge = (struct gui_entry *) data;
1300 multitext_free(&ge->eta.iotype);
1301 multitext_free(&ge->eta.bs);
1302 multitext_free(&ge->eta.ioengine);
1303 multitext_free(&ge->eta.iodepth);
1306 static GtkWidget *new_client_page(struct gui_entry *ge)
1308 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1309 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1311 main_vbox = gtk_vbox_new(FALSE, 3);
1313 top_align = gtk_alignment_new(0, 0, 1, 0);
1314 top_vbox = gtk_vbox_new(FALSE, 3);
1315 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1316 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1318 probe = gtk_frame_new("Job");
1319 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1320 probe_frame = gtk_vbox_new(FALSE, 3);
1321 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1323 probe_box = gtk_hbox_new(FALSE, 3);
1324 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1325 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1326 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1327 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1328 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1330 probe_box = gtk_hbox_new(FALSE, 3);
1331 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1333 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1334 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1335 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1336 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1337 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1338 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1339 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1340 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1341 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1343 probe_box = gtk_hbox_new(FALSE, 3);
1344 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1345 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1346 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1347 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1348 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1351 * Only add this if we have a commit rate
1354 probe_box = gtk_hbox_new(FALSE, 3);
1355 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1357 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1358 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1360 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1361 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1365 * Set up a drawing area and IOPS and bandwidth graphs
1367 ge->graphs.drawing_area = gtk_drawing_area_new();
1368 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1369 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1370 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1371 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1372 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1373 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1374 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1375 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1376 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1377 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1378 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1379 ge->graphs.drawing_area);
1380 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1382 setup_graphs(&ge->graphs);
1385 * Set up alignments for widgets at the bottom of ui,
1386 * align bottom left, expand horizontally but not vertically
1388 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1389 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1390 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1391 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1393 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
1396 * Set up thread status progress bar
1398 ge->thread_status_pb = gtk_progress_bar_new();
1399 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1400 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1401 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1407 static GtkWidget *new_main_page(struct gui *ui)
1409 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1410 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1412 main_vbox = gtk_vbox_new(FALSE, 3);
1415 * Set up alignments for widgets at the top of ui,
1416 * align top left, expand horizontally but not vertically
1418 top_align = gtk_alignment_new(0, 0, 1, 0);
1419 top_vbox = gtk_vbox_new(FALSE, 0);
1420 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1421 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1423 probe = gtk_frame_new("Run statistics");
1424 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1425 probe_frame = gtk_vbox_new(FALSE, 3);
1426 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1428 probe_box = gtk_hbox_new(FALSE, 3);
1429 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1430 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1431 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1432 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1433 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1434 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1437 * Only add this if we have a commit rate
1440 probe_box = gtk_hbox_new(FALSE, 3);
1441 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1443 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1444 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1446 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1447 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1451 * Set up a drawing area and IOPS and bandwidth graphs
1453 ui->graphs.drawing_area = gtk_drawing_area_new();
1454 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1455 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1456 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1457 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1458 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1459 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1460 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1461 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1462 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1463 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1464 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1465 ui->graphs.drawing_area);
1466 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1469 setup_graphs(&ui->graphs);
1472 * Set up alignments for widgets at the bottom of ui,
1473 * align bottom left, expand horizontally but not vertically
1475 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1476 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1477 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1478 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1481 * Set up thread status progress bar
1483 ui->thread_status_pb = gtk_progress_bar_new();
1484 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1485 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1486 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1491 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1492 guint page, gpointer data)
1495 struct gui *ui = (struct gui *) data;
1496 struct gui_entry *ge;
1499 set_job_menu_visible(ui, 0);
1500 set_view_results_visible(ui, 0);
1504 set_job_menu_visible(ui, 1);
1505 ge = get_ge_from_page(ui, page, NULL);
1507 update_button_states(ui, ge);
1512 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1514 time_t time_a = gtk_recent_info_get_visited(a);
1515 time_t time_b = gtk_recent_info_get_visited(b);
1517 return time_b - time_a;
1520 static void add_recent_file_items(struct gui *ui)
1522 const gchar *gfio = g_get_application_name();
1523 GList *items, *item;
1526 if (ui->recent_ui_id) {
1527 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1528 gtk_ui_manager_ensure_update(ui->uimanager);
1530 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1532 if (ui->actiongroup) {
1533 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1534 g_object_unref(ui->actiongroup);
1536 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1538 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1540 items = gtk_recent_manager_get_items(ui->recentmanager);
1541 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1543 for (item = items; item && item->data; item = g_list_next(item)) {
1544 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1549 if (!gtk_recent_info_has_application(info, gfio))
1553 * We only support local files for now
1555 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1558 action_name = g_strdup_printf("RecentFile%u", i++);
1559 label = gtk_recent_info_get_display_name(info);
1561 action = g_object_new(GTK_TYPE_ACTION,
1562 "name", action_name,
1563 "label", label, NULL);
1565 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1566 gtk_recent_info_ref(info),
1567 (GDestroyNotify) gtk_recent_info_unref);
1570 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1572 gtk_action_group_add_action(ui->actiongroup, action);
1573 g_object_unref(action);
1575 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1576 "/MainMenu/FileMenu/FileRecentFiles",
1578 GTK_UI_MANAGER_MENUITEM, FALSE);
1580 g_free(action_name);
1586 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1590 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1591 gint x, gint y, GtkSelectionData *seldata,
1592 guint info, guint time, gpointer *data)
1594 struct gui *ui = (struct gui *) data;
1598 source = gtk_drag_get_source_widget(ctx);
1599 if (source && widget == gtk_widget_get_toplevel(source)) {
1600 gtk_drag_finish(ctx, FALSE, FALSE, time);
1604 uris = gtk_selection_data_get_uris(seldata);
1606 gtk_drag_finish(ctx, FALSE, FALSE, time);
1611 do_file_open_with_tab(ui, uris[0]);
1613 gtk_drag_finish(ctx, TRUE, FALSE, time);
1617 static void init_ui(int *argc, char **argv[], struct gui *ui)
1619 GtkSettings *settings;
1622 /* Magical g*thread incantation, you just need this thread stuff.
1623 * Without it, the update that happens in gfio_update_thread_status
1624 * doesn't really happen in a timely fashion, you need expose events
1626 if (!g_thread_supported())
1627 g_thread_init(NULL);
1630 gtk_init(argc, argv);
1631 settings = gtk_settings_get_default();
1632 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1634 gdk_color_parse("white", &gfio_color_white);
1636 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1637 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1638 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1640 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1641 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1643 ui->vbox = gtk_vbox_new(FALSE, 0);
1644 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1646 ui->uimanager = gtk_ui_manager_new();
1647 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1648 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1650 ui->recentmanager = gtk_recent_manager_get_default();
1651 add_recent_file_items(ui);
1653 ui->notebook = gtk_notebook_new();
1654 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1655 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1656 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1657 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1659 vbox = new_main_page(ui);
1660 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1661 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1662 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1664 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1666 gfio_ui_setup_log(ui);
1668 gtk_widget_show_all(ui->window);
1671 int main(int argc, char *argv[], char *envp[])
1673 if (initialize_fio(envp))
1675 if (fio_init_options())
1678 memset(&main_ui, 0, sizeof(main_ui));
1679 INIT_FLIST_HEAD(&main_ui.list);
1681 init_ui(&argc, &argv, &main_ui);
1683 gdk_threads_enter();
1685 gdk_threads_leave();