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