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