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