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