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