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