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