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