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