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