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