gfio: put results from same job file into a notebook
[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>
5 *
6 * The license below covers all files distributed with fio unless otherwise
7 * noted in the file itself.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
8232e285 23#include <locale.h>
60f6b330 24#include <malloc.h>
8232e285 25
5b7573ab 26#include <glib.h>
ff1f3280
SC
27#include <gtk/gtk.h>
28
8232e285
SC
29#include "fio.h"
30
3e47bd25
JA
31static void gfio_update_thread_status(char *status_message, double perc);
32
f3074008
SC
33#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
3e47bd25 37static void connect_clicked(GtkWidget *widget, gpointer data);
f3074008
SC
38static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40static struct button_spec {
41 const char *buttontext;
42 clickfunction f;
43 const char *tooltiptext;
3e47bd25 44 const int start_insensitive;
f3074008 45} buttonspeclist[] = {
3e47bd25
JA
46#define CONNECT_BUTTON 0
47#define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
f3074008
SC
49 { "Start Job",
50 start_job_clicked,
3e47bd25 51 "Send current fio job to fio server to be executed", 1 },
f3074008
SC
52};
53
843ad237
JA
54struct probe_widget {
55 GtkWidget *hostname;
56 GtkWidget *os;
57 GtkWidget *arch;
58 GtkWidget *fio_ver;
59};
60
3e47bd25 61struct eta_widget {
807f9971
JA
62 GtkWidget *name;
63 GtkWidget *iotype;
64 GtkWidget *ioengine;
65 GtkWidget *iodepth;
3e47bd25
JA
66 GtkWidget *jobs;
67 GtkWidget *files;
68 GtkWidget *read_bw;
69 GtkWidget *read_iops;
70 GtkWidget *cr_bw;
71 GtkWidget *cr_iops;
72 GtkWidget *write_bw;
73 GtkWidget *write_iops;
74 GtkWidget *cw_bw;
75 GtkWidget *cw_iops;
76};
77
ff1f3280
SC
78struct gui {
79 GtkWidget *window;
5b7573ab 80 GtkWidget *vbox;
c36f98d9
SC
81 GtkWidget *topvbox;
82 GtkWidget *topalign;
83 GtkWidget *bottomalign;
04cc6b77 84 GtkWidget *thread_status_pb;
f3074008
SC
85 GtkWidget *buttonbox;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
736f2dff
SC
87 GtkWidget *scrolled_window;
88 GtkWidget *textview;
0420ba6a
JA
89 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
f9d40b48
JA
91 GtkWidget *results_notebook;
92 GtkWidget *results_window;
736f2dff 93 GtkTextBuffer *text;
843ad237 94 struct probe_widget probe;
3e47bd25 95 struct eta_widget eta;
3ec62ec4 96 int connected;
25927259 97 pthread_t t;
0420ba6a 98
3ec62ec4 99 struct fio_client *client;
0420ba6a
JA
100 int nr_job_files;
101 char **job_files;
5b7573ab 102} ui;
ff1f3280 103
8663ea65
JA
104static void clear_ui_info(struct gui *ui)
105{
106 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
107 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
108 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
109 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
ca850992
JA
110 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
111 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
112 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
113 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
114 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
115 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
116 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
117 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
118 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
119 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
8663ea65
JA
120}
121
3650a3ca
JA
122static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
123{
124 GtkWidget *entry, *frame;
125
126 frame = gtk_frame_new(label);
127 entry = gtk_entry_new();
1c1e4a5b 128 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
3650a3ca
JA
129 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
130 gtk_container_add(GTK_CONTAINER(frame), entry);
131
132 return entry;
133}
134
135static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
136{
137 GtkWidget *label_widget;
138 GtkWidget *frame;
139
140 frame = gtk_frame_new(label);
141 label_widget = gtk_label_new(NULL);
142 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
143 gtk_container_add(GTK_CONTAINER(frame), label_widget);
144
145 return label_widget;
146}
147
148static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
149{
150 GtkWidget *button, *box;
151
152 box = gtk_hbox_new(FALSE, 3);
153 gtk_container_add(GTK_CONTAINER(hbox), box);
154
155 button = gtk_spin_button_new_with_range(min, max, 1.0);
156 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
157
158 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
159 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
160
161 return button;
162}
163
3ec62ec4
JA
164static void gfio_set_connected(struct gui *ui, int connected)
165{
166 if (connected) {
167 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
168 ui->connected = 1;
169 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
170 } else {
171 ui->connected = 0;
172 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
173 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
174 }
175}
176
3650a3ca
JA
177static void label_set_int_value(GtkWidget *entry, unsigned int val)
178{
179 char tmp[80];
180
181 sprintf(tmp, "%u", val);
182 gtk_label_set_text(GTK_LABEL(entry), tmp);
183}
184
185static void entry_set_int_value(GtkWidget *entry, unsigned int val)
186{
187 char tmp[80];
188
189 sprintf(tmp, "%u", val);
190 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
191}
192
a269790c
JA
193#define ALIGN_LEFT 1
194#define ALIGN_RIGHT 2
195#define INVISIBLE 4
196#define UNSORTABLE 8
197
198GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
199{
200 GtkCellRenderer *renderer;
201 GtkTreeViewColumn *col;
202 double xalign = 0.0; /* left as default */
203 PangoAlignment align;
204 gboolean visible;
205
206 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
207 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
208 PANGO_ALIGN_CENTER;
209 visible = !(flags & INVISIBLE);
210
211 renderer = gtk_cell_renderer_text_new();
212 col = gtk_tree_view_column_new();
213
214 gtk_tree_view_column_set_title(col, title);
215 if (!(flags & UNSORTABLE))
216 gtk_tree_view_column_set_sort_column_id(col, index);
217 gtk_tree_view_column_set_resizable(col, TRUE);
218 gtk_tree_view_column_pack_start(col, renderer, TRUE);
219 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
220 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
221 switch (align) {
222 case PANGO_ALIGN_LEFT:
223 xalign = 0.0;
224 break;
225 case PANGO_ALIGN_CENTER:
226 xalign = 0.5;
227 break;
228 case PANGO_ALIGN_RIGHT:
229 xalign = 1.0;
230 break;
231 }
232 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
233 gtk_tree_view_column_set_visible(col, visible);
234 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
235 return col;
236}
237
238static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
239 fio_fp64_t *plist,
240 unsigned int len,
241 const char *base,
242 unsigned int scale)
243{
244 GType types[FIO_IO_U_LIST_MAX_LEN];
245 GtkWidget *tree_view;
246 GtkTreeSelection *selection;
247 GtkListStore *model;
248 GtkTreeIter iter;
249 int i;
250
251 for (i = 0; i < len; i++)
252 types[i] = G_TYPE_INT;
253
254 model = gtk_list_store_newv(len, types);
255
256 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
257 gtk_widget_set_can_focus(tree_view, FALSE);
258
259 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
260 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
261
262 for (i = 0; i < len; i++) {
263 char fbuf[8];
264
265 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
266 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
267 }
268
269 gtk_list_store_append(model, &iter);
270
271 for (i = 0; i < len; i++)
272 gtk_list_store_set(model, &iter, i, ovals[i], -1);
273
274 return tree_view;
275}
276
277static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
278 int ddir)
279{
280 unsigned int *io_u_plat = ts->io_u_plat[ddir];
281 unsigned long nr = ts->clat_stat[ddir].samples;
282 fio_fp64_t *plist = ts->percentile_list;
283 unsigned int *ovals, len, minv, maxv, scale_down;
284 const char *base;
285 GtkWidget *tree_view, *frame, *hbox;
286 char tmp[64];
287
288 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
289 if (!len)
290 goto out;
291
292 /*
293 * We default to usecs, but if the value range is such that we
294 * should scale down to msecs, do that.
295 */
296 if (minv > 2000 && maxv > 99999) {
297 scale_down = 1;
298 base = "msec";
299 } else {
300 scale_down = 0;
301 base = "usec";
302 }
303
304 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
305
306 sprintf(tmp, "Completion percentiles (%s)", base);
307 frame = gtk_frame_new(tmp);
308 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
309
310 hbox = gtk_hbox_new(FALSE, 3);
311 gtk_container_add(GTK_CONTAINER(frame), hbox);
312
313 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
314out:
315 if (ovals)
316 free(ovals);
317}
318
3650a3ca
JA
319static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
320 unsigned long max, double mean, double dev)
321{
322 const char *base = "(usec)";
1c1e4a5b 323 GtkWidget *hbox, *label, *frame;
3650a3ca
JA
324 char *minp, *maxp;
325 char tmp[64];
326
327 if (!usec_to_msec(&min, &max, &mean, &dev))
328 base = "(msec)";
329
330 minp = num2str(min, 6, 1, 0);
331 maxp = num2str(max, 6, 1, 0);
332
3650a3ca
JA
333 sprintf(tmp, "%s %s", name, base);
334 frame = gtk_frame_new(tmp);
335 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
336
3650a3ca 337 hbox = gtk_hbox_new(FALSE, 3);
1c1e4a5b 338 gtk_container_add(GTK_CONTAINER(frame), hbox);
3650a3ca
JA
339
340 label = new_info_label_in_frame(hbox, "Minimum");
341 gtk_label_set_text(GTK_LABEL(label), minp);
342 label = new_info_label_in_frame(hbox, "Maximum");
343 gtk_label_set_text(GTK_LABEL(label), maxp);
344 label = new_info_label_in_frame(hbox, "Average");
345 sprintf(tmp, "%5.02f", mean);
346 gtk_label_set_text(GTK_LABEL(label), tmp);
347 label = new_info_label_in_frame(hbox, "Standard deviation");
348 sprintf(tmp, "%5.02f", dev);
349 gtk_label_set_text(GTK_LABEL(label), tmp);
350
351 free(minp);
352 free(maxp);
353
354}
355
ca850992
JA
356#define GFIO_CLAT 1
357#define GFIO_SLAT 2
358#define GFIO_LAT 4
359
3650a3ca
JA
360static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
361 struct thread_stat *ts, int ddir)
362{
363 const char *ddir_label[2] = { "Read", "Write" };
0b761306 364 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
3650a3ca
JA
365 unsigned long min, max, runt;
366 unsigned long long bw, iops;
ca850992 367 unsigned int flags = 0;
3650a3ca
JA
368 double mean, dev;
369 char *io_p, *bw_p, *iops_p;
370 int i2p;
371
372 if (!ts->runtime[ddir])
373 return;
374
375 i2p = is_power_of_2(rs->kb_base);
376 runt = ts->runtime[ddir];
377
378 bw = (1000 * ts->io_bytes[ddir]) / runt;
379 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
380 bw_p = num2str(bw, 6, 1, i2p);
381
382 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
383 iops_p = num2str(iops, 6, 1, 0);
384
385 box = gtk_hbox_new(FALSE, 3);
386 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
387
388 frame = gtk_frame_new(ddir_label[ddir]);
389 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
390
0b761306
JA
391 main_vbox = gtk_vbox_new(FALSE, 3);
392 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
3650a3ca
JA
393
394 box = gtk_hbox_new(FALSE, 3);
0b761306 395 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
3650a3ca
JA
396
397 label = new_info_label_in_frame(box, "IO");
398 gtk_label_set_text(GTK_LABEL(label), io_p);
399 label = new_info_label_in_frame(box, "Bandwidth");
400 gtk_label_set_text(GTK_LABEL(label), bw_p);
401 label = new_info_label_in_frame(box, "IOPS");
402 gtk_label_set_text(GTK_LABEL(label), iops_p);
403 label = new_info_label_in_frame(box, "Runtime (msec)");
404 label_set_int_value(label, ts->runtime[ddir]);
405
ca850992
JA
406 if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
407 double p_of_agg = 100.0;
408 const char *bw_str = "KB";
409 char tmp[32];
410
411 if (rs->agg[ddir]) {
412 p_of_agg = mean * 100 / (double) rs->agg[ddir];
413 if (p_of_agg > 100.0)
414 p_of_agg = 100.0;
415 }
416
417 if (mean > 999999.9) {
418 min /= 1000.0;
419 max /= 1000.0;
420 mean /= 1000.0;
421 dev /= 1000.0;
422 bw_str = "MB";
423 }
424
0b761306
JA
425 sprintf(tmp, "Bandwidth (%s)", bw_str);
426 frame = gtk_frame_new(tmp);
427 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
ca850992 428
0b761306
JA
429 box = gtk_hbox_new(FALSE, 3);
430 gtk_container_add(GTK_CONTAINER(frame), box);
ca850992 431
0b761306 432 label = new_info_label_in_frame(box, "Minimum");
ca850992 433 label_set_int_value(label, min);
0b761306 434 label = new_info_label_in_frame(box, "Maximum");
ca850992 435 label_set_int_value(label, max);
0b761306 436 label = new_info_label_in_frame(box, "Percentage of jobs");
ca850992
JA
437 sprintf(tmp, "%3.2f%%", p_of_agg);
438 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 439 label = new_info_label_in_frame(box, "Average");
ca850992
JA
440 sprintf(tmp, "%5.02f", mean);
441 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 442 label = new_info_label_in_frame(box, "Standard deviation");
ca850992
JA
443 sprintf(tmp, "%5.02f", dev);
444 gtk_label_set_text(GTK_LABEL(label), tmp);
445 }
446
2b089892
JA
447 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
448 flags |= GFIO_SLAT;
449 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
450 flags |= GFIO_CLAT;
451 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
452 flags |= GFIO_LAT;
453
454 if (flags) {
455 frame = gtk_frame_new("Latency");
456 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
457
458 vbox = gtk_vbox_new(FALSE, 3);
459 gtk_container_add(GTK_CONTAINER(frame), vbox);
460
461 if (flags & GFIO_SLAT)
462 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
463 if (flags & GFIO_CLAT)
464 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
465 if (flags & GFIO_LAT)
466 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
467 }
468
469 if (ts->clat_percentiles)
470 gfio_show_clat_percentiles(main_vbox, ts, ddir);
471
472
3650a3ca
JA
473 free(io_p);
474 free(bw_p);
475 free(iops_p);
476}
477
e5bd1347
JA
478static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
479 const char **labels)
480{
481 GtkWidget *tree_view;
482 GtkTreeSelection *selection;
483 GtkListStore *model;
484 GtkTreeIter iter;
485 GType *types;
486 int i, skipped;
487
488 /*
489 * Check if all are empty, in which case don't bother
490 */
491 for (i = 0, skipped = 0; i < num; i++)
492 if (lat[i] <= 0.0)
493 skipped++;
494
495 if (skipped == num)
496 return NULL;
497
498 types = malloc(num * sizeof(GType));
499
500 for (i = 0; i < num; i++)
501 types[i] = G_TYPE_STRING;
502
503 model = gtk_list_store_newv(num, types);
504 free(types);
505 types = NULL;
506
507 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
508 gtk_widget_set_can_focus(tree_view, FALSE);
509
510 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
511 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
512
513 for (i = 0; i < num; i++)
514 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
515
516 gtk_list_store_append(model, &iter);
517
518 for (i = 0; i < num; i++) {
519 char fbuf[32];
520
521 if (lat[i] <= 0.0)
522 sprintf(fbuf, "0.00");
523 else
524 sprintf(fbuf, "%3.2f%%", lat[i]);
525
526 gtk_list_store_set(model, &iter, i, fbuf, -1);
527 }
528
529 return tree_view;
530}
531
532static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
533{
534 GtkWidget *box, *frame, *tree_view;
535 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
536 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
537 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
538 "250", "500", "750", "1000", };
539 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
540 "250", "500", "750", "1000", "2000",
541 ">= 2000", };
542
543 stat_calc_lat_u(ts, io_u_lat_u);
544 stat_calc_lat_m(ts, io_u_lat_m);
545
546 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
547 if (tree_view) {
548 frame = gtk_frame_new("Latency buckets (usec)");
549 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
550
551 box = gtk_hbox_new(FALSE, 3);
552 gtk_container_add(GTK_CONTAINER(frame), box);
553 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
554 }
555
556 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
557 if (tree_view) {
558 frame = gtk_frame_new("Latency buckets (msec)");
559 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
560
561 box = gtk_hbox_new(FALSE, 3);
562 gtk_container_add(GTK_CONTAINER(frame), box);
563 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
564 }
565}
566
2e33101f
JA
567static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
568{
569 GtkWidget *box, *frame, *entry;
570 double usr_cpu, sys_cpu;
571 unsigned long runtime;
572 char tmp[32];
573
574 runtime = ts->total_run_time;
575 if (runtime) {
576 double runt = (double) runtime;
577
578 usr_cpu = (double) ts->usr_time * 100 / runt;
579 sys_cpu = (double) ts->sys_time * 100 / runt;
580 } else {
581 usr_cpu = 0;
582 sys_cpu = 0;
583 }
584
585 frame = gtk_frame_new("OS resources");
586 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
587
588 box = gtk_hbox_new(FALSE, 3);
589 gtk_container_add(GTK_CONTAINER(frame), box);
590
591 entry = new_info_entry_in_frame(box, "User CPU");
592 sprintf(tmp, "%3.2f%%", usr_cpu);
593 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
594 entry = new_info_entry_in_frame(box, "System CPU");
595 sprintf(tmp, "%3.2f%%", sys_cpu);
596 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
597 entry = new_info_entry_in_frame(box, "Context switches");
598 entry_set_int_value(entry, ts->ctx);
599 entry = new_info_entry_in_frame(box, "Major faults");
600 entry_set_int_value(entry, ts->majf);
601 entry = new_info_entry_in_frame(box, "Minor faults");
602 entry_set_int_value(entry, ts->minf);
603}
19998dbc
JA
604static void gfio_add_sc_depths_tree(GtkListStore *model,
605 struct thread_stat *ts, unsigned int len,
606 int submit)
2e33101f
JA
607{
608 double io_u_dist[FIO_IO_U_MAP_NR];
2e33101f 609 GtkTreeIter iter;
19998dbc
JA
610 /* Bits 0, and 3-8 */
611 const int add_mask = 0x1f9;
612 int i, j;
2e33101f 613
19998dbc
JA
614 if (submit)
615 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
616 else
617 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
2e33101f 618
19998dbc 619 gtk_list_store_append(model, &iter);
2e33101f 620
19998dbc 621 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
2e33101f 622
19998dbc
JA
623 for (i = 1, j = 0; i < len; i++) {
624 char fbuf[32];
2e33101f 625
19998dbc
JA
626 if (!(add_mask & (1UL << (i - 1))))
627 sprintf(fbuf, "0.0%%");
628 else {
629 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
630 j++;
631 }
2e33101f 632
19998dbc
JA
633 gtk_list_store_set(model, &iter, i, fbuf, -1);
634 }
2e33101f 635
19998dbc 636}
2e33101f 637
19998dbc
JA
638static void gfio_add_total_depths_tree(GtkListStore *model,
639 struct thread_stat *ts, unsigned int len)
640{
641 double io_u_dist[FIO_IO_U_MAP_NR];
642 GtkTreeIter iter;
643 /* Bits 1-6, and 8 */
644 const int add_mask = 0x17e;
645 int i, j;
646
647 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
2e33101f
JA
648
649 gtk_list_store_append(model, &iter);
650
19998dbc
JA
651 gtk_list_store_set(model, &iter, 0, "Total", -1);
652
653 for (i = 1, j = 0; i < len; i++) {
2e33101f
JA
654 char fbuf[32];
655
19998dbc
JA
656 if (!(add_mask & (1UL << (i - 1))))
657 sprintf(fbuf, "0.0%%");
658 else {
659 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
660 j++;
2e33101f
JA
661 }
662
2e33101f
JA
663 gtk_list_store_set(model, &iter, i, fbuf, -1);
664 }
665
19998dbc 666}
2e33101f 667
19998dbc
JA
668static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
669{
670 GtkWidget *frame, *box, *tree_view;
671 GtkTreeSelection *selection;
672 GtkListStore *model;
673 GType types[FIO_IO_U_MAP_NR + 1];
674 int i;
675#define NR_LABELS 10
676 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
2e33101f 677
19998dbc
JA
678 frame = gtk_frame_new("IO depths");
679 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2e33101f 680
19998dbc
JA
681 box = gtk_hbox_new(FALSE, 3);
682 gtk_container_add(GTK_CONTAINER(frame), box);
2e33101f 683
19998dbc
JA
684 for (i = 0; i < NR_LABELS; i++)
685 types[i] = G_TYPE_STRING;
2e33101f 686
19998dbc 687 model = gtk_list_store_newv(NR_LABELS, types);
2e33101f 688
19998dbc
JA
689 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
690 gtk_widget_set_can_focus(tree_view, FALSE);
2e33101f 691
19998dbc
JA
692 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
693 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
694
695 for (i = 0; i < NR_LABELS; i++)
696 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
697
698 gfio_add_total_depths_tree(model, ts, NR_LABELS);
699 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
700 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
2e33101f
JA
701
702 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
703}
704
f9d40b48
JA
705static gboolean results_window_delete(GtkWidget *w, gpointer data)
706{
707 struct gui *ui = (struct gui *) data;
708
709 gtk_widget_destroy(w);
710 ui->results_window = NULL;
711 ui->results_notebook = NULL;
712 return TRUE;
713}
714
715static GtkWidget *get_results_window(struct gui *ui)
716{
717 GtkWidget *win, *notebook;
718
719 if (ui->results_window)
720 return ui->results_notebook;
721
722 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
723 gtk_window_set_title(GTK_WINDOW(win), "Results");
724 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
725 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
726
727 notebook = gtk_notebook_new();
728 gtk_container_add(GTK_CONTAINER(win), notebook);
729
730 ui->results_window = win;
731 ui->results_notebook = notebook;
732 return ui->results_notebook;
733}
734
3650a3ca
JA
735static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
736 struct group_run_stats *rs)
737{
f9d40b48 738 GtkWidget *res_win, *box, *vbox, *entry;
3650a3ca
JA
739 struct gui *ui = client->client_data;
740
741 gdk_threads_enter();
742
f9d40b48 743 res_win = get_results_window(ui);
3650a3ca
JA
744
745 vbox = gtk_vbox_new(FALSE, 3);
3650a3ca
JA
746
747 box = gtk_hbox_new(TRUE, 3);
748 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
749
f9d40b48
JA
750 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
751
3650a3ca
JA
752 entry = new_info_entry_in_frame(box, "Name");
753 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
754 if (strlen(ts->description)) {
755 entry = new_info_entry_in_frame(box, "Description");
756 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
757 }
758 entry = new_info_entry_in_frame(box, "Group ID");
759 entry_set_int_value(entry, ts->groupid);
760 entry = new_info_entry_in_frame(box, "Jobs");
761 entry_set_int_value(entry, ts->members);
762 entry = new_info_entry_in_frame(box, "Error");
763 entry_set_int_value(entry, ts->error);
764 entry = new_info_entry_in_frame(box, "PID");
765 entry_set_int_value(entry, ts->pid);
766
767 if (ts->io_bytes[DDIR_READ])
768 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
769 if (ts->io_bytes[DDIR_WRITE])
770 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
771
e5bd1347 772 gfio_show_latency_buckets(vbox, ts);
2e33101f
JA
773 gfio_show_cpu_usage(vbox, ts);
774 gfio_show_io_depths(vbox, ts);
e5bd1347 775
f9d40b48 776 gtk_widget_show_all(ui->results_window);
3650a3ca
JA
777 gdk_threads_leave();
778}
779
084d1c6f 780static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 781{
807f9971 782#if 0
736f2dff
SC
783 GtkTextBuffer *buffer;
784 GtkTextIter end;
785
786 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
787 gdk_threads_enter();
788 gtk_text_buffer_get_end_iter(buffer, &end);
789 gtk_text_buffer_insert(buffer, &end, buf, -1);
790 gdk_threads_leave();
791 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
792 &end, 0.0, FALSE, 0.0,0.0);
807f9971 793#else
084d1c6f 794 fio_client_ops.text_op(client, cmd);
807f9971 795#endif
a1820207
SC
796}
797
798static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
799{
0050e5f2 800 gdk_threads_enter();
a1820207
SC
801 printf("gfio_disk_util_op called\n");
802 fio_client_ops.disk_util(client, cmd);
0050e5f2 803 gdk_threads_leave();
a1820207
SC
804}
805
3650a3ca
JA
806extern int sum_stat_clients;
807extern struct thread_stat client_ts;
808extern struct group_run_stats client_gs;
809
810static int sum_stat_nr;
811
89e5fad9
JA
812static void gfio_thread_status_op(struct fio_client *client,
813 struct fio_net_cmd *cmd)
a1820207 814{
3650a3ca
JA
815 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
816
817 gfio_display_ts(client, &p->ts, &p->rs);
818
819 if (sum_stat_clients == 1)
820 return;
821
822 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
823 sum_group_stats(&client_gs, &p->rs);
824
825 client_ts.members++;
826 client_ts.groupid = p->ts.groupid;
827
828 if (++sum_stat_nr == sum_stat_clients) {
829 strcpy(client_ts.name, "All clients");
830 gfio_display_ts(client, &client_ts, &client_gs);
831 }
a1820207
SC
832}
833
89e5fad9
JA
834static void gfio_group_stats_op(struct fio_client *client,
835 struct fio_net_cmd *cmd)
a1820207 836{
0050e5f2 837 gdk_threads_enter();
a1820207 838 printf("gfio_group_stats_op called\n");
89e5fad9 839 fio_client_ops.group_stats(client, cmd);
0050e5f2 840 gdk_threads_leave();
a1820207
SC
841}
842
3e47bd25
JA
843static void gfio_update_eta(struct jobs_eta *je)
844{
845 static int eta_good;
846 char eta_str[128];
847 char output[256];
848 char tmp[32];
849 double perc = 0.0;
850 int i2p = 0;
851
0050e5f2
JA
852 gdk_threads_enter();
853
3e47bd25
JA
854 eta_str[0] = '\0';
855 output[0] = '\0';
856
857 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
858 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
859 eta_to_str(eta_str, je->eta_sec);
860 }
861
862 sprintf(tmp, "%u", je->nr_running);
ca850992 863 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
3e47bd25 864 sprintf(tmp, "%u", je->files_open);
ca850992 865 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
3e47bd25
JA
866
867#if 0
868 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
869 if (je->m_rate || je->t_rate) {
870 char *tr, *mr;
871
872 mr = num2str(je->m_rate, 4, 0, i2p);
873 tr = num2str(je->t_rate, 4, 0, i2p);
ca850992 874 gtk_entry_set_text(GTK_ENTRY(ui.eta);
3e47bd25
JA
875 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
876 free(tr);
877 free(mr);
878 } else if (je->m_iops || je->t_iops)
879 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 880
ca850992
JA
881 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
882 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
883 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
884 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
3e47bd25
JA
885#endif
886
887 if (je->eta_sec != INT_MAX && je->nr_running) {
888 char *iops_str[2];
889 char *rate_str[2];
890
891 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
892 strcpy(output, "-.-% done");
893 else {
894 eta_good = 1;
895 perc *= 100.0;
896 sprintf(output, "%3.1f%% done", perc);
897 }
898
899 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
900 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
901
902 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
903 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
904
ca850992
JA
905 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
906 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
907 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
908 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
3e47bd25
JA
909
910 free(rate_str[0]);
911 free(rate_str[1]);
912 free(iops_str[0]);
913 free(iops_str[1]);
914 }
915
916 if (eta_str[0]) {
917 char *dst = output + strlen(output);
918
919 sprintf(dst, " - %s", eta_str);
920 }
921
922 gfio_update_thread_status(output, perc);
0050e5f2 923 gdk_threads_leave();
3e47bd25
JA
924}
925
a1820207
SC
926static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
927{
843ad237
JA
928 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
929 const char *os, *arch;
930 char buf[64];
931
932 os = fio_get_os_string(probe->os);
933 if (!os)
934 os = "unknown";
935
936 arch = fio_get_arch_string(probe->arch);
937 if (!arch)
938 os = "unknown";
939
940 if (!client->name)
941 client->name = strdup((char *) probe->hostname);
942
0050e5f2
JA
943 gdk_threads_enter();
944
843ad237
JA
945 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
946 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
947 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
948 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
949 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
0050e5f2
JA
950
951 gdk_threads_leave();
a1820207
SC
952}
953
04cc6b77 954static void gfio_update_thread_status(char *status_message, double perc)
5b7573ab
SC
955{
956 static char message[100];
957 const char *m = message;
958
959 strncpy(message, status_message, sizeof(message) - 1);
04cc6b77
SC
960 gtk_progress_bar_set_text(
961 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
962 gtk_progress_bar_set_fraction(
963 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
5b7573ab 964 gtk_widget_queue_draw(ui.window);
5b7573ab
SC
965}
966
3ec62ec4
JA
967static void gfio_quit_op(struct fio_client *client)
968{
969 struct gui *ui = client->client_data;
970
0050e5f2 971 gdk_threads_enter();
3ec62ec4 972 gfio_set_connected(ui, 0);
0050e5f2 973 gdk_threads_leave();
3ec62ec4
JA
974}
975
807f9971
JA
976static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
977{
978 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
979 struct gui *ui = client->client_data;
980 char tmp[8];
981 int i;
982
983 p->iodepth = le32_to_cpu(p->iodepth);
984 p->rw = le32_to_cpu(p->rw);
985
986 for (i = 0; i < 2; i++) {
987 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
988 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
989 }
990
991 p->numjobs = le32_to_cpu(p->numjobs);
992 p->group_reporting = le32_to_cpu(p->group_reporting);
993
0050e5f2
JA
994 gdk_threads_enter();
995
ca850992
JA
996 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
997 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
998 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
807f9971
JA
999
1000 sprintf(tmp, "%u", p->iodepth);
ca850992 1001 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
0050e5f2
JA
1002
1003 gdk_threads_leave();
807f9971
JA
1004}
1005
ed727a46
JA
1006static void gfio_client_timed_out(struct fio_client *client)
1007{
1008 struct gui *ui = client->client_data;
1009 GtkWidget *dialog, *label, *content;
1010 char buf[256];
1011
1012 gdk_threads_enter();
1013
1014 gfio_set_connected(ui, 0);
88432651 1015 clear_ui_info(ui);
ed727a46
JA
1016
1017 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1018
1019 dialog = gtk_dialog_new_with_buttons("Timed out!",
1020 GTK_WINDOW(ui->window),
1021 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1022 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1023
1024 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1025 label = gtk_label_new((const gchar *) buf);
1026 gtk_container_add(GTK_CONTAINER(content), label);
1027 gtk_widget_show_all(dialog);
1028 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1029
1030 gtk_dialog_run(GTK_DIALOG(dialog));
1031 gtk_widget_destroy(dialog);
1032
1033 gdk_threads_leave();
1034}
1035
a1820207 1036struct client_ops gfio_client_ops = {
0420ba6a
JA
1037 .text_op = gfio_text_op,
1038 .disk_util = gfio_disk_util_op,
1039 .thread_status = gfio_thread_status_op,
1040 .group_stats = gfio_group_stats_op,
a5276616 1041 .eta = gfio_update_eta,
0420ba6a 1042 .probe = gfio_probe_op,
3ec62ec4 1043 .quit = gfio_quit_op,
807f9971 1044 .add_job = gfio_add_job_op,
ed727a46 1045 .timed_out = gfio_client_timed_out,
3ec62ec4 1046 .stay_connected = 1,
a1820207
SC
1047};
1048
ff1f3280
SC
1049static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1050 __attribute__((unused)) gpointer data)
1051{
1052 gtk_main_quit();
1053}
1054
25927259
SC
1055static void *job_thread(void *arg)
1056{
25927259 1057 fio_handle_clients(&gfio_client_ops);
25927259
SC
1058 return NULL;
1059}
1060
0420ba6a 1061static int send_job_files(struct gui *ui)
60f6b330 1062{
441013b4 1063 int i, ret = 0;
0420ba6a
JA
1064
1065 for (i = 0; i < ui->nr_job_files; i++) {
1066 ret = fio_clients_send_ini(ui->job_files[i]);
441013b4
JA
1067 if (ret)
1068 break;
1069
0420ba6a
JA
1070 free(ui->job_files[i]);
1071 ui->job_files[i] = NULL;
441013b4
JA
1072 }
1073 while (i < ui->nr_job_files) {
1074 free(ui->job_files[i]);
1075 ui->job_files[i] = NULL;
1076 i++;
0420ba6a
JA
1077 }
1078
441013b4 1079 return ret;
60f6b330
SC
1080}
1081
3ec62ec4 1082static void start_job_thread(struct gui *ui)
25927259 1083{
0420ba6a 1084 if (send_job_files(ui)) {
60f6b330 1085 printf("Yeah, I didn't really like those options too much.\n");
60f6b330
SC
1086 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1087 return;
1088 }
25927259
SC
1089}
1090
f3074008 1091static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 1092 gpointer data)
f3074008 1093{
25927259
SC
1094 struct gui *ui = data;
1095
25927259 1096 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
3ec62ec4 1097 start_job_thread(ui);
f3074008
SC
1098}
1099
df06f220
JA
1100static void file_open(GtkWidget *w, gpointer data);
1101
1102static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 1103{
3ec62ec4
JA
1104 struct gui *ui = data;
1105
1106 if (!ui->connected) {
df06f220
JA
1107 if (!ui->nr_job_files)
1108 file_open(widget, data);
8663ea65 1109 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
3ec62ec4
JA
1110 fio_clients_connect();
1111 pthread_create(&ui->t, NULL, job_thread, NULL);
1112 gfio_set_connected(ui, 1);
df06f220
JA
1113 } else {
1114 fio_clients_terminate();
3ec62ec4 1115 gfio_set_connected(ui, 0);
88432651 1116 clear_ui_info(ui);
df06f220 1117 }
3e47bd25
JA
1118}
1119
f3074008
SC
1120static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1121 struct button_spec *buttonspec)
1122{
1123 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1124 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
3ec62ec4 1125 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
f3074008 1126 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
3e47bd25 1127 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
f3074008
SC
1128}
1129
1130static void add_buttons(struct gui *ui,
1131 struct button_spec *buttonlist,
1132 int nbuttons)
1133{
1134 int i;
1135
f3074008
SC
1136 for (i = 0; i < nbuttons; i++)
1137 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1138}
1139
0420ba6a
JA
1140static void on_info_bar_response(GtkWidget *widget, gint response,
1141 gpointer data)
1142{
1143 if (response == GTK_RESPONSE_OK) {
1144 gtk_widget_destroy(widget);
1145 ui.error_info_bar = NULL;
1146 }
1147}
1148
df06f220 1149void report_error(GError *error)
0420ba6a
JA
1150{
1151 if (ui.error_info_bar == NULL) {
1152 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1153 GTK_RESPONSE_OK,
1154 NULL);
1155 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1156 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1157 GTK_MESSAGE_ERROR);
1158
1159 ui.error_label = gtk_label_new(error->message);
1160 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1161 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1162
1163 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1164 gtk_widget_show_all(ui.vbox);
1165 } else {
1166 char buffer[256];
1167 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1168 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1169 }
1170}
1171
b9f3c7ed
JA
1172static int get_connection_details(char **host, int *port, int *type,
1173 int *server_start)
a7a42ce1
JA
1174{
1175 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
b9f3c7ed 1176 GtkWidget *button;
a7a42ce1
JA
1177 char *typeentry;
1178
1179 dialog = gtk_dialog_new_with_buttons("Connection details",
1180 GTK_WINDOW(ui.window),
1181 GTK_DIALOG_DESTROY_WITH_PARENT,
1182 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1183 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1184
1185 frame = gtk_frame_new("Hostname / socket name");
1186 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1187 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1188
1189 box = gtk_vbox_new(FALSE, 6);
1190 gtk_container_add(GTK_CONTAINER(frame), box);
1191
1192 hbox = gtk_hbox_new(TRUE, 10);
1193 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1194 hentry = gtk_entry_new();
1195 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1196 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1197
1198 frame = gtk_frame_new("Port");
1199 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1200 box = gtk_vbox_new(FALSE, 10);
1201 gtk_container_add(GTK_CONTAINER(frame), box);
1202
1203 hbox = gtk_hbox_new(TRUE, 4);
1204 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1205 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1206
1207 frame = gtk_frame_new("Type");
1208 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1209 box = gtk_vbox_new(FALSE, 10);
1210 gtk_container_add(GTK_CONTAINER(frame), box);
1211
1212 hbox = gtk_hbox_new(TRUE, 4);
1213 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1214
1215 combo = gtk_combo_box_text_new();
1216 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1217 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1218 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1219 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1220
1221 gtk_container_add(GTK_CONTAINER(hbox), combo);
1222
b9f3c7ed
JA
1223 frame = gtk_frame_new("Options");
1224 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1225 box = gtk_vbox_new(FALSE, 10);
1226 gtk_container_add(GTK_CONTAINER(frame), box);
1227
1228 hbox = gtk_hbox_new(TRUE, 4);
1229 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1230
1231 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1233 gtk_widget_set_tooltip_text(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.");
1234 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1235
a7a42ce1
JA
1236 gtk_widget_show_all(dialog);
1237
1238 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1239 gtk_widget_destroy(dialog);
1240 return 1;
1241 }
1242
1243 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1244 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1245
1246 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1247 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1248 *type = Fio_client_ipv4;
1249 else if (!strncmp(typeentry, "IPv6", 4))
1250 *type = Fio_client_ipv6;
1251 else
1252 *type = Fio_client_socket;
1253 g_free(typeentry);
1254
b9f3c7ed
JA
1255 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1256
a7a42ce1
JA
1257 gtk_widget_destroy(dialog);
1258 return 0;
1259}
1260
0420ba6a
JA
1261static void file_open(GtkWidget *w, gpointer data)
1262{
1263 GtkWidget *dialog;
1264 GSList *filenames, *fn_glist;
1265 GtkFileFilter *filter;
a7a42ce1 1266 char *host;
b9f3c7ed 1267 int port, type, server_start;
0420ba6a
JA
1268
1269 dialog = gtk_file_chooser_dialog_new("Open File",
1270 GTK_WINDOW(ui.window),
1271 GTK_FILE_CHOOSER_ACTION_OPEN,
1272 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1273 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1274 NULL);
1275 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1276
1277 filter = gtk_file_filter_new();
1278 gtk_file_filter_add_pattern(filter, "*.fio");
1279 gtk_file_filter_add_pattern(filter, "*.job");
1280 gtk_file_filter_add_mime_type(filter, "text/fio");
1281 gtk_file_filter_set_name(filter, "Fio job file");
1282 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1283
1284 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1285 gtk_widget_destroy(dialog);
1286 return;
1287 }
1288
1289 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
1290
1291 gtk_widget_destroy(dialog);
1292
b9f3c7ed 1293 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
1294 goto err;
1295
0420ba6a
JA
1296 filenames = fn_glist;
1297 while (filenames != NULL) {
0420ba6a
JA
1298 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1299 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1300 ui.nr_job_files++;
1301
a5276616 1302 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
df06f220
JA
1303 if (!ui.client) {
1304 GError *error;
1305
1306 error = g_error_new(g_quark_from_string("fio"), 1,
1307 "Failed to add client %s", host);
0420ba6a
JA
1308 report_error(error);
1309 g_error_free(error);
0420ba6a 1310 }
df06f220 1311 ui.client->client_data = &ui;
0420ba6a
JA
1312
1313 g_free(filenames->data);
1314 filenames = g_slist_next(filenames);
1315 }
a7a42ce1
JA
1316 free(host);
1317err:
0420ba6a 1318 g_slist_free(fn_glist);
0420ba6a
JA
1319}
1320
1321static void file_save(GtkWidget *w, gpointer data)
1322{
1323 GtkWidget *dialog;
1324
1325 dialog = gtk_file_chooser_dialog_new("Save File",
1326 GTK_WINDOW(ui.window),
1327 GTK_FILE_CHOOSER_ACTION_SAVE,
1328 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1329 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1330 NULL);
1331
1332 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1333 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1334
1335 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1336 char *filename;
1337
1338 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1339 // save_job_file(filename);
1340 g_free(filename);
1341 }
1342 gtk_widget_destroy(dialog);
1343}
1344
46974a7d
JA
1345static void preferences(GtkWidget *w, gpointer data)
1346{
1347 GtkWidget *dialog, *frame, *box, **buttons;
1348 int i;
1349
1350 dialog = gtk_dialog_new_with_buttons("Preferences",
1351 GTK_WINDOW(ui.window),
1352 GTK_DIALOG_DESTROY_WITH_PARENT,
1353 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1354 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1355 NULL);
1356
0b8d11ed 1357 frame = gtk_frame_new("Debug logging");
46974a7d
JA
1358 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1359 box = gtk_hbox_new(FALSE, 6);
1360 gtk_container_add(GTK_CONTAINER(frame), box);
1361
1362 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1363
1364 for (i = 0; i < FD_DEBUG_MAX; i++) {
1365 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
0b8d11ed 1366 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
46974a7d
JA
1367 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1368 }
1369
1370 gtk_widget_show_all(dialog);
1371
1372 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1373 gtk_widget_destroy(dialog);
1374 return;
1375 }
1376
1377 for (i = 0; i < FD_DEBUG_MAX; i++) {
1378 int set;
1379
1380 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1381 if (set)
1382 fio_debug |= (1UL << i);
1383 }
1384
1385 gtk_widget_destroy(dialog);
1386}
1387
0420ba6a
JA
1388static void about_dialog(GtkWidget *w, gpointer data)
1389{
1390 gtk_show_about_dialog(NULL,
1391 "program-name", "gfio",
1392 "comments", "Gtk2 UI for fio",
1393 "license", "GPLv2",
1394 "version", fio_version_string,
1395 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1396 "logo-icon-name", "fio",
1397 /* Must be last: */
1398 NULL, NULL,
1399 NULL);
1400}
1401
1402static GtkActionEntry menu_items[] = {
46974a7d
JA
1403 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1404 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1405 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1406 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1407 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1408 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1409 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 1410};
3e47bd25 1411static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
1412
1413static const gchar *ui_string = " \
1414 <ui> \
1415 <menubar name=\"MainMenu\"> \
1416 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1417 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1418 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1419 <separator name=\"Separator\"/> \
46974a7d
JA
1420 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1421 <separator name=\"Separator2\"/> \
0420ba6a
JA
1422 <menuitem name=\"Quit\" action=\"Quit\" /> \
1423 </menu> \
1424 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1425 <menuitem name=\"About\" action=\"About\" /> \
1426 </menu> \
1427 </menubar> \
1428 </ui> \
1429";
1430
1431static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1432{
1433 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1434 GError *error = 0;
1435
1436 action_group = gtk_action_group_new("Menu");
1437 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1438
1439 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1440 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1441
1442 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1443 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1444}
1445
1446void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1447 GtkWidget *vbox, GtkUIManager *ui_manager)
1448{
1449 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1450}
1451
ff1f3280
SC
1452static void init_ui(int *argc, char **argv[], struct gui *ui)
1453{
0420ba6a
JA
1454 GtkSettings *settings;
1455 GtkUIManager *uimanager;
843ad237 1456 GtkWidget *menu, *probe, *probe_frame, *probe_box;
0420ba6a
JA
1457
1458 memset(ui, 0, sizeof(*ui));
45032dd8 1459
2839f0c6 1460 /* Magical g*thread incantation, you just need this thread stuff.
04cc6b77 1461 * Without it, the update that happens in gfio_update_thread_status
2839f0c6
SC
1462 * doesn't really happen in a timely fashion, you need expose events
1463 */
ed727a46 1464 if (!g_thread_supported())
2839f0c6
SC
1465 g_thread_init(NULL);
1466 gdk_threads_init();
1467
ff1f3280 1468 gtk_init(argc, argv);
0420ba6a
JA
1469 settings = gtk_settings_get_default();
1470 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1471 g_type_init();
ff1f3280
SC
1472
1473 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1474 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1475 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1476
0420ba6a
JA
1477 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1478 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
ff1f3280 1479
5b7573ab
SC
1480 ui->vbox = gtk_vbox_new(FALSE, 0);
1481 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
04cc6b77 1482
0420ba6a
JA
1483 uimanager = gtk_ui_manager_new();
1484 menu = get_menubar_menu(ui->window, uimanager);
1485 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1486
c36f98d9
SC
1487 /*
1488 * Set up alignments for widgets at the top of ui,
1489 * align top left, expand horizontally but not vertically
1490 */
1491 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
3ec62ec4 1492 ui->topvbox = gtk_vbox_new(FALSE, 3);
c36f98d9 1493 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
e164534f 1494 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 1495
3e47bd25 1496 probe = gtk_frame_new("Job");
843ad237
JA
1497 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1498 probe_frame = gtk_vbox_new(FALSE, 3);
1499 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1500
1501 probe_box = gtk_hbox_new(FALSE, 3);
1502 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
843ad237
JA
1503 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1504 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1505 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1506 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1507
3e47bd25
JA
1508 probe_box = gtk_hbox_new(FALSE, 3);
1509 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971 1510
ca850992
JA
1511 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1512 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1513 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1514 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1515 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1516 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3e47bd25
JA
1517
1518 probe_box = gtk_hbox_new(FALSE, 3);
1519 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
ca850992
JA
1520 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1521 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1522 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1523 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 1524
807f9971
JA
1525 /*
1526 * Only add this if we have a commit rate
1527 */
1528#if 0
3e47bd25
JA
1529 probe_box = gtk_hbox_new(FALSE, 3);
1530 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
1531
1532 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1533 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1534
3e47bd25
JA
1535 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1536 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 1537#endif
3e47bd25 1538
736f2dff
SC
1539 /*
1540 * Add a text box for text op messages
1541 */
1542 ui->textview = gtk_text_view_new();
1543 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1544 gtk_text_buffer_set_text(ui->text, "", -1);
1545 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1546 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1547 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1548 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1549 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1550 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
e164534f
SC
1551 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1552 TRUE, TRUE, 0);
736f2dff 1553
c36f98d9
SC
1554 /*
1555 * Set up alignments for widgets at the bottom of ui,
1556 * align bottom left, expand horizontally but not vertically
1557 */
1558 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1559 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1560 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
e164534f
SC
1561 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1562 FALSE, FALSE, 0);
c36f98d9 1563
f3074008 1564 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
3ec62ec4
JA
1565
1566 /*
1567 * Set up thread status progress bar
1568 */
1569 ui->thread_status_pb = gtk_progress_bar_new();
1570 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 1571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
1572 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1573
1574
ff1f3280
SC
1575 gtk_widget_show_all(ui->window);
1576}
1577
8232e285 1578int main(int argc, char *argv[], char *envp[])
ff1f3280 1579{
8232e285
SC
1580 if (initialize_fio(envp))
1581 return 1;
0420ba6a
JA
1582 if (fio_init_options())
1583 return 1;
a1820207 1584
ff1f3280 1585 init_ui(&argc, &argv, &ui);
5b7573ab 1586
2839f0c6 1587 gdk_threads_enter();
ff1f3280 1588 gtk_main();
2839f0c6 1589 gdk_threads_leave();
ff1f3280
SC
1590 return 0;
1591}