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