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