gfio: start of proper end results data display
[fio.git] / gfio.c
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  */
23 #include <locale.h>
24 #include <malloc.h>
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28
29 #include "fio.h"
30
31 static void gfio_update_thread_status(char *status_message, double perc);
32
33 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
37 static void connect_clicked(GtkWidget *widget, gpointer data);
38 static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40 static struct button_spec {
41         const char *buttontext;
42         clickfunction f;
43         const char *tooltiptext;
44         const int start_insensitive;
45 } buttonspeclist[] = {
46 #define CONNECT_BUTTON 0
47 #define START_JOB_BUTTON 1
48         { "Connect", connect_clicked, "Connect to host", 0 },
49         { "Start Job",
50                 start_job_clicked,
51                 "Send current fio job to fio server to be executed", 1 },
52 };
53
54 struct probe_widget {
55         GtkWidget *hostname;
56         GtkWidget *os;
57         GtkWidget *arch;
58         GtkWidget *fio_ver;
59 };
60
61 struct eta_widget {
62         GtkWidget *name;
63         GtkWidget *iotype;
64         GtkWidget *ioengine;
65         GtkWidget *iodepth;
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
78 struct gui {
79         GtkWidget *window;
80         GtkWidget *vbox;
81         GtkWidget *topvbox;
82         GtkWidget *topalign;
83         GtkWidget *bottomalign;
84         GtkWidget *thread_status_pb;
85         GtkWidget *buttonbox;
86         GtkWidget *button[ARRAYSIZE(buttonspeclist)];
87         GtkWidget *scrolled_window;
88         GtkWidget *textview;
89         GtkWidget *error_info_bar;
90         GtkWidget *error_label;
91         GtkTextBuffer *text;
92         struct probe_widget probe;
93         struct eta_widget eta;
94         int connected;
95         pthread_t t;
96
97         struct fio_client *client;
98         int nr_job_files;
99         char **job_files;
100 } ui;
101
102 static void clear_ui_info(struct gui *ui)
103 {
104         gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
105         gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
106         gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
107         gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
108         gtk_label_set_text(GTK_LABEL(ui->eta.name), "");
109         gtk_label_set_text(GTK_LABEL(ui->eta.iotype), "");
110         gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), "");
111         gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), "");
112         gtk_label_set_text(GTK_LABEL(ui->eta.jobs), "");
113         gtk_label_set_text(GTK_LABEL(ui->eta.files), "");
114         gtk_label_set_text(GTK_LABEL(ui->eta.read_bw), "");
115         gtk_label_set_text(GTK_LABEL(ui->eta.read_iops), "");
116         gtk_label_set_text(GTK_LABEL(ui->eta.write_bw), "");
117         gtk_label_set_text(GTK_LABEL(ui->eta.write_iops), "");
118 }
119
120 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
121 {
122         GtkWidget *entry, *frame;
123
124         frame = gtk_frame_new(label);
125         entry = gtk_entry_new();
126         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
127         gtk_container_add(GTK_CONTAINER(frame), entry);
128
129         return entry;
130 }
131
132 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
133 {
134         GtkWidget *label_widget;
135         GtkWidget *frame;
136
137         frame = gtk_frame_new(label);
138         label_widget = gtk_label_new(NULL);
139         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
140         gtk_container_add(GTK_CONTAINER(frame), label_widget);
141
142         return label_widget;
143 }
144
145 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
146 {
147         GtkWidget *button, *box;
148
149         box = gtk_hbox_new(FALSE, 3);
150         gtk_container_add(GTK_CONTAINER(hbox), box);
151
152         button = gtk_spin_button_new_with_range(min, max, 1.0);
153         gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
154
155         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
156         gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
157
158         return button;
159 }
160
161 static void gfio_set_connected(struct gui *ui, int connected)
162 {
163         if (connected) {
164                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
165                 ui->connected = 1;
166                 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
167         } else {
168                 ui->connected = 0;
169                 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
170                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
171         }
172 }
173
174 static void label_set_int_value(GtkWidget *entry, unsigned int val)
175 {
176         char tmp[80];
177
178         sprintf(tmp, "%u", val);
179         gtk_label_set_text(GTK_LABEL(entry), tmp);
180 }
181
182 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
183 {
184         char tmp[80];
185
186         sprintf(tmp, "%u", val);
187         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
188 }
189
190 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
191                           unsigned long max, double mean, double dev)
192 {
193         const char *base = "(usec)";
194         GtkWidget *hbox, *label, *frame, *box;
195         char *minp, *maxp;
196         char tmp[64];
197
198         if (!usec_to_msec(&min, &max, &mean, &dev))
199                 base = "(msec)";
200
201         minp = num2str(min, 6, 1, 0);
202         maxp = num2str(max, 6, 1, 0);
203
204         printf("adding %s\n", name);
205
206         sprintf(tmp, "%s %s", name, base);
207         frame = gtk_frame_new(tmp);
208         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
209
210         box = gtk_vbox_new(FALSE, 3);
211         gtk_container_add(GTK_CONTAINER(frame), box);
212
213         hbox = gtk_hbox_new(FALSE, 3);
214         gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, FALSE, 3);
215
216         label = new_info_label_in_frame(hbox, "Minimum");
217         gtk_label_set_text(GTK_LABEL(label), minp);
218         label = new_info_label_in_frame(hbox, "Maximum");
219         gtk_label_set_text(GTK_LABEL(label), maxp);
220         label = new_info_label_in_frame(hbox, "Average");
221         sprintf(tmp, "%5.02f", mean);
222         gtk_label_set_text(GTK_LABEL(label), tmp);
223         label = new_info_label_in_frame(hbox, "Standard deviation");
224         sprintf(tmp, "%5.02f", dev);
225         gtk_label_set_text(GTK_LABEL(label), tmp);
226
227         free(minp);
228         free(maxp);
229
230 }
231
232 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
233                                   struct thread_stat *ts, int ddir)
234 {
235         const char *ddir_label[2] = { "Read", "Write" };
236         GtkWidget *frame, *label, *box, *vbox;
237         unsigned long min, max, runt;
238         unsigned long long bw, iops;
239         double mean, dev;
240         char *io_p, *bw_p, *iops_p;
241         int i2p;
242
243         if (!ts->runtime[ddir])
244                 return;
245
246         i2p = is_power_of_2(rs->kb_base);
247         runt = ts->runtime[ddir];
248
249         bw = (1000 * ts->io_bytes[ddir]) / runt;
250         io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
251         bw_p = num2str(bw, 6, 1, i2p);
252
253         iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
254         iops_p = num2str(iops, 6, 1, 0);
255
256         box = gtk_hbox_new(FALSE, 3);
257         gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
258
259         frame = gtk_frame_new(ddir_label[ddir]);
260         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
261
262         vbox = gtk_vbox_new(FALSE, 3);
263         gtk_container_add(GTK_CONTAINER(frame), vbox);
264
265         box = gtk_hbox_new(FALSE, 3);
266         gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 3);
267
268         label = new_info_label_in_frame(box, "IO");
269         gtk_label_set_text(GTK_LABEL(label), io_p);
270         label = new_info_label_in_frame(box, "Bandwidth");
271         gtk_label_set_text(GTK_LABEL(label), bw_p);
272         label = new_info_label_in_frame(box, "IOPS");
273         gtk_label_set_text(GTK_LABEL(label), iops_p);
274         label = new_info_label_in_frame(box, "Runtime (msec)");
275         label_set_int_value(label, ts->runtime[ddir]);
276
277         frame = gtk_frame_new("Latency");
278         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
279
280         vbox = gtk_vbox_new(FALSE, 3);
281         gtk_container_add(GTK_CONTAINER(frame), vbox);
282
283         if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
284                 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
285         if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
286                 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
287         if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
288                 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
289
290         free(io_p);
291         free(bw_p);
292         free(iops_p);
293 }
294
295 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
296                             struct group_run_stats *rs)
297 {
298         GtkWidget *dialog, *box, *vbox, *entry, *content;
299         struct gui *ui = client->client_data;
300
301         gdk_threads_enter();
302
303         dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
304                         GTK_DIALOG_DESTROY_WITH_PARENT,
305                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
306
307         g_signal_connect_swapped(dialog, "response",
308                              G_CALLBACK(gtk_widget_destroy),
309                              dialog);
310
311         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
312
313         vbox = gtk_vbox_new(FALSE, 3);
314         gtk_container_add(GTK_CONTAINER(content), vbox);
315
316         box = gtk_hbox_new(TRUE, 3);
317         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
318
319         entry = new_info_entry_in_frame(box, "Name");
320         gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
321         if (strlen(ts->description)) {
322                 entry = new_info_entry_in_frame(box, "Description");
323                 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
324         }
325         entry = new_info_entry_in_frame(box, "Group ID");
326         entry_set_int_value(entry, ts->groupid);
327         entry = new_info_entry_in_frame(box, "Jobs");
328         entry_set_int_value(entry, ts->members);
329         entry = new_info_entry_in_frame(box, "Error");
330         entry_set_int_value(entry, ts->error);
331         entry = new_info_entry_in_frame(box, "PID");
332         entry_set_int_value(entry, ts->pid);
333
334         if (ts->io_bytes[DDIR_READ])
335                 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
336         if (ts->io_bytes[DDIR_WRITE])
337                 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
338
339         gtk_widget_show_all(dialog);
340
341         gdk_threads_leave();
342 }
343
344 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
345 {
346 #if 0
347         GtkTextBuffer *buffer;
348         GtkTextIter end;
349
350         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
351         gdk_threads_enter();
352         gtk_text_buffer_get_end_iter(buffer, &end);
353         gtk_text_buffer_insert(buffer, &end, buf, -1);
354         gdk_threads_leave();
355         gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
356                                         &end, 0.0, FALSE, 0.0,0.0);
357 #else
358         fio_client_ops.text_op(client, cmd);
359 #endif
360 }
361
362 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
363 {
364         printf("gfio_disk_util_op called\n");
365         fio_client_ops.disk_util(client, cmd);
366 }
367
368 extern int sum_stat_clients;
369 extern struct thread_stat client_ts;
370 extern struct group_run_stats client_gs;
371
372 static int sum_stat_nr;
373
374 static void gfio_thread_status_op(struct fio_client *client,
375                                   struct fio_net_cmd *cmd)
376 {
377         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
378
379         gfio_display_ts(client, &p->ts, &p->rs);
380
381         if (sum_stat_clients == 1)
382                 return;
383
384         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
385         sum_group_stats(&client_gs, &p->rs);
386
387         client_ts.members++;
388         client_ts.groupid = p->ts.groupid;
389
390         if (++sum_stat_nr == sum_stat_clients) {
391                 strcpy(client_ts.name, "All clients");
392                 gfio_display_ts(client, &client_ts, &client_gs);
393         }
394 }
395
396 static void gfio_group_stats_op(struct fio_client *client,
397                                 struct fio_net_cmd *cmd)
398 {
399         printf("gfio_group_stats_op called\n");
400         fio_client_ops.group_stats(client, cmd);
401 }
402
403 static void gfio_update_eta(struct jobs_eta *je)
404 {
405         static int eta_good;
406         char eta_str[128];
407         char output[256];
408         char tmp[32];
409         double perc = 0.0;
410         int i2p = 0;
411
412         eta_str[0] = '\0';
413         output[0] = '\0';
414
415         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
416                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
417                 eta_to_str(eta_str, je->eta_sec);
418         }
419
420         sprintf(tmp, "%u", je->nr_running);
421         gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
422         sprintf(tmp, "%u", je->files_open);
423         gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
424
425 #if 0
426         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
427         if (je->m_rate || je->t_rate) {
428                 char *tr, *mr;
429
430                 mr = num2str(je->m_rate, 4, 0, i2p);
431                 tr = num2str(je->t_rate, 4, 0, i2p);
432                 gtk_label_set_text(GTK_LABEL(ui.eta.
433                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
434                 free(tr);
435                 free(mr);
436         } else if (je->m_iops || je->t_iops)
437                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
438
439         gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
440         gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
441         gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
442         gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
443 #endif
444
445         if (je->eta_sec != INT_MAX && je->nr_running) {
446                 char *iops_str[2];
447                 char *rate_str[2];
448
449                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
450                         strcpy(output, "-.-% done");
451                 else {
452                         eta_good = 1;
453                         perc *= 100.0;
454                         sprintf(output, "%3.1f%% done", perc);
455                 }
456
457                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
458                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
459
460                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
461                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
462
463                 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
464                 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
465                 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
466                 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
467
468                 free(rate_str[0]);
469                 free(rate_str[1]);
470                 free(iops_str[0]);
471                 free(iops_str[1]);
472         }
473
474         if (eta_str[0]) {
475                 char *dst = output + strlen(output);
476
477                 sprintf(dst, " - %s", eta_str);
478         }
479                 
480         gfio_update_thread_status(output, perc);
481 }
482
483 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
484 {
485         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
486         const char *os, *arch;
487         char buf[64];
488
489         os = fio_get_os_string(probe->os);
490         if (!os)
491                 os = "unknown";
492
493         arch = fio_get_arch_string(probe->arch);
494         if (!arch)
495                 os = "unknown";
496
497         if (!client->name)
498                 client->name = strdup((char *) probe->hostname);
499
500         gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
501         gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
502         gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
503         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
504         gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
505 }
506
507 static void gfio_update_thread_status(char *status_message, double perc)
508 {
509         static char message[100];
510         const char *m = message;
511
512         strncpy(message, status_message, sizeof(message) - 1);
513         gtk_progress_bar_set_text(
514                 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
515         gtk_progress_bar_set_fraction(
516                 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
517         gdk_threads_enter();
518         gtk_widget_queue_draw(ui.window);
519         gdk_threads_leave();
520 }
521
522 static void gfio_quit_op(struct fio_client *client)
523 {
524         struct gui *ui = client->client_data;
525
526         gfio_set_connected(ui, 0);
527 }
528
529 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
530 {
531         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
532         struct gui *ui = client->client_data;
533         char tmp[8];
534         int i;
535
536         p->iodepth              = le32_to_cpu(p->iodepth);
537         p->rw                   = le32_to_cpu(p->rw);
538
539         for (i = 0; i < 2; i++) {
540                 p->min_bs[i]    = le32_to_cpu(p->min_bs[i]);
541                 p->max_bs[i]    = le32_to_cpu(p->max_bs[i]);
542         }
543
544         p->numjobs              = le32_to_cpu(p->numjobs);
545         p->group_reporting      = le32_to_cpu(p->group_reporting);
546
547         gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
548         gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
549         gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
550
551         sprintf(tmp, "%u", p->iodepth);
552         gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
553 }
554
555 static void gfio_client_timed_out(struct fio_client *client)
556 {
557         struct gui *ui = client->client_data;
558         GtkWidget *dialog, *label, *content;
559         char buf[256];
560
561         gdk_threads_enter();
562
563         gfio_set_connected(ui, 0);
564         clear_ui_info(ui);
565
566         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
567
568         dialog = gtk_dialog_new_with_buttons("Timed out!",
569                         GTK_WINDOW(ui->window),
570                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
571                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
572
573         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
574         label = gtk_label_new((const gchar *) buf);
575         gtk_container_add(GTK_CONTAINER(content), label);
576         gtk_widget_show_all(dialog);
577         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
578
579         gtk_dialog_run(GTK_DIALOG(dialog));
580         gtk_widget_destroy(dialog);
581
582         gdk_threads_leave();
583 }
584
585 struct client_ops gfio_client_ops = {
586         .text_op                = gfio_text_op,
587         .disk_util              = gfio_disk_util_op,
588         .thread_status          = gfio_thread_status_op,
589         .group_stats            = gfio_group_stats_op,
590         .eta                    = gfio_update_eta,
591         .probe                  = gfio_probe_op,
592         .quit                   = gfio_quit_op,
593         .add_job                = gfio_add_job_op,
594         .timed_out              = gfio_client_timed_out,
595         .stay_connected         = 1,
596 };
597
598 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
599                 __attribute__((unused)) gpointer data)
600 {
601         gtk_main_quit();
602 }
603
604 static void *job_thread(void *arg)
605 {
606         fio_handle_clients(&gfio_client_ops);
607         return NULL;
608 }
609
610 static int send_job_files(struct gui *ui)
611 {
612         int i, ret = 0;
613
614         for (i = 0; i < ui->nr_job_files; i++) {
615                 ret = fio_clients_send_ini(ui->job_files[i]);
616                 if (ret)
617                         break;
618
619                 free(ui->job_files[i]);
620                 ui->job_files[i] = NULL;
621         }
622         while (i < ui->nr_job_files) {
623                 free(ui->job_files[i]);
624                 ui->job_files[i] = NULL;
625                 i++;
626         }
627
628         return ret;
629 }
630
631 static void start_job_thread(struct gui *ui)
632 {
633         if (send_job_files(ui)) {
634                 printf("Yeah, I didn't really like those options too much.\n");
635                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
636                 return;
637         }
638 }
639
640 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
641                 gpointer data)
642 {
643         struct gui *ui = data;
644
645         gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
646         start_job_thread(ui);
647 }
648
649 static void file_open(GtkWidget *w, gpointer data);
650
651 static void connect_clicked(GtkWidget *widget, gpointer data)
652 {
653         struct gui *ui = data;
654
655         if (!ui->connected) {
656                 if (!ui->nr_job_files)
657                         file_open(widget, data);
658                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
659                 fio_clients_connect();
660                 pthread_create(&ui->t, NULL, job_thread, NULL);
661                 gfio_set_connected(ui, 1);
662         } else {
663                 fio_clients_terminate();
664                 gfio_set_connected(ui, 0);
665                 clear_ui_info(ui);
666         }
667 }
668
669 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
670                         struct button_spec *buttonspec)
671 {
672         ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
673         g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
674         gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
675         gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
676         gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
677 }
678
679 static void add_buttons(struct gui *ui,
680                                 struct button_spec *buttonlist,
681                                 int nbuttons)
682 {
683         int i;
684
685         for (i = 0; i < nbuttons; i++)
686                 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
687 }
688
689 static void on_info_bar_response(GtkWidget *widget, gint response,
690                                  gpointer data)
691 {
692         if (response == GTK_RESPONSE_OK) {
693                 gtk_widget_destroy(widget);
694                 ui.error_info_bar = NULL;
695         }
696 }
697
698 void report_error(GError *error)
699 {
700         if (ui.error_info_bar == NULL) {
701                 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
702                                                                GTK_RESPONSE_OK,
703                                                                NULL);
704                 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
705                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
706                                               GTK_MESSAGE_ERROR);
707                 
708                 ui.error_label = gtk_label_new(error->message);
709                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
710                 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
711                 
712                 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
713                 gtk_widget_show_all(ui.vbox);
714         } else {
715                 char buffer[256];
716                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
717                 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
718         }
719 }
720
721 static int get_connection_details(char **host, int *port, int *type,
722                                   int *server_start)
723 {
724         GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
725         GtkWidget *button;
726         char *typeentry;
727
728         dialog = gtk_dialog_new_with_buttons("Connection details",
729                         GTK_WINDOW(ui.window),
730                         GTK_DIALOG_DESTROY_WITH_PARENT,
731                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
732                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
733
734         frame = gtk_frame_new("Hostname / socket name");
735         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
736         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
737
738         box = gtk_vbox_new(FALSE, 6);
739         gtk_container_add(GTK_CONTAINER(frame), box);
740
741         hbox = gtk_hbox_new(TRUE, 10);
742         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
743         hentry = gtk_entry_new();
744         gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
745         gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
746
747         frame = gtk_frame_new("Port");
748         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
749         box = gtk_vbox_new(FALSE, 10);
750         gtk_container_add(GTK_CONTAINER(frame), box);
751
752         hbox = gtk_hbox_new(TRUE, 4);
753         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
754         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
755
756         frame = gtk_frame_new("Type");
757         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
758         box = gtk_vbox_new(FALSE, 10);
759         gtk_container_add(GTK_CONTAINER(frame), box);
760
761         hbox = gtk_hbox_new(TRUE, 4);
762         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
763
764         combo = gtk_combo_box_text_new();
765         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
766         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
767         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
768         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
769
770         gtk_container_add(GTK_CONTAINER(hbox), combo);
771
772         frame = gtk_frame_new("Options");
773         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
774         box = gtk_vbox_new(FALSE, 10);
775         gtk_container_add(GTK_CONTAINER(frame), box);
776
777         hbox = gtk_hbox_new(TRUE, 4);
778         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
779
780         button = gtk_check_button_new_with_label("Auto-spawn fio backend");
781         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
782         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.");
783         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
784
785         gtk_widget_show_all(dialog);
786
787         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
788                 gtk_widget_destroy(dialog);
789                 return 1;
790         }
791
792         *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
793         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
794
795         typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
796         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
797                 *type = Fio_client_ipv4;
798         else if (!strncmp(typeentry, "IPv6", 4))
799                 *type = Fio_client_ipv6;
800         else
801                 *type = Fio_client_socket;
802         g_free(typeentry);
803
804         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
805
806         gtk_widget_destroy(dialog);
807         return 0;
808 }
809
810 static void file_open(GtkWidget *w, gpointer data)
811 {
812         GtkWidget *dialog;
813         GSList *filenames, *fn_glist;
814         GtkFileFilter *filter;
815         char *host;
816         int port, type, server_start;
817
818         dialog = gtk_file_chooser_dialog_new("Open File",
819                 GTK_WINDOW(ui.window),
820                 GTK_FILE_CHOOSER_ACTION_OPEN,
821                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
822                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
823                 NULL);
824         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
825
826         filter = gtk_file_filter_new();
827         gtk_file_filter_add_pattern(filter, "*.fio");
828         gtk_file_filter_add_pattern(filter, "*.job");
829         gtk_file_filter_add_mime_type(filter, "text/fio");
830         gtk_file_filter_set_name(filter, "Fio job file");
831         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
832
833         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
834                 gtk_widget_destroy(dialog);
835                 return;
836         }
837
838         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
839
840         gtk_widget_destroy(dialog);
841
842         if (get_connection_details(&host, &port, &type, &server_start))
843                 goto err;
844
845         filenames = fn_glist;
846         while (filenames != NULL) {
847                 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
848                 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
849                 ui.nr_job_files++;
850
851                 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
852                 if (!ui.client) {
853                         GError *error;
854
855                         error = g_error_new(g_quark_from_string("fio"), 1,
856                                         "Failed to add client %s", host);
857                         report_error(error);
858                         g_error_free(error);
859                 }
860                 ui.client->client_data = &ui;
861                         
862                 g_free(filenames->data);
863                 filenames = g_slist_next(filenames);
864         }
865         free(host);
866 err:
867         g_slist_free(fn_glist);
868 }
869
870 static void file_save(GtkWidget *w, gpointer data)
871 {
872         GtkWidget *dialog;
873
874         dialog = gtk_file_chooser_dialog_new("Save File",
875                 GTK_WINDOW(ui.window),
876                 GTK_FILE_CHOOSER_ACTION_SAVE,
877                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
878                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
879                 NULL);
880
881         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
882         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
883
884         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
885                 char *filename;
886
887                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
888                 // save_job_file(filename);
889                 g_free(filename);
890         }
891         gtk_widget_destroy(dialog);
892 }
893
894 static void preferences(GtkWidget *w, gpointer data)
895 {
896         GtkWidget *dialog, *frame, *box, **buttons;
897         int i;
898
899         dialog = gtk_dialog_new_with_buttons("Preferences",
900                 GTK_WINDOW(ui.window),
901                 GTK_DIALOG_DESTROY_WITH_PARENT,
902                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
903                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
904                 NULL);
905
906         frame = gtk_frame_new("Debug logging");
907         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
908         box = gtk_hbox_new(FALSE, 6);
909         gtk_container_add(GTK_CONTAINER(frame), box);
910
911         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
912
913         for (i = 0; i < FD_DEBUG_MAX; i++) {
914                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
915                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
916                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
917         }
918
919         gtk_widget_show_all(dialog);
920
921         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
922                 gtk_widget_destroy(dialog);
923                 return;
924         }
925
926         for (i = 0; i < FD_DEBUG_MAX; i++) {
927                 int set;
928
929                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
930                 if (set)
931                         fio_debug |= (1UL << i);
932         }
933
934         gtk_widget_destroy(dialog);
935 }
936
937 static void about_dialog(GtkWidget *w, gpointer data)
938 {
939         gtk_show_about_dialog(NULL,
940                 "program-name", "gfio",
941                 "comments", "Gtk2 UI for fio",
942                 "license", "GPLv2",
943                 "version", fio_version_string,
944                 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
945                 "logo-icon-name", "fio",
946                 /* Must be last: */
947                 NULL, NULL,
948                 NULL);
949 }
950
951 static GtkActionEntry menu_items[] = {
952         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
953         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
954         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
955         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
956         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
957         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
958         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
959 };
960 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
961
962 static const gchar *ui_string = " \
963         <ui> \
964                 <menubar name=\"MainMenu\"> \
965                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
966                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
967                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
968                                 <separator name=\"Separator\"/> \
969                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
970                                 <separator name=\"Separator2\"/> \
971                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
972                         </menu> \
973                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
974                                 <menuitem name=\"About\" action=\"About\" /> \
975                         </menu> \
976                 </menubar> \
977         </ui> \
978 ";
979
980 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
981 {
982         GtkActionGroup *action_group = gtk_action_group_new("Menu");
983         GError *error = 0;
984
985         action_group = gtk_action_group_new("Menu");
986         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
987
988         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
989         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
990
991         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
992         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
993 }
994
995 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
996                    GtkWidget *vbox, GtkUIManager *ui_manager)
997 {
998         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
999 }
1000
1001 static void init_ui(int *argc, char **argv[], struct gui *ui)
1002 {
1003         GtkSettings *settings;
1004         GtkUIManager *uimanager;
1005         GtkWidget *menu, *probe, *probe_frame, *probe_box;
1006
1007         memset(ui, 0, sizeof(*ui));
1008
1009         /* Magical g*thread incantation, you just need this thread stuff.
1010          * Without it, the update that happens in gfio_update_thread_status
1011          * doesn't really happen in a timely fashion, you need expose events
1012          */
1013         if (!g_thread_supported())
1014                 g_thread_init(NULL);
1015         gdk_threads_init();
1016
1017         gtk_init(argc, argv);
1018         settings = gtk_settings_get_default();
1019         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1020         g_type_init();
1021         
1022         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1023         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1024         gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1025
1026         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1027         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1028
1029         ui->vbox = gtk_vbox_new(FALSE, 0);
1030         gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1031
1032         uimanager = gtk_ui_manager_new();
1033         menu = get_menubar_menu(ui->window, uimanager);
1034         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1035
1036         /*
1037          * Set up alignments for widgets at the top of ui, 
1038          * align top left, expand horizontally but not vertically
1039          */
1040         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1041         ui->topvbox = gtk_vbox_new(FALSE, 3);
1042         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1043         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1044
1045         probe = gtk_frame_new("Job");
1046         gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1047         probe_frame = gtk_vbox_new(FALSE, 3);
1048         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1049
1050         probe_box = gtk_hbox_new(FALSE, 3);
1051         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1052         ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1053         ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1054         ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1055         ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1056
1057         probe_box = gtk_hbox_new(FALSE, 3);
1058         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1059
1060         ui->eta.name = new_info_label_in_frame(probe_box, "Name");
1061         ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
1062         ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
1063         ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
1064         ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
1065         ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
1066
1067         probe_box = gtk_hbox_new(FALSE, 3);
1068         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1069         ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
1070         ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
1071         ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
1072         ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
1073
1074         /*
1075          * Only add this if we have a commit rate
1076          */
1077 #if 0
1078         probe_box = gtk_hbox_new(FALSE, 3);
1079         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1080
1081         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1082         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1083
1084         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1085         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1086 #endif
1087
1088         /*
1089          * Add a text box for text op messages 
1090          */
1091         ui->textview = gtk_text_view_new();
1092         ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1093         gtk_text_buffer_set_text(ui->text, "", -1);
1094         gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1095         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1096         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1097         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1098                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1099         gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1100         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1101                         TRUE, TRUE, 0);
1102
1103         /*
1104          * Set up alignments for widgets at the bottom of ui, 
1105          * align bottom left, expand horizontally but not vertically
1106          */
1107         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1108         ui->buttonbox = gtk_hbox_new(FALSE, 0);
1109         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1110         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1111                                         FALSE, FALSE, 0);
1112
1113         add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1114
1115         /*
1116          * Set up thread status progress bar
1117          */
1118         ui->thread_status_pb = gtk_progress_bar_new();
1119         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1120         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1121         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1122
1123
1124         gtk_widget_show_all(ui->window);
1125 }
1126
1127 int main(int argc, char *argv[], char *envp[])
1128 {
1129         if (initialize_fio(envp))
1130                 return 1;
1131         if (fio_init_options())
1132                 return 1;
1133
1134         init_ui(&argc, &argv, &ui);
1135
1136         gdk_threads_enter();
1137         gtk_main();
1138         gdk_threads_leave();
1139         return 0;
1140 }