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);
391 flist_del(&ge->list);
395 static void ge_widget_destroy(GtkWidget *w, gpointer data)
397 struct gui_entry *ge = (struct gui_entry *) data;
402 static void gfio_quit(struct gui *ui)
404 struct gui_entry *ge;
406 while (!flist_empty(&ui->list)) {
407 ge = flist_entry(ui->list.next, struct gui_entry, list);
414 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
415 __attribute__((unused)) gpointer data)
417 struct gui *ui = (struct gui *) data;
422 static void *job_thread(void *arg)
424 struct gui *ui = arg;
426 ui->handler_running = 1;
427 fio_handle_clients(&gfio_client_ops);
428 ui->handler_running = 0;
432 static int send_job_file(struct gui_entry *ge)
434 struct gfio_client *gc = ge->client;
437 ret = fio_client_send_ini(gc->client, ge->job_file);
441 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
445 static void *server_thread(void *arg)
448 gfio_server_running = 1;
449 fio_start_server(NULL);
450 gfio_server_running = 0;
454 static void gfio_start_server(struct gui *ui)
456 if (!gfio_server_running) {
457 gfio_server_running = 1;
458 pthread_create(&ui->server_t, NULL, server_thread, NULL);
459 pthread_detach(ui->server_t);
463 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
466 struct gui_entry *ge = data;
467 struct gfio_client *gc = ge->client;
470 fio_start_client(gc->client);
473 static void file_open(GtkWidget *w, gpointer data);
475 struct connection_widgets
482 static void hostname_cb(GtkEntry *entry, gpointer data)
484 struct connection_widgets *cw = data;
485 int uses_net = 0, is_localhost = 0;
490 * Check whether to display the 'auto start backend' box
491 * or not. Show it if we are a localhost and using network,
494 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
495 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
500 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
501 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
502 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
503 !strcmp(text, "ip6-loopback"))
507 if (!uses_net || is_localhost) {
508 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
509 gtk_widget_set_sensitive(cw->button, 1);
511 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
512 gtk_widget_set_sensitive(cw->button, 0);
516 static int get_connection_details(struct gui_entry *ge)
518 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
519 struct connection_widgets cw;
520 struct gui *ui = ge->ui;
526 dialog = gtk_dialog_new_with_buttons("Connection details",
527 GTK_WINDOW(ui->window),
528 GTK_DIALOG_DESTROY_WITH_PARENT,
529 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
530 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
532 frame = gtk_frame_new("Hostname / socket name");
533 /* gtk_dialog_get_content_area() is 2.14 and newer */
534 vbox = GTK_DIALOG(dialog)->vbox;
535 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
537 box = gtk_vbox_new(FALSE, 6);
538 gtk_container_add(GTK_CONTAINER(frame), box);
540 hbox = gtk_hbox_new(TRUE, 10);
541 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
542 cw.hentry = gtk_entry_new();
543 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
544 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
546 frame = gtk_frame_new("Port");
547 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
548 box = gtk_vbox_new(FALSE, 10);
549 gtk_container_add(GTK_CONTAINER(frame), box);
551 hbox = gtk_hbox_new(TRUE, 4);
552 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
553 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
555 frame = gtk_frame_new("Type");
556 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
557 box = gtk_vbox_new(FALSE, 10);
558 gtk_container_add(GTK_CONTAINER(frame), box);
560 hbox = gtk_hbox_new(TRUE, 4);
561 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
563 cw.combo = gtk_combo_box_new_text();
564 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
565 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
566 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
567 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
569 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
571 frame = gtk_frame_new("Options");
572 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
573 box = gtk_vbox_new(FALSE, 10);
574 gtk_container_add(GTK_CONTAINER(frame), box);
576 hbox = gtk_hbox_new(TRUE, 4);
577 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
579 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
580 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
581 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.");
582 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
585 * Connect edit signal, so we can show/not-show the auto start button
587 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
588 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
590 gtk_widget_show_all(dialog);
592 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
593 gtk_widget_destroy(dialog);
597 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
598 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
600 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
601 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
602 ge->type = Fio_client_ipv4;
603 else if (!strncmp(typeentry, "IPv6", 4))
604 ge->type = Fio_client_ipv6;
606 ge->type = Fio_client_socket;
609 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
611 gtk_widget_destroy(dialog);
615 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
617 gc->client = fio_get_client(client);
618 client->client_data = gc;
621 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
623 struct gfio_client *gc;
625 gc = malloc(sizeof(*gc));
626 memset(gc, 0, sizeof(*gc));
627 options_default_fill(&gc->o);
630 gfio_set_client(gc, client);
633 static void connect_clicked(GtkWidget *widget, gpointer data)
635 struct gui_entry *ge = data;
636 struct gfio_client *gc = ge->client;
638 if (ge->state == GE_STATE_NEW) {
642 file_open(widget, ge->ui);
649 struct fio_client *client;
651 if (get_connection_details(ge)) {
652 gfio_report_error(ge, "Failed to get connection details\n");
656 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
658 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
663 gfio_set_client(gc, client);
666 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
667 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
668 ret = fio_client_connect(gc->client);
670 if (!ge->ui->handler_running)
671 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
672 gfio_set_state(ge, GE_STATE_CONNECTED);
674 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
677 fio_client_terminate(gc->client);
678 gfio_set_state(ge, GE_STATE_NEW);
679 clear_ge_ui_info(ge);
683 static void send_clicked(GtkWidget *widget, gpointer data)
685 struct gui_entry *ge = data;
687 if (send_job_file(ge))
688 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
691 static GtkWidget *new_client_page(struct gui_entry *ge);
693 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
695 struct gui_entry *ge;
697 ge = malloc(sizeof(*ge));
698 memset(ge, 0, sizeof(*ge));
699 ge->state = GE_STATE_NEW;
700 INIT_FLIST_HEAD(&ge->list);
701 flist_add_tail(&ge->list, &ui->list);
706 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
708 struct gui_entry *ge;
710 ge = alloc_new_gui_entry(ui);
712 ge->vbox = new_client_page(ge);
713 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
715 ge->page_label = gtk_label_new(name);
716 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
718 gtk_widget_show_all(ui->window);
722 static void file_new(GtkWidget *w, gpointer data)
724 struct gui *ui = (struct gui *) data;
725 struct gui_entry *ge;
727 ge = get_new_ge_with_tab(ui, "Untitled");
728 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
732 * Return the 'ge' corresponding to the tab. If the active tab is the
733 * main tab, open a new tab.
735 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
738 struct flist_head *entry;
739 struct gui_entry *ge;
744 return get_new_ge_with_tab(ui, "Untitled");
750 flist_for_each(entry, &ui->list) {
751 ge = flist_entry(entry, struct gui_entry, list);
752 if (ge->page_num == cur_page)
759 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
764 * Main tab is tab 0, so any current page other than 0 holds
767 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
769 return get_ge_from_page(ui, cur_page, NULL);
774 static void file_close(GtkWidget *w, gpointer data)
776 struct gui *ui = (struct gui *) data;
777 struct gui_entry *ge;
780 * Can't close the main tab
782 ge = get_ge_from_cur_tab(ui);
784 gtk_widget_destroy(ge->vbox);
788 if (!flist_empty(&ui->list)) {
789 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
796 static void file_add_recent(struct gui *ui, const gchar *uri)
800 memset(&grd, 0, sizeof(grd));
801 grd.display_name = strdup("gfio");
802 grd.description = strdup("Fio job file");
803 grd.mime_type = strdup(GFIO_MIME);
804 grd.app_name = strdup(g_get_application_name());
805 grd.app_exec = strdup("gfio %f/%u");
807 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
810 static gchar *get_filename_from_uri(const gchar *uri)
812 if (strncmp(uri, "file://", 7))
815 return strdup(uri + 7);
818 static int do_file_open(struct gui_entry *ge, const gchar *uri)
820 struct fio_client *client;
822 assert(!ge->job_file);
824 ge->job_file = get_filename_from_uri(uri);
826 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
828 gfio_client_added(ge, client);
829 file_add_recent(ge->ui, uri);
833 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
839 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
841 struct gui_entry *ge;
843 int ret, ge_is_new = 0;
846 * Creates new tab if current tab is the main window, or the
847 * current tab already has a client.
849 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
850 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
852 ge = get_new_ge_with_tab(ui, "Untitled");
856 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
858 if (get_connection_details(ge)) {
860 gtk_widget_destroy(ge->vbox);
865 ret = do_file_open(ge, uri);
868 if (ge->server_start)
869 gfio_start_server(ui);
872 gtk_widget_destroy(ge->vbox);
878 static void recent_open(GtkAction *action, gpointer data)
880 struct gui *ui = (struct gui *) data;
884 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
885 uri = gtk_recent_info_get_uri(info);
887 do_file_open_with_tab(ui, uri);
890 static void file_open(GtkWidget *w, gpointer data)
892 struct gui *ui = data;
894 GtkFileFilter *filter;
897 dialog = gtk_file_chooser_dialog_new("Open File",
898 GTK_WINDOW(ui->window),
899 GTK_FILE_CHOOSER_ACTION_OPEN,
900 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
901 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
903 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
905 filter = gtk_file_filter_new();
906 gtk_file_filter_add_pattern(filter, "*.fio");
907 gtk_file_filter_add_pattern(filter, "*.job");
908 gtk_file_filter_add_pattern(filter, "*.ini");
909 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
910 gtk_file_filter_set_name(filter, "Fio job file");
911 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
913 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
914 gtk_widget_destroy(dialog);
918 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
920 gtk_widget_destroy(dialog);
922 do_file_open_with_tab(ui, filename);
926 static void file_save(GtkWidget *w, gpointer data)
928 struct gui *ui = data;
931 dialog = gtk_file_chooser_dialog_new("Save File",
932 GTK_WINDOW(ui->window),
933 GTK_FILE_CHOOSER_ACTION_SAVE,
934 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
935 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
938 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
939 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
941 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
944 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
945 // save_job_file(filename);
948 gtk_widget_destroy(dialog);
951 static void view_log_destroy(GtkWidget *w, gpointer data)
953 struct gui *ui = (struct gui *) data;
955 gtk_widget_ref(ui->log_tree);
956 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
957 gtk_widget_destroy(w);
961 void gfio_view_log(struct gui *ui)
963 GtkWidget *win, *scroll, *vbox, *box;
968 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
969 gtk_window_set_title(GTK_WINDOW(win), "Log");
970 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
972 scroll = gtk_scrolled_window_new(NULL, NULL);
974 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
976 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
978 box = gtk_hbox_new(TRUE, 0);
979 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
980 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
981 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
983 vbox = gtk_vbox_new(TRUE, 5);
984 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
986 gtk_container_add(GTK_CONTAINER(win), vbox);
987 gtk_widget_show_all(win);
990 static void view_log(GtkWidget *w, gpointer data)
992 struct gui *ui = (struct gui *) data;
997 static void connect_job_entry(GtkWidget *w, gpointer data)
999 struct gui *ui = (struct gui *) data;
1000 struct gui_entry *ge;
1002 ge = get_ge_from_cur_tab(ui);
1004 connect_clicked(w, ge);
1007 static void send_job_entry(GtkWidget *w, gpointer data)
1009 struct gui *ui = (struct gui *) data;
1010 struct gui_entry *ge;
1012 ge = get_ge_from_cur_tab(ui);
1014 send_clicked(w, ge);
1017 static void edit_job_entry(GtkWidget *w, gpointer data)
1019 struct gui *ui = (struct gui *) data;
1020 struct gui_entry *ge;
1022 ge = get_ge_from_cur_tab(ui);
1023 if (ge && ge->client)
1024 gopt_get_options_window(ui->window, &ge->client->o);
1027 static void start_job_entry(GtkWidget *w, gpointer data)
1029 struct gui *ui = (struct gui *) data;
1030 struct gui_entry *ge;
1032 ge = get_ge_from_cur_tab(ui);
1034 start_job_clicked(w, ge);
1037 static void view_results(GtkWidget *w, gpointer data)
1039 struct gui *ui = (struct gui *) data;
1040 struct gfio_client *gc;
1041 struct gui_entry *ge;
1043 ge = get_ge_from_cur_tab(ui);
1047 if (ge->results_window)
1051 if (gc && gc->nr_results)
1052 gfio_display_end_results(gc);
1055 static void __update_graph_limits(struct gfio_graphs *g)
1057 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1058 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1061 static void update_graph_limits(void)
1063 struct flist_head *entry;
1064 struct gui_entry *ge;
1066 __update_graph_limits(&main_ui.graphs);
1068 flist_for_each(entry, &main_ui.list) {
1069 ge = flist_entry(entry, struct gui_entry, list);
1070 __update_graph_limits(&ge->graphs);
1074 static void preferences(GtkWidget *w, gpointer data)
1076 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1077 GtkWidget *hbox, *spin, *entry, *spin_int;
1078 struct gui *ui = (struct gui *) data;
1081 dialog = gtk_dialog_new_with_buttons("Preferences",
1082 GTK_WINDOW(ui->window),
1083 GTK_DIALOG_DESTROY_WITH_PARENT,
1084 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1085 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1088 frame = gtk_frame_new("Graphing");
1089 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1090 vbox = gtk_vbox_new(FALSE, 6);
1091 gtk_container_add(GTK_CONTAINER(frame), vbox);
1093 hbox = gtk_hbox_new(FALSE, 5);
1094 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1095 entry = gtk_label_new("Font face to use for graph labels");
1096 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1098 font = gtk_font_button_new();
1099 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1101 box = gtk_vbox_new(FALSE, 6);
1102 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1104 hbox = gtk_hbox_new(FALSE, 5);
1105 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1106 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1107 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1109 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1111 box = gtk_vbox_new(FALSE, 6);
1112 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1114 hbox = gtk_hbox_new(FALSE, 5);
1115 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1116 entry = gtk_label_new("Client ETA request interval (msec)");
1117 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1119 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
1120 frame = gtk_frame_new("Debug logging");
1121 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1122 vbox = gtk_vbox_new(FALSE, 6);
1123 gtk_container_add(GTK_CONTAINER(frame), vbox);
1125 box = gtk_hbox_new(FALSE, 6);
1126 gtk_container_add(GTK_CONTAINER(vbox), box);
1128 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1130 for (i = 0; i < FD_DEBUG_MAX; i++) {
1132 box = gtk_hbox_new(FALSE, 6);
1133 gtk_container_add(GTK_CONTAINER(vbox), box);
1137 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1138 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1139 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1142 gtk_widget_show_all(dialog);
1144 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1145 gtk_widget_destroy(dialog);
1149 for (i = 0; i < FD_DEBUG_MAX; i++) {
1152 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1154 fio_debug |= (1UL << i);
1157 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1158 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1159 update_graph_limits();
1160 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1162 gtk_widget_destroy(dialog);
1165 static void about_dialog(GtkWidget *w, gpointer data)
1167 const char *authors[] = {
1168 "Jens Axboe <axboe@kernel.dk>",
1169 "Stephen Carmeron <stephenmcameron@gmail.com>",
1172 const char *license[] = {
1173 "Fio is free software; you can redistribute it and/or modify "
1174 "it under the terms of the GNU General Public License as published by "
1175 "the Free Software Foundation; either version 2 of the License, or "
1176 "(at your option) any later version.\n",
1177 "Fio is distributed in the hope that it will be useful, "
1178 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1179 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1180 "GNU General Public License for more details.\n",
1181 "You should have received a copy of the GNU General Public License "
1182 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1183 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1185 char *license_trans;
1187 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1188 license[2], "\n", NULL);
1190 gtk_show_about_dialog(NULL,
1191 "program-name", "gfio",
1192 "comments", "Gtk2 UI for fio",
1193 "license", license_trans,
1194 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1196 "version", fio_version_string,
1197 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1198 "logo-icon-name", "fio",
1200 "wrap-license", TRUE,
1203 g_free(license_trans);
1206 static GtkActionEntry menu_items[] = {
1207 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1208 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1209 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1210 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1211 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1212 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1213 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1214 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1215 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1216 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1217 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1218 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1219 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1220 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1221 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1222 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1223 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1225 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1227 static const gchar *ui_string = " \
1229 <menubar name=\"MainMenu\"> \
1230 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1231 <menuitem name=\"New\" action=\"NewFile\" /> \
1232 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1233 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1234 <separator name=\"Separator1\"/> \
1235 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1236 <separator name=\"Separator2\"/> \
1237 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1238 <separator name=\"Separator3\"/> \
1239 <placeholder name=\"FileRecentFiles\"/> \
1240 <separator name=\"Separator4\"/> \
1241 <menuitem name=\"Quit\" action=\"Quit\" /> \
1243 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1244 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1245 <separator name=\"Separator5\"/> \
1246 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1247 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1248 <separator name=\"Separator6\"/> \
1249 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1251 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1252 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1253 <separator name=\"Separator7\"/> \
1254 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1256 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1257 <menuitem name=\"About\" action=\"About\" /> \
1263 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1266 GtkActionGroup *action_group;
1269 action_group = gtk_action_group_new("Menu");
1270 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1272 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1273 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1275 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1277 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1280 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1281 GtkWidget *vbox, GtkUIManager *ui_manager)
1283 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1286 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1288 struct gui_entry *ge = (struct gui_entry *) data;
1291 index = gtk_combo_box_get_active(box);
1293 multitext_set_entry(&ge->eta.iotype, index);
1294 multitext_set_entry(&ge->eta.bs, index);
1295 multitext_set_entry(&ge->eta.ioengine, index);
1296 multitext_set_entry(&ge->eta.iodepth, index);
1299 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1301 struct gui_entry *ge = (struct gui_entry *) data;
1303 multitext_free(&ge->eta.iotype);
1304 multitext_free(&ge->eta.bs);
1305 multitext_free(&ge->eta.ioengine);
1306 multitext_free(&ge->eta.iodepth);
1309 static GtkWidget *new_client_page(struct gui_entry *ge)
1311 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1312 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1314 main_vbox = gtk_vbox_new(FALSE, 3);
1316 top_align = gtk_alignment_new(0, 0, 1, 0);
1317 top_vbox = gtk_vbox_new(FALSE, 3);
1318 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1319 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1321 probe = gtk_frame_new("Job");
1322 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1323 probe_frame = gtk_vbox_new(FALSE, 3);
1324 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1326 probe_box = gtk_hbox_new(FALSE, 3);
1327 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1328 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1329 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1330 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1331 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1333 probe_box = gtk_hbox_new(FALSE, 3);
1334 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1336 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1337 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1338 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1339 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1340 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1341 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1342 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1343 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1344 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1346 probe_box = gtk_hbox_new(FALSE, 3);
1347 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1348 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1349 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1350 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1351 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1354 * Only add this if we have a commit rate
1357 probe_box = gtk_hbox_new(FALSE, 3);
1358 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1360 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1361 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1363 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1364 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1368 * Set up a drawing area and IOPS and bandwidth graphs
1370 ge->graphs.drawing_area = gtk_drawing_area_new();
1371 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1372 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1373 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1374 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1375 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1376 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1377 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1378 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1379 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1380 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1381 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1382 ge->graphs.drawing_area);
1383 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1385 setup_graphs(&ge->graphs);
1388 * Set up alignments for widgets at the bottom of ui,
1389 * align bottom left, expand horizontally but not vertically
1391 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1392 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1393 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1394 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1396 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
1399 * Set up thread status progress bar
1401 ge->thread_status_pb = gtk_progress_bar_new();
1402 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1403 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1404 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1410 static GtkWidget *new_main_page(struct gui *ui)
1412 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1413 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1415 main_vbox = gtk_vbox_new(FALSE, 3);
1418 * Set up alignments for widgets at the top of ui,
1419 * align top left, expand horizontally but not vertically
1421 top_align = gtk_alignment_new(0, 0, 1, 0);
1422 top_vbox = gtk_vbox_new(FALSE, 0);
1423 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1424 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1426 probe = gtk_frame_new("Run statistics");
1427 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1428 probe_frame = gtk_vbox_new(FALSE, 3);
1429 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1431 probe_box = gtk_hbox_new(FALSE, 3);
1432 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1433 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1434 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1435 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1436 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1437 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1440 * Only add this if we have a commit rate
1443 probe_box = gtk_hbox_new(FALSE, 3);
1444 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1446 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1447 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1449 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1450 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1454 * Set up a drawing area and IOPS and bandwidth graphs
1456 ui->graphs.drawing_area = gtk_drawing_area_new();
1457 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1458 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1459 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
1460 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1461 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1462 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1463 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1464 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1465 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1466 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1467 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1468 ui->graphs.drawing_area);
1469 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1472 setup_graphs(&ui->graphs);
1475 * Set up alignments for widgets at the bottom of ui,
1476 * align bottom left, expand horizontally but not vertically
1478 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1479 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1480 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1481 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1484 * Set up thread status progress bar
1486 ui->thread_status_pb = gtk_progress_bar_new();
1487 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1488 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1489 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1494 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1495 guint page, gpointer data)
1498 struct gui *ui = (struct gui *) data;
1499 struct gui_entry *ge;
1502 set_job_menu_visible(ui, 0);
1503 set_view_results_visible(ui, 0);
1507 set_job_menu_visible(ui, 1);
1508 ge = get_ge_from_page(ui, page, NULL);
1510 update_button_states(ui, ge);
1515 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1517 time_t time_a = gtk_recent_info_get_visited(a);
1518 time_t time_b = gtk_recent_info_get_visited(b);
1520 return time_b - time_a;
1523 static void add_recent_file_items(struct gui *ui)
1525 const gchar *gfio = g_get_application_name();
1526 GList *items, *item;
1529 if (ui->recent_ui_id) {
1530 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1531 gtk_ui_manager_ensure_update(ui->uimanager);
1533 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1535 if (ui->actiongroup) {
1536 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1537 g_object_unref(ui->actiongroup);
1539 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1541 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1543 items = gtk_recent_manager_get_items(ui->recentmanager);
1544 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1546 for (item = items; item && item->data; item = g_list_next(item)) {
1547 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1552 if (!gtk_recent_info_has_application(info, gfio))
1556 * We only support local files for now
1558 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1561 action_name = g_strdup_printf("RecentFile%u", i++);
1562 label = gtk_recent_info_get_display_name(info);
1564 action = g_object_new(GTK_TYPE_ACTION,
1565 "name", action_name,
1566 "label", label, NULL);
1568 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1569 gtk_recent_info_ref(info),
1570 (GDestroyNotify) gtk_recent_info_unref);
1573 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1575 gtk_action_group_add_action(ui->actiongroup, action);
1576 g_object_unref(action);
1578 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1579 "/MainMenu/FileMenu/FileRecentFiles",
1581 GTK_UI_MANAGER_MENUITEM, FALSE);
1583 g_free(action_name);
1589 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1593 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1594 gint x, gint y, GtkSelectionData *seldata,
1595 guint info, guint time, gpointer *data)
1597 struct gui *ui = (struct gui *) data;
1601 source = gtk_drag_get_source_widget(ctx);
1602 if (source && widget == gtk_widget_get_toplevel(source)) {
1603 gtk_drag_finish(ctx, FALSE, FALSE, time);
1607 uris = gtk_selection_data_get_uris(seldata);
1609 gtk_drag_finish(ctx, FALSE, FALSE, time);
1614 do_file_open_with_tab(ui, uris[0]);
1616 gtk_drag_finish(ctx, TRUE, FALSE, time);
1620 static void init_ui(int *argc, char **argv[], struct gui *ui)
1622 GtkSettings *settings;
1625 /* Magical g*thread incantation, you just need this thread stuff.
1626 * Without it, the update that happens in gfio_update_thread_status
1627 * doesn't really happen in a timely fashion, you need expose events
1629 if (!g_thread_supported())
1630 g_thread_init(NULL);
1633 gtk_init(argc, argv);
1634 settings = gtk_settings_get_default();
1635 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1637 gdk_color_parse("white", &gfio_color_white);
1639 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1640 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1641 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1643 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1644 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1646 ui->vbox = gtk_vbox_new(FALSE, 0);
1647 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1649 ui->uimanager = gtk_ui_manager_new();
1650 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1651 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1653 ui->recentmanager = gtk_recent_manager_get_default();
1654 add_recent_file_items(ui);
1656 ui->notebook = gtk_notebook_new();
1657 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1658 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1659 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1660 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1662 vbox = new_main_page(ui);
1663 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1664 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1665 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1667 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1669 gfio_ui_setup_log(ui);
1671 gtk_widget_show_all(ui->window);
1674 int main(int argc, char *argv[], char *envp[])
1676 if (initialize_fio(envp))
1678 if (fio_init_options())
1681 memset(&main_ui, 0, sizeof(main_ui));
1682 INIT_FLIST_HEAD(&main_ui.list);
1684 init_ui(&argc, &argv, &main_ui);
1686 gdk_threads_enter();
1688 gdk_threads_leave();