• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * gfio - gui front end for fio - the flexible io tester
3  *
4  * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5  * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
6  *
7  * The license below covers all files distributed with fio unless otherwise
8  * noted in the file itself.
9  *
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.
13  *
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.
18  *
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
22  *
23  */
24 #include <locale.h>
25 #include <malloc.h>
26 #include <string.h>
27 
28 #include <glib.h>
29 #include <cairo.h>
30 #include <gtk/gtk.h>
31 
32 #include "fio.h"
33 #include "gfio.h"
34 #include "ghelpers.h"
35 #include "goptions.h"
36 #include "gerror.h"
37 #include "gclient.h"
38 #include "graph.h"
39 
40 static int gfio_server_running;
41 static unsigned int gfio_graph_limit = 100;
42 
43 GdkColor gfio_color_white;
44 GdkColor gfio_color_lightyellow;
45 const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
46 
47 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
48 
49 static void connect_clicked(GtkWidget *widget, gpointer data);
50 static void start_job_clicked(GtkWidget *widget, gpointer data);
51 static void send_clicked(GtkWidget *widget, gpointer data);
52 
53 static struct button_spec {
54 	const char *buttontext;
55 	clickfunction f;
56 	const char *tooltiptext[2];
57 	const int start_sensitive;
58 } buttonspeclist[] = {
59 	{
60 	  .buttontext		= "Connect",
61 	  .f			= connect_clicked,
62 	  .tooltiptext		= { "Disconnect from host", "Connect to host" },
63 	  .start_sensitive	= 1,
64 	},
65 	{
66 	  .buttontext		= "Send",
67 	  .f			= send_clicked,
68 	  .tooltiptext		= { "Send job description to host", NULL },
69 	  .start_sensitive	= 0,
70 	},
71 	{
72 	  .buttontext		= "Start Job",
73 	  .f			= start_job_clicked,
74 	  .tooltiptext		= { "Start the current job on the server", NULL },
75 	  .start_sensitive	= 0,
76 	},
77 };
78 
setup_iops_graph(struct gfio_graphs * gg)79 static void setup_iops_graph(struct gfio_graphs *gg)
80 {
81 	struct graph *g;
82 
83 	g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
84 	graph_title(g, "IOPS (IOs/sec)");
85 	graph_x_title(g, "Time (secs)");
86 	gg->read_iops = graph_add_label(g, "Read IOPS");
87 	gg->write_iops = graph_add_label(g, "Write IOPS");
88 	gg->trim_iops = graph_add_label(g, "Trim IOPS");
89 	graph_set_color(g, gg->read_iops, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
90 	graph_set_color(g, gg->write_iops, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
91 	graph_set_color(g, gg->trim_iops, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
92 	line_graph_set_data_count_limit(g, gfio_graph_limit);
93 	graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
94 	graph_set_graph_all_zeroes(g, 0);
95 	gg->iops_graph = g;
96 }
97 
setup_bandwidth_graph(struct gfio_graphs * gg)98 static void setup_bandwidth_graph(struct gfio_graphs *gg)
99 {
100 	struct graph *g;
101 
102 	g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
103 	graph_title(g, "Bandwidth (bytes/sec)");
104 	graph_x_title(g, "Time (secs)");
105 	gg->read_bw = graph_add_label(g, "Read Bandwidth");
106 	gg->write_bw = graph_add_label(g, "Write Bandwidth");
107 	gg->trim_bw = graph_add_label(g, "Trim Bandwidth");
108 	graph_set_color(g, gg->read_bw, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
109 	graph_set_color(g, gg->write_bw, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
110 	graph_set_color(g, gg->trim_bw, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
111 	graph_set_base_offset(g, 1);
112 	line_graph_set_data_count_limit(g, 100);
113 	graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
114 	graph_set_graph_all_zeroes(g, 0);
115 	gg->bandwidth_graph = g;
116 }
117 
setup_graphs(struct gfio_graphs * g)118 static void setup_graphs(struct gfio_graphs *g)
119 {
120 	setup_iops_graph(g);
121 	setup_bandwidth_graph(g);
122 }
123 
clear_ge_ui_info(struct gui_entry * ge)124 void clear_ge_ui_info(struct gui_entry *ge)
125 {
126 	gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
127 	gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
128 	gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
129 	gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
130 #if 0
131 	/* should we empty it... */
132 	gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
133 #endif
134 	multitext_update_entry(&ge->eta.iotype, 0, "");
135 	multitext_update_entry(&ge->eta.bs, 0, "");
136 	multitext_update_entry(&ge->eta.ioengine, 0, "");
137 	multitext_update_entry(&ge->eta.iodepth, 0, "");
138 	gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
139 	gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
140 	gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
141 	gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
142 	gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
143 	gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
144 }
145 
set_menu_entry_text(struct gui * ui,const char * path,const char * text)146 static void set_menu_entry_text(struct gui *ui, const char *path,
147 				const char *text)
148 {
149 	GtkWidget *w;
150 
151 	w = gtk_ui_manager_get_widget(ui->uimanager, path);
152 	if (w)
153 		gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
154 	else
155 		fprintf(stderr, "gfio: can't find path %s\n", path);
156 }
157 
158 
set_menu_entry_visible(struct gui * ui,const char * path,int show)159 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
160 {
161 	GtkWidget *w;
162 
163 	w = gtk_ui_manager_get_widget(ui->uimanager, path);
164 	if (w)
165 		gtk_widget_set_sensitive(w, show);
166 	else
167 		fprintf(stderr, "gfio: can't find path %s\n", path);
168 }
169 
set_job_menu_visible(struct gui * ui,int visible)170 static void set_job_menu_visible(struct gui *ui, int visible)
171 {
172 	set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
173 }
174 
set_view_results_visible(struct gui * ui,int visible)175 static void set_view_results_visible(struct gui *ui, int visible)
176 {
177 	set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
178 }
179 
get_button_tooltip(struct button_spec * s,int sensitive)180 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
181 {
182 	if (s->tooltiptext[sensitive])
183 		return s->tooltiptext[sensitive];
184 
185 	return s->tooltiptext[0];
186 }
187 
add_button(GtkWidget * buttonbox,struct button_spec * buttonspec,gpointer data)188 static GtkWidget *add_button(GtkWidget *buttonbox,
189 			     struct button_spec *buttonspec, gpointer data)
190 {
191 	GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
192 	gboolean sens = buttonspec->start_sensitive;
193 
194 	g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
195 	gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
196 
197 	sens = buttonspec->start_sensitive;
198 	gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
199 	gtk_widget_set_sensitive(button, sens);
200 
201 	return button;
202 }
203 
add_buttons(struct gui_entry * ge,struct button_spec * buttonlist,int nbuttons)204 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
205 			int nbuttons)
206 {
207 	int i;
208 
209 	for (i = 0; i < nbuttons; i++)
210 		ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
211 }
212 
213 /*
214  * Update sensitivity of job buttons and job menu items, based on the
215  * state of the client.
216  */
update_button_states(struct gui * ui,struct gui_entry * ge)217 static void update_button_states(struct gui *ui, struct gui_entry *ge)
218 {
219 	unsigned int connect_state, send_state, start_state, edit_state;
220 	const char *connect_str = NULL;
221 
222 	switch (ge->state) {
223 	default:
224 		gfio_report_error(ge, "Bad client state: %u\n", ge->state);
225 		/* fall through to new state */
226 	case GE_STATE_NEW:
227 		connect_state = 1;
228 		edit_state = 1;
229 		connect_str = "Connect";
230 		send_state = 0;
231 		start_state = 0;
232 		break;
233 	case GE_STATE_CONNECTED:
234 		connect_state = 1;
235 		edit_state = 1;
236 		connect_str = "Disconnect";
237 		send_state = 1;
238 		start_state = 0;
239 		break;
240 	case GE_STATE_JOB_SENT:
241 		connect_state = 1;
242 		edit_state = 1;
243 		connect_str = "Disconnect";
244 		send_state = 0;
245 		start_state = 1;
246 		break;
247 	case GE_STATE_JOB_STARTED:
248 		connect_state = 1;
249 		edit_state = 1;
250 		connect_str = "Disconnect";
251 		send_state = 0;
252 		start_state = 1;
253 		break;
254 	case GE_STATE_JOB_RUNNING:
255 		connect_state = 1;
256 		edit_state = 0;
257 		connect_str = "Disconnect";
258 		send_state = 0;
259 		start_state = 0;
260 		break;
261 	case GE_STATE_JOB_DONE:
262 		connect_state = 1;
263 		edit_state = 0;
264 		connect_str = "Connect";
265 		send_state = 0;
266 		start_state = 0;
267 		break;
268 	}
269 
270 	gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
271 	gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
272 	gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
273 	gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
274 	gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
275 
276 	set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
277 	set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
278 
279 	set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
280 	set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
281 	set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
282 
283 	if (ge->client && ge->client->nr_results)
284 		set_view_results_visible(ui, 1);
285 	else
286 		set_view_results_visible(ui, 0);
287 }
288 
gfio_set_state(struct gui_entry * ge,unsigned int state)289 void gfio_set_state(struct gui_entry *ge, unsigned int state)
290 {
291 	ge->state = state;
292 	update_button_states(ge->ui, ge);
293 }
294 
gfio_ui_setup_log(struct gui * ui)295 static void gfio_ui_setup_log(struct gui *ui)
296 {
297 	GtkTreeSelection *selection;
298 	GtkListStore *model;
299 	GtkWidget *tree_view;
300 
301 	model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
302 
303 	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
304 	gtk_widget_set_can_focus(tree_view, FALSE);
305 
306 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
307 	gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
308 	g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
309 		"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
310 
311 	tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
312 	tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
313 	tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
314 	tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
315 
316 	ui->log_model = model;
317 	ui->log_tree = tree_view;
318 }
319 
on_config_drawing_area(GtkWidget * w,GdkEventConfigure * event,gpointer data)320 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
321 				   gpointer data)
322 {
323 	guint width = gtk_widget_get_allocated_width(w);
324 	guint height = gtk_widget_get_allocated_height(w);
325 	struct gfio_graphs *g = data;
326 
327 	graph_set_size(g->iops_graph, width / 2.0, height);
328 	graph_set_position(g->iops_graph, width / 2.0, 0.0);
329 	graph_set_size(g->bandwidth_graph, width / 2.0, height);
330 	graph_set_position(g->bandwidth_graph, 0, 0);
331 	return TRUE;
332 }
333 
draw_graph(struct graph * g,cairo_t * cr)334 static void draw_graph(struct graph *g, cairo_t *cr)
335 {
336 	line_graph_draw(g, cr);
337 	cairo_stroke(cr);
338 }
339 
graph_tooltip(GtkWidget * w,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer data)340 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
341 			      gboolean keyboard_mode, GtkTooltip *tooltip,
342 			      gpointer data)
343 {
344 	struct gfio_graphs *g = data;
345 	const char *text = NULL;
346 
347 	if (graph_contains_xy(g->iops_graph, x, y))
348 		text = graph_find_tooltip(g->iops_graph, x, y);
349 	else if (graph_contains_xy(g->bandwidth_graph, x, y))
350 		text = graph_find_tooltip(g->bandwidth_graph, x, y);
351 
352 	if (text) {
353 		gtk_tooltip_set_text(tooltip, text);
354 		return TRUE;
355 	}
356 
357 	return FALSE;
358 }
359 
on_expose_drawing_area(GtkWidget * w,GdkEvent * event,gpointer p)360 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
361 {
362 	struct gfio_graphs *g = p;
363 	cairo_t *cr;
364 
365 	cr = gdk_cairo_create(gtk_widget_get_window(w));
366 
367 	if (graph_has_tooltips(g->iops_graph) ||
368 	    graph_has_tooltips(g->bandwidth_graph)) {
369 		g_object_set(w, "has-tooltip", TRUE, NULL);
370 		g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
371 	}
372 
373 	cairo_set_source_rgb(cr, 0, 0, 0);
374 	draw_graph(g->iops_graph, cr);
375 	draw_graph(g->bandwidth_graph, cr);
376 	cairo_destroy(cr);
377 
378 	return FALSE;
379 }
380 
381 /*
382  * FIXME: need more handling here
383  */
ge_destroy(struct gui_entry * ge)384 static void ge_destroy(struct gui_entry *ge)
385 {
386 	struct gfio_client *gc = ge->client;
387 
388 	if (gc) {
389 		if (gc->client) {
390 			if (ge->state >= GE_STATE_CONNECTED)
391 				fio_client_terminate(gc->client);
392 
393 			fio_put_client(gc->client);
394 		}
395 		free(gc);
396 	}
397 
398 	g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
399 
400 	free(ge->job_file);
401 	free(ge->host);
402 	free(ge);
403 }
404 
ge_widget_destroy(GtkWidget * w,gpointer data)405 static void ge_widget_destroy(GtkWidget *w, gpointer data)
406 {
407 	struct gui_entry *ge = (struct gui_entry *) data;
408 
409 	ge_destroy(ge);
410 }
411 
gfio_quit(struct gui * ui)412 static void gfio_quit(struct gui *ui)
413 {
414 	gtk_main_quit();
415 }
416 
quit_clicked(GtkWidget * widget,gpointer data)417 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
418 			 gpointer data)
419 {
420 	struct gui *ui = (struct gui *) data;
421 
422 	gfio_quit(ui);
423 }
424 
job_thread(void * arg)425 static void *job_thread(void *arg)
426 {
427 	struct gui *ui = arg;
428 
429 	ui->handler_running = 1;
430 	fio_handle_clients(&gfio_client_ops);
431 	ui->handler_running = 0;
432 	return NULL;
433 }
434 
send_job_file(struct gui_entry * ge)435 static int send_job_file(struct gui_entry *ge)
436 {
437 	struct gfio_client *gc = ge->client;
438 	int ret = 0;
439 
440 	/*
441 	 * Prune old options, we are expecting the return options
442 	 * when the job file is parsed remotely and returned to us.
443 	 */
444 	while (!flist_empty(&gc->o_list)) {
445 		struct gfio_client_options *gco;
446 
447 		gco = flist_first_entry(&gc->o_list, struct gfio_client_options, list);
448 		flist_del(&gco->list);
449 		free(gco);
450 	}
451 
452 	ret = fio_client_send_ini(gc->client, ge->job_file, false);
453 	if (!ret)
454 		return 0;
455 
456 	gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
457 	return 1;
458 }
459 
server_thread(void * arg)460 static void *server_thread(void *arg)
461 {
462 	fio_server_create_sk_key();
463 	is_backend = 1;
464 	gfio_server_running = 1;
465 	fio_start_server(NULL);
466 	gfio_server_running = 0;
467 	fio_server_destroy_sk_key();
468 	return NULL;
469 }
470 
gfio_start_server(struct gui * ui)471 static void gfio_start_server(struct gui *ui)
472 {
473 	if (!gfio_server_running) {
474 		gfio_server_running = 1;
475 		pthread_create(&ui->server_t, NULL, server_thread, NULL);
476 		pthread_detach(ui->server_t);
477 	}
478 }
479 
start_job_clicked(GtkWidget * widget,gpointer data)480 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
481 			      gpointer data)
482 {
483 	struct gui_entry *ge = data;
484 	struct gfio_client *gc = ge->client;
485 
486 	if (gc)
487 		fio_start_client(gc->client);
488 }
489 
490 static void file_open(GtkWidget *w, gpointer data);
491 
492 struct connection_widgets
493 {
494 	GtkWidget *hentry;
495 	GtkWidget *combo;
496 	GtkWidget *button;
497 };
498 
hostname_cb(GtkEntry * entry,gpointer data)499 static void hostname_cb(GtkEntry *entry, gpointer data)
500 {
501 	struct connection_widgets *cw = data;
502 	int uses_net = 0, is_localhost = 0;
503 	const gchar *text;
504 	gchar *ctext;
505 
506 	/*
507 	 * Check whether to display the 'auto start backend' box
508 	 * or not. Show it if we are a localhost and using network,
509 	 * or using a socket.
510 	 */
511 	ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
512 	if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
513 		uses_net = 1;
514 	g_free(ctext);
515 
516 	if (uses_net) {
517 		text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
518 		if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
519 		    !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
520 		    !strcmp(text, "ip6-loopback"))
521 			is_localhost = 1;
522 	}
523 
524 	if (!uses_net || is_localhost) {
525 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
526 		gtk_widget_set_sensitive(cw->button, 1);
527 	} else {
528 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
529 		gtk_widget_set_sensitive(cw->button, 0);
530 	}
531 }
532 
get_connection_details(struct gui_entry * ge)533 static int get_connection_details(struct gui_entry *ge)
534 {
535 	GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
536 	struct connection_widgets cw;
537 	struct gui *ui = ge->ui;
538 	char *typeentry;
539 
540 	if (ge->host)
541 		return 0;
542 
543 	dialog = gtk_dialog_new_with_buttons("Connection details",
544 			GTK_WINDOW(ui->window),
545 			GTK_DIALOG_DESTROY_WITH_PARENT,
546 			GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
547 			GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
548 
549 	frame = gtk_frame_new("Hostname / socket name");
550 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
551 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
552 
553 	box = gtk_vbox_new(FALSE, 6);
554 	gtk_container_add(GTK_CONTAINER(frame), box);
555 
556 	hbox = gtk_hbox_new(TRUE, 10);
557 	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
558 	cw.hentry = gtk_entry_new();
559 	gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
560 	gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
561 
562 	frame = gtk_frame_new("Port");
563 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
564 	box = gtk_vbox_new(FALSE, 10);
565 	gtk_container_add(GTK_CONTAINER(frame), box);
566 
567 	hbox = gtk_hbox_new(TRUE, 4);
568 	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
569 	pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
570 
571 	frame = gtk_frame_new("Type");
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);
575 
576 	hbox = gtk_hbox_new(TRUE, 4);
577 	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
578 
579 	cw.combo = gtk_combo_box_text_new();
580 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
581 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
582 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
583 	gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
584 
585 	gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
586 
587 	frame = gtk_frame_new("Options");
588 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
589 	box = gtk_vbox_new(FALSE, 10);
590 	gtk_container_add(GTK_CONTAINER(frame), box);
591 
592 	hbox = gtk_hbox_new(TRUE, 4);
593 	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
594 
595 	cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
596 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
597 	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.");
598 	gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
599 
600 	/*
601 	 * Connect edit signal, so we can show/not-show the auto start button
602 	 */
603 	g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
604 	g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
605 
606 	gtk_widget_show_all(dialog);
607 
608 	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
609 		gtk_widget_destroy(dialog);
610 		return 1;
611 	}
612 
613 	ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
614 	ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
615 
616 	typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
617 	if (!typeentry || !strncmp(typeentry, "IPv4", 4))
618 		ge->type = Fio_client_ipv4;
619 	else if (!strncmp(typeentry, "IPv6", 4))
620 		ge->type = Fio_client_ipv6;
621 	else
622 		ge->type = Fio_client_socket;
623 	g_free(typeentry);
624 
625 	ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
626 
627 	gtk_widget_destroy(dialog);
628 	return 0;
629 }
630 
gfio_set_client(struct gfio_client * gc,struct fio_client * client)631 static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
632 {
633 	gc->client = fio_get_client(client);
634 	client->client_data = gc;
635 }
636 
gfio_client_added(struct gui_entry * ge,struct fio_client * client)637 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
638 {
639 	struct gfio_client_options *gco;
640 	struct gfio_client *gc;
641 
642 	gc = calloc(1, sizeof(*gc));
643 	INIT_FLIST_HEAD(&gc->o_list);
644 	gc->ge = ge;
645 	ge->client = gc;
646 	gfio_set_client(gc, client);
647 
648 	/*
649 	 * Just add a default set of options, need to consider how best
650 	 * to handle this
651 	 */
652 	gco = calloc(1, sizeof(*gco));
653 	INIT_FLIST_HEAD(&gco->list);
654 	options_default_fill(&gco->o);
655 	flist_add_tail(&gco->list, &gc->o_list);
656 	gc->o_list_nr++;
657 }
658 
gfio_clear_graph_data(struct gfio_graphs * g)659 static void gfio_clear_graph_data(struct gfio_graphs *g)
660 {
661 	graph_clear_values(g->iops_graph);
662 	graph_clear_values(g->bandwidth_graph);
663 }
664 
connect_clicked(GtkWidget * widget,gpointer data)665 static void connect_clicked(GtkWidget *widget, gpointer data)
666 {
667 	struct gui_entry *ge = data;
668 	struct gfio_client *gc = ge->client;
669 
670 	if (ge->state == GE_STATE_NEW) {
671 		int ret;
672 
673 		if (!ge->job_file)
674 			file_open(widget, ge->ui);
675 		if (!ge->job_file)
676 			return;
677 
678 		gc = ge->client;
679 
680 		if (!gc->client) {
681 			struct fio_client *client;
682 
683 			if (get_connection_details(ge)) {
684 				gfio_report_error(ge, "Failed to get connection details\n");
685 				return;
686 			}
687 
688 			client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
689 			if (!client) {
690 				gfio_report_error(ge, "Failed to add client %s\n", ge->host);
691 				free(ge->host);
692 				ge->host = NULL;
693 				return;
694 			}
695 			gfio_set_client(gc, client);
696 		}
697 
698 		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
699 		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
700 		ret = fio_client_connect(gc->client);
701 		if (!ret) {
702 			if (!ge->ui->handler_running)
703 				pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
704 			gfio_set_state(ge, GE_STATE_CONNECTED);
705 			gfio_clear_graph_data(&ge->graphs);
706 		} else {
707 			gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
708 		}
709 	} else {
710 		fio_client_terminate(gc->client);
711 		gfio_set_state(ge, GE_STATE_NEW);
712 		clear_ge_ui_info(ge);
713 	}
714 }
715 
send_clicked(GtkWidget * widget,gpointer data)716 static void send_clicked(GtkWidget *widget, gpointer data)
717 {
718 	struct gui_entry *ge = data;
719 
720 	if (send_job_file(ge))
721 		gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
722 }
723 
724 static GtkWidget *new_client_page(struct gui_entry *ge);
725 
alloc_new_gui_entry(struct gui * ui)726 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
727 {
728 	struct gui_entry *ge;
729 
730 	ge = malloc(sizeof(*ge));
731 	memset(ge, 0, sizeof(*ge));
732 	ge->state = GE_STATE_NEW;
733 	ge->ui = ui;
734 	return ge;
735 }
736 
get_new_ge_with_tab(struct gui * ui,const char * name)737 static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
738 {
739 	struct gui_entry *ge;
740 
741 	ge = alloc_new_gui_entry(ui);
742 
743 	ge->vbox = new_client_page(ge);
744 	g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
745 
746 	ge->page_label = gtk_label_new(name);
747 	ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
748 
749 	g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
750 
751 	gtk_widget_show_all(ui->window);
752 	return ge;
753 }
754 
file_new(GtkWidget * w,gpointer data)755 static void file_new(GtkWidget *w, gpointer data)
756 {
757 	struct gui *ui = (struct gui *) data;
758 	struct gui_entry *ge;
759 
760 	ge = get_new_ge_with_tab(ui, "Untitled");
761 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
762 }
763 
764 /*
765  * Return the 'ge' corresponding to the tab. If the active tab is the
766  * main tab, open a new tab.
767  */
get_ge_from_page(struct gui * ui,gint cur_page,int * created)768 static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
769 					  int *created)
770 {
771 	if (!cur_page) {
772 		if (created)
773 			*created = 1;
774 		return get_new_ge_with_tab(ui, "Untitled");
775 	}
776 
777 	if (created)
778 		*created = 0;
779 
780 	return g_hash_table_lookup(ui->ge_hash, &cur_page);
781 }
782 
get_ge_from_cur_tab(struct gui * ui)783 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
784 {
785 	gint cur_page;
786 
787 	/*
788 	 * Main tab is tab 0, so any current page other than 0 holds
789 	 * a ge entry.
790 	 */
791 	cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
792 	if (cur_page)
793 		return get_ge_from_page(ui, cur_page, NULL);
794 
795 	return NULL;
796 }
797 
file_close(GtkWidget * w,gpointer data)798 static void file_close(GtkWidget *w, gpointer data)
799 {
800 	struct gui *ui = (struct gui *) data;
801 	struct gui_entry *ge;
802 
803 	/*
804 	 * Can't close the main tab
805 	 */
806 	ge = get_ge_from_cur_tab(ui);
807 	if (ge) {
808 		gtk_widget_destroy(ge->vbox);
809 		return;
810 	}
811 
812 	if (g_hash_table_size(ui->ge_hash)) {
813 		gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
814 		return;
815 	}
816 
817 	gfio_quit(ui);
818 }
819 
file_add_recent(struct gui * ui,const gchar * uri)820 static void file_add_recent(struct gui *ui, const gchar *uri)
821 {
822 	GtkRecentData grd;
823 
824 	memset(&grd, 0, sizeof(grd));
825 	grd.display_name = strdup("gfio");
826 	grd.description = strdup("Fio job file");
827 	grd.mime_type = strdup(GFIO_MIME);
828 	grd.app_name = strdup(g_get_application_name());
829 	grd.app_exec = strdup("gfio %f/%u");
830 
831 	gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
832 }
833 
get_filename_from_uri(const gchar * uri)834 static gchar *get_filename_from_uri(const gchar *uri)
835 {
836 	if (strncmp(uri, "file://", 7))
837 		return strdup(uri);
838 
839 	return strdup(uri + 7);
840 }
841 
do_file_open(struct gui_entry * ge,const gchar * uri)842 static int do_file_open(struct gui_entry *ge, const gchar *uri)
843 {
844 	struct fio_client *client;
845 
846 	assert(!ge->job_file);
847 
848 	ge->job_file = get_filename_from_uri(uri);
849 
850 	client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
851 	if (client) {
852 		char *label = strdup(uri);
853 
854 		basename(label);
855 		gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
856 		free(label);
857 
858 		gfio_client_added(ge, client);
859 		file_add_recent(ge->ui, uri);
860 		return 0;
861 	}
862 
863 	gfio_report_error(ge, "Failed to add client %s\n", ge->host);
864 	free(ge->host);
865 	ge->host = NULL;
866 	free(ge->job_file);
867 	ge->job_file = NULL;
868 	return 1;
869 }
870 
do_file_open_with_tab(struct gui * ui,const gchar * uri)871 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
872 {
873 	struct gui_entry *ge;
874 	gint cur_page;
875 	int ret, ge_is_new = 0;
876 
877 	/*
878 	 * Creates new tab if current tab is the main window, or the
879 	 * current tab already has a client.
880 	 */
881 	cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
882 	ge = get_ge_from_page(ui, cur_page, &ge_is_new);
883 	if (ge->client) {
884 		ge = get_new_ge_with_tab(ui, "Untitled");
885 		ge_is_new = 1;
886 	}
887 
888 	gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
889 
890 	if (get_connection_details(ge)) {
891 		if (ge_is_new)
892 			gtk_widget_destroy(ge->vbox);
893 
894 		return 1;
895 	}
896 
897 	ret = do_file_open(ge, uri);
898 
899 	if (!ret) {
900 		if (ge->server_start)
901 			gfio_start_server(ui);
902 	} else {
903 		if (ge_is_new)
904 			gtk_widget_destroy(ge->vbox);
905 	}
906 
907 	return ret;
908 }
909 
recent_open(GtkAction * action,gpointer data)910 static void recent_open(GtkAction *action, gpointer data)
911 {
912 	struct gui *ui = (struct gui *) data;
913 	GtkRecentInfo *info;
914 	const gchar *uri;
915 
916 	info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
917 	uri = gtk_recent_info_get_uri(info);
918 
919 	do_file_open_with_tab(ui, uri);
920 }
921 
file_open(GtkWidget * w,gpointer data)922 static void file_open(GtkWidget *w, gpointer data)
923 {
924 	struct gui *ui = data;
925 	GtkWidget *dialog;
926 	GtkFileFilter *filter;
927 	gchar *filename;
928 
929 	dialog = gtk_file_chooser_dialog_new("Open File",
930 		GTK_WINDOW(ui->window),
931 		GTK_FILE_CHOOSER_ACTION_OPEN,
932 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
933 		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
934 		NULL);
935 	gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
936 
937 	filter = gtk_file_filter_new();
938 	gtk_file_filter_add_pattern(filter, "*.fio");
939 	gtk_file_filter_add_pattern(filter, "*.job");
940 	gtk_file_filter_add_pattern(filter, "*.ini");
941 	gtk_file_filter_add_mime_type(filter, GFIO_MIME);
942 	gtk_file_filter_set_name(filter, "Fio job file");
943 	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
944 
945 	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
946 		gtk_widget_destroy(dialog);
947 		return;
948 	}
949 
950 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
951 
952 	gtk_widget_destroy(dialog);
953 
954 	do_file_open_with_tab(ui, filename);
955 	g_free(filename);
956 }
957 
file_save(GtkWidget * w,gpointer data)958 static void file_save(GtkWidget *w, gpointer data)
959 {
960 	struct gui *ui = data;
961 	GtkWidget *dialog;
962 
963 	dialog = gtk_file_chooser_dialog_new("Save File",
964 		GTK_WINDOW(ui->window),
965 		GTK_FILE_CHOOSER_ACTION_SAVE,
966 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
967 		GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
968 		NULL);
969 
970 	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
971 	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
972 
973 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
974 		char *filename;
975 
976 		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
977 		// save_job_file(filename);
978 		g_free(filename);
979 	}
980 	gtk_widget_destroy(dialog);
981 }
982 
view_log_destroy(GtkWidget * w,gpointer data)983 static void view_log_destroy(GtkWidget *w, gpointer data)
984 {
985 	struct gui *ui = (struct gui *) data;
986 
987 	g_object_ref(G_OBJECT(ui->log_tree));
988 	gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
989 	gtk_widget_destroy(w);
990 	ui->log_view = NULL;
991 }
992 
gfio_view_log(struct gui * ui)993 void gfio_view_log(struct gui *ui)
994 {
995 	GtkWidget *win, *scroll, *vbox, *box;
996 
997 	if (ui->log_view)
998 		return;
999 
1000 	ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1001 	gtk_window_set_title(GTK_WINDOW(win), "Log");
1002 	gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1003 
1004 	scroll = gtk_scrolled_window_new(NULL, NULL);
1005 
1006 	gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1007 
1008 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1009 
1010 	box = gtk_hbox_new(TRUE, 0);
1011 	gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
1012 	g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1013 	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1014 
1015 	vbox = gtk_vbox_new(TRUE, 5);
1016 	gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
1017 
1018 	gtk_container_add(GTK_CONTAINER(win), vbox);
1019 	gtk_widget_show_all(win);
1020 }
1021 
view_log(GtkWidget * w,gpointer data)1022 static void view_log(GtkWidget *w, gpointer data)
1023 {
1024 	struct gui *ui = (struct gui *) data;
1025 
1026 	gfio_view_log(ui);
1027 }
1028 
connect_job_entry(GtkWidget * w,gpointer data)1029 static void connect_job_entry(GtkWidget *w, gpointer data)
1030 {
1031 	struct gui *ui = (struct gui *) data;
1032 	struct gui_entry *ge;
1033 
1034 	ge = get_ge_from_cur_tab(ui);
1035 	if (ge)
1036 		connect_clicked(w, ge);
1037 }
1038 
send_job_entry(GtkWidget * w,gpointer data)1039 static void send_job_entry(GtkWidget *w, gpointer data)
1040 {
1041 	struct gui *ui = (struct gui *) data;
1042 	struct gui_entry *ge;
1043 
1044 	ge = get_ge_from_cur_tab(ui);
1045 	if (ge)
1046 		send_clicked(w, ge);
1047 }
1048 
edit_job_entry(GtkWidget * w,gpointer data)1049 static void edit_job_entry(GtkWidget *w, gpointer data)
1050 {
1051 	struct gui *ui = (struct gui *) data;
1052 	struct gui_entry *ge;
1053 
1054 	ge = get_ge_from_cur_tab(ui);
1055 	if (ge && ge->client)
1056 		gopt_get_options_window(ui->window, ge->client);
1057 }
1058 
start_job_entry(GtkWidget * w,gpointer data)1059 static void start_job_entry(GtkWidget *w, gpointer data)
1060 {
1061 	struct gui *ui = (struct gui *) data;
1062 	struct gui_entry *ge;
1063 
1064 	ge = get_ge_from_cur_tab(ui);
1065 	if (ge)
1066 		start_job_clicked(w, ge);
1067 }
1068 
view_results(GtkWidget * w,gpointer data)1069 static void view_results(GtkWidget *w, gpointer data)
1070 {
1071 	struct gui *ui = (struct gui *) data;
1072 	struct gfio_client *gc;
1073 	struct gui_entry *ge;
1074 
1075 	ge = get_ge_from_cur_tab(ui);
1076 	if (!ge)
1077 		return;
1078 
1079 	if (ge->results_window)
1080 		return;
1081 
1082 	gc = ge->client;
1083 	if (gc && gc->nr_results)
1084 		gfio_display_end_results(gc);
1085 }
1086 
__update_graph_settings(struct gfio_graphs * g)1087 static void __update_graph_settings(struct gfio_graphs *g)
1088 {
1089 	line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1090 	graph_set_font(g->iops_graph, gfio_graph_font);
1091 	line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1092 	graph_set_font(g->bandwidth_graph, gfio_graph_font);
1093 }
1094 
ge_update_settings_fn(gpointer key,gpointer value,gpointer data)1095 static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
1096 {
1097 	struct gui_entry *ge = (struct gui_entry *) value;
1098 	GdkEvent *ev;
1099 
1100 	__update_graph_settings(&ge->graphs);
1101 
1102 	ev = gdk_event_new(GDK_EXPOSE);
1103 	g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1104 	gdk_event_free(ev);
1105 }
1106 
update_graph_limits(void)1107 static void update_graph_limits(void)
1108 {
1109 	struct gui *ui = &main_ui;
1110 	GdkEvent *ev;
1111 
1112 	__update_graph_settings(&ui->graphs);
1113 
1114 	ev = gdk_event_new(GDK_EXPOSE);
1115 	g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1116 	gdk_event_free(ev);
1117 
1118 	g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
1119 }
1120 
preferences(GtkWidget * w,gpointer data)1121 static void preferences(GtkWidget *w, gpointer data)
1122 {
1123 	GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1124 	GtkWidget *hbox, *spin, *entry, *spin_int;
1125 	struct gui *ui = (struct gui *) data;
1126 	int i;
1127 
1128 	dialog = gtk_dialog_new_with_buttons("Preferences",
1129 		GTK_WINDOW(ui->window),
1130 		GTK_DIALOG_DESTROY_WITH_PARENT,
1131 		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1132 		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1133 		NULL);
1134 
1135 	frame = gtk_frame_new("Graphing");
1136 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1137 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1138 	vbox = gtk_vbox_new(FALSE, 6);
1139 	gtk_container_add(GTK_CONTAINER(frame), vbox);
1140 
1141 	hbox = gtk_hbox_new(FALSE, 5);
1142 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1143 	entry = gtk_label_new("Font face to use for graph labels");
1144 	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1145 
1146 	font = gtk_font_button_new_with_font(gfio_graph_font);
1147 	gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1148 
1149 	box = gtk_vbox_new(FALSE, 6);
1150 	gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1151 
1152 	hbox = gtk_hbox_new(FALSE, 5);
1153 	gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1154 	entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1155 	gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1156 
1157 	spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1158 
1159 	box = gtk_vbox_new(FALSE, 6);
1160 	gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1161 
1162 	hbox = gtk_hbox_new(FALSE, 5);
1163 	gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1164 	entry = gtk_label_new("Client ETA request interval (msec)");
1165 	gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1166 
1167 	spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
1168 	frame = gtk_frame_new("Debug logging");
1169 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1170 	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1171 	vbox = gtk_vbox_new(FALSE, 6);
1172 	gtk_container_add(GTK_CONTAINER(frame), vbox);
1173 
1174 	box = gtk_hbox_new(FALSE, 6);
1175 	gtk_container_add(GTK_CONTAINER(vbox), box);
1176 
1177 	buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1178 
1179 	for (i = 0; i < FD_DEBUG_MAX; i++) {
1180 		if (i == 7) {
1181 			box = gtk_hbox_new(FALSE, 6);
1182 			gtk_container_add(GTK_CONTAINER(vbox), box);
1183 		}
1184 
1185 
1186 		buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1187 		gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1188 		gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1189 	}
1190 
1191 	gtk_widget_show_all(dialog);
1192 
1193 	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1194 		gtk_widget_destroy(dialog);
1195 		return;
1196 	}
1197 
1198 	for (i = 0; i < FD_DEBUG_MAX; i++) {
1199 		int set;
1200 
1201 		set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1202 		if (set)
1203 			fio_debug |= (1UL << i);
1204 	}
1205 
1206 	gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1207 	gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1208 	update_graph_limits();
1209 	gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1210 
1211 	gtk_widget_destroy(dialog);
1212 }
1213 
about_dialog(GtkWidget * w,gpointer data)1214 static void about_dialog(GtkWidget *w, gpointer data)
1215 {
1216 	const char *authors[] = {
1217 		"Jens Axboe <axboe@kernel.dk>",
1218 		"Stephen Cameron <stephenmcameron@gmail.com>",
1219 		NULL
1220 	};
1221 	const char *license[] = {
1222 		"Fio is free software; you can redistribute it and/or modify "
1223 		"it under the terms of the GNU General Public License as published by "
1224 		"the Free Software Foundation; either version 2 of the License, or "
1225 		"(at your option) any later version.\n",
1226 		"Fio is distributed in the hope that it will be useful, "
1227 		"but WITHOUT ANY WARRANTY; without even the implied warranty of "
1228 		"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
1229 		"GNU General Public License for more details.\n",
1230 		"You should have received a copy of the GNU General Public License "
1231 		"along with Fio; if not, write to the Free Software Foundation, Inc., "
1232 		"51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA\n"
1233 	};
1234 	char *license_trans;
1235 
1236 	license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1237 				     license[2], "\n", NULL);
1238 
1239 	gtk_show_about_dialog(NULL,
1240 		"program-name", "gfio",
1241 		"comments", "Gtk2 UI for fio",
1242 		"license", license_trans,
1243 		"website", "http://git.kernel.dk/cgit/fio/",
1244 		"authors", authors,
1245 		"version", fio_version_string,
1246 		"copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1247 		"logo-icon-name", "fio",
1248 		/* Must be last: */
1249 		"wrap-license", TRUE,
1250 		NULL);
1251 
1252 	g_free(license_trans);
1253 }
1254 
1255 static GtkActionEntry menu_items[] = {
1256 	{ "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1257 	{ "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1258 	{ "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1259 	{ "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1260 	{ "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1261 	{ "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
1262 	{ "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1263 	{ "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1264 	{ "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1265 	{ "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1266 	{ "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1267 	{ "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
1268 	{ "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1269 	{ "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1270 	{ "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
1271 	{ "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1272 	{ "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1273 };
1274 static gint nmenu_items = ARRAY_SIZE(menu_items);
1275 
1276 static const gchar *ui_string = " \
1277 	<ui> \
1278 		<menubar name=\"MainMenu\"> \
1279 			<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1280 				<menuitem name=\"New\" action=\"NewFile\" /> \
1281 				<menuitem name=\"Open\" action=\"OpenFile\" /> \
1282 				<menuitem name=\"Close\" action=\"CloseFile\" /> \
1283 				<separator name=\"Separator1\"/> \
1284 				<menuitem name=\"Save\" action=\"SaveFile\" /> \
1285 				<separator name=\"Separator2\"/> \
1286 				<menuitem name=\"Preferences\" action=\"Preferences\" /> \
1287 				<separator name=\"Separator3\"/> \
1288 				<placeholder name=\"FileRecentFiles\"/> \
1289 				<separator name=\"Separator4\"/> \
1290 				<menuitem name=\"Quit\" action=\"Quit\" /> \
1291 			</menu> \
1292 			<menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1293 				<menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1294 				<separator name=\"Separator5\"/> \
1295 				<menuitem name=\"Edit job\" action=\"EditJob\" /> \
1296 				<menuitem name=\"Send job\" action=\"SendJob\" /> \
1297 				<separator name=\"Separator6\"/> \
1298 				<menuitem name=\"Start job\" action=\"StartJob\" /> \
1299 			</menu>\
1300 			<menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1301 				<menuitem name=\"Results\" action=\"ViewResults\" /> \
1302 				<separator name=\"Separator7\"/> \
1303 				<menuitem name=\"Log\" action=\"ViewLog\" /> \
1304 			</menu>\
1305 			<menu name=\"Help\" action=\"HelpMenuAction\"> \
1306 				<menuitem name=\"About\" action=\"About\" /> \
1307 			</menu> \
1308 		</menubar> \
1309 	</ui> \
1310 ";
1311 
get_menubar_menu(GtkWidget * window,GtkUIManager * ui_manager,struct gui * ui)1312 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1313 				   struct gui *ui)
1314 {
1315 	GtkActionGroup *action_group;
1316 	GError *error = 0;
1317 
1318 	action_group = gtk_action_group_new("Menu");
1319 	gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1320 
1321 	gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1322 	gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1323 
1324 	gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1325 
1326 	return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1327 }
1328 
gfio_ui_setup(GtkSettings * settings,GtkWidget * menubar,GtkWidget * vbox,GtkUIManager * ui_manager)1329 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1330 		   GtkWidget *vbox, GtkUIManager *ui_manager)
1331 {
1332 	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1333 }
1334 
combo_entry_changed(GtkComboBox * box,gpointer data)1335 static void combo_entry_changed(GtkComboBox *box, gpointer data)
1336 {
1337 	struct gui_entry *ge = (struct gui_entry *) data;
1338 	gint index;
1339 
1340 	index = gtk_combo_box_get_active(box);
1341 
1342 	multitext_set_entry(&ge->eta.iotype, index);
1343 	multitext_set_entry(&ge->eta.bs, index);
1344 	multitext_set_entry(&ge->eta.ioengine, index);
1345 	multitext_set_entry(&ge->eta.iodepth, index);
1346 }
1347 
combo_entry_destroy(GtkWidget * widget,gpointer data)1348 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1349 {
1350 	struct gui_entry *ge = (struct gui_entry *) data;
1351 
1352 	multitext_free(&ge->eta.iotype);
1353 	multitext_free(&ge->eta.bs);
1354 	multitext_free(&ge->eta.ioengine);
1355 	multitext_free(&ge->eta.iodepth);
1356 }
1357 
new_client_page(struct gui_entry * ge)1358 static GtkWidget *new_client_page(struct gui_entry *ge)
1359 {
1360 	GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1361 	GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1362 
1363 	main_vbox = gtk_vbox_new(FALSE, 3);
1364 
1365 	top_align = gtk_alignment_new(0, 0, 1, 0);
1366 	top_vbox = gtk_vbox_new(FALSE, 3);
1367 	gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1368 	gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1369 
1370 	probe = gtk_frame_new("Job");
1371 	gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1372 	probe_frame = gtk_vbox_new(FALSE, 3);
1373 	gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1374 
1375 	probe_box = gtk_hbox_new(FALSE, 3);
1376 	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1377 	ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1378 	ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1379 	ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1380 	ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1381 
1382 	probe_box = gtk_hbox_new(FALSE, 3);
1383 	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1384 
1385 	ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1386 	g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1387 	g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1388 	ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1389 	ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write/Trim)");
1390 	ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1391 	ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1392 	ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1393 	ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1394 
1395 	probe_box = gtk_hbox_new(FALSE, 3);
1396 	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1397 	ge->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1398 	ge->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "Read IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1399 	ge->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1400 	ge->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "Write IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1401 	ge->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1402 	ge->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "Trim IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1403 
1404 	/*
1405 	 * Only add this if we have a commit rate
1406 	 */
1407 #if 0
1408 	probe_box = gtk_hbox_new(FALSE, 3);
1409 	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1410 
1411 	ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1412 	ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1413 
1414 	ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1415 	ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1416 #endif
1417 
1418 	/*
1419 	 * Set up a drawing area and IOPS and bandwidth graphs
1420 	 */
1421 	ge->graphs.drawing_area = gtk_drawing_area_new();
1422 	gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1423 		DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1424 	gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
1425 	g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
1426 				G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1427 	g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1428 				G_CALLBACK(on_config_drawing_area), &ge->graphs);
1429 	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1430 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1431 					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1432 	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1433 					ge->graphs.drawing_area);
1434 	gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1435 
1436 	setup_graphs(&ge->graphs);
1437 
1438 	/*
1439 	 * Set up alignments for widgets at the bottom of ui,
1440 	 * align bottom left, expand horizontally but not vertically
1441 	 */
1442 	bottom_align = gtk_alignment_new(0, 1, 1, 0);
1443 	ge->buttonbox = gtk_hbox_new(FALSE, 0);
1444 	gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1445 	gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1446 
1447 	add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
1448 
1449 	/*
1450 	 * Set up thread status progress bar
1451 	 */
1452 	ge->thread_status_pb = gtk_progress_bar_new();
1453 	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1454 	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1455 	gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1456 
1457 
1458 	return main_vbox;
1459 }
1460 
new_main_page(struct gui * ui)1461 static GtkWidget *new_main_page(struct gui *ui)
1462 {
1463 	GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1464 	GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1465 
1466 	main_vbox = gtk_vbox_new(FALSE, 3);
1467 
1468 	/*
1469 	 * Set up alignments for widgets at the top of ui,
1470 	 * align top left, expand horizontally but not vertically
1471 	 */
1472 	top_align = gtk_alignment_new(0, 0, 1, 0);
1473 	top_vbox = gtk_vbox_new(FALSE, 0);
1474 	gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1475 	gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1476 
1477 	probe = gtk_frame_new("Run statistics");
1478 	gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1479 	probe_frame = gtk_vbox_new(FALSE, 3);
1480 	gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1481 
1482 	probe_box = gtk_hbox_new(FALSE, 3);
1483 	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1484 	ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1485 	ui->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1486 	ui->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1487 	ui->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1488 	ui->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1489 	ui->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1490 	ui->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1491 
1492 	/*
1493 	 * Only add this if we have a commit rate
1494 	 */
1495 #if 0
1496 	probe_box = gtk_hbox_new(FALSE, 3);
1497 	gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1498 
1499 	ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1500 	ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1501 
1502 	ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1503 	ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1504 #endif
1505 
1506 	/*
1507 	 * Set up a drawing area and IOPS and bandwidth graphs
1508 	 */
1509 	ui->graphs.drawing_area = gtk_drawing_area_new();
1510 	gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1511 		DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1512 	gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
1513 	g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
1514 			G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1515 	g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1516 			G_CALLBACK(on_config_drawing_area), &ui->graphs);
1517 	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1518 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1519 					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1520 	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1521 					ui->graphs.drawing_area);
1522 	gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1523 			TRUE, TRUE, 0);
1524 
1525 	setup_graphs(&ui->graphs);
1526 
1527 	/*
1528 	 * Set up alignments for widgets at the bottom of ui,
1529 	 * align bottom left, expand horizontally but not vertically
1530 	 */
1531 	bottom_align = gtk_alignment_new(0, 1, 1, 0);
1532 	ui->buttonbox = gtk_hbox_new(FALSE, 0);
1533 	gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1534 	gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1535 
1536 	/*
1537 	 * Set up thread status progress bar
1538 	 */
1539 	ui->thread_status_pb = gtk_progress_bar_new();
1540 	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1541 	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1542 	gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1543 
1544 	return main_vbox;
1545 }
1546 
notebook_switch_page(GtkNotebook * notebook,GtkWidget * widget,guint page,gpointer data)1547 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1548 				     guint page, gpointer data)
1549 
1550 {
1551 	struct gui *ui = (struct gui *) data;
1552 	struct gui_entry *ge;
1553 
1554 	if (!page) {
1555 		set_job_menu_visible(ui, 0);
1556 		set_view_results_visible(ui, 0);
1557 		return TRUE;
1558 	}
1559 
1560 	set_job_menu_visible(ui, 1);
1561 	ge = get_ge_from_page(ui, page, NULL);
1562 	if (ge)
1563 		update_button_states(ui, ge);
1564 
1565 	return TRUE;
1566 }
1567 
compare_recent_items(GtkRecentInfo * a,GtkRecentInfo * b)1568 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1569 {
1570 	time_t time_a = gtk_recent_info_get_visited(a);
1571 	time_t time_b = gtk_recent_info_get_visited(b);
1572 
1573 	return time_b - time_a;
1574 }
1575 
add_recent_file_items(struct gui * ui)1576 static void add_recent_file_items(struct gui *ui)
1577 {
1578 	const gchar *gfio = g_get_application_name();
1579 	GList *items, *item;
1580 	int i = 0;
1581 
1582 	if (ui->recent_ui_id) {
1583 		gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1584 		gtk_ui_manager_ensure_update(ui->uimanager);
1585 	}
1586 	ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1587 
1588 	if (ui->actiongroup) {
1589 		gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1590 		g_object_unref(ui->actiongroup);
1591 	}
1592 	ui->actiongroup = gtk_action_group_new("RecentFileActions");
1593 
1594 	gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1595 
1596 	items = gtk_recent_manager_get_items(ui->recentmanager);
1597 	items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1598 
1599 	for (item = items; item && item->data; item = g_list_next(item)) {
1600 		GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1601 		gchar *action_name;
1602 		const gchar *label;
1603 		GtkAction *action;
1604 
1605 		if (!gtk_recent_info_has_application(info, gfio))
1606 			continue;
1607 
1608 		/*
1609 		 * We only support local files for now
1610 		 */
1611 		if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1612 			continue;
1613 
1614 		action_name = g_strdup_printf("RecentFile%u", i++);
1615 		label = gtk_recent_info_get_display_name(info);
1616 
1617 		action = g_object_new(GTK_TYPE_ACTION,
1618 					"name", action_name,
1619 					"label", label, NULL);
1620 
1621 		g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1622 					gtk_recent_info_ref(info),
1623 					(GDestroyNotify) gtk_recent_info_unref);
1624 
1625 
1626 		g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1627 
1628 		gtk_action_group_add_action(ui->actiongroup, action);
1629 		g_object_unref(action);
1630 
1631 		gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1632 					"/MainMenu/FileMenu/FileRecentFiles",
1633 					label, action_name,
1634 					GTK_UI_MANAGER_MENUITEM, FALSE);
1635 
1636 		g_free(action_name);
1637 
1638 		if (i == 8)
1639 			break;
1640 	}
1641 
1642 	g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1643 	g_list_free(items);
1644 }
1645 
drag_and_drop_received(GtkWidget * widget,GdkDragContext * ctx,gint x,gint y,GtkSelectionData * seldata,guint info,guint time,gpointer * data)1646 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1647 				   gint x, gint y, GtkSelectionData *seldata,
1648 				   guint info, guint time, gpointer *data)
1649 {
1650 	struct gui *ui = (struct gui *) data;
1651 	gchar **uris;
1652 	GtkWidget *source;
1653 
1654 	source = gtk_drag_get_source_widget(ctx);
1655 	if (source && widget == gtk_widget_get_toplevel(source)) {
1656 		gtk_drag_finish(ctx, FALSE, FALSE, time);
1657 		return;
1658 	}
1659 
1660 	uris = gtk_selection_data_get_uris(seldata);
1661 	if (!uris) {
1662 		gtk_drag_finish(ctx, FALSE, FALSE, time);
1663 		return;
1664 	}
1665 
1666 	if (uris[0])
1667 		do_file_open_with_tab(ui, uris[0]);
1668 
1669 	gtk_drag_finish(ctx, TRUE, FALSE, time);
1670 	g_strfreev(uris);
1671 }
1672 
init_ui(int * argc,char ** argv[],struct gui * ui)1673 static void init_ui(int *argc, char **argv[], struct gui *ui)
1674 {
1675 	GtkSettings *settings;
1676 	GtkWidget *vbox;
1677 
1678 	/* Magical g*thread incantation, you just need this thread stuff.
1679 	 * Without it, the update that happens in gfio_update_thread_status
1680 	 * doesn't really happen in a timely fashion, you need expose events
1681 	 */
1682 #if !GLIB_CHECK_VERSION(2, 31, 0)
1683 	if (!g_thread_supported())
1684 		g_thread_init(NULL);
1685 #endif
1686 
1687 	gdk_threads_init();
1688 
1689 	gtk_init(argc, argv);
1690 	settings = gtk_settings_get_default();
1691 	gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1692 #if !GLIB_CHECK_VERSION(2, 36, 0)
1693 	g_type_init();
1694 #endif
1695 	gdk_color_parse("#fffff4", &gfio_color_lightyellow);
1696 	gdk_color_parse("white", &gfio_color_white);
1697 
1698 	ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1699 	gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1700 	gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1701 
1702 	g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1703 	g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1704 
1705 	ui->vbox = gtk_vbox_new(FALSE, 0);
1706 	gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1707 
1708 	ui->uimanager = gtk_ui_manager_new();
1709 	ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1710 	gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1711 
1712 	ui->recentmanager = gtk_recent_manager_get_default();
1713 	add_recent_file_items(ui);
1714 
1715 	ui->notebook = gtk_notebook_new();
1716 	g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1717 	gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1718 	gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1719 	gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1720 
1721 	vbox = new_main_page(ui);
1722 	gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1723 	gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1724 	g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1725 
1726 	gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1727 
1728 	gfio_ui_setup_log(ui);
1729 
1730 	gtk_widget_show_all(ui->window);
1731 }
1732 
main(int argc,char * argv[],char * envp[])1733 int main(int argc, char *argv[], char *envp[])
1734 {
1735 	if (initialize_fio(envp))
1736 		return 1;
1737 	if (fio_init_options())
1738 		return 1;
1739 
1740 	gopt_init();
1741 
1742 	memset(&main_ui, 0, sizeof(main_ui));
1743 	main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
1744 
1745 	init_ui(&argc, &argv, &main_ui);
1746 
1747 	gdk_threads_enter();
1748 	gtk_main();
1749 	gdk_threads_leave();
1750 
1751 	g_hash_table_destroy(main_ui.ge_hash);
1752 
1753 	gopt_exit();
1754 	return 0;
1755 }
1756