gfio: move more code to ghelpers.c
[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"
2fd3bb0e 36#include "graph.h"
8232e285 37
63a130b7 38static int gfio_server_running;
f3e8440f 39static const char *gfio_graph_font;
8577f4fd 40static unsigned int gfio_graph_limit = 100;
814479d5 41static GdkColor white;
63a130b7 42
6b79c80c 43static void view_log(GtkWidget *w, gpointer data);
3e47bd25 44
f3074008
SC
45typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
46
3e47bd25 47static void connect_clicked(GtkWidget *widget, gpointer data);
f3074008 48static void start_job_clicked(GtkWidget *widget, gpointer data);
b9d2f30a 49static void send_clicked(GtkWidget *widget, gpointer data);
f3074008
SC
50
51static struct button_spec {
52 const char *buttontext;
53 clickfunction f;
014f4024
JA
54 const char *tooltiptext[2];
55 const int start_sensitive;
f3074008 56} buttonspeclist[] = {
53e0e85d
JA
57 {
58 .buttontext = "Connect",
59 .f = connect_clicked,
60 .tooltiptext = { "Disconnect from host", "Connect to host" },
61 .start_sensitive = 1,
62 },
63 {
64 .buttontext = "Send",
65 .f = send_clicked,
66 .tooltiptext = { "Send job description to host", NULL },
67 .start_sensitive = 0,
68 },
69 {
70 .buttontext = "Start Job",
71 .f = start_job_clicked,
72 .tooltiptext = { "Start the current job on the server", NULL },
73 .start_sensitive = 0,
74 },
e0681f3e
JA
75};
76
9988ca70
JA
77static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
78static void gfio_update_thread_status_all(char *status_message, double perc);
49c34176 79static void report_error(GError *error);
9988ca70 80
2f99deb0
JA
81static struct graph *setup_iops_graph(void)
82{
83 struct graph *g;
84
85 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
d8fbeefb 86 graph_title(g, "IOPS (IOs/sec)");
2f99deb0 87 graph_x_title(g, "Time (secs)");
2f99deb0
JA
88 graph_add_label(g, "Read IOPS");
89 graph_add_label(g, "Write IOPS");
90 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
91 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
8577f4fd 92 line_graph_set_data_count_limit(g, gfio_graph_limit);
d8fbeefb 93 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
2f99deb0
JA
94 return g;
95}
96
97static struct graph *setup_bandwidth_graph(void)
98{
99 struct graph *g;
100
101 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
d8fbeefb 102 graph_title(g, "Bandwidth (bytes/sec)");
2f99deb0 103 graph_x_title(g, "Time (secs)");
2f99deb0
JA
104 graph_add_label(g, "Read Bandwidth");
105 graph_add_label(g, "Write Bandwidth");
106 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
107 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
d8fbeefb 108 graph_set_base_offset(g, 1);
2f99deb0 109 line_graph_set_data_count_limit(g, 100);
d8fbeefb 110 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
2f99deb0
JA
111 return g;
112}
113
114static void setup_graphs(struct gfio_graphs *g)
115{
116 g->iops_graph = setup_iops_graph();
117 g->bandwidth_graph = setup_bandwidth_graph();
118}
119
120static void clear_ge_ui_info(struct gui_entry *ge)
121{
122 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
123 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
124 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
125 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
3863d1ad
JA
126#if 0
127 /* should we empty it... */
2f99deb0 128 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
3863d1ad 129#endif
c80b74b0 130 multitext_update_entry(&ge->eta.iotype, 0, "");
99d633af 131 multitext_update_entry(&ge->eta.bs, 0, "");
c80b74b0
JA
132 multitext_update_entry(&ge->eta.ioengine, 0, "");
133 multitext_update_entry(&ge->eta.iodepth, 0, "");
2f99deb0
JA
134 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
137 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
138 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
139 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
8663ea65
JA
140}
141
16ce5adf
JA
142static void show_info_dialog(struct gui *ui, const char *title,
143 const char *message)
144{
145 GtkWidget *dialog, *content, *label;
146
147 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
148 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
149 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
150
151 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
152 label = gtk_label_new(message);
153 gtk_container_add(GTK_CONTAINER(content), label);
154 gtk_widget_show_all(dialog);
155 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
156 gtk_dialog_run(GTK_DIALOG(dialog));
157 gtk_widget_destroy(dialog);
158}
159
781ccba6
JA
160static void set_menu_entry_text(struct gui *ui, const char *path,
161 const char *text)
162{
163 GtkWidget *w;
164
165 w = gtk_ui_manager_get_widget(ui->uimanager, path);
166 if (w)
167 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
168 else
169 fprintf(stderr, "gfio: can't find path %s\n", path);
170}
171
172
173static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
174{
175 GtkWidget *w;
176
177 w = gtk_ui_manager_get_widget(ui->uimanager, path);
178 if (w)
179 gtk_widget_set_sensitive(w, show);
180 else
181 fprintf(stderr, "gfio: can't find path %s\n", path);
182}
183
184static void set_job_menu_visible(struct gui *ui, int visible)
185{
186 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
187}
188
189static void set_view_results_visible(struct gui *ui, int visible)
190{
191 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
192}
193
014f4024
JA
194static const char *get_button_tooltip(struct button_spec *s, int sensitive)
195{
196 if (s->tooltiptext[sensitive])
197 return s->tooltiptext[sensitive];
198
199 return s->tooltiptext[0];
200}
201
202static GtkWidget *add_button(GtkWidget *buttonbox,
203 struct button_spec *buttonspec, gpointer data)
204{
205 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
206 gboolean sens = buttonspec->start_sensitive;
207
208 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
209 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
210
211 sens = buttonspec->start_sensitive;
212 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
213 gtk_widget_set_sensitive(button, sens);
214
215 return button;
216}
217
218static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
219 int nbuttons)
220{
221 int i;
222
223 for (i = 0; i < nbuttons; i++)
224 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
225}
226
85dd01e7
JA
227/*
228 * Update sensitivity of job buttons and job menu items, based on the
229 * state of the client.
230 */
231static void update_button_states(struct gui *ui, struct gui_entry *ge)
232{
233 unsigned int connect_state, send_state, start_state, edit_state;
234 const char *connect_str = NULL;
85dd01e7
JA
235
236 switch (ge->state) {
237 default: {
238 char tmp[80];
239
240 sprintf(tmp, "Bad client state: %u\n", ge->state);
241 show_info_dialog(ui, "Error", tmp);
242 /* fall through to new state */
243 }
244
245 case GE_STATE_NEW:
246 connect_state = 1;
9af4a244 247 edit_state = 1;
85dd01e7
JA
248 connect_str = "Connect";
249 send_state = 0;
250 start_state = 0;
251 break;
252 case GE_STATE_CONNECTED:
253 connect_state = 1;
9af4a244 254 edit_state = 1;
85dd01e7
JA
255 connect_str = "Disconnect";
256 send_state = 1;
257 start_state = 0;
258 break;
259 case GE_STATE_JOB_SENT:
260 connect_state = 1;
9af4a244 261 edit_state = 1;
85dd01e7
JA
262 connect_str = "Disconnect";
263 send_state = 0;
264 start_state = 1;
265 break;
266 case GE_STATE_JOB_STARTED:
267 connect_state = 1;
268 edit_state = 1;
269 connect_str = "Disconnect";
270 send_state = 0;
271 start_state = 1;
272 break;
273 case GE_STATE_JOB_RUNNING:
274 connect_state = 1;
275 edit_state = 0;
276 connect_str = "Disconnect";
277 send_state = 0;
278 start_state = 0;
279 break;
280 case GE_STATE_JOB_DONE:
281 connect_state = 1;
282 edit_state = 0;
283 connect_str = "Connect";
284 send_state = 0;
285 start_state = 0;
286 break;
287 }
288
53e0e85d
JA
289 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
290 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
291 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
292 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
293 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
85dd01e7 294
781ccba6
JA
295 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
296 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
85dd01e7 297
781ccba6
JA
298 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
299 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
300 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
85dd01e7 301
781ccba6
JA
302 if (ge->client && ge->client->nr_results)
303 set_view_results_visible(ui, 1);
304 else
305 set_view_results_visible(ui, 0);
85dd01e7
JA
306}
307
308static void gfio_set_state(struct gui_entry *ge, unsigned int state)
309{
310 ge->state = state;
311 update_button_states(ge->ui, ge);
312}
313
9b260bdf
JA
314static void gfio_ui_setup_log(struct gui *ui)
315{
316 GtkTreeSelection *selection;
317 GtkListStore *model;
318 GtkWidget *tree_view;
319
320 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
321
322 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
323 gtk_widget_set_can_focus(tree_view, FALSE);
324
325 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
326 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
661f741a
JA
327 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
328 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
9b260bdf
JA
329
330 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
331 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
332 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
f095d563 333 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
9b260bdf
JA
334
335 ui->log_model = model;
336 ui->log_tree = tree_view;
337}
338
c57f254c
SC
339static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
340 fio_fp64_t *plist,
341 unsigned int len,
342 double xdim, double ydim)
343{
344 struct graph *g;
345 int i;
346
347 g = graph_new(xdim, ydim, gfio_graph_font);
348 graph_title(g, title);
349 graph_x_title(g, "Percentile");
350
351 for (i = 0; i < len; i++) {
352 char fbuf[8];
353
354 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
355 graph_add_label(g, fbuf);
356 graph_add_data(g, fbuf, (double) ovals[i]);
357 }
358
359 return g;
360}
361
a269790c
JA
362static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
363 fio_fp64_t *plist,
364 unsigned int len,
365 const char *base,
366 unsigned int scale)
367{
368 GType types[FIO_IO_U_LIST_MAX_LEN];
369 GtkWidget *tree_view;
370 GtkTreeSelection *selection;
371 GtkListStore *model;
372 GtkTreeIter iter;
373 int i;
374
375 for (i = 0; i < len; i++)
376 types[i] = G_TYPE_INT;
377
378 model = gtk_list_store_newv(len, types);
379
380 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
381 gtk_widget_set_can_focus(tree_view, FALSE);
382
661f741a
JA
383 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
384 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
385
a269790c
JA
386 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
387 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
388
389 for (i = 0; i < len; i++) {
390 char fbuf[8];
391
392 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
393 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
394 }
395
396 gtk_list_store_append(model, &iter);
397
e0681f3e
JA
398 for (i = 0; i < len; i++) {
399 if (scale)
400 ovals[i] = (ovals[i] + 999) / 1000;
a269790c 401 gtk_list_store_set(model, &iter, i, ovals[i], -1);
e0681f3e 402 }
a269790c
JA
403
404 return tree_view;
405}
406
09d574e3 407static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
c57f254c
SC
408{
409 struct graph *g = p;
410 cairo_t *cr;
411
412 cr = gdk_cairo_create(w->window);
413#if 0
414 if (graph_has_tooltips(g)) {
415 g_object_set(w, "has-tooltip", TRUE, NULL);
416 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
417 }
418#endif
419 cairo_set_source_rgb(cr, 0, 0, 0);
420 bar_graph_draw(g, cr);
421 cairo_destroy(cr);
422
423 return FALSE;
424}
425
09d574e3
JA
426static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
427 gpointer data)
c57f254c
SC
428{
429 struct graph *g = data;
430
431 graph_set_size(g, w->allocation.width, w->allocation.height);
432 graph_set_size(g, w->allocation.width, w->allocation.height);
433 graph_set_position(g, 0, 0);
434 return TRUE;
435}
436
437static void gfio_show_clat_percentiles(struct gfio_client *gc,
438 GtkWidget *vbox, struct thread_stat *ts,
a269790c
JA
439 int ddir)
440{
441 unsigned int *io_u_plat = ts->io_u_plat[ddir];
442 unsigned long nr = ts->clat_stat[ddir].samples;
443 fio_fp64_t *plist = ts->percentile_list;
444 unsigned int *ovals, len, minv, maxv, scale_down;
445 const char *base;
c57f254c 446 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
09d574e3 447 struct gui_entry *ge = gc->ge;
a269790c
JA
448 char tmp[64];
449
450 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
451 if (!len)
452 goto out;
453
454 /*
455 * We default to usecs, but if the value range is such that we
456 * should scale down to msecs, do that.
457 */
458 if (minv > 2000 && maxv > 99999) {
459 scale_down = 1;
460 base = "msec";
461 } else {
462 scale_down = 0;
463 base = "usec";
464 }
465
c57f254c 466 sprintf(tmp, "Completion percentiles (%s)", base);
a269790c 467 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
09d574e3 468 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
a269790c 469
a269790c
JA
470 frame = gtk_frame_new(tmp);
471 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
472
c57f254c
SC
473 completion_vbox = gtk_vbox_new(FALSE, 3);
474 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
a269790c 475 hbox = gtk_hbox_new(FALSE, 3);
c57f254c
SC
476 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
477 drawing_area = gtk_drawing_area_new();
478 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
479 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
480 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
09d574e3
JA
481 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
482 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
a269790c
JA
483
484 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
485out:
486 if (ovals)
487 free(ovals);
488}
489
3650a3ca
JA
490static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
491 unsigned long max, double mean, double dev)
492{
493 const char *base = "(usec)";
1c1e4a5b 494 GtkWidget *hbox, *label, *frame;
3650a3ca
JA
495 char *minp, *maxp;
496 char tmp[64];
497
498 if (!usec_to_msec(&min, &max, &mean, &dev))
499 base = "(msec)";
500
501 minp = num2str(min, 6, 1, 0);
502 maxp = num2str(max, 6, 1, 0);
503
3650a3ca
JA
504 sprintf(tmp, "%s %s", name, base);
505 frame = gtk_frame_new(tmp);
506 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
507
3650a3ca 508 hbox = gtk_hbox_new(FALSE, 3);
1c1e4a5b 509 gtk_container_add(GTK_CONTAINER(frame), hbox);
3650a3ca
JA
510
511 label = new_info_label_in_frame(hbox, "Minimum");
512 gtk_label_set_text(GTK_LABEL(label), minp);
513 label = new_info_label_in_frame(hbox, "Maximum");
514 gtk_label_set_text(GTK_LABEL(label), maxp);
515 label = new_info_label_in_frame(hbox, "Average");
516 sprintf(tmp, "%5.02f", mean);
517 gtk_label_set_text(GTK_LABEL(label), tmp);
518 label = new_info_label_in_frame(hbox, "Standard deviation");
519 sprintf(tmp, "%5.02f", dev);
520 gtk_label_set_text(GTK_LABEL(label), tmp);
521
522 free(minp);
523 free(maxp);
524
525}
526
ca850992
JA
527#define GFIO_CLAT 1
528#define GFIO_SLAT 2
529#define GFIO_LAT 4
530
c57f254c
SC
531static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
532 struct group_run_stats *rs,
3650a3ca
JA
533 struct thread_stat *ts, int ddir)
534{
535 const char *ddir_label[2] = { "Read", "Write" };
0b761306 536 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
e0681f3e 537 unsigned long min[3], max[3], runt;
3650a3ca 538 unsigned long long bw, iops;
ca850992 539 unsigned int flags = 0;
e0681f3e 540 double mean[3], dev[3];
3650a3ca
JA
541 char *io_p, *bw_p, *iops_p;
542 int i2p;
543
544 if (!ts->runtime[ddir])
545 return;
546
547 i2p = is_power_of_2(rs->kb_base);
548 runt = ts->runtime[ddir];
549
550 bw = (1000 * ts->io_bytes[ddir]) / runt;
551 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
552 bw_p = num2str(bw, 6, 1, i2p);
553
554 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
555 iops_p = num2str(iops, 6, 1, 0);
556
557 box = gtk_hbox_new(FALSE, 3);
558 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
559
560 frame = gtk_frame_new(ddir_label[ddir]);
561 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
562
0b761306
JA
563 main_vbox = gtk_vbox_new(FALSE, 3);
564 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
3650a3ca
JA
565
566 box = gtk_hbox_new(FALSE, 3);
0b761306 567 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
3650a3ca
JA
568
569 label = new_info_label_in_frame(box, "IO");
570 gtk_label_set_text(GTK_LABEL(label), io_p);
571 label = new_info_label_in_frame(box, "Bandwidth");
572 gtk_label_set_text(GTK_LABEL(label), bw_p);
573 label = new_info_label_in_frame(box, "IOPS");
574 gtk_label_set_text(GTK_LABEL(label), iops_p);
575 label = new_info_label_in_frame(box, "Runtime (msec)");
576 label_set_int_value(label, ts->runtime[ddir]);
577
e0681f3e 578 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
ca850992
JA
579 double p_of_agg = 100.0;
580 const char *bw_str = "KB";
581 char tmp[32];
582
583 if (rs->agg[ddir]) {
e0681f3e 584 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
ca850992
JA
585 if (p_of_agg > 100.0)
586 p_of_agg = 100.0;
587 }
588
e0681f3e
JA
589 if (mean[0] > 999999.9) {
590 min[0] /= 1000.0;
591 max[0] /= 1000.0;
592 mean[0] /= 1000.0;
593 dev[0] /= 1000.0;
ca850992
JA
594 bw_str = "MB";
595 }
596
0b761306
JA
597 sprintf(tmp, "Bandwidth (%s)", bw_str);
598 frame = gtk_frame_new(tmp);
599 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
ca850992 600
0b761306
JA
601 box = gtk_hbox_new(FALSE, 3);
602 gtk_container_add(GTK_CONTAINER(frame), box);
ca850992 603
0b761306 604 label = new_info_label_in_frame(box, "Minimum");
e0681f3e 605 label_set_int_value(label, min[0]);
0b761306 606 label = new_info_label_in_frame(box, "Maximum");
e0681f3e 607 label_set_int_value(label, max[0]);
0b761306 608 label = new_info_label_in_frame(box, "Percentage of jobs");
ca850992
JA
609 sprintf(tmp, "%3.2f%%", p_of_agg);
610 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 611 label = new_info_label_in_frame(box, "Average");
e0681f3e 612 sprintf(tmp, "%5.02f", mean[0]);
ca850992 613 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 614 label = new_info_label_in_frame(box, "Standard deviation");
e0681f3e 615 sprintf(tmp, "%5.02f", dev[0]);
ca850992
JA
616 gtk_label_set_text(GTK_LABEL(label), tmp);
617 }
618
e0681f3e 619 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
2b089892 620 flags |= GFIO_SLAT;
e0681f3e 621 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
2b089892 622 flags |= GFIO_CLAT;
e0681f3e 623 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
2b089892
JA
624 flags |= GFIO_LAT;
625
626 if (flags) {
627 frame = gtk_frame_new("Latency");
628 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
629
630 vbox = gtk_vbox_new(FALSE, 3);
631 gtk_container_add(GTK_CONTAINER(frame), vbox);
632
633 if (flags & GFIO_SLAT)
e0681f3e 634 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
2b089892 635 if (flags & GFIO_CLAT)
e0681f3e 636 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
2b089892 637 if (flags & GFIO_LAT)
e0681f3e 638 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
2b089892
JA
639 }
640
641 if (ts->clat_percentiles)
c57f254c 642 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
2b089892 643
3650a3ca
JA
644 free(io_p);
645 free(bw_p);
646 free(iops_p);
647}
648
09d574e3
JA
649static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
650 const char **labels,
651 unsigned int len,
652 double xdim, double ydim)
653{
654 struct graph *g;
655 int i;
656
657 g = graph_new(xdim, ydim, gfio_graph_font);
658 graph_title(g, title);
659 graph_x_title(g, "Buckets");
660
661 for (i = 0; i < len; i++) {
662 graph_add_label(g, labels[i]);
663 graph_add_data(g, labels[i], lat[i]);
664 }
665
666 return g;
667}
668
669static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
670 int num)
e5bd1347
JA
671{
672 GtkWidget *tree_view;
673 GtkTreeSelection *selection;
674 GtkListStore *model;
675 GtkTreeIter iter;
676 GType *types;
09d574e3 677 int i;
e5bd1347
JA
678
679 types = malloc(num * sizeof(GType));
680
681 for (i = 0; i < num; i++)
682 types[i] = G_TYPE_STRING;
683
684 model = gtk_list_store_newv(num, types);
685 free(types);
686 types = NULL;
687
688 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
689 gtk_widget_set_can_focus(tree_view, FALSE);
690
661f741a
JA
691 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
692 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
693
e5bd1347
JA
694 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
695 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
696
697 for (i = 0; i < num; i++)
698 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
699
700 gtk_list_store_append(model, &iter);
701
702 for (i = 0; i < num; i++) {
703 char fbuf[32];
704
705 if (lat[i] <= 0.0)
706 sprintf(fbuf, "0.00");
707 else
708 sprintf(fbuf, "%3.2f%%", lat[i]);
709
710 gtk_list_store_set(model, &iter, i, fbuf, -1);
711 }
712
713 return tree_view;
714}
715
09d574e3
JA
716static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
717 struct thread_stat *ts)
e5bd1347 718{
09d574e3
JA
719 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
720 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
721 "250u", "500u", "750u", "1m", "2m",
722 "4m", "10m", "20m", "50m", "100m",
723 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
724 int start, end, i;
725 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
726 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
727 struct gui_entry *ge = gc->ge;
728
729 stat_calc_lat_u(ts, io_u_lat);
730 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
e5bd1347 731
09d574e3
JA
732 /*
733 * Found out which first bucket has entries, and which last bucket
734 */
735 start = end = -1U;
736 for (i = 0; i < total; i++) {
737 if (io_u_lat[i] == 0.00)
738 continue;
739
740 if (start == -1U)
741 start = i;
742 end = i;
e5bd1347
JA
743 }
744
09d574e3
JA
745 /*
746 * No entries...
747 */
748 if (start == -1U)
749 return;
750
751 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
752 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
e5bd1347 753
09d574e3
JA
754 frame = gtk_frame_new("Latency buckets");
755 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
756
757 completion_vbox = gtk_vbox_new(FALSE, 3);
758 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
759 hbox = gtk_hbox_new(FALSE, 3);
760 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
761
762 drawing_area = gtk_drawing_area_new();
763 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
764 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
765 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
766 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
767 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
768
769 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
e5bd1347
JA
770}
771
2e33101f
JA
772static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
773{
774 GtkWidget *box, *frame, *entry;
775 double usr_cpu, sys_cpu;
776 unsigned long runtime;
777 char tmp[32];
778
779 runtime = ts->total_run_time;
780 if (runtime) {
781 double runt = (double) runtime;
782
783 usr_cpu = (double) ts->usr_time * 100 / runt;
784 sys_cpu = (double) ts->sys_time * 100 / runt;
785 } else {
786 usr_cpu = 0;
787 sys_cpu = 0;
788 }
789
790 frame = gtk_frame_new("OS resources");
791 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
792
793 box = gtk_hbox_new(FALSE, 3);
794 gtk_container_add(GTK_CONTAINER(frame), box);
795
796 entry = new_info_entry_in_frame(box, "User CPU");
797 sprintf(tmp, "%3.2f%%", usr_cpu);
798 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
799 entry = new_info_entry_in_frame(box, "System CPU");
800 sprintf(tmp, "%3.2f%%", sys_cpu);
801 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
802 entry = new_info_entry_in_frame(box, "Context switches");
803 entry_set_int_value(entry, ts->ctx);
804 entry = new_info_entry_in_frame(box, "Major faults");
805 entry_set_int_value(entry, ts->majf);
806 entry = new_info_entry_in_frame(box, "Minor faults");
807 entry_set_int_value(entry, ts->minf);
808}
19998dbc
JA
809static void gfio_add_sc_depths_tree(GtkListStore *model,
810 struct thread_stat *ts, unsigned int len,
811 int submit)
2e33101f
JA
812{
813 double io_u_dist[FIO_IO_U_MAP_NR];
2e33101f 814 GtkTreeIter iter;
19998dbc
JA
815 /* Bits 0, and 3-8 */
816 const int add_mask = 0x1f9;
817 int i, j;
2e33101f 818
19998dbc
JA
819 if (submit)
820 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
821 else
822 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
2e33101f 823
19998dbc 824 gtk_list_store_append(model, &iter);
2e33101f 825
19998dbc 826 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
2e33101f 827
19998dbc
JA
828 for (i = 1, j = 0; i < len; i++) {
829 char fbuf[32];
2e33101f 830
19998dbc
JA
831 if (!(add_mask & (1UL << (i - 1))))
832 sprintf(fbuf, "0.0%%");
833 else {
834 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
835 j++;
836 }
2e33101f 837
19998dbc
JA
838 gtk_list_store_set(model, &iter, i, fbuf, -1);
839 }
2e33101f 840
19998dbc 841}
2e33101f 842
19998dbc
JA
843static void gfio_add_total_depths_tree(GtkListStore *model,
844 struct thread_stat *ts, unsigned int len)
845{
846 double io_u_dist[FIO_IO_U_MAP_NR];
847 GtkTreeIter iter;
848 /* Bits 1-6, and 8 */
849 const int add_mask = 0x17e;
850 int i, j;
851
852 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
2e33101f
JA
853
854 gtk_list_store_append(model, &iter);
855
19998dbc
JA
856 gtk_list_store_set(model, &iter, 0, "Total", -1);
857
858 for (i = 1, j = 0; i < len; i++) {
2e33101f
JA
859 char fbuf[32];
860
19998dbc
JA
861 if (!(add_mask & (1UL << (i - 1))))
862 sprintf(fbuf, "0.0%%");
863 else {
864 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
865 j++;
2e33101f
JA
866 }
867
2e33101f
JA
868 gtk_list_store_set(model, &iter, i, fbuf, -1);
869 }
870
19998dbc 871}
2e33101f 872
19998dbc
JA
873static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
874{
875 GtkWidget *frame, *box, *tree_view;
876 GtkTreeSelection *selection;
877 GtkListStore *model;
878 GType types[FIO_IO_U_MAP_NR + 1];
879 int i;
880#define NR_LABELS 10
881 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
2e33101f 882
19998dbc
JA
883 frame = gtk_frame_new("IO depths");
884 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2e33101f 885
19998dbc
JA
886 box = gtk_hbox_new(FALSE, 3);
887 gtk_container_add(GTK_CONTAINER(frame), box);
2e33101f 888
19998dbc
JA
889 for (i = 0; i < NR_LABELS; i++)
890 types[i] = G_TYPE_STRING;
2e33101f 891
19998dbc 892 model = gtk_list_store_newv(NR_LABELS, types);
2e33101f 893
19998dbc
JA
894 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
895 gtk_widget_set_can_focus(tree_view, FALSE);
2e33101f 896
661f741a
JA
897 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
898 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
899
19998dbc
JA
900 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
901 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
902
903 for (i = 0; i < NR_LABELS; i++)
904 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
905
906 gfio_add_total_depths_tree(model, ts, NR_LABELS);
907 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
908 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
2e33101f
JA
909
910 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
911}
912
f9d40b48
JA
913static gboolean results_window_delete(GtkWidget *w, gpointer data)
914{
2f99deb0 915 struct gui_entry *ge = (struct gui_entry *) data;
f9d40b48
JA
916
917 gtk_widget_destroy(w);
2f99deb0
JA
918 ge->results_window = NULL;
919 ge->results_notebook = NULL;
f9d40b48
JA
920 return TRUE;
921}
922
17b9721a
JA
923static void results_close(GtkWidget *w, gpointer *data)
924{
925 struct gui_entry *ge = (struct gui_entry *) data;
926
927 gtk_widget_destroy(ge->results_window);
928}
929
930static GtkActionEntry results_menu_items[] = {
931 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
932 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
933 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
934};
935static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
936
937static const gchar *results_ui_string = " \
938 <ui> \
939 <menubar name=\"MainMenu\"> \
940 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
941 <menuitem name=\"Close\" action=\"CloseFile\" /> \
942 </menu> \
943 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
944 </menu>\
945 </menubar> \
946 </ui> \
947";
948
949static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
950{
951 GtkActionGroup *action_group;
952 GtkWidget *widget;
953 GError *error = 0;
954
955 ge->results_uimanager = gtk_ui_manager_new();
956
957 action_group = gtk_action_group_new("ResultsMenu");
958 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
959
960 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
961 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
962
963 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
964
965 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
966 return widget;
967}
968
2f99deb0 969static GtkWidget *get_results_window(struct gui_entry *ge)
f9d40b48 970{
17b9721a 971 GtkWidget *win, *notebook, *vbox;
f9d40b48 972
2f99deb0
JA
973 if (ge->results_window)
974 return ge->results_notebook;
f9d40b48
JA
975
976 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
977 gtk_window_set_title(GTK_WINDOW(win), "Results");
b01329d0 978 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
2f99deb0
JA
979 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
980 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
f9d40b48 981
17b9721a
JA
982 vbox = gtk_vbox_new(FALSE, 0);
983 gtk_container_add(GTK_CONTAINER(win), vbox);
984
985 ge->results_menu = get_results_menubar(win, ge);
986 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
987
f9d40b48 988 notebook = gtk_notebook_new();
0aa928c4
JA
989 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
990 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
17b9721a 991 gtk_container_add(GTK_CONTAINER(vbox), notebook);
f9d40b48 992
2f99deb0
JA
993 ge->results_window = win;
994 ge->results_notebook = notebook;
995 return ge->results_notebook;
f9d40b48
JA
996}
997
7da23b48
JA
998static void disk_util_destroy(GtkWidget *w, gpointer data)
999{
1000 struct gui_entry *ge = (struct gui_entry *) data;
1001
1002 ge->disk_util_vbox = NULL;
1003 gtk_widget_destroy(w);
1004}
1005
f0602d78
JA
1006static GtkWidget *get_scrolled_window(gint border_width)
1007{
1008 GtkWidget *scroll;
1009
1010 scroll = gtk_scrolled_window_new(NULL, NULL);
1011 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1012 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1013
1014 return scroll;
1015}
1016
1017static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1018{
1019 GtkWidget *vbox, *box, *scroll, *res_notebook;
1020
1021 if (ge->disk_util_vbox)
1022 return ge->disk_util_vbox;
1023
1024 scroll = get_scrolled_window(5);
1025 vbox = gtk_vbox_new(FALSE, 3);
1026 box = gtk_hbox_new(FALSE, 0);
1027 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1028
1029 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1030 res_notebook = get_results_window(ge);
1031
1032 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1033 ge->disk_util_vbox = box;
1034 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1035
1036 return ge->disk_util_vbox;
1037}
1038
7da23b48
JA
1039static int __gfio_disk_util_show(GtkWidget *res_notebook,
1040 struct gfio_client *gc, struct cmd_du_pdu *p)
1041{
f0602d78 1042 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
7da23b48
JA
1043 struct gui_entry *ge = gc->ge;
1044 double util;
1045 char tmp[16];
1046
f0602d78 1047 util_vbox = gfio_disk_util_get_vbox(ge);
7da23b48
JA
1048
1049 vbox = gtk_vbox_new(FALSE, 3);
f0602d78 1050 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
7da23b48
JA
1051
1052 frame = gtk_frame_new((char *) p->dus.name);
1053 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1054
1055 box = gtk_vbox_new(FALSE, 3);
1056 gtk_container_add(GTK_CONTAINER(frame), box);
1057
1058 frame = gtk_frame_new("Read");
1059 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1060 vbox = gtk_hbox_new(TRUE, 3);
1061 gtk_container_add(GTK_CONTAINER(frame), vbox);
1062 entry = new_info_entry_in_frame(vbox, "IOs");
1063 entry_set_int_value(entry, p->dus.ios[0]);
1064 entry = new_info_entry_in_frame(vbox, "Merges");
1065 entry_set_int_value(entry, p->dus.merges[0]);
1066 entry = new_info_entry_in_frame(vbox, "Sectors");
1067 entry_set_int_value(entry, p->dus.sectors[0]);
1068 entry = new_info_entry_in_frame(vbox, "Ticks");
1069 entry_set_int_value(entry, p->dus.ticks[0]);
1070
1071 frame = gtk_frame_new("Write");
1072 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1073 vbox = gtk_hbox_new(TRUE, 3);
1074 gtk_container_add(GTK_CONTAINER(frame), vbox);
1075 entry = new_info_entry_in_frame(vbox, "IOs");
1076 entry_set_int_value(entry, p->dus.ios[1]);
1077 entry = new_info_entry_in_frame(vbox, "Merges");
1078 entry_set_int_value(entry, p->dus.merges[1]);
1079 entry = new_info_entry_in_frame(vbox, "Sectors");
1080 entry_set_int_value(entry, p->dus.sectors[1]);
1081 entry = new_info_entry_in_frame(vbox, "Ticks");
1082 entry_set_int_value(entry, p->dus.ticks[1]);
1083
1084 frame = gtk_frame_new("Shared");
1085 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1086 vbox = gtk_hbox_new(TRUE, 3);
1087 gtk_container_add(GTK_CONTAINER(frame), vbox);
1088 entry = new_info_entry_in_frame(vbox, "IO ticks");
1089 entry_set_int_value(entry, p->dus.io_ticks);
1090 entry = new_info_entry_in_frame(vbox, "Time in queue");
1091 entry_set_int_value(entry, p->dus.time_in_queue);
1092
1093 util = 0.0;
1094 if (p->dus.msec)
1095 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1096 if (util > 100.0)
1097 util = 100.0;
1098
1099 sprintf(tmp, "%3.2f%%", util);
1100 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1101 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1102
1103 gtk_widget_show_all(ge->results_window);
1104 return 0;
1105}
1106
1107static int gfio_disk_util_show(struct gfio_client *gc)
1108{
1109 struct gui_entry *ge = gc->ge;
1110 GtkWidget *res_notebook;
1111 int i;
1112
1113 if (!gc->nr_du)
1114 return 1;
1115
1116 res_notebook = get_results_window(ge);
1117
1118 for (i = 0; i < gc->nr_du; i++) {
1119 struct cmd_du_pdu *p = &gc->du[i];
1120
1121 __gfio_disk_util_show(res_notebook, gc, p);
1122 }
1123
1124 gtk_widget_show_all(ge->results_window);
1125 return 0;
1126}
1127
781ccba6
JA
1128static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1129 struct group_run_stats *rs)
3650a3ca 1130{
781ccba6 1131 unsigned int nr = gc->nr_results;
3650a3ca 1132
781ccba6
JA
1133 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1134 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1135 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1136 gc->nr_results++;
1137}
3650a3ca 1138
781ccba6
JA
1139static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1140 struct thread_stat *ts,
1141 struct group_run_stats *rs)
1142{
1143 GtkWidget *box, *vbox, *entry, *scroll;
3650a3ca 1144
b01329d0
JA
1145 scroll = gtk_scrolled_window_new(NULL, NULL);
1146 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1147 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1148
3650a3ca 1149 vbox = gtk_vbox_new(FALSE, 3);
3650a3ca 1150
b01329d0
JA
1151 box = gtk_hbox_new(FALSE, 0);
1152 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1153
1154 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
3650a3ca 1155
781ccba6 1156 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
f9d40b48 1157
3650a3ca
JA
1158 entry = new_info_entry_in_frame(box, "Name");
1159 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1160 if (strlen(ts->description)) {
1161 entry = new_info_entry_in_frame(box, "Description");
1162 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1163 }
1164 entry = new_info_entry_in_frame(box, "Group ID");
1165 entry_set_int_value(entry, ts->groupid);
1166 entry = new_info_entry_in_frame(box, "Jobs");
1167 entry_set_int_value(entry, ts->members);
6b79c80c 1168 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
3650a3ca
JA
1169 entry_set_int_value(entry, ts->error);
1170 entry = new_info_entry_in_frame(box, "PID");
1171 entry_set_int_value(entry, ts->pid);
1172
1173 if (ts->io_bytes[DDIR_READ])
c57f254c 1174 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
3650a3ca 1175 if (ts->io_bytes[DDIR_WRITE])
c57f254c 1176 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
3650a3ca 1177
09d574e3 1178 gfio_show_latency_buckets(gc, vbox, ts);
2e33101f
JA
1179 gfio_show_cpu_usage(vbox, ts);
1180 gfio_show_io_depths(vbox, ts);
781ccba6
JA
1181}
1182
1183static void gfio_display_end_results(struct gfio_client *gc)
1184{
60d0929e
JA
1185 struct gui_entry *ge = gc->ge;
1186 GtkWidget *res_notebook;
781ccba6
JA
1187 int i;
1188
60d0929e 1189 res_notebook = get_results_window(ge);
781ccba6
JA
1190
1191 for (i = 0; i < gc->nr_results; i++) {
1192 struct end_results *e = &gc->results[i];
1193
60d0929e 1194 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
781ccba6 1195 }
e5bd1347 1196
7da23b48
JA
1197 if (gfio_disk_util_show(gc))
1198 gtk_widget_show_all(ge->results_window);
781ccba6
JA
1199}
1200
1201static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1202 struct group_run_stats *rs)
1203{
1204 struct gfio_client *gc = client->client_data;
0ed83bce 1205 struct gui_entry *ge = gc->ge;
781ccba6
JA
1206
1207 gfio_add_end_results(gc, ts, rs);
1208
1209 gdk_threads_enter();
0ed83bce
JA
1210 if (ge->results_window)
1211 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1212 else
1213 gfio_display_end_results(gc);
3650a3ca
JA
1214 gdk_threads_leave();
1215}
1216
084d1c6f 1217static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 1218{
9b260bdf 1219 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
2f99deb0 1220 struct gui *ui = &main_ui;
9b260bdf
JA
1221 GtkTreeIter iter;
1222 struct tm *tm;
1223 time_t sec;
1224 char tmp[64], timebuf[80];
1225
1226 sec = p->log_sec;
1227 tm = localtime(&sec);
1228 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1229 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
736f2dff 1230
736f2dff 1231 gdk_threads_enter();
9b260bdf 1232
2f99deb0
JA
1233 gtk_list_store_append(ui->log_model, &iter);
1234 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1235 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1236 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1237 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
9b260bdf 1238
6b79c80c 1239 if (p->level == FIO_LOG_ERR)
2f99deb0 1240 view_log(NULL, (gpointer) ui);
6b79c80c 1241
736f2dff 1242 gdk_threads_leave();
a1820207
SC
1243}
1244
1245static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1246{
e0681f3e
JA
1247 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1248 struct gfio_client *gc = client->client_data;
0ed83bce 1249 struct gui_entry *ge = gc->ge;
7da23b48 1250 unsigned int nr = gc->nr_du;
e0681f3e 1251
7da23b48
JA
1252 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1253 memcpy(&gc->du[nr], p, sizeof(*p));
1254 gc->nr_du++;
e0681f3e 1255
7da23b48 1256 gdk_threads_enter();
0ed83bce
JA
1257 if (ge->results_window)
1258 __gfio_disk_util_show(ge->results_notebook, gc, p);
1259 else
1260 gfio_disk_util_show(gc);
0050e5f2 1261 gdk_threads_leave();
a1820207
SC
1262}
1263
3650a3ca
JA
1264extern int sum_stat_clients;
1265extern struct thread_stat client_ts;
1266extern struct group_run_stats client_gs;
1267
1268static int sum_stat_nr;
1269
89e5fad9
JA
1270static void gfio_thread_status_op(struct fio_client *client,
1271 struct fio_net_cmd *cmd)
a1820207 1272{
3650a3ca
JA
1273 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1274
1275 gfio_display_ts(client, &p->ts, &p->rs);
1276
1277 if (sum_stat_clients == 1)
1278 return;
1279
1280 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1281 sum_group_stats(&client_gs, &p->rs);
1282
1283 client_ts.members++;
2f122b13 1284 client_ts.thread_number = p->ts.thread_number;
3650a3ca
JA
1285 client_ts.groupid = p->ts.groupid;
1286
1287 if (++sum_stat_nr == sum_stat_clients) {
1288 strcpy(client_ts.name, "All clients");
1289 gfio_display_ts(client, &client_ts, &client_gs);
1290 }
a1820207
SC
1291}
1292
89e5fad9
JA
1293static void gfio_group_stats_op(struct fio_client *client,
1294 struct fio_net_cmd *cmd)
a1820207 1295{
98ceabd6 1296 /* We're ignoring group stats for now */
a1820207
SC
1297}
1298
2f99deb0
JA
1299static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1300 gpointer data)
3ea48b88 1301{
2f99deb0
JA
1302 struct gfio_graphs *g = data;
1303
57f9d28e
SC
1304 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1305 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1306 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1307 graph_set_position(g->bandwidth_graph, 0, 0);
3ea48b88
SC
1308 return TRUE;
1309}
1310
57f9d28e
SC
1311static void draw_graph(struct graph *g, cairo_t *cr)
1312{
1313 line_graph_draw(g, cr);
1314 cairo_stroke(cr);
1315}
1316
93e2db2b
JA
1317static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1318 gboolean keyboard_mode, GtkTooltip *tooltip,
1319 gpointer data)
1320{
1321 struct gfio_graphs *g = data;
1322 const char *text = NULL;
1323
1324 if (graph_contains_xy(g->iops_graph, x, y))
1325 text = graph_find_tooltip(g->iops_graph, x, y);
1326 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1327 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1328
1329 if (text) {
1330 gtk_tooltip_set_text(tooltip, text);
1331 return TRUE;
1332 }
1333
1334 return FALSE;
1335}
1336
2fd3bb0e
JA
1337static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1338{
2f99deb0 1339 struct gfio_graphs *g = p;
2fd3bb0e
JA
1340 cairo_t *cr;
1341
1342 cr = gdk_cairo_create(w->window);
93e2db2b
JA
1343
1344 if (graph_has_tooltips(g->iops_graph) ||
1345 graph_has_tooltips(g->bandwidth_graph)) {
1346 g_object_set(w, "has-tooltip", TRUE, NULL);
1347 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1348 }
1349
2fd3bb0e 1350 cairo_set_source_rgb(cr, 0, 0, 0);
57f9d28e
SC
1351 draw_graph(g->iops_graph, cr);
1352 draw_graph(g->bandwidth_graph, cr);
2fd3bb0e
JA
1353 cairo_destroy(cr);
1354
1355 return FALSE;
1356}
1357
2f99deb0
JA
1358/*
1359 * Client specific ETA
1360 */
1361static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
3e47bd25 1362{
2f99deb0
JA
1363 struct gfio_client *gc = client->client_data;
1364 struct gui_entry *ge = gc->ge;
3e47bd25
JA
1365 static int eta_good;
1366 char eta_str[128];
1367 char output[256];
1368 char tmp[32];
1369 double perc = 0.0;
1370 int i2p = 0;
1371
0050e5f2
JA
1372 gdk_threads_enter();
1373
3e47bd25
JA
1374 eta_str[0] = '\0';
1375 output[0] = '\0';
1376
1377 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1378 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1379 eta_to_str(eta_str, je->eta_sec);
1380 }
1381
1382 sprintf(tmp, "%u", je->nr_running);
2f99deb0 1383 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
3e47bd25 1384 sprintf(tmp, "%u", je->files_open);
2f99deb0 1385 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
3e47bd25
JA
1386
1387#if 0
1388 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1389 if (je->m_rate || je->t_rate) {
1390 char *tr, *mr;
1391
1392 mr = num2str(je->m_rate, 4, 0, i2p);
1393 tr = num2str(je->t_rate, 4, 0, i2p);
2f99deb0 1394 gtk_entry_set_text(GTK_ENTRY(ge->eta);
3e47bd25
JA
1395 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1396 free(tr);
1397 free(mr);
1398 } else if (je->m_iops || je->t_iops)
1399 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 1400
2f99deb0
JA
1401 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1402 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1403 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1404 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
3e47bd25
JA
1405#endif
1406
1407 if (je->eta_sec != INT_MAX && je->nr_running) {
1408 char *iops_str[2];
1409 char *rate_str[2];
1410
1411 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1412 strcpy(output, "-.-% done");
1413 else {
1414 eta_good = 1;
1415 perc *= 100.0;
1416 sprintf(output, "%3.1f%% done", perc);
1417 }
1418
1419 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1420 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1421
1422 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1423 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1424
2f99deb0
JA
1425 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1426 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1427 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1428 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
3e47bd25 1429
93e2db2b
JA
1430 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1431 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1432 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1433 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
2f99deb0
JA
1434
1435 free(rate_str[0]);
1436 free(rate_str[1]);
1437 free(iops_str[0]);
1438 free(iops_str[1]);
1439 }
1440
1441 if (eta_str[0]) {
1442 char *dst = output + strlen(output);
1443
1444 sprintf(dst, " - %s", eta_str);
1445 }
1446
9988ca70 1447 gfio_update_thread_status(ge, output, perc);
2f99deb0
JA
1448 gdk_threads_leave();
1449}
1450
1451/*
1452 * Update ETA in main window for all clients
1453 */
1454static void gfio_update_all_eta(struct jobs_eta *je)
1455{
1456 struct gui *ui = &main_ui;
1457 static int eta_good;
1458 char eta_str[128];
1459 char output[256];
1460 double perc = 0.0;
1461 int i2p = 0;
1462
1463 gdk_threads_enter();
1464
1465 eta_str[0] = '\0';
1466 output[0] = '\0';
1467
1468 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1469 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1470 eta_to_str(eta_str, je->eta_sec);
1471 }
1472
1473#if 0
1474 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1475 if (je->m_rate || je->t_rate) {
1476 char *tr, *mr;
1477
1478 mr = num2str(je->m_rate, 4, 0, i2p);
1479 tr = num2str(je->t_rate, 4, 0, i2p);
1480 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1481 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1482 free(tr);
1483 free(mr);
1484 } else if (je->m_iops || je->t_iops)
1485 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1486
1487 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1488 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1489 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1490 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1491#endif
1492
3863d1ad
JA
1493 entry_set_int_value(ui->eta.jobs, je->nr_running);
1494
2f99deb0
JA
1495 if (je->eta_sec != INT_MAX && je->nr_running) {
1496 char *iops_str[2];
1497 char *rate_str[2];
1498
1499 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1500 strcpy(output, "-.-% done");
1501 else {
1502 eta_good = 1;
1503 perc *= 100.0;
1504 sprintf(output, "%3.1f%% done", perc);
1505 }
1506
1507 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1508 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1509
1510 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1511 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1512
1513 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1514 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1515 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1516 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1517
93e2db2b
JA
1518 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1519 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1520 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1521 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
2fd3bb0e 1522
3e47bd25
JA
1523 free(rate_str[0]);
1524 free(rate_str[1]);
1525 free(iops_str[0]);
1526 free(iops_str[1]);
1527 }
1528
1529 if (eta_str[0]) {
1530 char *dst = output + strlen(output);
1531
1532 sprintf(dst, " - %s", eta_str);
1533 }
1534
9988ca70 1535 gfio_update_thread_status_all(output, perc);
0050e5f2 1536 gdk_threads_leave();
3e47bd25
JA
1537}
1538
a1820207
SC
1539static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1540{
843ad237 1541 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
88f6e7ad 1542 struct gfio_client *gc = client->client_data;
2f99deb0 1543 struct gui_entry *ge = gc->ge;
843ad237
JA
1544 const char *os, *arch;
1545 char buf[64];
1546
1547 os = fio_get_os_string(probe->os);
1548 if (!os)
1549 os = "unknown";
1550
1551 arch = fio_get_arch_string(probe->arch);
1552 if (!arch)
1553 os = "unknown";
1554
1555 if (!client->name)
1556 client->name = strdup((char *) probe->hostname);
1557
0050e5f2
JA
1558 gdk_threads_enter();
1559
2f99deb0
JA
1560 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1561 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1562 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
843ad237 1563 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
2f99deb0 1564 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
88f6e7ad 1565
85dd01e7 1566 gfio_set_state(ge, GE_STATE_CONNECTED);
0050e5f2
JA
1567
1568 gdk_threads_leave();
a1820207
SC
1569}
1570
9988ca70
JA
1571static void gfio_update_thread_status(struct gui_entry *ge,
1572 char *status_message, double perc)
1573{
1574 static char message[100];
1575 const char *m = message;
1576
1577 strncpy(message, status_message, sizeof(message) - 1);
1578 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1579 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1580 gtk_widget_queue_draw(main_ui.window);
1581}
1582
1583static void gfio_update_thread_status_all(char *status_message, double perc)
5b7573ab 1584{
2f99deb0 1585 struct gui *ui = &main_ui;
5b7573ab
SC
1586 static char message[100];
1587 const char *m = message;
1588
1589 strncpy(message, status_message, sizeof(message) - 1);
2f99deb0
JA
1590 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1591 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1592 gtk_widget_queue_draw(ui->window);
5b7573ab
SC
1593}
1594
35c0ba7f 1595static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
3ec62ec4 1596{
e0681f3e 1597 struct gfio_client *gc = client->client_data;
3ec62ec4 1598
0050e5f2 1599 gdk_threads_enter();
85dd01e7 1600 gfio_set_state(gc->ge, GE_STATE_NEW);
0050e5f2 1601 gdk_threads_leave();
3ec62ec4
JA
1602}
1603
807f9971
JA
1604static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1605{
1606 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
e0681f3e 1607 struct gfio_client *gc = client->client_data;
dcaeb606 1608 struct thread_options *o = &gc->o;
2f99deb0 1609 struct gui_entry *ge = gc->ge;
99d633af
JA
1610 char *c1, *c2, *c3, *c4;
1611 char tmp[80];
807f9971 1612
2f122b13
JA
1613 p->thread_number = le32_to_cpu(p->thread_number);
1614 p->groupid = le32_to_cpu(p->groupid);
dcaeb606 1615 convert_thread_options_to_cpu(o, &p->top);
807f9971 1616
0050e5f2
JA
1617 gdk_threads_enter();
1618
2f99deb0
JA
1619 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1620
3863d1ad
JA
1621 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1622 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1623
ddbafc14
JA
1624 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1625 multitext_add_entry(&ge->eta.iotype, tmp);
99d633af
JA
1626
1627 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1628 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1629 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1630 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1631 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1632 free(c1);
1633 free(c2);
1634 free(c3);
1635 free(c4);
1636 multitext_add_entry(&ge->eta.bs, tmp);
1637
c80b74b0 1638 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
807f9971 1639
dcaeb606 1640 sprintf(tmp, "%u", o->iodepth);
c80b74b0
JA
1641 multitext_add_entry(&ge->eta.iodepth, tmp);
1642
1643 multitext_set_entry(&ge->eta.iotype, 0);
99d633af 1644 multitext_set_entry(&ge->eta.bs, 0);
c80b74b0
JA
1645 multitext_set_entry(&ge->eta.ioengine, 0);
1646 multitext_set_entry(&ge->eta.iodepth, 0);
0050e5f2 1647
85dd01e7
JA
1648 gfio_set_state(ge, GE_STATE_JOB_SENT);
1649
0050e5f2 1650 gdk_threads_leave();
807f9971
JA
1651}
1652
ed727a46
JA
1653static void gfio_client_timed_out(struct fio_client *client)
1654{
e0681f3e 1655 struct gfio_client *gc = client->client_data;
ed727a46
JA
1656 char buf[256];
1657
1658 gdk_threads_enter();
1659
85dd01e7 1660 gfio_set_state(gc->ge, GE_STATE_NEW);
2f99deb0 1661 clear_ge_ui_info(gc->ge);
ed727a46
JA
1662
1663 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
16ce5adf 1664 show_info_dialog(gc->ge->ui, "Network timeout", buf);
ed727a46
JA
1665
1666 gdk_threads_leave();
1667}
1668
6b79c80c
JA
1669static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1670{
1671 struct gfio_client *gc = client->client_data;
1672
1673 gdk_threads_enter();
1674
85dd01e7 1675 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
6b79c80c
JA
1676
1677 if (gc->err_entry)
1678 entry_set_int_value(gc->err_entry, client->error);
1679
1680 gdk_threads_leave();
1681}
1682
85dd01e7
JA
1683static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1684{
1685 struct gfio_client *gc = client->client_data;
1686
1687 gdk_threads_enter();
1688 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1689 gdk_threads_leave();
1690}
1691
1692static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1693{
1694 struct gfio_client *gc = client->client_data;
1695
1696 gdk_threads_enter();
1697 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1698 gdk_threads_leave();
1699}
1700
1b42725f
JA
1701static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1702{
284b1e65 1703 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1b42725f
JA
1704 free(pdu);
1705}
1706
a1820207 1707struct client_ops gfio_client_ops = {
35c0ba7f 1708 .text = gfio_text_op,
0420ba6a
JA
1709 .disk_util = gfio_disk_util_op,
1710 .thread_status = gfio_thread_status_op,
1711 .group_stats = gfio_group_stats_op,
2f99deb0
JA
1712 .jobs_eta = gfio_update_client_eta,
1713 .eta = gfio_update_all_eta,
0420ba6a 1714 .probe = gfio_probe_op,
3ec62ec4 1715 .quit = gfio_quit_op,
807f9971 1716 .add_job = gfio_add_job_op,
ed727a46 1717 .timed_out = gfio_client_timed_out,
6b79c80c 1718 .stop = gfio_client_stop,
85dd01e7
JA
1719 .start = gfio_client_start,
1720 .job_start = gfio_client_job_start,
1b42725f 1721 .iolog = gfio_client_iolog,
6433ee05 1722 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
3ec62ec4 1723 .stay_connected = 1,
46bcd498 1724 .client_type = FIO_CLIENT_TYPE_GUI,
a1820207
SC
1725};
1726
0fd18982
JA
1727/*
1728 * FIXME: need more handling here
1729 */
1730static void ge_destroy(struct gui_entry *ge)
1731{
1732 struct gfio_client *gc = ge->client;
1733
1734 if (gc && gc->client) {
1735 if (ge->state >= GE_STATE_CONNECTED)
1736 fio_client_terminate(gc->client);
1737
1738 fio_put_client(gc->client);
1739 }
1740
1741 flist_del(&ge->list);
1742 free(ge);
1743}
1744
1745static void ge_widget_destroy(GtkWidget *w, gpointer data)
1746{
0fd18982
JA
1747}
1748
1749static void gfio_quit(struct gui *ui)
1750{
1751 struct gui_entry *ge;
1752
1753 while (!flist_empty(&ui->list)) {
1754 ge = flist_entry(ui->list.next, struct gui_entry, list);
1755 ge_destroy(ge);
1756 }
1757
1758 gtk_main_quit();
1759}
1760
ff1f3280
SC
1761static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1762 __attribute__((unused)) gpointer data)
1763{
0fd18982 1764 gfio_quit(data);
ff1f3280
SC
1765}
1766
25927259
SC
1767static void *job_thread(void *arg)
1768{
a9eccde4
JA
1769 struct gui *ui = arg;
1770
1771 ui->handler_running = 1;
25927259 1772 fio_handle_clients(&gfio_client_ops);
a9eccde4 1773 ui->handler_running = 0;
25927259
SC
1774 return NULL;
1775}
1776
2f99deb0 1777static int send_job_files(struct gui_entry *ge)
60f6b330 1778{
9988ca70 1779 struct gfio_client *gc = ge->client;
441013b4 1780 int i, ret = 0;
0420ba6a 1781
2f99deb0 1782 for (i = 0; i < ge->nr_job_files; i++) {
9988ca70 1783 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
c724926b
JA
1784 if (ret < 0) {
1785 GError *error;
1786
1787 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1788 report_error(error);
1789 g_error_free(error);
1790 break;
1791 } else if (ret)
441013b4
JA
1792 break;
1793
2f99deb0
JA
1794 free(ge->job_files[i]);
1795 ge->job_files[i] = NULL;
441013b4 1796 }
2f99deb0
JA
1797 while (i < ge->nr_job_files) {
1798 free(ge->job_files[i]);
1799 ge->job_files[i] = NULL;
441013b4 1800 i++;
0420ba6a
JA
1801 }
1802
2c77d831
JA
1803 free(ge->job_files);
1804 ge->job_files = NULL;
3af45201 1805 ge->nr_job_files = 0;
441013b4 1806 return ret;
60f6b330
SC
1807}
1808
63a130b7
JA
1809static void *server_thread(void *arg)
1810{
1811 is_backend = 1;
1812 gfio_server_running = 1;
1813 fio_start_server(NULL);
1814 gfio_server_running = 0;
1815 return NULL;
1816}
1817
2f99deb0 1818static void gfio_start_server(void)
63a130b7 1819{
2f99deb0
JA
1820 struct gui *ui = &main_ui;
1821
63a130b7
JA
1822 if (!gfio_server_running) {
1823 gfio_server_running = 1;
1824 pthread_create(&ui->server_t, NULL, server_thread, NULL);
e34f6ad7 1825 pthread_detach(ui->server_t);
63a130b7
JA
1826 }
1827}
1828
f3074008 1829static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 1830 gpointer data)
f3074008 1831{
2f99deb0
JA
1832 struct gui_entry *ge = data;
1833 struct gfio_client *gc = ge->client;
25927259 1834
78cb2fe5
JA
1835 if (gc)
1836 fio_start_client(gc->client);
f3074008
SC
1837}
1838
df06f220
JA
1839static void file_open(GtkWidget *w, gpointer data);
1840
1841static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 1842{
2f99deb0
JA
1843 struct gui_entry *ge = data;
1844 struct gfio_client *gc = ge->client;
3ec62ec4 1845
85dd01e7 1846 if (ge->state == GE_STATE_NEW) {
c724926b
JA
1847 int ret;
1848
2f99deb0 1849 if (!ge->nr_job_files)
cf4b0443 1850 file_open(widget, ge->ui);
2f99deb0
JA
1851 if (!ge->nr_job_files)
1852 return;
1853
d3b70f32
JA
1854 gc = ge->client;
1855
2f99deb0
JA
1856 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1857 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
c724926b
JA
1858 ret = fio_client_connect(gc->client);
1859 if (!ret) {
a9eccde4
JA
1860 if (!ge->ui->handler_running)
1861 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
85dd01e7 1862 gfio_set_state(ge, GE_STATE_CONNECTED);
c724926b
JA
1863 } else {
1864 GError *error;
1865
1866 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1867 report_error(error);
1868 g_error_free(error);
69406b92 1869 }
df06f220 1870 } else {
2f99deb0 1871 fio_client_terminate(gc->client);
85dd01e7 1872 gfio_set_state(ge, GE_STATE_NEW);
2f99deb0 1873 clear_ge_ui_info(ge);
df06f220 1874 }
3e47bd25
JA
1875}
1876
b9d2f30a
JA
1877static void send_clicked(GtkWidget *widget, gpointer data)
1878{
2f99deb0 1879 struct gui_entry *ge = data;
b9d2f30a 1880
2f99deb0 1881 if (send_job_files(ge)) {
c724926b
JA
1882 GError *error;
1883
1884 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1885 report_error(error);
1886 g_error_free(error);
1887
53e0e85d 1888 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
b9d2f30a 1889 }
b9d2f30a
JA
1890}
1891
0420ba6a
JA
1892static void on_info_bar_response(GtkWidget *widget, gint response,
1893 gpointer data)
1894{
2f99deb0
JA
1895 struct gui *ui = &main_ui;
1896
0420ba6a
JA
1897 if (response == GTK_RESPONSE_OK) {
1898 gtk_widget_destroy(widget);
2f99deb0 1899 ui->error_info_bar = NULL;
0420ba6a
JA
1900 }
1901}
1902
49c34176 1903static void report_error(GError *error)
0420ba6a 1904{
2f99deb0
JA
1905 struct gui *ui = &main_ui;
1906
1907 if (ui->error_info_bar == NULL) {
1908 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
0420ba6a
JA
1909 GTK_RESPONSE_OK,
1910 NULL);
2f99deb0
JA
1911 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1912 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
0420ba6a
JA
1913 GTK_MESSAGE_ERROR);
1914
2f99deb0
JA
1915 ui->error_label = gtk_label_new(error->message);
1916 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1917 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
0420ba6a 1918
2f99deb0
JA
1919 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1920 gtk_widget_show_all(ui->vbox);
0420ba6a
JA
1921 } else {
1922 char buffer[256];
1923 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2f99deb0 1924 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
0420ba6a
JA
1925 }
1926}
1927
62bc937f
JA
1928struct connection_widgets
1929{
1930 GtkWidget *hentry;
1931 GtkWidget *combo;
1932 GtkWidget *button;
1933};
1934
1935static void hostname_cb(GtkEntry *entry, gpointer data)
1936{
1937 struct connection_widgets *cw = data;
1938 int uses_net = 0, is_localhost = 0;
1939 const gchar *text;
1940 gchar *ctext;
1941
1942 /*
1943 * Check whether to display the 'auto start backend' box
1944 * or not. Show it if we are a localhost and using network,
1945 * or using a socket.
1946 */
1947 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1948 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1949 uses_net = 1;
1950 g_free(ctext);
1951
1952 if (uses_net) {
1953 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1954 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1955 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1956 !strcmp(text, "ip6-loopback"))
1957 is_localhost = 1;
1958 }
1959
1960 if (!uses_net || is_localhost) {
1961 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1962 gtk_widget_set_sensitive(cw->button, 1);
1963 } else {
1964 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1965 gtk_widget_set_sensitive(cw->button, 0);
1966 }
1967}
1968
b9f3c7ed
JA
1969static int get_connection_details(char **host, int *port, int *type,
1970 int *server_start)
a7a42ce1 1971{
62bc937f
JA
1972 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1973 struct connection_widgets cw;
a7a42ce1
JA
1974 char *typeentry;
1975
1976 dialog = gtk_dialog_new_with_buttons("Connection details",
2f99deb0 1977 GTK_WINDOW(main_ui.window),
a7a42ce1
JA
1978 GTK_DIALOG_DESTROY_WITH_PARENT,
1979 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1980 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1981
1982 frame = gtk_frame_new("Hostname / socket name");
f129909e
JA
1983 /* gtk_dialog_get_content_area() is 2.14 and newer */
1984 vbox = GTK_DIALOG(dialog)->vbox;
a7a42ce1
JA
1985 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1986
1987 box = gtk_vbox_new(FALSE, 6);
1988 gtk_container_add(GTK_CONTAINER(frame), box);
1989
1990 hbox = gtk_hbox_new(TRUE, 10);
1991 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
62bc937f
JA
1992 cw.hentry = gtk_entry_new();
1993 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1994 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
a7a42ce1
JA
1995
1996 frame = gtk_frame_new("Port");
1997 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1998 box = gtk_vbox_new(FALSE, 10);
1999 gtk_container_add(GTK_CONTAINER(frame), box);
2000
2001 hbox = gtk_hbox_new(TRUE, 4);
2002 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2003 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2004
2005 frame = gtk_frame_new("Type");
2006 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2007 box = gtk_vbox_new(FALSE, 10);
2008 gtk_container_add(GTK_CONTAINER(frame), box);
2009
2010 hbox = gtk_hbox_new(TRUE, 4);
2011 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2012
62bc937f
JA
2013 cw.combo = gtk_combo_box_new_text();
2014 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2015 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2016 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2017 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
a7a42ce1 2018
62bc937f 2019 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
a7a42ce1 2020
b9f3c7ed
JA
2021 frame = gtk_frame_new("Options");
2022 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2023 box = gtk_vbox_new(FALSE, 10);
2024 gtk_container_add(GTK_CONTAINER(frame), box);
2025
2026 hbox = gtk_hbox_new(TRUE, 4);
2027 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2028
62bc937f
JA
2029 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2030 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2031 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.");
2032 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2033
2034 /*
2035 * Connect edit signal, so we can show/not-show the auto start button
2036 */
2037 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2038 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
b9f3c7ed 2039
a7a42ce1
JA
2040 gtk_widget_show_all(dialog);
2041
2042 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2043 gtk_widget_destroy(dialog);
2044 return 1;
2045 }
2046
62bc937f 2047 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
a7a42ce1
JA
2048 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2049
62bc937f 2050 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
a7a42ce1
JA
2051 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2052 *type = Fio_client_ipv4;
2053 else if (!strncmp(typeentry, "IPv6", 4))
2054 *type = Fio_client_ipv6;
2055 else
2056 *type = Fio_client_socket;
2057 g_free(typeentry);
2058
62bc937f 2059 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
b9f3c7ed 2060
a7a42ce1
JA
2061 gtk_widget_destroy(dialog);
2062 return 0;
2063}
2064
2f99deb0 2065static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
e0681f3e
JA
2066{
2067 struct gfio_client *gc;
2068
2069 gc = malloc(sizeof(*gc));
2070 memset(gc, 0, sizeof(*gc));
2f99deb0 2071 gc->ge = ge;
343cb4a9 2072 gc->client = fio_get_client(client);
b9d2f30a 2073
2f99deb0 2074 ge->client = gc;
e0681f3e
JA
2075
2076 client->client_data = gc;
2077}
2078
2f99deb0
JA
2079static GtkWidget *new_client_page(struct gui_entry *ge);
2080
2081static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2082{
2083 struct gui_entry *ge;
2084
2085 ge = malloc(sizeof(*ge));
2086 memset(ge, 0, sizeof(*ge));
85dd01e7 2087 ge->state = GE_STATE_NEW;
2f99deb0
JA
2088 INIT_FLIST_HEAD(&ge->list);
2089 flist_add_tail(&ge->list, &ui->list);
2090 ge->ui = ui;
2091 return ge;
2092}
2093
2f99deb0
JA
2094static struct gui_entry *get_new_ge_with_tab(const char *name)
2095{
2096 struct gui_entry *ge;
2097
2098 ge = alloc_new_gui_entry(&main_ui);
2099
2100 ge->vbox = new_client_page(ge);
0fd18982 2101 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2f99deb0
JA
2102
2103 ge->page_label = gtk_label_new(name);
2104 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2105
2106 gtk_widget_show_all(main_ui.window);
2107 return ge;
2108}
2109
2110static void file_new(GtkWidget *w, gpointer data)
2111{
16ce5adf
JA
2112 struct gui *ui = (struct gui *) data;
2113 struct gui_entry *ge;
2114
2115 ge = get_new_ge_with_tab("Untitled");
2116 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2f99deb0
JA
2117}
2118
2119/*
2120 * Return the 'ge' corresponding to the tab. If the active tab is the
2121 * main tab, open a new tab.
2122 */
38634cb1 2123static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2f99deb0
JA
2124{
2125 struct flist_head *entry;
2126 struct gui_entry *ge;
2127
38634cb1
JA
2128 if (!cur_page) {
2129 if (created)
2130 *created = 1;
2f99deb0 2131 return get_new_ge_with_tab("Untitled");
38634cb1
JA
2132 }
2133
2134 if (created)
2135 *created = 0;
2f99deb0
JA
2136
2137 flist_for_each(entry, &main_ui.list) {
2138 ge = flist_entry(entry, struct gui_entry, list);
2139 if (ge->page_num == cur_page)
2140 return ge;
2141 }
2142
2143 return NULL;
2144}
2145
85dd01e7 2146static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
16ce5adf 2147{
16ce5adf
JA
2148 gint cur_page;
2149
2150 /*
85dd01e7
JA
2151 * Main tab is tab 0, so any current page other than 0 holds
2152 * a ge entry.
16ce5adf
JA
2153 */
2154 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
85dd01e7 2155 if (cur_page)
38634cb1 2156 return get_ge_from_page(cur_page, NULL);
85dd01e7
JA
2157
2158 return NULL;
2159}
2160
2161static void file_close(GtkWidget *w, gpointer data)
2162{
2163 struct gui *ui = (struct gui *) data;
2164 struct gui_entry *ge;
16ce5adf 2165
85dd01e7
JA
2166 /*
2167 * Can't close the main tab
2168 */
2169 ge = get_ge_from_cur_tab(ui);
2170 if (ge) {
16ce5adf
JA
2171 gtk_widget_destroy(ge->vbox);
2172 return;
2173 }
2174
f5c6726e
JA
2175 if (!flist_empty(&ui->list)) {
2176 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2177 return;
2178 }
2179
0fd18982 2180 gfio_quit(ui);
16ce5adf
JA
2181}
2182
38634cb1
JA
2183static void file_add_recent(struct gui *ui, const gchar *uri)
2184{
a217ba7d
JA
2185 GtkRecentData grd;
2186
2187 memset(&grd, 0, sizeof(grd));
2188 grd.display_name = strdup("gfio");
2189 grd.description = strdup("Fio job file");
2190 grd.mime_type = strdup(GFIO_MIME);
2191 grd.app_name = strdup(g_get_application_name());
2192 grd.app_exec = strdup("gfio %f/%u");
2193
2194 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
38634cb1
JA
2195}
2196
2197static gchar *get_filename_from_uri(const gchar *uri)
2198{
2199 if (strncmp(uri, "file://", 7))
2200 return strdup(uri);
2201
2202 return strdup(uri + 7);
2203}
2204
2205static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2206 int type, int port)
2207{
2208 struct fio_client *client;
2209 gchar *filename;
2210
2211 filename = get_filename_from_uri(uri);
2212
2213 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2214 ge->job_files[ge->nr_job_files] = strdup(filename);
2215 ge->nr_job_files++;
2216
2217 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2218 if (!client) {
2219 GError *error;
2220
2221 error = g_error_new(g_quark_from_string("fio"), 1,
2222 "Failed to add client %s", host);
2223 report_error(error);
2224 g_error_free(error);
2225 return 1;
2226 }
2227
2228 gfio_client_added(ge, client);
2229 file_add_recent(ge->ui, uri);
2230 return 0;
2231}
2232
a6790906 2233static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
0420ba6a 2234{
a6790906 2235 int port, type, server_start;
2f99deb0
JA
2236 struct gui_entry *ge;
2237 gint cur_page;
38634cb1 2238 char *host;
a6790906 2239 int ret, ge_is_new = 0;
2f99deb0
JA
2240
2241 /*
2242 * Creates new tab if current tab is the main window, or the
2243 * current tab already has a client.
2244 */
2245 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
38634cb1
JA
2246 ge = get_ge_from_page(cur_page, &ge_is_new);
2247 if (ge->client) {
2f99deb0 2248 ge = get_new_ge_with_tab("Untitled");
38634cb1
JA
2249 ge_is_new = 1;
2250 }
2f99deb0
JA
2251
2252 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
0420ba6a 2253
a6790906
JA
2254 if (get_connection_details(&host, &port, &type, &server_start)) {
2255 if (ge_is_new)
2256 gtk_widget_destroy(ge->vbox);
2257
2258 return 1;
2259 }
2260
2261 ret = do_file_open(ge, uri, host, type, port);
2262
2263 free(host);
2264
2265 if (!ret) {
2266 if (server_start)
2267 gfio_start_server();
2268 } else {
2269 if (ge_is_new)
2270 gtk_widget_destroy(ge->vbox);
2271 }
2272
2273 return ret;
2274}
2275
2276static void recent_open(GtkAction *action, gpointer data)
2277{
2278 struct gui *ui = (struct gui *) data;
2279 GtkRecentInfo *info;
2280 const gchar *uri;
2281
2282 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2283 uri = gtk_recent_info_get_uri(info);
2284
2285 do_file_open_with_tab(ui, uri);
2286}
2287
2288static void file_open(GtkWidget *w, gpointer data)
2289{
2290 struct gui *ui = data;
2291 GtkWidget *dialog;
2292 GSList *filenames, *fn_glist;
2293 GtkFileFilter *filter;
2294
0420ba6a 2295 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 2296 GTK_WINDOW(ui->window),
0420ba6a
JA
2297 GTK_FILE_CHOOSER_ACTION_OPEN,
2298 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2299 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2300 NULL);
2301 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2302
2303 filter = gtk_file_filter_new();
2304 gtk_file_filter_add_pattern(filter, "*.fio");
2305 gtk_file_filter_add_pattern(filter, "*.job");
2d262990 2306 gtk_file_filter_add_pattern(filter, "*.ini");
38634cb1 2307 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
0420ba6a
JA
2308 gtk_file_filter_set_name(filter, "Fio job file");
2309 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2310
2311 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2312 gtk_widget_destroy(dialog);
2313 return;
2314 }
2315
2316 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
2317
2318 gtk_widget_destroy(dialog);
2319
0420ba6a
JA
2320 filenames = fn_glist;
2321 while (filenames != NULL) {
a6790906
JA
2322 if (do_file_open_with_tab(ui, filenames->data))
2323 break;
0420ba6a
JA
2324 filenames = g_slist_next(filenames);
2325 }
63a130b7 2326
0420ba6a 2327 g_slist_free(fn_glist);
0420ba6a
JA
2328}
2329
2330static void file_save(GtkWidget *w, gpointer data)
2331{
63a130b7 2332 struct gui *ui = data;
0420ba6a
JA
2333 GtkWidget *dialog;
2334
2335 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 2336 GTK_WINDOW(ui->window),
0420ba6a
JA
2337 GTK_FILE_CHOOSER_ACTION_SAVE,
2338 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2339 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2340 NULL);
2341
2342 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2343 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2344
2345 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2346 char *filename;
2347
2348 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2349 // save_job_file(filename);
2350 g_free(filename);
2351 }
2352 gtk_widget_destroy(dialog);
2353}
2354
9b260bdf
JA
2355static void view_log_destroy(GtkWidget *w, gpointer data)
2356{
2357 struct gui *ui = (struct gui *) data;
2358
2359 gtk_widget_ref(ui->log_tree);
2360 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2361 gtk_widget_destroy(w);
4cbe7211 2362 ui->log_view = NULL;
9b260bdf
JA
2363}
2364
2365static void view_log(GtkWidget *w, gpointer data)
2366{
4cbe7211
JA
2367 GtkWidget *win, *scroll, *vbox, *box;
2368 struct gui *ui = (struct gui *) data;
9b260bdf 2369
4cbe7211
JA
2370 if (ui->log_view)
2371 return;
2372
2373 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 2374 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 2375 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 2376
4cbe7211
JA
2377 scroll = gtk_scrolled_window_new(NULL, NULL);
2378
2379 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2380
2381 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2382
2383 box = gtk_hbox_new(TRUE, 0);
2384 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2385 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2386 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2387
2388 vbox = gtk_vbox_new(TRUE, 5);
2389 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 2390
4cbe7211 2391 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
2392 gtk_widget_show_all(win);
2393}
2394
85dd01e7
JA
2395static void connect_job_entry(GtkWidget *w, gpointer data)
2396{
2397 struct gui *ui = (struct gui *) data;
2398 struct gui_entry *ge;
2399
2400 ge = get_ge_from_cur_tab(ui);
2401 if (ge)
2402 connect_clicked(w, ge);
2403}
2404
2405static void send_job_entry(GtkWidget *w, gpointer data)
2406{
2407 struct gui *ui = (struct gui *) data;
2408 struct gui_entry *ge;
2409
2410 ge = get_ge_from_cur_tab(ui);
2411 if (ge)
2412 send_clicked(w, ge);
2413
2414}
2415
2416static void edit_job_entry(GtkWidget *w, gpointer data)
16ce5adf 2417{
9af4a244
JA
2418 struct gui *ui = (struct gui *) data;
2419
2420 gopt_get_options_window(ui->window);
16ce5adf
JA
2421}
2422
85dd01e7
JA
2423static void start_job_entry(GtkWidget *w, gpointer data)
2424{
2425 struct gui *ui = (struct gui *) data;
2426 struct gui_entry *ge;
2427
2428 ge = get_ge_from_cur_tab(ui);
2429 if (ge)
2430 start_job_clicked(w, ge);
2431}
2432
781ccba6
JA
2433static void view_results(GtkWidget *w, gpointer data)
2434{
2435 struct gui *ui = (struct gui *) data;
2436 struct gfio_client *gc;
2437 struct gui_entry *ge;
2438
2439 ge = get_ge_from_cur_tab(ui);
2440 if (!ge)
2441 return;
2442
2443 if (ge->results_window)
2444 return;
2445
2446 gc = ge->client;
2447 if (gc && gc->nr_results)
2448 gfio_display_end_results(gc);
2449}
2450
8577f4fd
JA
2451static void __update_graph_limits(struct gfio_graphs *g)
2452{
2453 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2454 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2455}
2456
2457static void update_graph_limits(void)
2458{
2459 struct flist_head *entry;
2460 struct gui_entry *ge;
2461
2462 __update_graph_limits(&main_ui.graphs);
2463
2464 flist_for_each(entry, &main_ui.list) {
2465 ge = flist_entry(entry, struct gui_entry, list);
2466 __update_graph_limits(&ge->graphs);
2467 }
2468}
2469
46974a7d
JA
2470static void preferences(GtkWidget *w, gpointer data)
2471{
f3e8440f 2472 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 2473 GtkWidget *hbox, *spin, *entry, *spin_int;
46974a7d
JA
2474 int i;
2475
2476 dialog = gtk_dialog_new_with_buttons("Preferences",
2f99deb0 2477 GTK_WINDOW(main_ui.window),
46974a7d
JA
2478 GTK_DIALOG_DESTROY_WITH_PARENT,
2479 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2480 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2481 NULL);
2482
8577f4fd 2483 frame = gtk_frame_new("Graphing");
f3e8440f
JA
2484 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2485 vbox = gtk_vbox_new(FALSE, 6);
2486 gtk_container_add(GTK_CONTAINER(frame), vbox);
2487
1cf6bca5
JA
2488 hbox = gtk_hbox_new(FALSE, 5);
2489 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2490 entry = gtk_label_new("Font face to use for graph labels");
2491 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2492
f3e8440f 2493 font = gtk_font_button_new();
1cf6bca5 2494 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 2495
8577f4fd
JA
2496 box = gtk_vbox_new(FALSE, 6);
2497 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2498
2499 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 2500 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
2501 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2502 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2503
c05d9056 2504 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
8577f4fd 2505
1cf6bca5
JA
2506 box = gtk_vbox_new(FALSE, 6);
2507 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2508
2509 hbox = gtk_hbox_new(FALSE, 5);
2510 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2511 entry = gtk_label_new("Client ETA request interval (msec)");
2512 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2513
2514 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4
JA
2515 frame = gtk_frame_new("Debug logging");
2516 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2517 vbox = gtk_vbox_new(FALSE, 6);
2518 gtk_container_add(GTK_CONTAINER(frame), vbox);
2519
2520 box = gtk_hbox_new(FALSE, 6);
2521 gtk_container_add(GTK_CONTAINER(vbox), box);
2522
2523 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2524
2525 for (i = 0; i < FD_DEBUG_MAX; i++) {
2526 if (i == 7) {
2527 box = gtk_hbox_new(FALSE, 6);
2528 gtk_container_add(GTK_CONTAINER(vbox), box);
2529 }
2530
2531
2532 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2533 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2534 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2535 }
2536
46974a7d
JA
2537 gtk_widget_show_all(dialog);
2538
2539 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2540 gtk_widget_destroy(dialog);
2541 return;
2542 }
2543
2544 for (i = 0; i < FD_DEBUG_MAX; i++) {
2545 int set;
2546
2547 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2548 if (set)
2549 fio_debug |= (1UL << i);
2550 }
2551
f3e8440f 2552 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
2553 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2554 update_graph_limits();
1cf6bca5 2555 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 2556
46974a7d
JA
2557 gtk_widget_destroy(dialog);
2558}
2559
0420ba6a
JA
2560static void about_dialog(GtkWidget *w, gpointer data)
2561{
81e4ea6e
JA
2562 const char *authors[] = {
2563 "Jens Axboe <axboe@kernel.dk>",
2564 "Stephen Carmeron <stephenmcameron@gmail.com>",
2565 NULL
2566 };
84a72ed3
JA
2567 const char *license[] = {
2568 "Fio is free software; you can redistribute it and/or modify "
2569 "it under the terms of the GNU General Public License as published by "
2570 "the Free Software Foundation; either version 2 of the License, or "
2571 "(at your option) any later version.\n",
2572 "Fio is distributed in the hope that it will be useful, "
2573 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2574 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2575 "GNU General Public License for more details.\n",
2576 "You should have received a copy of the GNU General Public License "
2577 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2578 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2579 };
2580 char *license_trans;
2581
2582 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2583 license[2], "\n", NULL);
81e4ea6e 2584
0420ba6a
JA
2585 gtk_show_about_dialog(NULL,
2586 "program-name", "gfio",
2587 "comments", "Gtk2 UI for fio",
84a72ed3 2588 "license", license_trans,
81e4ea6e
JA
2589 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2590 "authors", authors,
0420ba6a 2591 "version", fio_version_string,
81e4ea6e 2592 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
2593 "logo-icon-name", "fio",
2594 /* Must be last: */
81e4ea6e 2595 "wrap-license", TRUE,
0420ba6a 2596 NULL);
84a72ed3 2597
2f99deb0 2598 g_free(license_trans);
0420ba6a
JA
2599}
2600
2601static GtkActionEntry menu_items[] = {
46974a7d 2602 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 2603 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
16ce5adf 2604 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
46974a7d 2605 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 2606 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
16ce5adf 2607 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
46974a7d
JA
2608 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2609 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2610 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 2611 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
781ccba6 2612 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
bc271d87 2613 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
85dd01e7
JA
2614 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2615 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2616 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
46974a7d
JA
2617 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2618 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 2619};
3e47bd25 2620static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
2621
2622static const gchar *ui_string = " \
2623 <ui> \
2624 <menubar name=\"MainMenu\"> \
2625 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0 2626 <menuitem name=\"New\" action=\"NewFile\" /> \
bf64138b 2627 <menuitem name=\"Open\" action=\"OpenFile\" /> \
16ce5adf 2628 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2f99deb0 2629 <separator name=\"Separator1\"/> \
0420ba6a 2630 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 2631 <separator name=\"Separator2\"/> \
2f99deb0
JA
2632 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2633 <separator name=\"Separator3\"/> \
261f21d0
JA
2634 <placeholder name=\"FileRecentFiles\"/> \
2635 <separator name=\"Separator4\"/> \
0420ba6a
JA
2636 <menuitem name=\"Quit\" action=\"Quit\" /> \
2637 </menu> \
16ce5adf 2638 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
85dd01e7 2639 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
261f21d0 2640 <separator name=\"Separator5\"/> \
85dd01e7
JA
2641 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2642 <menuitem name=\"Send job\" action=\"SendJob\" /> \
261f21d0 2643 <separator name=\"Separator6\"/> \
85dd01e7 2644 <menuitem name=\"Start job\" action=\"StartJob\" /> \
16ce5adf 2645 </menu>\
9b260bdf 2646 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
781ccba6
JA
2647 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2648 <separator name=\"Separator7\"/> \
9b260bdf
JA
2649 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2650 </menu>\
0420ba6a
JA
2651 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2652 <menuitem name=\"About\" action=\"About\" /> \
2653 </menu> \
2654 </menubar> \
2655 </ui> \
2656";
2657
4cbe7211
JA
2658static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2659 struct gui *ui)
0420ba6a 2660{
ca664f49 2661 GtkActionGroup *action_group;
0420ba6a
JA
2662 GError *error = 0;
2663
2664 action_group = gtk_action_group_new("Menu");
4cbe7211 2665 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
2666
2667 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2668 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2669
2670 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
02421e69 2671
0420ba6a
JA
2672 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2673}
2674
2675void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2676 GtkWidget *vbox, GtkUIManager *ui_manager)
2677{
2678 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2679}
2680
c80b74b0
JA
2681static void combo_entry_changed(GtkComboBox *box, gpointer data)
2682{
2683 struct gui_entry *ge = (struct gui_entry *) data;
2684 gint index;
2685
2686 index = gtk_combo_box_get_active(box);
2687
2688 multitext_set_entry(&ge->eta.iotype, index);
99d633af 2689 multitext_set_entry(&ge->eta.bs, index);
c80b74b0
JA
2690 multitext_set_entry(&ge->eta.ioengine, index);
2691 multitext_set_entry(&ge->eta.iodepth, index);
2692}
2693
2694static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2695{
2696 struct gui_entry *ge = (struct gui_entry *) data;
2697
2698 multitext_free(&ge->eta.iotype);
99d633af 2699 multitext_free(&ge->eta.bs);
c80b74b0
JA
2700 multitext_free(&ge->eta.ioengine);
2701 multitext_free(&ge->eta.iodepth);
2702}
2703
2f99deb0 2704static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 2705{
2f99deb0 2706 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 2707 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
0420ba6a 2708
2f99deb0 2709 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 2710
65476336
JA
2711 top_align = gtk_alignment_new(0, 0, 1, 0);
2712 top_vbox = gtk_vbox_new(FALSE, 3);
2713 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2714 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2f99deb0
JA
2715
2716 probe = gtk_frame_new("Job");
2717 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2718 probe_frame = gtk_vbox_new(FALSE, 3);
2719 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2720
2721 probe_box = gtk_hbox_new(FALSE, 3);
2722 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2723 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2724 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2725 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2726 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2727
2728 probe_box = gtk_hbox_new(FALSE, 3);
2729 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2730
3863d1ad 2731 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
c80b74b0
JA
2732 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2733 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2734 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
99d633af 2735 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
c80b74b0
JA
2736 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2737 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2f99deb0
JA
2738 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2739 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2740
2741 probe_box = gtk_hbox_new(FALSE, 3);
2742 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2743 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2744 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2745 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2746 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2747
2748 /*
2749 * Only add this if we have a commit rate
2839f0c6 2750 */
2f99deb0
JA
2751#if 0
2752 probe_box = gtk_hbox_new(FALSE, 3);
2753 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 2754
2f99deb0
JA
2755 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2756 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 2757
2f99deb0
JA
2758 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2759 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2760#endif
ff1f3280 2761
2f99deb0
JA
2762 /*
2763 * Set up a drawing area and IOPS and bandwidth graphs
2764 */
2f99deb0 2765 ge->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 2766 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
57f9d28e 2767 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
2768 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2769 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2770 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2771 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2772 G_CALLBACK(on_config_drawing_area), &ge->graphs);
65476336
JA
2773 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2774 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 2775 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 2776 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 2777 ge->graphs.drawing_area);
65476336 2778 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
04cc6b77 2779
2f99deb0
JA
2780 setup_graphs(&ge->graphs);
2781
2782 /*
2783 * Set up alignments for widgets at the bottom of ui,
2784 * align bottom left, expand horizontally but not vertically
2785 */
65476336 2786 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2f99deb0 2787 ge->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
2788 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2789 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2f99deb0
JA
2790
2791 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
0420ba6a 2792
c36f98d9 2793 /*
2f99deb0
JA
2794 * Set up thread status progress bar
2795 */
2796 ge->thread_status_pb = gtk_progress_bar_new();
2797 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2798 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2799 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2800
2801
2802 return main_vbox;
2803}
2804
2805static GtkWidget *new_main_page(struct gui *ui)
2806{
2807 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 2808 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2f99deb0
JA
2809
2810 main_vbox = gtk_vbox_new(FALSE, 3);
2811
2812 /*
2813 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
2814 * align top left, expand horizontally but not vertically
2815 */
65476336
JA
2816 top_align = gtk_alignment_new(0, 0, 1, 0);
2817 top_vbox = gtk_vbox_new(FALSE, 0);
2818 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2819 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
c36f98d9 2820
2f99deb0
JA
2821 probe = gtk_frame_new("Run statistics");
2822 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
2823 probe_frame = gtk_vbox_new(FALSE, 3);
2824 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2825
2826 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 2827 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 2828 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
ca850992
JA
2829 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2830 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2831 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2832 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 2833
807f9971
JA
2834 /*
2835 * Only add this if we have a commit rate
2836 */
2837#if 0
3e47bd25
JA
2838 probe_box = gtk_hbox_new(FALSE, 3);
2839 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
2840
2841 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2842 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2843
3e47bd25
JA
2844 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2845 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 2846#endif
3e47bd25 2847
736f2dff 2848 /*
2fd3bb0e 2849 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 2850 */
2f99deb0 2851 ui->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 2852 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
57f9d28e 2853 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
2854 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2855 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2856 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2857 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2858 G_CALLBACK(on_config_drawing_area), &ui->graphs);
65476336
JA
2859 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2860 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
736f2dff 2861 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 2862 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 2863 ui->graphs.drawing_area);
65476336 2864 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
e164534f 2865 TRUE, TRUE, 0);
736f2dff 2866
2f99deb0 2867 setup_graphs(&ui->graphs);
2fd3bb0e 2868
c36f98d9
SC
2869 /*
2870 * Set up alignments for widgets at the bottom of ui,
2871 * align bottom left, expand horizontally but not vertically
2872 */
65476336 2873 bottom_align = gtk_alignment_new(0, 1, 1, 0);
c36f98d9 2874 ui->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
2875 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2876 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
c36f98d9 2877
3ec62ec4
JA
2878 /*
2879 * Set up thread status progress bar
2880 */
2881 ui->thread_status_pb = gtk_progress_bar_new();
2882 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 2883 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
2884 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2885
2f99deb0
JA
2886 return main_vbox;
2887}
2888
2889static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2890 guint page, gpointer data)
2891
2892{
02421e69 2893 struct gui *ui = (struct gui *) data;
85dd01e7
JA
2894 struct gui_entry *ge;
2895
2896 if (!page) {
2897 set_job_menu_visible(ui, 0);
781ccba6 2898 set_view_results_visible(ui, 0);
85dd01e7
JA
2899 return TRUE;
2900 }
2901
2902 set_job_menu_visible(ui, 1);
38634cb1 2903 ge = get_ge_from_page(page, NULL);
85dd01e7
JA
2904 if (ge)
2905 update_button_states(ui, ge);
02421e69 2906
2f99deb0
JA
2907 return TRUE;
2908}
2909
38634cb1
JA
2910static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
2911{
2912 time_t time_a = gtk_recent_info_get_visited(a);
2913 time_t time_b = gtk_recent_info_get_visited(b);
2914
2915 return time_b - time_a;
2916}
2917
2918static void add_recent_file_items(struct gui *ui)
2919{
2920 const gchar *gfio = g_get_application_name();
2921 GList *items, *item;
2922 int i = 0;
2923
2924 if (ui->recent_ui_id) {
2925 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
2926 gtk_ui_manager_ensure_update(ui->uimanager);
2927 }
2928 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
2929
2930 if (ui->actiongroup) {
2931 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
2932 g_object_unref(ui->actiongroup);
2933 }
2934 ui->actiongroup = gtk_action_group_new("RecentFileActions");
2935
2936 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
2937
2938 items = gtk_recent_manager_get_items(ui->recentmanager);
2939 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
2940
2941 for (item = items; item && item->data; item = g_list_next(item)) {
2942 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
2943 gchar *action_name;
2944 const gchar *label;
2945 GtkAction *action;
2946
2947 if (!gtk_recent_info_has_application(info, gfio))
2948 continue;
2949
2950 /*
2951 * We only support local files for now
2952 */
2953 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
2954 continue;
2955
2956 action_name = g_strdup_printf("RecentFile%u", i++);
2957 label = gtk_recent_info_get_display_name(info);
2958
2959 action = g_object_new(GTK_TYPE_ACTION,
2960 "name", action_name,
2961 "label", label, NULL);
2962
2963 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
2964 gtk_recent_info_ref(info),
2965 (GDestroyNotify) gtk_recent_info_unref);
2966
2967
2968 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
2969
2970 gtk_action_group_add_action(ui->actiongroup, action);
2971 g_object_unref(action);
2972
2973 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
2974 "/MainMenu/FileMenu/FileRecentFiles",
2975 label, action_name,
2976 GTK_UI_MANAGER_MENUITEM, FALSE);
2977
2978 g_free(action_name);
2979
2980 if (i == 8)
2981 break;
2982 }
2983
2984 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
2985 g_list_free(items);
2986}
2987
a6790906
JA
2988static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
2989 gint x, gint y, GtkSelectionData *data,
2990 guint info, guint time)
2991{
2992 struct gui *ui = &main_ui;
2993 gchar **uris;
2994 GtkWidget *source;
2995 int i;
2996
2997 source = gtk_drag_get_source_widget(ctx);
2998 if (source && widget == gtk_widget_get_toplevel(source)) {
2999 gtk_drag_finish(ctx, FALSE, FALSE, time);
3000 return;
3001 }
3002
3003 uris = gtk_selection_data_get_uris(data);
3004 if (!uris) {
3005 gtk_drag_finish(ctx, FALSE, FALSE, time);
3006 return;
3007 }
3008
3009 i = 0;
3010 while (uris[i]) {
3011 if (do_file_open_with_tab(ui, uris[i]))
3012 break;
3013 i++;
3014 }
3015
3016 gtk_drag_finish(ctx, TRUE, FALSE, time);
3017 g_strfreev(uris);
3018}
3019
2f99deb0
JA
3020static void init_ui(int *argc, char **argv[], struct gui *ui)
3021{
3022 GtkSettings *settings;
02421e69 3023 GtkWidget *vbox;
2f99deb0
JA
3024
3025 /* Magical g*thread incantation, you just need this thread stuff.
3026 * Without it, the update that happens in gfio_update_thread_status
3027 * doesn't really happen in a timely fashion, you need expose events
3028 */
3029 if (!g_thread_supported())
3030 g_thread_init(NULL);
3031 gdk_threads_init();
3032
3033 gtk_init(argc, argv);
3034 settings = gtk_settings_get_default();
3035 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3036 g_type_init();
814479d5 3037 gdk_color_parse("white", &white);
2f99deb0
JA
3038
3039 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
814479d5 3040 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2f99deb0
JA
3041 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3042
3043 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3044 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3045
3046 ui->vbox = gtk_vbox_new(FALSE, 0);
3047 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3048
02421e69
JA
3049 ui->uimanager = gtk_ui_manager_new();
3050 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3051 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2f99deb0 3052
38634cb1
JA
3053 ui->recentmanager = gtk_recent_manager_get_default();
3054 add_recent_file_items(ui);
3055
2f99deb0
JA
3056 ui->notebook = gtk_notebook_new();
3057 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 3058 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 3059 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
3060 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3061
3062 vbox = new_main_page(ui);
a6790906
JA
3063 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3064 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3065 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
2f99deb0
JA
3066
3067 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3068
9b260bdf 3069 gfio_ui_setup_log(ui);
3ec62ec4 3070
ff1f3280
SC
3071 gtk_widget_show_all(ui->window);
3072}
3073
8232e285 3074int main(int argc, char *argv[], char *envp[])
ff1f3280 3075{
8232e285
SC
3076 if (initialize_fio(envp))
3077 return 1;
0420ba6a
JA
3078 if (fio_init_options())
3079 return 1;
a1820207 3080
2f99deb0
JA
3081 memset(&main_ui, 0, sizeof(main_ui));
3082 INIT_FLIST_HEAD(&main_ui.list);
3083
3084 init_ui(&argc, &argv, &main_ui);
5b7573ab 3085
2839f0c6 3086 gdk_threads_enter();
ff1f3280 3087 gtk_main();
2839f0c6 3088 gdk_threads_leave();
ff1f3280
SC
3089 return 0;
3090}