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