gfio: move File->Open above File->Close
[fio.git] / gfio.c
CommitLineData
ff1f3280
SC
1/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
c0187f3b 5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
ff1f3280
SC
6 *
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
8232e285 24#include <locale.h>
60f6b330 25#include <malloc.h>
6b79c80c 26#include <string.h>
8232e285 27
5b7573ab 28#include <glib.h>
2fd3bb0e 29#include <cairo.h>
ff1f3280
SC
30#include <gtk/gtk.h>
31
8232e285 32#include "fio.h"
2fd3bb0e 33#include "graph.h"
8232e285 34
38634cb1
JA
35#define GFIO_MIME "text/fio"
36
63a130b7 37static int gfio_server_running;
f3e8440f 38static const char *gfio_graph_font;
8577f4fd 39static unsigned int gfio_graph_limit = 100;
814479d5 40static GdkColor white;
63a130b7 41
6b79c80c 42static void view_log(GtkWidget *w, gpointer data);
3e47bd25 43
f3074008
SC
44#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
45
46typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47
3e47bd25 48static void connect_clicked(GtkWidget *widget, gpointer data);
f3074008 49static void start_job_clicked(GtkWidget *widget, gpointer data);
b9d2f30a 50static void send_clicked(GtkWidget *widget, gpointer data);
f3074008
SC
51
52static struct button_spec {
53 const char *buttontext;
54 clickfunction f;
55 const char *tooltiptext;
3e47bd25 56 const int start_insensitive;
f3074008 57} buttonspeclist[] = {
3e47bd25 58#define CONNECT_BUTTON 0
b9d2f30a
JA
59#define SEND_BUTTON 1
60#define START_JOB_BUTTON 2
3e47bd25 61 { "Connect", connect_clicked, "Connect to host", 0 },
b9d2f30a
JA
62 { "Send", send_clicked, "Send job description to host", 1 },
63 { "Start Job", start_job_clicked,
2f99deb0 64 "Start the current job on the server", 1 },
f3074008
SC
65};
66
843ad237
JA
67struct probe_widget {
68 GtkWidget *hostname;
69 GtkWidget *os;
70 GtkWidget *arch;
71 GtkWidget *fio_ver;
72};
73
c80b74b0
JA
74struct multitext_widget {
75 GtkWidget *entry;
76 char **text;
77 unsigned int cur_text;
78 unsigned int max_text;
79};
80
3e47bd25 81struct eta_widget {
3863d1ad 82 GtkWidget *names;
c80b74b0
JA
83 struct multitext_widget iotype;
84 struct multitext_widget ioengine;
85 struct multitext_widget iodepth;
3e47bd25
JA
86 GtkWidget *jobs;
87 GtkWidget *files;
88 GtkWidget *read_bw;
89 GtkWidget *read_iops;
90 GtkWidget *cr_bw;
91 GtkWidget *cr_iops;
92 GtkWidget *write_bw;
93 GtkWidget *write_iops;
94 GtkWidget *cw_bw;
95 GtkWidget *cw_iops;
96};
97
2f99deb0
JA
98struct gfio_graphs {
99#define DRAWING_AREA_XDIM 1000
100#define DRAWING_AREA_YDIM 400
101 GtkWidget *drawing_area;
2f99deb0
JA
102 struct graph *iops_graph;
103 struct graph *bandwidth_graph;
104};
105
106/*
107 * Main window widgets and data
108 */
ff1f3280 109struct gui {
02421e69 110 GtkUIManager *uimanager;
38634cb1
JA
111 GtkRecentManager *recentmanager;
112 GtkActionGroup *actiongroup;
113 guint recent_ui_id;
02421e69 114 GtkWidget *menu;
ff1f3280 115 GtkWidget *window;
2f99deb0 116 GtkWidget *vbox;
2f99deb0
JA
117 GtkWidget *thread_status_pb;
118 GtkWidget *buttonbox;
2f99deb0
JA
119 GtkWidget *notebook;
120 GtkWidget *error_info_bar;
121 GtkWidget *error_label;
122 GtkListStore *log_model;
123 GtkWidget *log_tree;
124 GtkWidget *log_view;
125 struct gfio_graphs graphs;
126 struct probe_widget probe;
127 struct eta_widget eta;
128 pthread_t server_t;
129
a9eccde4
JA
130 pthread_t t;
131 int handler_running;
132
2f99deb0
JA
133 struct flist_head list;
134} main_ui;
135
85dd01e7
JA
136enum {
137 GE_STATE_NEW = 1,
138 GE_STATE_CONNECTED,
139 GE_STATE_JOB_SENT,
140 GE_STATE_JOB_STARTED,
141 GE_STATE_JOB_RUNNING,
142 GE_STATE_JOB_DONE,
143};
144
2f99deb0
JA
145/*
146 * Notebook entry
147 */
148struct gui_entry {
149 struct flist_head list;
150 struct gui *ui;
151
5b7573ab 152 GtkWidget *vbox;
c80b74b0 153 GtkWidget *job_notebook;
04cc6b77 154 GtkWidget *thread_status_pb;
f3074008
SC
155 GtkWidget *buttonbox;
156 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
2f99deb0 157 GtkWidget *notebook;
0420ba6a
JA
158 GtkWidget *error_info_bar;
159 GtkWidget *error_label;
f9d40b48 160 GtkWidget *results_window;
7da23b48 161 GtkWidget *results_notebook;
17b9721a 162 GtkUIManager *results_uimanager;
17b9721a 163 GtkWidget *results_menu;
7da23b48 164 GtkWidget *disk_util_vbox;
9b260bdf
JA
165 GtkListStore *log_model;
166 GtkWidget *log_tree;
4cbe7211 167 GtkWidget *log_view;
2f99deb0 168 struct gfio_graphs graphs;
843ad237 169 struct probe_widget probe;
3e47bd25 170 struct eta_widget eta;
2f99deb0
JA
171 GtkWidget *page_label;
172 gint page_num;
85dd01e7 173 unsigned int state;
0420ba6a 174
09d574e3
JA
175 struct graph *clat_graph;
176 struct graph *lat_bucket_graph;
177
b9d2f30a 178 struct gfio_client *client;
0420ba6a
JA
179 int nr_job_files;
180 char **job_files;
2f99deb0 181};
ff1f3280 182
781ccba6
JA
183struct end_results {
184 struct group_run_stats gs;
185 struct thread_stat ts;
186};
187
e0681f3e 188struct gfio_client {
2f99deb0 189 struct gui_entry *ge;
b9d2f30a 190 struct fio_client *client;
6b79c80c 191 GtkWidget *err_entry;
dcaeb606
JA
192 unsigned int job_added;
193 struct thread_options o;
781ccba6
JA
194
195 struct end_results *results;
196 unsigned int nr_results;
7da23b48
JA
197
198 struct cmd_du_pdu *du;
199 unsigned int nr_du;
e0681f3e
JA
200};
201
9988ca70
JA
202static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
203static void gfio_update_thread_status_all(char *status_message, double perc);
c724926b 204void report_error(GError *error);
9988ca70 205
2f99deb0
JA
206static struct graph *setup_iops_graph(void)
207{
208 struct graph *g;
209
210 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
d8fbeefb 211 graph_title(g, "IOPS (IOs/sec)");
2f99deb0 212 graph_x_title(g, "Time (secs)");
2f99deb0
JA
213 graph_add_label(g, "Read IOPS");
214 graph_add_label(g, "Write IOPS");
215 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
216 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
8577f4fd 217 line_graph_set_data_count_limit(g, gfio_graph_limit);
d8fbeefb 218 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
2f99deb0
JA
219 return g;
220}
221
222static struct graph *setup_bandwidth_graph(void)
223{
224 struct graph *g;
225
226 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
d8fbeefb 227 graph_title(g, "Bandwidth (bytes/sec)");
2f99deb0 228 graph_x_title(g, "Time (secs)");
2f99deb0
JA
229 graph_add_label(g, "Read Bandwidth");
230 graph_add_label(g, "Write Bandwidth");
231 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
232 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
d8fbeefb 233 graph_set_base_offset(g, 1);
2f99deb0 234 line_graph_set_data_count_limit(g, 100);
d8fbeefb 235 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
2f99deb0
JA
236 return g;
237}
238
239static void setup_graphs(struct gfio_graphs *g)
240{
241 g->iops_graph = setup_iops_graph();
242 g->bandwidth_graph = setup_bandwidth_graph();
243}
244
c80b74b0
JA
245static void multitext_add_entry(struct multitext_widget *mt, const char *text)
246{
247 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
248 mt->text[mt->max_text] = strdup(text);
249 mt->max_text++;
250}
251
252static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
253{
254 if (index >= mt->max_text)
255 return;
da185431 256 if (!mt->text || !mt->text[index])
c80b74b0
JA
257 return;
258
259 mt->cur_text = index;
260 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
261}
262
263static void multitext_update_entry(struct multitext_widget *mt,
264 unsigned int index, const char *text)
265{
da185431
JA
266 if (!mt->text)
267 return;
268
c80b74b0
JA
269 if (mt->text[index])
270 free(mt->text[index]);
271
272 mt->text[index] = strdup(text);
273 if (mt->cur_text == index)
274 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
275}
276
277static void multitext_free(struct multitext_widget *mt)
278{
279 int i;
280
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
282
283 for (i = 0; i < mt->max_text; i++) {
284 if (mt->text[i])
285 free(mt->text[i]);
286 }
287
288 free(mt->text);
289 mt->cur_text = -1;
290 mt->max_text = 0;
291}
292
2f99deb0
JA
293static void clear_ge_ui_info(struct gui_entry *ge)
294{
295 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
296 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
297 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
298 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
3863d1ad
JA
299#if 0
300 /* should we empty it... */
2f99deb0 301 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
3863d1ad 302#endif
c80b74b0
JA
303 multitext_update_entry(&ge->eta.iotype, 0, "");
304 multitext_update_entry(&ge->eta.ioengine, 0, "");
305 multitext_update_entry(&ge->eta.iodepth, 0, "");
2f99deb0
JA
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
8663ea65
JA
312}
313
3863d1ad
JA
314static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
315{
316 GtkWidget *entry, *frame;
317
318 frame = gtk_frame_new(label);
319 entry = gtk_combo_box_new_text();
320 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
321 gtk_container_add(GTK_CONTAINER(frame), entry);
322
323 return entry;
324}
325
3650a3ca
JA
326static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
327{
328 GtkWidget *entry, *frame;
329
330 frame = gtk_frame_new(label);
331 entry = gtk_entry_new();
1c1e4a5b 332 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
3650a3ca
JA
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), entry);
335
336 return entry;
337}
338
339static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
340{
341 GtkWidget *label_widget;
342 GtkWidget *frame;
343
344 frame = gtk_frame_new(label);
345 label_widget = gtk_label_new(NULL);
346 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), label_widget);
348
349 return label_widget;
350}
351
352static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
353{
354 GtkWidget *button, *box;
355
356 box = gtk_hbox_new(FALSE, 3);
357 gtk_container_add(GTK_CONTAINER(hbox), box);
358
359 button = gtk_spin_button_new_with_range(min, max, 1.0);
360 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
361
362 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
363 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
364
365 return button;
366}
367
3650a3ca
JA
368static void label_set_int_value(GtkWidget *entry, unsigned int val)
369{
370 char tmp[80];
371
372 sprintf(tmp, "%u", val);
373 gtk_label_set_text(GTK_LABEL(entry), tmp);
374}
375
376static void entry_set_int_value(GtkWidget *entry, unsigned int val)
377{
378 char tmp[80];
379
380 sprintf(tmp, "%u", val);
381 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
382}
383
16ce5adf
JA
384static void show_info_dialog(struct gui *ui, const char *title,
385 const char *message)
386{
387 GtkWidget *dialog, *content, *label;
388
389 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
390 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
391 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
392
393 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
394 label = gtk_label_new(message);
395 gtk_container_add(GTK_CONTAINER(content), label);
396 gtk_widget_show_all(dialog);
397 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
398 gtk_dialog_run(GTK_DIALOG(dialog));
399 gtk_widget_destroy(dialog);
400}
401
781ccba6
JA
402static void set_menu_entry_text(struct gui *ui, const char *path,
403 const char *text)
404{
405 GtkWidget *w;
406
407 w = gtk_ui_manager_get_widget(ui->uimanager, path);
408 if (w)
409 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
410 else
411 fprintf(stderr, "gfio: can't find path %s\n", path);
412}
413
414
415static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
416{
417 GtkWidget *w;
418
419 w = gtk_ui_manager_get_widget(ui->uimanager, path);
420 if (w)
421 gtk_widget_set_sensitive(w, show);
422 else
423 fprintf(stderr, "gfio: can't find path %s\n", path);
424}
425
426static void set_job_menu_visible(struct gui *ui, int visible)
427{
428 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
429}
430
431static void set_view_results_visible(struct gui *ui, int visible)
432{
433 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
434}
435
85dd01e7
JA
436/*
437 * Update sensitivity of job buttons and job menu items, based on the
438 * state of the client.
439 */
440static void update_button_states(struct gui *ui, struct gui_entry *ge)
441{
442 unsigned int connect_state, send_state, start_state, edit_state;
443 const char *connect_str = NULL;
85dd01e7
JA
444
445 switch (ge->state) {
446 default: {
447 char tmp[80];
448
449 sprintf(tmp, "Bad client state: %u\n", ge->state);
450 show_info_dialog(ui, "Error", tmp);
451 /* fall through to new state */
452 }
453
454 case GE_STATE_NEW:
455 connect_state = 1;
456 edit_state = 0;
457 connect_str = "Connect";
458 send_state = 0;
459 start_state = 0;
460 break;
461 case GE_STATE_CONNECTED:
462 connect_state = 1;
463 edit_state = 0;
464 connect_str = "Disconnect";
465 send_state = 1;
466 start_state = 0;
467 break;
468 case GE_STATE_JOB_SENT:
469 connect_state = 1;
470 edit_state = 0;
471 connect_str = "Disconnect";
472 send_state = 0;
473 start_state = 1;
474 break;
475 case GE_STATE_JOB_STARTED:
476 connect_state = 1;
477 edit_state = 1;
478 connect_str = "Disconnect";
479 send_state = 0;
480 start_state = 1;
481 break;
482 case GE_STATE_JOB_RUNNING:
483 connect_state = 1;
484 edit_state = 0;
485 connect_str = "Disconnect";
486 send_state = 0;
487 start_state = 0;
488 break;
489 case GE_STATE_JOB_DONE:
490 connect_state = 1;
491 edit_state = 0;
492 connect_str = "Connect";
493 send_state = 0;
494 start_state = 0;
495 break;
496 }
497
498 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
499 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
500 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
501 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
502
781ccba6
JA
503 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
504 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
85dd01e7 505
781ccba6
JA
506 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
507 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
508 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
85dd01e7 509
781ccba6
JA
510 if (ge->client && ge->client->nr_results)
511 set_view_results_visible(ui, 1);
512 else
513 set_view_results_visible(ui, 0);
85dd01e7
JA
514}
515
516static void gfio_set_state(struct gui_entry *ge, unsigned int state)
517{
518 ge->state = state;
519 update_button_states(ge->ui, ge);
520}
521
a269790c
JA
522#define ALIGN_LEFT 1
523#define ALIGN_RIGHT 2
524#define INVISIBLE 4
525#define UNSORTABLE 8
526
527GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
528{
529 GtkCellRenderer *renderer;
530 GtkTreeViewColumn *col;
531 double xalign = 0.0; /* left as default */
532 PangoAlignment align;
533 gboolean visible;
534
535 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
536 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
537 PANGO_ALIGN_CENTER;
538 visible = !(flags & INVISIBLE);
539
540 renderer = gtk_cell_renderer_text_new();
541 col = gtk_tree_view_column_new();
542
543 gtk_tree_view_column_set_title(col, title);
544 if (!(flags & UNSORTABLE))
545 gtk_tree_view_column_set_sort_column_id(col, index);
546 gtk_tree_view_column_set_resizable(col, TRUE);
547 gtk_tree_view_column_pack_start(col, renderer, TRUE);
548 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
549 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
550 switch (align) {
551 case PANGO_ALIGN_LEFT:
552 xalign = 0.0;
553 break;
554 case PANGO_ALIGN_CENTER:
555 xalign = 0.5;
556 break;
557 case PANGO_ALIGN_RIGHT:
558 xalign = 1.0;
559 break;
560 }
561 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
562 gtk_tree_view_column_set_visible(col, visible);
563 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
564 return col;
565}
566
9b260bdf
JA
567static void gfio_ui_setup_log(struct gui *ui)
568{
569 GtkTreeSelection *selection;
570 GtkListStore *model;
571 GtkWidget *tree_view;
572
573 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
574
575 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
576 gtk_widget_set_can_focus(tree_view, FALSE);
577
578 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
579 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
661f741a
JA
580 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
581 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
9b260bdf
JA
582
583 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
584 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
585 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
f095d563 586 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
9b260bdf
JA
587
588 ui->log_model = model;
589 ui->log_tree = tree_view;
590}
591
c57f254c
SC
592static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
593 fio_fp64_t *plist,
594 unsigned int len,
595 double xdim, double ydim)
596{
597 struct graph *g;
598 int i;
599
600 g = graph_new(xdim, ydim, gfio_graph_font);
601 graph_title(g, title);
602 graph_x_title(g, "Percentile");
603
604 for (i = 0; i < len; i++) {
605 char fbuf[8];
606
607 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
608 graph_add_label(g, fbuf);
609 graph_add_data(g, fbuf, (double) ovals[i]);
610 }
611
612 return g;
613}
614
a269790c
JA
615static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
616 fio_fp64_t *plist,
617 unsigned int len,
618 const char *base,
619 unsigned int scale)
620{
621 GType types[FIO_IO_U_LIST_MAX_LEN];
622 GtkWidget *tree_view;
623 GtkTreeSelection *selection;
624 GtkListStore *model;
625 GtkTreeIter iter;
626 int i;
627
628 for (i = 0; i < len; i++)
629 types[i] = G_TYPE_INT;
630
631 model = gtk_list_store_newv(len, types);
632
633 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
634 gtk_widget_set_can_focus(tree_view, FALSE);
635
661f741a
JA
636 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
637 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
638
a269790c
JA
639 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
640 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
641
642 for (i = 0; i < len; i++) {
643 char fbuf[8];
644
645 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
646 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
647 }
648
649 gtk_list_store_append(model, &iter);
650
e0681f3e
JA
651 for (i = 0; i < len; i++) {
652 if (scale)
653 ovals[i] = (ovals[i] + 999) / 1000;
a269790c 654 gtk_list_store_set(model, &iter, i, ovals[i], -1);
e0681f3e 655 }
a269790c
JA
656
657 return tree_view;
658}
659
09d574e3 660static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
c57f254c
SC
661{
662 struct graph *g = p;
663 cairo_t *cr;
664
665 cr = gdk_cairo_create(w->window);
666#if 0
667 if (graph_has_tooltips(g)) {
668 g_object_set(w, "has-tooltip", TRUE, NULL);
669 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
670 }
671#endif
672 cairo_set_source_rgb(cr, 0, 0, 0);
673 bar_graph_draw(g, cr);
674 cairo_destroy(cr);
675
676 return FALSE;
677}
678
09d574e3
JA
679static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
680 gpointer data)
c57f254c
SC
681{
682 struct graph *g = data;
683
684 graph_set_size(g, w->allocation.width, w->allocation.height);
685 graph_set_size(g, w->allocation.width, w->allocation.height);
686 graph_set_position(g, 0, 0);
687 return TRUE;
688}
689
690static void gfio_show_clat_percentiles(struct gfio_client *gc,
691 GtkWidget *vbox, struct thread_stat *ts,
a269790c
JA
692 int ddir)
693{
694 unsigned int *io_u_plat = ts->io_u_plat[ddir];
695 unsigned long nr = ts->clat_stat[ddir].samples;
696 fio_fp64_t *plist = ts->percentile_list;
697 unsigned int *ovals, len, minv, maxv, scale_down;
698 const char *base;
c57f254c 699 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
09d574e3 700 struct gui_entry *ge = gc->ge;
a269790c
JA
701 char tmp[64];
702
703 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
704 if (!len)
705 goto out;
706
707 /*
708 * We default to usecs, but if the value range is such that we
709 * should scale down to msecs, do that.
710 */
711 if (minv > 2000 && maxv > 99999) {
712 scale_down = 1;
713 base = "msec";
714 } else {
715 scale_down = 0;
716 base = "usec";
717 }
718
c57f254c 719 sprintf(tmp, "Completion percentiles (%s)", base);
a269790c 720 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
09d574e3 721 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
a269790c 722
a269790c
JA
723 frame = gtk_frame_new(tmp);
724 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
725
c57f254c
SC
726 completion_vbox = gtk_vbox_new(FALSE, 3);
727 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
a269790c 728 hbox = gtk_hbox_new(FALSE, 3);
c57f254c
SC
729 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
730 drawing_area = gtk_drawing_area_new();
731 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
732 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
733 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
09d574e3
JA
734 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
735 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
a269790c
JA
736
737 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
738out:
739 if (ovals)
740 free(ovals);
741}
742
3650a3ca
JA
743static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
744 unsigned long max, double mean, double dev)
745{
746 const char *base = "(usec)";
1c1e4a5b 747 GtkWidget *hbox, *label, *frame;
3650a3ca
JA
748 char *minp, *maxp;
749 char tmp[64];
750
751 if (!usec_to_msec(&min, &max, &mean, &dev))
752 base = "(msec)";
753
754 minp = num2str(min, 6, 1, 0);
755 maxp = num2str(max, 6, 1, 0);
756
3650a3ca
JA
757 sprintf(tmp, "%s %s", name, base);
758 frame = gtk_frame_new(tmp);
759 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
760
3650a3ca 761 hbox = gtk_hbox_new(FALSE, 3);
1c1e4a5b 762 gtk_container_add(GTK_CONTAINER(frame), hbox);
3650a3ca
JA
763
764 label = new_info_label_in_frame(hbox, "Minimum");
765 gtk_label_set_text(GTK_LABEL(label), minp);
766 label = new_info_label_in_frame(hbox, "Maximum");
767 gtk_label_set_text(GTK_LABEL(label), maxp);
768 label = new_info_label_in_frame(hbox, "Average");
769 sprintf(tmp, "%5.02f", mean);
770 gtk_label_set_text(GTK_LABEL(label), tmp);
771 label = new_info_label_in_frame(hbox, "Standard deviation");
772 sprintf(tmp, "%5.02f", dev);
773 gtk_label_set_text(GTK_LABEL(label), tmp);
774
775 free(minp);
776 free(maxp);
777
778}
779
ca850992
JA
780#define GFIO_CLAT 1
781#define GFIO_SLAT 2
782#define GFIO_LAT 4
783
c57f254c
SC
784static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
785 struct group_run_stats *rs,
3650a3ca
JA
786 struct thread_stat *ts, int ddir)
787{
788 const char *ddir_label[2] = { "Read", "Write" };
0b761306 789 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
e0681f3e 790 unsigned long min[3], max[3], runt;
3650a3ca 791 unsigned long long bw, iops;
ca850992 792 unsigned int flags = 0;
e0681f3e 793 double mean[3], dev[3];
3650a3ca
JA
794 char *io_p, *bw_p, *iops_p;
795 int i2p;
796
797 if (!ts->runtime[ddir])
798 return;
799
800 i2p = is_power_of_2(rs->kb_base);
801 runt = ts->runtime[ddir];
802
803 bw = (1000 * ts->io_bytes[ddir]) / runt;
804 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
805 bw_p = num2str(bw, 6, 1, i2p);
806
807 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
808 iops_p = num2str(iops, 6, 1, 0);
809
810 box = gtk_hbox_new(FALSE, 3);
811 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
812
813 frame = gtk_frame_new(ddir_label[ddir]);
814 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
815
0b761306
JA
816 main_vbox = gtk_vbox_new(FALSE, 3);
817 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
3650a3ca
JA
818
819 box = gtk_hbox_new(FALSE, 3);
0b761306 820 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
3650a3ca
JA
821
822 label = new_info_label_in_frame(box, "IO");
823 gtk_label_set_text(GTK_LABEL(label), io_p);
824 label = new_info_label_in_frame(box, "Bandwidth");
825 gtk_label_set_text(GTK_LABEL(label), bw_p);
826 label = new_info_label_in_frame(box, "IOPS");
827 gtk_label_set_text(GTK_LABEL(label), iops_p);
828 label = new_info_label_in_frame(box, "Runtime (msec)");
829 label_set_int_value(label, ts->runtime[ddir]);
830
e0681f3e 831 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
ca850992
JA
832 double p_of_agg = 100.0;
833 const char *bw_str = "KB";
834 char tmp[32];
835
836 if (rs->agg[ddir]) {
e0681f3e 837 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
ca850992
JA
838 if (p_of_agg > 100.0)
839 p_of_agg = 100.0;
840 }
841
e0681f3e
JA
842 if (mean[0] > 999999.9) {
843 min[0] /= 1000.0;
844 max[0] /= 1000.0;
845 mean[0] /= 1000.0;
846 dev[0] /= 1000.0;
ca850992
JA
847 bw_str = "MB";
848 }
849
0b761306
JA
850 sprintf(tmp, "Bandwidth (%s)", bw_str);
851 frame = gtk_frame_new(tmp);
852 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
ca850992 853
0b761306
JA
854 box = gtk_hbox_new(FALSE, 3);
855 gtk_container_add(GTK_CONTAINER(frame), box);
ca850992 856
0b761306 857 label = new_info_label_in_frame(box, "Minimum");
e0681f3e 858 label_set_int_value(label, min[0]);
0b761306 859 label = new_info_label_in_frame(box, "Maximum");
e0681f3e 860 label_set_int_value(label, max[0]);
0b761306 861 label = new_info_label_in_frame(box, "Percentage of jobs");
ca850992
JA
862 sprintf(tmp, "%3.2f%%", p_of_agg);
863 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 864 label = new_info_label_in_frame(box, "Average");
e0681f3e 865 sprintf(tmp, "%5.02f", mean[0]);
ca850992 866 gtk_label_set_text(GTK_LABEL(label), tmp);
0b761306 867 label = new_info_label_in_frame(box, "Standard deviation");
e0681f3e 868 sprintf(tmp, "%5.02f", dev[0]);
ca850992
JA
869 gtk_label_set_text(GTK_LABEL(label), tmp);
870 }
871
e0681f3e 872 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
2b089892 873 flags |= GFIO_SLAT;
e0681f3e 874 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
2b089892 875 flags |= GFIO_CLAT;
e0681f3e 876 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
2b089892
JA
877 flags |= GFIO_LAT;
878
879 if (flags) {
880 frame = gtk_frame_new("Latency");
881 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
882
883 vbox = gtk_vbox_new(FALSE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), vbox);
885
886 if (flags & GFIO_SLAT)
e0681f3e 887 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
2b089892 888 if (flags & GFIO_CLAT)
e0681f3e 889 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
2b089892 890 if (flags & GFIO_LAT)
e0681f3e 891 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
2b089892
JA
892 }
893
894 if (ts->clat_percentiles)
c57f254c 895 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
2b089892 896
3650a3ca
JA
897 free(io_p);
898 free(bw_p);
899 free(iops_p);
900}
901
09d574e3
JA
902static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
903 const char **labels,
904 unsigned int len,
905 double xdim, double ydim)
906{
907 struct graph *g;
908 int i;
909
910 g = graph_new(xdim, ydim, gfio_graph_font);
911 graph_title(g, title);
912 graph_x_title(g, "Buckets");
913
914 for (i = 0; i < len; i++) {
915 graph_add_label(g, labels[i]);
916 graph_add_data(g, labels[i], lat[i]);
917 }
918
919 return g;
920}
921
922static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
923 int num)
e5bd1347
JA
924{
925 GtkWidget *tree_view;
926 GtkTreeSelection *selection;
927 GtkListStore *model;
928 GtkTreeIter iter;
929 GType *types;
09d574e3 930 int i;
e5bd1347
JA
931
932 types = malloc(num * sizeof(GType));
933
934 for (i = 0; i < num; i++)
935 types[i] = G_TYPE_STRING;
936
937 model = gtk_list_store_newv(num, types);
938 free(types);
939 types = NULL;
940
941 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
942 gtk_widget_set_can_focus(tree_view, FALSE);
943
661f741a
JA
944 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
945 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
946
e5bd1347
JA
947 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
948 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
949
950 for (i = 0; i < num; i++)
951 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
952
953 gtk_list_store_append(model, &iter);
954
955 for (i = 0; i < num; i++) {
956 char fbuf[32];
957
958 if (lat[i] <= 0.0)
959 sprintf(fbuf, "0.00");
960 else
961 sprintf(fbuf, "%3.2f%%", lat[i]);
962
963 gtk_list_store_set(model, &iter, i, fbuf, -1);
964 }
965
966 return tree_view;
967}
968
09d574e3
JA
969static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
970 struct thread_stat *ts)
e5bd1347 971{
09d574e3
JA
972 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
973 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
974 "250u", "500u", "750u", "1m", "2m",
975 "4m", "10m", "20m", "50m", "100m",
976 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
977 int start, end, i;
978 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
979 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
980 struct gui_entry *ge = gc->ge;
981
982 stat_calc_lat_u(ts, io_u_lat);
983 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
e5bd1347 984
09d574e3
JA
985 /*
986 * Found out which first bucket has entries, and which last bucket
987 */
988 start = end = -1U;
989 for (i = 0; i < total; i++) {
990 if (io_u_lat[i] == 0.00)
991 continue;
992
993 if (start == -1U)
994 start = i;
995 end = i;
e5bd1347
JA
996 }
997
09d574e3
JA
998 /*
999 * No entries...
1000 */
1001 if (start == -1U)
1002 return;
1003
1004 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1005 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
e5bd1347 1006
09d574e3
JA
1007 frame = gtk_frame_new("Latency buckets");
1008 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1009
1010 completion_vbox = gtk_vbox_new(FALSE, 3);
1011 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1012 hbox = gtk_hbox_new(FALSE, 3);
1013 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1014
1015 drawing_area = gtk_drawing_area_new();
1016 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1017 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1018 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1019 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1020 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1021
1022 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
e5bd1347
JA
1023}
1024
2e33101f
JA
1025static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1026{
1027 GtkWidget *box, *frame, *entry;
1028 double usr_cpu, sys_cpu;
1029 unsigned long runtime;
1030 char tmp[32];
1031
1032 runtime = ts->total_run_time;
1033 if (runtime) {
1034 double runt = (double) runtime;
1035
1036 usr_cpu = (double) ts->usr_time * 100 / runt;
1037 sys_cpu = (double) ts->sys_time * 100 / runt;
1038 } else {
1039 usr_cpu = 0;
1040 sys_cpu = 0;
1041 }
1042
1043 frame = gtk_frame_new("OS resources");
1044 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1045
1046 box = gtk_hbox_new(FALSE, 3);
1047 gtk_container_add(GTK_CONTAINER(frame), box);
1048
1049 entry = new_info_entry_in_frame(box, "User CPU");
1050 sprintf(tmp, "%3.2f%%", usr_cpu);
1051 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1052 entry = new_info_entry_in_frame(box, "System CPU");
1053 sprintf(tmp, "%3.2f%%", sys_cpu);
1054 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1055 entry = new_info_entry_in_frame(box, "Context switches");
1056 entry_set_int_value(entry, ts->ctx);
1057 entry = new_info_entry_in_frame(box, "Major faults");
1058 entry_set_int_value(entry, ts->majf);
1059 entry = new_info_entry_in_frame(box, "Minor faults");
1060 entry_set_int_value(entry, ts->minf);
1061}
19998dbc
JA
1062static void gfio_add_sc_depths_tree(GtkListStore *model,
1063 struct thread_stat *ts, unsigned int len,
1064 int submit)
2e33101f
JA
1065{
1066 double io_u_dist[FIO_IO_U_MAP_NR];
2e33101f 1067 GtkTreeIter iter;
19998dbc
JA
1068 /* Bits 0, and 3-8 */
1069 const int add_mask = 0x1f9;
1070 int i, j;
2e33101f 1071
19998dbc
JA
1072 if (submit)
1073 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1074 else
1075 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
2e33101f 1076
19998dbc 1077 gtk_list_store_append(model, &iter);
2e33101f 1078
19998dbc 1079 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
2e33101f 1080
19998dbc
JA
1081 for (i = 1, j = 0; i < len; i++) {
1082 char fbuf[32];
2e33101f 1083
19998dbc
JA
1084 if (!(add_mask & (1UL << (i - 1))))
1085 sprintf(fbuf, "0.0%%");
1086 else {
1087 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1088 j++;
1089 }
2e33101f 1090
19998dbc
JA
1091 gtk_list_store_set(model, &iter, i, fbuf, -1);
1092 }
2e33101f 1093
19998dbc 1094}
2e33101f 1095
19998dbc
JA
1096static void gfio_add_total_depths_tree(GtkListStore *model,
1097 struct thread_stat *ts, unsigned int len)
1098{
1099 double io_u_dist[FIO_IO_U_MAP_NR];
1100 GtkTreeIter iter;
1101 /* Bits 1-6, and 8 */
1102 const int add_mask = 0x17e;
1103 int i, j;
1104
1105 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
2e33101f
JA
1106
1107 gtk_list_store_append(model, &iter);
1108
19998dbc
JA
1109 gtk_list_store_set(model, &iter, 0, "Total", -1);
1110
1111 for (i = 1, j = 0; i < len; i++) {
2e33101f
JA
1112 char fbuf[32];
1113
19998dbc
JA
1114 if (!(add_mask & (1UL << (i - 1))))
1115 sprintf(fbuf, "0.0%%");
1116 else {
1117 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1118 j++;
2e33101f
JA
1119 }
1120
2e33101f
JA
1121 gtk_list_store_set(model, &iter, i, fbuf, -1);
1122 }
1123
19998dbc 1124}
2e33101f 1125
19998dbc
JA
1126static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1127{
1128 GtkWidget *frame, *box, *tree_view;
1129 GtkTreeSelection *selection;
1130 GtkListStore *model;
1131 GType types[FIO_IO_U_MAP_NR + 1];
1132 int i;
1133#define NR_LABELS 10
1134 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
2e33101f 1135
19998dbc
JA
1136 frame = gtk_frame_new("IO depths");
1137 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2e33101f 1138
19998dbc
JA
1139 box = gtk_hbox_new(FALSE, 3);
1140 gtk_container_add(GTK_CONTAINER(frame), box);
2e33101f 1141
19998dbc
JA
1142 for (i = 0; i < NR_LABELS; i++)
1143 types[i] = G_TYPE_STRING;
2e33101f 1144
19998dbc 1145 model = gtk_list_store_newv(NR_LABELS, types);
2e33101f 1146
19998dbc
JA
1147 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1148 gtk_widget_set_can_focus(tree_view, FALSE);
2e33101f 1149
661f741a
JA
1150 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1151 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1152
19998dbc
JA
1153 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1154 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1155
1156 for (i = 0; i < NR_LABELS; i++)
1157 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1158
1159 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1160 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1161 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
2e33101f
JA
1162
1163 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1164}
1165
f9d40b48
JA
1166static gboolean results_window_delete(GtkWidget *w, gpointer data)
1167{
2f99deb0 1168 struct gui_entry *ge = (struct gui_entry *) data;
f9d40b48
JA
1169
1170 gtk_widget_destroy(w);
2f99deb0
JA
1171 ge->results_window = NULL;
1172 ge->results_notebook = NULL;
f9d40b48
JA
1173 return TRUE;
1174}
1175
17b9721a
JA
1176static void results_close(GtkWidget *w, gpointer *data)
1177{
1178 struct gui_entry *ge = (struct gui_entry *) data;
1179
1180 gtk_widget_destroy(ge->results_window);
1181}
1182
1183static GtkActionEntry results_menu_items[] = {
1184 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1185 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1186 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1187};
1188static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1189
1190static const gchar *results_ui_string = " \
1191 <ui> \
1192 <menubar name=\"MainMenu\"> \
1193 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1194 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1195 </menu> \
1196 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1197 </menu>\
1198 </menubar> \
1199 </ui> \
1200";
1201
1202static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1203{
1204 GtkActionGroup *action_group;
1205 GtkWidget *widget;
1206 GError *error = 0;
1207
1208 ge->results_uimanager = gtk_ui_manager_new();
1209
1210 action_group = gtk_action_group_new("ResultsMenu");
1211 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1212
1213 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1214 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1215
1216 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1217
1218 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1219 return widget;
1220}
1221
2f99deb0 1222static GtkWidget *get_results_window(struct gui_entry *ge)
f9d40b48 1223{
17b9721a 1224 GtkWidget *win, *notebook, *vbox;
f9d40b48 1225
2f99deb0
JA
1226 if (ge->results_window)
1227 return ge->results_notebook;
f9d40b48
JA
1228
1229 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1230 gtk_window_set_title(GTK_WINDOW(win), "Results");
b01329d0 1231 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
2f99deb0
JA
1232 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1233 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
f9d40b48 1234
17b9721a
JA
1235 vbox = gtk_vbox_new(FALSE, 0);
1236 gtk_container_add(GTK_CONTAINER(win), vbox);
1237
1238 ge->results_menu = get_results_menubar(win, ge);
1239 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1240
f9d40b48 1241 notebook = gtk_notebook_new();
0aa928c4
JA
1242 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1243 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
17b9721a 1244 gtk_container_add(GTK_CONTAINER(vbox), notebook);
f9d40b48 1245
2f99deb0
JA
1246 ge->results_window = win;
1247 ge->results_notebook = notebook;
1248 return ge->results_notebook;
f9d40b48
JA
1249}
1250
7da23b48
JA
1251static void disk_util_destroy(GtkWidget *w, gpointer data)
1252{
1253 struct gui_entry *ge = (struct gui_entry *) data;
1254
1255 ge->disk_util_vbox = NULL;
1256 gtk_widget_destroy(w);
1257}
1258
1259static int __gfio_disk_util_show(GtkWidget *res_notebook,
1260 struct gfio_client *gc, struct cmd_du_pdu *p)
1261{
1262 GtkWidget *box, *frame, *entry, *vbox;
1263 struct gui_entry *ge = gc->ge;
1264 double util;
1265 char tmp[16];
1266
1267 res_notebook = get_results_window(ge);
1268
1269 if (!ge->disk_util_vbox) {
1270 vbox = gtk_vbox_new(FALSE, 3);
1271 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization"));
1272 ge->disk_util_vbox = vbox;
1273 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1274 }
1275
1276 vbox = gtk_vbox_new(FALSE, 3);
1277 gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox);
1278
1279 frame = gtk_frame_new((char *) p->dus.name);
1280 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1281
1282 box = gtk_vbox_new(FALSE, 3);
1283 gtk_container_add(GTK_CONTAINER(frame), box);
1284
1285 frame = gtk_frame_new("Read");
1286 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1287 vbox = gtk_hbox_new(TRUE, 3);
1288 gtk_container_add(GTK_CONTAINER(frame), vbox);
1289 entry = new_info_entry_in_frame(vbox, "IOs");
1290 entry_set_int_value(entry, p->dus.ios[0]);
1291 entry = new_info_entry_in_frame(vbox, "Merges");
1292 entry_set_int_value(entry, p->dus.merges[0]);
1293 entry = new_info_entry_in_frame(vbox, "Sectors");
1294 entry_set_int_value(entry, p->dus.sectors[0]);
1295 entry = new_info_entry_in_frame(vbox, "Ticks");
1296 entry_set_int_value(entry, p->dus.ticks[0]);
1297
1298 frame = gtk_frame_new("Write");
1299 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1300 vbox = gtk_hbox_new(TRUE, 3);
1301 gtk_container_add(GTK_CONTAINER(frame), vbox);
1302 entry = new_info_entry_in_frame(vbox, "IOs");
1303 entry_set_int_value(entry, p->dus.ios[1]);
1304 entry = new_info_entry_in_frame(vbox, "Merges");
1305 entry_set_int_value(entry, p->dus.merges[1]);
1306 entry = new_info_entry_in_frame(vbox, "Sectors");
1307 entry_set_int_value(entry, p->dus.sectors[1]);
1308 entry = new_info_entry_in_frame(vbox, "Ticks");
1309 entry_set_int_value(entry, p->dus.ticks[1]);
1310
1311 frame = gtk_frame_new("Shared");
1312 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1313 vbox = gtk_hbox_new(TRUE, 3);
1314 gtk_container_add(GTK_CONTAINER(frame), vbox);
1315 entry = new_info_entry_in_frame(vbox, "IO ticks");
1316 entry_set_int_value(entry, p->dus.io_ticks);
1317 entry = new_info_entry_in_frame(vbox, "Time in queue");
1318 entry_set_int_value(entry, p->dus.time_in_queue);
1319
1320 util = 0.0;
1321 if (p->dus.msec)
1322 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1323 if (util > 100.0)
1324 util = 100.0;
1325
1326 sprintf(tmp, "%3.2f%%", util);
1327 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1328 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1329
1330 gtk_widget_show_all(ge->results_window);
1331 return 0;
1332}
1333
1334static int gfio_disk_util_show(struct gfio_client *gc)
1335{
1336 struct gui_entry *ge = gc->ge;
1337 GtkWidget *res_notebook;
1338 int i;
1339
1340 if (!gc->nr_du)
1341 return 1;
1342
1343 res_notebook = get_results_window(ge);
1344
1345 for (i = 0; i < gc->nr_du; i++) {
1346 struct cmd_du_pdu *p = &gc->du[i];
1347
1348 __gfio_disk_util_show(res_notebook, gc, p);
1349 }
1350
1351 gtk_widget_show_all(ge->results_window);
1352 return 0;
1353}
1354
781ccba6
JA
1355static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1356 struct group_run_stats *rs)
3650a3ca 1357{
781ccba6 1358 unsigned int nr = gc->nr_results;
3650a3ca 1359
781ccba6
JA
1360 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1361 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1362 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1363 gc->nr_results++;
1364}
3650a3ca 1365
781ccba6
JA
1366static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1367 struct thread_stat *ts,
1368 struct group_run_stats *rs)
1369{
1370 GtkWidget *box, *vbox, *entry, *scroll;
3650a3ca 1371
b01329d0
JA
1372 scroll = gtk_scrolled_window_new(NULL, NULL);
1373 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1374 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1375
3650a3ca 1376 vbox = gtk_vbox_new(FALSE, 3);
3650a3ca 1377
b01329d0
JA
1378 box = gtk_hbox_new(FALSE, 0);
1379 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1380
1381 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
3650a3ca 1382
781ccba6 1383 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
f9d40b48 1384
3650a3ca
JA
1385 entry = new_info_entry_in_frame(box, "Name");
1386 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1387 if (strlen(ts->description)) {
1388 entry = new_info_entry_in_frame(box, "Description");
1389 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1390 }
1391 entry = new_info_entry_in_frame(box, "Group ID");
1392 entry_set_int_value(entry, ts->groupid);
1393 entry = new_info_entry_in_frame(box, "Jobs");
1394 entry_set_int_value(entry, ts->members);
6b79c80c 1395 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
3650a3ca
JA
1396 entry_set_int_value(entry, ts->error);
1397 entry = new_info_entry_in_frame(box, "PID");
1398 entry_set_int_value(entry, ts->pid);
1399
1400 if (ts->io_bytes[DDIR_READ])
c57f254c 1401 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
3650a3ca 1402 if (ts->io_bytes[DDIR_WRITE])
c57f254c 1403 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
3650a3ca 1404
09d574e3 1405 gfio_show_latency_buckets(gc, vbox, ts);
2e33101f
JA
1406 gfio_show_cpu_usage(vbox, ts);
1407 gfio_show_io_depths(vbox, ts);
781ccba6
JA
1408}
1409
1410static void gfio_display_end_results(struct gfio_client *gc)
1411{
60d0929e
JA
1412 struct gui_entry *ge = gc->ge;
1413 GtkWidget *res_notebook;
781ccba6
JA
1414 int i;
1415
60d0929e 1416 res_notebook = get_results_window(ge);
781ccba6
JA
1417
1418 for (i = 0; i < gc->nr_results; i++) {
1419 struct end_results *e = &gc->results[i];
1420
60d0929e 1421 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
781ccba6 1422 }
e5bd1347 1423
7da23b48
JA
1424 if (gfio_disk_util_show(gc))
1425 gtk_widget_show_all(ge->results_window);
781ccba6
JA
1426}
1427
1428static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1429 struct group_run_stats *rs)
1430{
1431 struct gfio_client *gc = client->client_data;
1432
1433 gfio_add_end_results(gc, ts, rs);
1434
1435 gdk_threads_enter();
1436 gfio_display_end_results(gc);
3650a3ca
JA
1437 gdk_threads_leave();
1438}
1439
084d1c6f 1440static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 1441{
9b260bdf 1442 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
2f99deb0 1443 struct gui *ui = &main_ui;
9b260bdf
JA
1444 GtkTreeIter iter;
1445 struct tm *tm;
1446 time_t sec;
1447 char tmp[64], timebuf[80];
1448
1449 sec = p->log_sec;
1450 tm = localtime(&sec);
1451 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1452 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
736f2dff 1453
736f2dff 1454 gdk_threads_enter();
9b260bdf 1455
2f99deb0
JA
1456 gtk_list_store_append(ui->log_model, &iter);
1457 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1458 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1459 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1460 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
9b260bdf 1461
6b79c80c 1462 if (p->level == FIO_LOG_ERR)
2f99deb0 1463 view_log(NULL, (gpointer) ui);
6b79c80c 1464
736f2dff 1465 gdk_threads_leave();
a1820207
SC
1466}
1467
1468static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1469{
e0681f3e
JA
1470 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1471 struct gfio_client *gc = client->client_data;
7da23b48 1472 unsigned int nr = gc->nr_du;
e0681f3e 1473
7da23b48
JA
1474 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1475 memcpy(&gc->du[nr], p, sizeof(*p));
1476 gc->nr_du++;
e0681f3e 1477
7da23b48
JA
1478 gdk_threads_enter();
1479 gfio_disk_util_show(gc);
0050e5f2 1480 gdk_threads_leave();
a1820207
SC
1481}
1482
3650a3ca
JA
1483extern int sum_stat_clients;
1484extern struct thread_stat client_ts;
1485extern struct group_run_stats client_gs;
1486
1487static int sum_stat_nr;
1488
89e5fad9
JA
1489static void gfio_thread_status_op(struct fio_client *client,
1490 struct fio_net_cmd *cmd)
a1820207 1491{
3650a3ca
JA
1492 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1493
1494 gfio_display_ts(client, &p->ts, &p->rs);
1495
1496 if (sum_stat_clients == 1)
1497 return;
1498
1499 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1500 sum_group_stats(&client_gs, &p->rs);
1501
1502 client_ts.members++;
2f122b13 1503 client_ts.thread_number = p->ts.thread_number;
3650a3ca
JA
1504 client_ts.groupid = p->ts.groupid;
1505
1506 if (++sum_stat_nr == sum_stat_clients) {
1507 strcpy(client_ts.name, "All clients");
1508 gfio_display_ts(client, &client_ts, &client_gs);
1509 }
a1820207
SC
1510}
1511
89e5fad9
JA
1512static void gfio_group_stats_op(struct fio_client *client,
1513 struct fio_net_cmd *cmd)
a1820207 1514{
98ceabd6 1515 /* We're ignoring group stats for now */
a1820207
SC
1516}
1517
2f99deb0
JA
1518static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1519 gpointer data)
3ea48b88 1520{
2f99deb0
JA
1521 struct gfio_graphs *g = data;
1522
57f9d28e
SC
1523 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1524 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1525 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1526 graph_set_position(g->bandwidth_graph, 0, 0);
3ea48b88
SC
1527 return TRUE;
1528}
1529
57f9d28e
SC
1530static void draw_graph(struct graph *g, cairo_t *cr)
1531{
1532 line_graph_draw(g, cr);
1533 cairo_stroke(cr);
1534}
1535
93e2db2b
JA
1536static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1537 gboolean keyboard_mode, GtkTooltip *tooltip,
1538 gpointer data)
1539{
1540 struct gfio_graphs *g = data;
1541 const char *text = NULL;
1542
1543 if (graph_contains_xy(g->iops_graph, x, y))
1544 text = graph_find_tooltip(g->iops_graph, x, y);
1545 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1546 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1547
1548 if (text) {
1549 gtk_tooltip_set_text(tooltip, text);
1550 return TRUE;
1551 }
1552
1553 return FALSE;
1554}
1555
2fd3bb0e
JA
1556static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1557{
2f99deb0 1558 struct gfio_graphs *g = p;
2fd3bb0e
JA
1559 cairo_t *cr;
1560
1561 cr = gdk_cairo_create(w->window);
93e2db2b
JA
1562
1563 if (graph_has_tooltips(g->iops_graph) ||
1564 graph_has_tooltips(g->bandwidth_graph)) {
1565 g_object_set(w, "has-tooltip", TRUE, NULL);
1566 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1567 }
1568
2fd3bb0e 1569 cairo_set_source_rgb(cr, 0, 0, 0);
57f9d28e
SC
1570 draw_graph(g->iops_graph, cr);
1571 draw_graph(g->bandwidth_graph, cr);
2fd3bb0e
JA
1572 cairo_destroy(cr);
1573
1574 return FALSE;
1575}
1576
2f99deb0
JA
1577/*
1578 * Client specific ETA
1579 */
1580static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
3e47bd25 1581{
2f99deb0
JA
1582 struct gfio_client *gc = client->client_data;
1583 struct gui_entry *ge = gc->ge;
3e47bd25
JA
1584 static int eta_good;
1585 char eta_str[128];
1586 char output[256];
1587 char tmp[32];
1588 double perc = 0.0;
1589 int i2p = 0;
1590
0050e5f2
JA
1591 gdk_threads_enter();
1592
3e47bd25
JA
1593 eta_str[0] = '\0';
1594 output[0] = '\0';
1595
1596 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1597 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1598 eta_to_str(eta_str, je->eta_sec);
1599 }
1600
1601 sprintf(tmp, "%u", je->nr_running);
2f99deb0 1602 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
3e47bd25 1603 sprintf(tmp, "%u", je->files_open);
2f99deb0 1604 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
3e47bd25
JA
1605
1606#if 0
1607 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1608 if (je->m_rate || je->t_rate) {
1609 char *tr, *mr;
1610
1611 mr = num2str(je->m_rate, 4, 0, i2p);
1612 tr = num2str(je->t_rate, 4, 0, i2p);
2f99deb0 1613 gtk_entry_set_text(GTK_ENTRY(ge->eta);
3e47bd25
JA
1614 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1615 free(tr);
1616 free(mr);
1617 } else if (je->m_iops || je->t_iops)
1618 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 1619
2f99deb0
JA
1620 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1621 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1622 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1623 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
3e47bd25
JA
1624#endif
1625
1626 if (je->eta_sec != INT_MAX && je->nr_running) {
1627 char *iops_str[2];
1628 char *rate_str[2];
1629
1630 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1631 strcpy(output, "-.-% done");
1632 else {
1633 eta_good = 1;
1634 perc *= 100.0;
1635 sprintf(output, "%3.1f%% done", perc);
1636 }
1637
1638 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1639 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1640
1641 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1642 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1643
2f99deb0
JA
1644 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1645 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1646 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1647 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
3e47bd25 1648
93e2db2b
JA
1649 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1650 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1651 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1652 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
2f99deb0
JA
1653
1654 free(rate_str[0]);
1655 free(rate_str[1]);
1656 free(iops_str[0]);
1657 free(iops_str[1]);
1658 }
1659
1660 if (eta_str[0]) {
1661 char *dst = output + strlen(output);
1662
1663 sprintf(dst, " - %s", eta_str);
1664 }
1665
9988ca70 1666 gfio_update_thread_status(ge, output, perc);
2f99deb0
JA
1667 gdk_threads_leave();
1668}
1669
1670/*
1671 * Update ETA in main window for all clients
1672 */
1673static void gfio_update_all_eta(struct jobs_eta *je)
1674{
1675 struct gui *ui = &main_ui;
1676 static int eta_good;
1677 char eta_str[128];
1678 char output[256];
1679 double perc = 0.0;
1680 int i2p = 0;
1681
1682 gdk_threads_enter();
1683
1684 eta_str[0] = '\0';
1685 output[0] = '\0';
1686
1687 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1688 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1689 eta_to_str(eta_str, je->eta_sec);
1690 }
1691
1692#if 0
1693 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1694 if (je->m_rate || je->t_rate) {
1695 char *tr, *mr;
1696
1697 mr = num2str(je->m_rate, 4, 0, i2p);
1698 tr = num2str(je->t_rate, 4, 0, i2p);
1699 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1700 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1701 free(tr);
1702 free(mr);
1703 } else if (je->m_iops || je->t_iops)
1704 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1705
1706 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1707 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1708 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1709 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1710#endif
1711
3863d1ad
JA
1712 entry_set_int_value(ui->eta.jobs, je->nr_running);
1713
2f99deb0
JA
1714 if (je->eta_sec != INT_MAX && je->nr_running) {
1715 char *iops_str[2];
1716 char *rate_str[2];
1717
1718 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1719 strcpy(output, "-.-% done");
1720 else {
1721 eta_good = 1;
1722 perc *= 100.0;
1723 sprintf(output, "%3.1f%% done", perc);
1724 }
1725
1726 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1727 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1728
1729 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1730 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1731
1732 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1733 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1734 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1735 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1736
93e2db2b
JA
1737 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1738 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1739 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1740 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
2fd3bb0e 1741
3e47bd25
JA
1742 free(rate_str[0]);
1743 free(rate_str[1]);
1744 free(iops_str[0]);
1745 free(iops_str[1]);
1746 }
1747
1748 if (eta_str[0]) {
1749 char *dst = output + strlen(output);
1750
1751 sprintf(dst, " - %s", eta_str);
1752 }
1753
9988ca70 1754 gfio_update_thread_status_all(output, perc);
0050e5f2 1755 gdk_threads_leave();
3e47bd25
JA
1756}
1757
a1820207
SC
1758static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1759{
843ad237 1760 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
88f6e7ad 1761 struct gfio_client *gc = client->client_data;
2f99deb0 1762 struct gui_entry *ge = gc->ge;
843ad237
JA
1763 const char *os, *arch;
1764 char buf[64];
1765
1766 os = fio_get_os_string(probe->os);
1767 if (!os)
1768 os = "unknown";
1769
1770 arch = fio_get_arch_string(probe->arch);
1771 if (!arch)
1772 os = "unknown";
1773
1774 if (!client->name)
1775 client->name = strdup((char *) probe->hostname);
1776
0050e5f2
JA
1777 gdk_threads_enter();
1778
2f99deb0
JA
1779 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1780 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1781 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
843ad237 1782 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
2f99deb0 1783 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
88f6e7ad 1784
85dd01e7 1785 gfio_set_state(ge, GE_STATE_CONNECTED);
0050e5f2
JA
1786
1787 gdk_threads_leave();
a1820207
SC
1788}
1789
9988ca70
JA
1790static void gfio_update_thread_status(struct gui_entry *ge,
1791 char *status_message, double perc)
1792{
1793 static char message[100];
1794 const char *m = message;
1795
1796 strncpy(message, status_message, sizeof(message) - 1);
1797 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1798 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1799 gtk_widget_queue_draw(main_ui.window);
1800}
1801
1802static void gfio_update_thread_status_all(char *status_message, double perc)
5b7573ab 1803{
2f99deb0 1804 struct gui *ui = &main_ui;
5b7573ab
SC
1805 static char message[100];
1806 const char *m = message;
1807
1808 strncpy(message, status_message, sizeof(message) - 1);
2f99deb0
JA
1809 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1810 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1811 gtk_widget_queue_draw(ui->window);
5b7573ab
SC
1812}
1813
35c0ba7f 1814static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
3ec62ec4 1815{
e0681f3e 1816 struct gfio_client *gc = client->client_data;
3ec62ec4 1817
0050e5f2 1818 gdk_threads_enter();
85dd01e7 1819 gfio_set_state(gc->ge, GE_STATE_NEW);
0050e5f2 1820 gdk_threads_leave();
3ec62ec4
JA
1821}
1822
807f9971
JA
1823static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1824{
1825 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
e0681f3e 1826 struct gfio_client *gc = client->client_data;
dcaeb606 1827 struct thread_options *o = &gc->o;
2f99deb0 1828 struct gui_entry *ge = gc->ge;
807f9971 1829 char tmp[8];
807f9971 1830
2f122b13
JA
1831 p->thread_number = le32_to_cpu(p->thread_number);
1832 p->groupid = le32_to_cpu(p->groupid);
dcaeb606 1833 convert_thread_options_to_cpu(o, &p->top);
807f9971 1834
0050e5f2
JA
1835 gdk_threads_enter();
1836
2f99deb0
JA
1837 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1838
3863d1ad
JA
1839 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1840 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1841
c80b74b0
JA
1842 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1843 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
807f9971 1844
dcaeb606 1845 sprintf(tmp, "%u", o->iodepth);
c80b74b0
JA
1846 multitext_add_entry(&ge->eta.iodepth, tmp);
1847
1848 multitext_set_entry(&ge->eta.iotype, 0);
1849 multitext_set_entry(&ge->eta.ioengine, 0);
1850 multitext_set_entry(&ge->eta.iodepth, 0);
0050e5f2 1851
dcaeb606
JA
1852 gc->job_added++;
1853
85dd01e7
JA
1854 gfio_set_state(ge, GE_STATE_JOB_SENT);
1855
0050e5f2 1856 gdk_threads_leave();
807f9971
JA
1857}
1858
ed727a46
JA
1859static void gfio_client_timed_out(struct fio_client *client)
1860{
e0681f3e 1861 struct gfio_client *gc = client->client_data;
ed727a46
JA
1862 char buf[256];
1863
1864 gdk_threads_enter();
1865
85dd01e7 1866 gfio_set_state(gc->ge, GE_STATE_NEW);
2f99deb0 1867 clear_ge_ui_info(gc->ge);
ed727a46
JA
1868
1869 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
16ce5adf 1870 show_info_dialog(gc->ge->ui, "Network timeout", buf);
ed727a46
JA
1871
1872 gdk_threads_leave();
1873}
1874
6b79c80c
JA
1875static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1876{
1877 struct gfio_client *gc = client->client_data;
1878
1879 gdk_threads_enter();
1880
85dd01e7 1881 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
6b79c80c
JA
1882
1883 if (gc->err_entry)
1884 entry_set_int_value(gc->err_entry, client->error);
1885
1886 gdk_threads_leave();
1887}
1888
85dd01e7
JA
1889static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1890{
1891 struct gfio_client *gc = client->client_data;
1892
1893 gdk_threads_enter();
1894 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1895 gdk_threads_leave();
1896}
1897
1898static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1899{
1900 struct gfio_client *gc = client->client_data;
1901
1902 gdk_threads_enter();
1903 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1904 gdk_threads_leave();
1905}
1906
1b42725f
JA
1907static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1908{
284b1e65 1909 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1b42725f
JA
1910 free(pdu);
1911}
1912
a1820207 1913struct client_ops gfio_client_ops = {
35c0ba7f 1914 .text = gfio_text_op,
0420ba6a
JA
1915 .disk_util = gfio_disk_util_op,
1916 .thread_status = gfio_thread_status_op,
1917 .group_stats = gfio_group_stats_op,
2f99deb0
JA
1918 .jobs_eta = gfio_update_client_eta,
1919 .eta = gfio_update_all_eta,
0420ba6a 1920 .probe = gfio_probe_op,
3ec62ec4 1921 .quit = gfio_quit_op,
807f9971 1922 .add_job = gfio_add_job_op,
ed727a46 1923 .timed_out = gfio_client_timed_out,
6b79c80c 1924 .stop = gfio_client_stop,
85dd01e7
JA
1925 .start = gfio_client_start,
1926 .job_start = gfio_client_job_start,
1b42725f 1927 .iolog = gfio_client_iolog,
6433ee05 1928 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
3ec62ec4 1929 .stay_connected = 1,
46bcd498 1930 .client_type = FIO_CLIENT_TYPE_GUI,
a1820207
SC
1931};
1932
0fd18982
JA
1933/*
1934 * FIXME: need more handling here
1935 */
1936static void ge_destroy(struct gui_entry *ge)
1937{
1938 struct gfio_client *gc = ge->client;
1939
1940 if (gc && gc->client) {
1941 if (ge->state >= GE_STATE_CONNECTED)
1942 fio_client_terminate(gc->client);
1943
1944 fio_put_client(gc->client);
1945 }
1946
1947 flist_del(&ge->list);
1948 free(ge);
1949}
1950
1951static void ge_widget_destroy(GtkWidget *w, gpointer data)
1952{
0fd18982
JA
1953}
1954
1955static void gfio_quit(struct gui *ui)
1956{
1957 struct gui_entry *ge;
1958
1959 while (!flist_empty(&ui->list)) {
1960 ge = flist_entry(ui->list.next, struct gui_entry, list);
1961 ge_destroy(ge);
1962 }
1963
1964 gtk_main_quit();
1965}
1966
ff1f3280
SC
1967static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1968 __attribute__((unused)) gpointer data)
1969{
0fd18982 1970 gfio_quit(data);
ff1f3280
SC
1971}
1972
25927259
SC
1973static void *job_thread(void *arg)
1974{
a9eccde4
JA
1975 struct gui *ui = arg;
1976
1977 ui->handler_running = 1;
25927259 1978 fio_handle_clients(&gfio_client_ops);
a9eccde4 1979 ui->handler_running = 0;
25927259
SC
1980 return NULL;
1981}
1982
2f99deb0 1983static int send_job_files(struct gui_entry *ge)
60f6b330 1984{
9988ca70 1985 struct gfio_client *gc = ge->client;
441013b4 1986 int i, ret = 0;
0420ba6a 1987
2f99deb0 1988 for (i = 0; i < ge->nr_job_files; i++) {
9988ca70 1989 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
c724926b
JA
1990 if (ret < 0) {
1991 GError *error;
1992
1993 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1994 report_error(error);
1995 g_error_free(error);
1996 break;
1997 } else if (ret)
441013b4
JA
1998 break;
1999
2f99deb0
JA
2000 free(ge->job_files[i]);
2001 ge->job_files[i] = NULL;
441013b4 2002 }
2f99deb0
JA
2003 while (i < ge->nr_job_files) {
2004 free(ge->job_files[i]);
2005 ge->job_files[i] = NULL;
441013b4 2006 i++;
0420ba6a
JA
2007 }
2008
2c77d831
JA
2009 free(ge->job_files);
2010 ge->job_files = NULL;
3af45201 2011 ge->nr_job_files = 0;
441013b4 2012 return ret;
60f6b330
SC
2013}
2014
63a130b7
JA
2015static void *server_thread(void *arg)
2016{
2017 is_backend = 1;
2018 gfio_server_running = 1;
2019 fio_start_server(NULL);
2020 gfio_server_running = 0;
2021 return NULL;
2022}
2023
2f99deb0 2024static void gfio_start_server(void)
63a130b7 2025{
2f99deb0
JA
2026 struct gui *ui = &main_ui;
2027
63a130b7
JA
2028 if (!gfio_server_running) {
2029 gfio_server_running = 1;
2030 pthread_create(&ui->server_t, NULL, server_thread, NULL);
e34f6ad7 2031 pthread_detach(ui->server_t);
63a130b7
JA
2032 }
2033}
2034
f3074008 2035static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 2036 gpointer data)
f3074008 2037{
2f99deb0
JA
2038 struct gui_entry *ge = data;
2039 struct gfio_client *gc = ge->client;
25927259 2040
78cb2fe5
JA
2041 if (gc)
2042 fio_start_client(gc->client);
f3074008
SC
2043}
2044
df06f220
JA
2045static void file_open(GtkWidget *w, gpointer data);
2046
2047static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 2048{
2f99deb0
JA
2049 struct gui_entry *ge = data;
2050 struct gfio_client *gc = ge->client;
3ec62ec4 2051
85dd01e7 2052 if (ge->state == GE_STATE_NEW) {
c724926b
JA
2053 int ret;
2054
2f99deb0 2055 if (!ge->nr_job_files)
cf4b0443 2056 file_open(widget, ge->ui);
2f99deb0
JA
2057 if (!ge->nr_job_files)
2058 return;
2059
2060 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2061 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
c724926b
JA
2062 ret = fio_client_connect(gc->client);
2063 if (!ret) {
a9eccde4
JA
2064 if (!ge->ui->handler_running)
2065 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
85dd01e7 2066 gfio_set_state(ge, GE_STATE_CONNECTED);
c724926b
JA
2067 } else {
2068 GError *error;
2069
2070 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2071 report_error(error);
2072 g_error_free(error);
69406b92 2073 }
df06f220 2074 } else {
2f99deb0 2075 fio_client_terminate(gc->client);
85dd01e7 2076 gfio_set_state(ge, GE_STATE_NEW);
2f99deb0 2077 clear_ge_ui_info(ge);
df06f220 2078 }
3e47bd25
JA
2079}
2080
b9d2f30a
JA
2081static void send_clicked(GtkWidget *widget, gpointer data)
2082{
2f99deb0 2083 struct gui_entry *ge = data;
b9d2f30a 2084
2f99deb0 2085 if (send_job_files(ge)) {
c724926b
JA
2086 GError *error;
2087
2088 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
2089 report_error(error);
2090 g_error_free(error);
2091
2f99deb0 2092 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a 2093 }
b9d2f30a
JA
2094}
2095
2f99deb0
JA
2096static GtkWidget *add_button(GtkWidget *buttonbox,
2097 struct button_spec *buttonspec, gpointer data)
f3074008 2098{
2f99deb0
JA
2099 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2100
2101 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2102 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2103 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2104 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2105
2106 return button;
f3074008
SC
2107}
2108
2f99deb0
JA
2109static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2110 int nbuttons)
f3074008
SC
2111{
2112 int i;
2113
f3074008 2114 for (i = 0; i < nbuttons; i++)
2f99deb0 2115 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
f3074008
SC
2116}
2117
0420ba6a
JA
2118static void on_info_bar_response(GtkWidget *widget, gint response,
2119 gpointer data)
2120{
2f99deb0
JA
2121 struct gui *ui = &main_ui;
2122
0420ba6a
JA
2123 if (response == GTK_RESPONSE_OK) {
2124 gtk_widget_destroy(widget);
2f99deb0 2125 ui->error_info_bar = NULL;
0420ba6a
JA
2126 }
2127}
2128
df06f220 2129void report_error(GError *error)
0420ba6a 2130{
2f99deb0
JA
2131 struct gui *ui = &main_ui;
2132
2133 if (ui->error_info_bar == NULL) {
2134 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
0420ba6a
JA
2135 GTK_RESPONSE_OK,
2136 NULL);
2f99deb0
JA
2137 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2138 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
0420ba6a
JA
2139 GTK_MESSAGE_ERROR);
2140
2f99deb0
JA
2141 ui->error_label = gtk_label_new(error->message);
2142 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2143 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
0420ba6a 2144
2f99deb0
JA
2145 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2146 gtk_widget_show_all(ui->vbox);
0420ba6a
JA
2147 } else {
2148 char buffer[256];
2149 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2f99deb0 2150 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
0420ba6a
JA
2151 }
2152}
2153
62bc937f
JA
2154struct connection_widgets
2155{
2156 GtkWidget *hentry;
2157 GtkWidget *combo;
2158 GtkWidget *button;
2159};
2160
2161static void hostname_cb(GtkEntry *entry, gpointer data)
2162{
2163 struct connection_widgets *cw = data;
2164 int uses_net = 0, is_localhost = 0;
2165 const gchar *text;
2166 gchar *ctext;
2167
2168 /*
2169 * Check whether to display the 'auto start backend' box
2170 * or not. Show it if we are a localhost and using network,
2171 * or using a socket.
2172 */
2173 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2174 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2175 uses_net = 1;
2176 g_free(ctext);
2177
2178 if (uses_net) {
2179 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2180 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2181 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2182 !strcmp(text, "ip6-loopback"))
2183 is_localhost = 1;
2184 }
2185
2186 if (!uses_net || is_localhost) {
2187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2188 gtk_widget_set_sensitive(cw->button, 1);
2189 } else {
2190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2191 gtk_widget_set_sensitive(cw->button, 0);
2192 }
2193}
2194
b9f3c7ed
JA
2195static int get_connection_details(char **host, int *port, int *type,
2196 int *server_start)
a7a42ce1 2197{
62bc937f
JA
2198 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2199 struct connection_widgets cw;
a7a42ce1
JA
2200 char *typeentry;
2201
2202 dialog = gtk_dialog_new_with_buttons("Connection details",
2f99deb0 2203 GTK_WINDOW(main_ui.window),
a7a42ce1
JA
2204 GTK_DIALOG_DESTROY_WITH_PARENT,
2205 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2206 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2207
2208 frame = gtk_frame_new("Hostname / socket name");
f129909e
JA
2209 /* gtk_dialog_get_content_area() is 2.14 and newer */
2210 vbox = GTK_DIALOG(dialog)->vbox;
a7a42ce1
JA
2211 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2212
2213 box = gtk_vbox_new(FALSE, 6);
2214 gtk_container_add(GTK_CONTAINER(frame), box);
2215
2216 hbox = gtk_hbox_new(TRUE, 10);
2217 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
62bc937f
JA
2218 cw.hentry = gtk_entry_new();
2219 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2220 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
a7a42ce1
JA
2221
2222 frame = gtk_frame_new("Port");
2223 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2224 box = gtk_vbox_new(FALSE, 10);
2225 gtk_container_add(GTK_CONTAINER(frame), box);
2226
2227 hbox = gtk_hbox_new(TRUE, 4);
2228 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2229 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2230
2231 frame = gtk_frame_new("Type");
2232 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2233 box = gtk_vbox_new(FALSE, 10);
2234 gtk_container_add(GTK_CONTAINER(frame), box);
2235
2236 hbox = gtk_hbox_new(TRUE, 4);
2237 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2238
62bc937f
JA
2239 cw.combo = gtk_combo_box_new_text();
2240 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2241 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2242 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2243 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
a7a42ce1 2244
62bc937f 2245 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
a7a42ce1 2246
b9f3c7ed
JA
2247 frame = gtk_frame_new("Options");
2248 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2249 box = gtk_vbox_new(FALSE, 10);
2250 gtk_container_add(GTK_CONTAINER(frame), box);
2251
2252 hbox = gtk_hbox_new(TRUE, 4);
2253 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2254
62bc937f
JA
2255 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2256 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2257 gtk_widget_set_tooltip_text(cw.button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
2258 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2259
2260 /*
2261 * Connect edit signal, so we can show/not-show the auto start button
2262 */
2263 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2264 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
b9f3c7ed 2265
a7a42ce1
JA
2266 gtk_widget_show_all(dialog);
2267
2268 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2269 gtk_widget_destroy(dialog);
2270 return 1;
2271 }
2272
62bc937f 2273 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
a7a42ce1
JA
2274 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2275
62bc937f 2276 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
a7a42ce1
JA
2277 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2278 *type = Fio_client_ipv4;
2279 else if (!strncmp(typeentry, "IPv6", 4))
2280 *type = Fio_client_ipv6;
2281 else
2282 *type = Fio_client_socket;
2283 g_free(typeentry);
2284
62bc937f 2285 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
b9f3c7ed 2286
a7a42ce1
JA
2287 gtk_widget_destroy(dialog);
2288 return 0;
2289}
2290
2f99deb0 2291static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
e0681f3e
JA
2292{
2293 struct gfio_client *gc;
2294
2295 gc = malloc(sizeof(*gc));
2296 memset(gc, 0, sizeof(*gc));
2f99deb0 2297 gc->ge = ge;
343cb4a9 2298 gc->client = fio_get_client(client);
b9d2f30a 2299
2f99deb0 2300 ge->client = gc;
e0681f3e
JA
2301
2302 client->client_data = gc;
2303}
2304
2f99deb0
JA
2305static GtkWidget *new_client_page(struct gui_entry *ge);
2306
2307static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2308{
2309 struct gui_entry *ge;
2310
2311 ge = malloc(sizeof(*ge));
2312 memset(ge, 0, sizeof(*ge));
85dd01e7 2313 ge->state = GE_STATE_NEW;
2f99deb0
JA
2314 INIT_FLIST_HEAD(&ge->list);
2315 flist_add_tail(&ge->list, &ui->list);
2316 ge->ui = ui;
2317 return ge;
2318}
2319
2f99deb0
JA
2320static struct gui_entry *get_new_ge_with_tab(const char *name)
2321{
2322 struct gui_entry *ge;
2323
2324 ge = alloc_new_gui_entry(&main_ui);
2325
2326 ge->vbox = new_client_page(ge);
0fd18982 2327 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2f99deb0
JA
2328
2329 ge->page_label = gtk_label_new(name);
2330 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2331
2332 gtk_widget_show_all(main_ui.window);
2333 return ge;
2334}
2335
2336static void file_new(GtkWidget *w, gpointer data)
2337{
16ce5adf
JA
2338 struct gui *ui = (struct gui *) data;
2339 struct gui_entry *ge;
2340
2341 ge = get_new_ge_with_tab("Untitled");
2342 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2f99deb0
JA
2343}
2344
2345/*
2346 * Return the 'ge' corresponding to the tab. If the active tab is the
2347 * main tab, open a new tab.
2348 */
38634cb1 2349static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2f99deb0
JA
2350{
2351 struct flist_head *entry;
2352 struct gui_entry *ge;
2353
38634cb1
JA
2354 if (!cur_page) {
2355 if (created)
2356 *created = 1;
2f99deb0 2357 return get_new_ge_with_tab("Untitled");
38634cb1
JA
2358 }
2359
2360 if (created)
2361 *created = 0;
2f99deb0
JA
2362
2363 flist_for_each(entry, &main_ui.list) {
2364 ge = flist_entry(entry, struct gui_entry, list);
2365 if (ge->page_num == cur_page)
2366 return ge;
2367 }
2368
2369 return NULL;
2370}
2371
85dd01e7 2372static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
16ce5adf 2373{
16ce5adf
JA
2374 gint cur_page;
2375
2376 /*
85dd01e7
JA
2377 * Main tab is tab 0, so any current page other than 0 holds
2378 * a ge entry.
16ce5adf
JA
2379 */
2380 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
85dd01e7 2381 if (cur_page)
38634cb1 2382 return get_ge_from_page(cur_page, NULL);
85dd01e7
JA
2383
2384 return NULL;
2385}
2386
2387static void file_close(GtkWidget *w, gpointer data)
2388{
2389 struct gui *ui = (struct gui *) data;
2390 struct gui_entry *ge;
16ce5adf 2391
85dd01e7
JA
2392 /*
2393 * Can't close the main tab
2394 */
2395 ge = get_ge_from_cur_tab(ui);
2396 if (ge) {
16ce5adf
JA
2397 gtk_widget_destroy(ge->vbox);
2398 return;
2399 }
2400
f5c6726e
JA
2401 if (!flist_empty(&ui->list)) {
2402 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2403 return;
2404 }
2405
0fd18982 2406 gfio_quit(ui);
16ce5adf
JA
2407}
2408
38634cb1
JA
2409static void file_add_recent(struct gui *ui, const gchar *uri)
2410{
a217ba7d
JA
2411 GtkRecentData grd;
2412
2413 memset(&grd, 0, sizeof(grd));
2414 grd.display_name = strdup("gfio");
2415 grd.description = strdup("Fio job file");
2416 grd.mime_type = strdup(GFIO_MIME);
2417 grd.app_name = strdup(g_get_application_name());
2418 grd.app_exec = strdup("gfio %f/%u");
2419
2420 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
38634cb1
JA
2421}
2422
2423static gchar *get_filename_from_uri(const gchar *uri)
2424{
2425 if (strncmp(uri, "file://", 7))
2426 return strdup(uri);
2427
2428 return strdup(uri + 7);
2429}
2430
2431static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2432 int type, int port)
2433{
2434 struct fio_client *client;
2435 gchar *filename;
2436
2437 filename = get_filename_from_uri(uri);
2438
2439 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2440 ge->job_files[ge->nr_job_files] = strdup(filename);
2441 ge->nr_job_files++;
2442
2443 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2444 if (!client) {
2445 GError *error;
2446
2447 error = g_error_new(g_quark_from_string("fio"), 1,
2448 "Failed to add client %s", host);
2449 report_error(error);
2450 g_error_free(error);
2451 return 1;
2452 }
2453
2454 gfio_client_added(ge, client);
2455 file_add_recent(ge->ui, uri);
2456 return 0;
2457}
2458
a6790906 2459static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
0420ba6a 2460{
a6790906 2461 int port, type, server_start;
2f99deb0
JA
2462 struct gui_entry *ge;
2463 gint cur_page;
38634cb1 2464 char *host;
a6790906 2465 int ret, ge_is_new = 0;
2f99deb0
JA
2466
2467 /*
2468 * Creates new tab if current tab is the main window, or the
2469 * current tab already has a client.
2470 */
2471 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
38634cb1
JA
2472 ge = get_ge_from_page(cur_page, &ge_is_new);
2473 if (ge->client) {
2f99deb0 2474 ge = get_new_ge_with_tab("Untitled");
38634cb1
JA
2475 ge_is_new = 1;
2476 }
2f99deb0
JA
2477
2478 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
0420ba6a 2479
a6790906
JA
2480 if (get_connection_details(&host, &port, &type, &server_start)) {
2481 if (ge_is_new)
2482 gtk_widget_destroy(ge->vbox);
2483
2484 return 1;
2485 }
2486
2487 ret = do_file_open(ge, uri, host, type, port);
2488
2489 free(host);
2490
2491 if (!ret) {
2492 if (server_start)
2493 gfio_start_server();
2494 } else {
2495 if (ge_is_new)
2496 gtk_widget_destroy(ge->vbox);
2497 }
2498
2499 return ret;
2500}
2501
2502static void recent_open(GtkAction *action, gpointer data)
2503{
2504 struct gui *ui = (struct gui *) data;
2505 GtkRecentInfo *info;
2506 const gchar *uri;
2507
2508 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2509 uri = gtk_recent_info_get_uri(info);
2510
2511 do_file_open_with_tab(ui, uri);
2512}
2513
2514static void file_open(GtkWidget *w, gpointer data)
2515{
2516 struct gui *ui = data;
2517 GtkWidget *dialog;
2518 GSList *filenames, *fn_glist;
2519 GtkFileFilter *filter;
2520
0420ba6a 2521 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 2522 GTK_WINDOW(ui->window),
0420ba6a
JA
2523 GTK_FILE_CHOOSER_ACTION_OPEN,
2524 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2525 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2526 NULL);
2527 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2528
2529 filter = gtk_file_filter_new();
2530 gtk_file_filter_add_pattern(filter, "*.fio");
2531 gtk_file_filter_add_pattern(filter, "*.job");
2d262990 2532 gtk_file_filter_add_pattern(filter, "*.ini");
38634cb1 2533 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
0420ba6a
JA
2534 gtk_file_filter_set_name(filter, "Fio job file");
2535 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2536
2537 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2538 gtk_widget_destroy(dialog);
2539 return;
2540 }
2541
2542 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
2543
2544 gtk_widget_destroy(dialog);
2545
0420ba6a
JA
2546 filenames = fn_glist;
2547 while (filenames != NULL) {
a6790906
JA
2548 if (do_file_open_with_tab(ui, filenames->data))
2549 break;
0420ba6a
JA
2550 filenames = g_slist_next(filenames);
2551 }
63a130b7 2552
0420ba6a 2553 g_slist_free(fn_glist);
0420ba6a
JA
2554}
2555
2556static void file_save(GtkWidget *w, gpointer data)
2557{
63a130b7 2558 struct gui *ui = data;
0420ba6a
JA
2559 GtkWidget *dialog;
2560
2561 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 2562 GTK_WINDOW(ui->window),
0420ba6a
JA
2563 GTK_FILE_CHOOSER_ACTION_SAVE,
2564 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2565 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2566 NULL);
2567
2568 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2569 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2570
2571 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2572 char *filename;
2573
2574 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2575 // save_job_file(filename);
2576 g_free(filename);
2577 }
2578 gtk_widget_destroy(dialog);
2579}
2580
9b260bdf
JA
2581static void view_log_destroy(GtkWidget *w, gpointer data)
2582{
2583 struct gui *ui = (struct gui *) data;
2584
2585 gtk_widget_ref(ui->log_tree);
2586 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2587 gtk_widget_destroy(w);
4cbe7211 2588 ui->log_view = NULL;
9b260bdf
JA
2589}
2590
2591static void view_log(GtkWidget *w, gpointer data)
2592{
4cbe7211
JA
2593 GtkWidget *win, *scroll, *vbox, *box;
2594 struct gui *ui = (struct gui *) data;
9b260bdf 2595
4cbe7211
JA
2596 if (ui->log_view)
2597 return;
2598
2599 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 2600 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 2601 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 2602
4cbe7211
JA
2603 scroll = gtk_scrolled_window_new(NULL, NULL);
2604
2605 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2606
2607 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2608
2609 box = gtk_hbox_new(TRUE, 0);
2610 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2611 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2612 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2613
2614 vbox = gtk_vbox_new(TRUE, 5);
2615 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 2616
4cbe7211 2617 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
2618 gtk_widget_show_all(win);
2619}
2620
85dd01e7
JA
2621static void connect_job_entry(GtkWidget *w, gpointer data)
2622{
2623 struct gui *ui = (struct gui *) data;
2624 struct gui_entry *ge;
2625
2626 ge = get_ge_from_cur_tab(ui);
2627 if (ge)
2628 connect_clicked(w, ge);
2629}
2630
2631static void send_job_entry(GtkWidget *w, gpointer data)
2632{
2633 struct gui *ui = (struct gui *) data;
2634 struct gui_entry *ge;
2635
2636 ge = get_ge_from_cur_tab(ui);
2637 if (ge)
2638 send_clicked(w, ge);
2639
2640}
2641
2642static void edit_job_entry(GtkWidget *w, gpointer data)
16ce5adf
JA
2643{
2644}
2645
85dd01e7
JA
2646static void start_job_entry(GtkWidget *w, gpointer data)
2647{
2648 struct gui *ui = (struct gui *) data;
2649 struct gui_entry *ge;
2650
2651 ge = get_ge_from_cur_tab(ui);
2652 if (ge)
2653 start_job_clicked(w, ge);
2654}
2655
781ccba6
JA
2656static void view_results(GtkWidget *w, gpointer data)
2657{
2658 struct gui *ui = (struct gui *) data;
2659 struct gfio_client *gc;
2660 struct gui_entry *ge;
2661
2662 ge = get_ge_from_cur_tab(ui);
2663 if (!ge)
2664 return;
2665
2666 if (ge->results_window)
2667 return;
2668
2669 gc = ge->client;
2670 if (gc && gc->nr_results)
2671 gfio_display_end_results(gc);
2672}
2673
8577f4fd
JA
2674static void __update_graph_limits(struct gfio_graphs *g)
2675{
2676 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2677 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2678}
2679
2680static void update_graph_limits(void)
2681{
2682 struct flist_head *entry;
2683 struct gui_entry *ge;
2684
2685 __update_graph_limits(&main_ui.graphs);
2686
2687 flist_for_each(entry, &main_ui.list) {
2688 ge = flist_entry(entry, struct gui_entry, list);
2689 __update_graph_limits(&ge->graphs);
2690 }
2691}
2692
46974a7d
JA
2693static void preferences(GtkWidget *w, gpointer data)
2694{
f3e8440f 2695 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 2696 GtkWidget *hbox, *spin, *entry, *spin_int;
46974a7d
JA
2697 int i;
2698
2699 dialog = gtk_dialog_new_with_buttons("Preferences",
2f99deb0 2700 GTK_WINDOW(main_ui.window),
46974a7d
JA
2701 GTK_DIALOG_DESTROY_WITH_PARENT,
2702 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2703 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2704 NULL);
2705
8577f4fd 2706 frame = gtk_frame_new("Graphing");
f3e8440f
JA
2707 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2708 vbox = gtk_vbox_new(FALSE, 6);
2709 gtk_container_add(GTK_CONTAINER(frame), vbox);
2710
1cf6bca5
JA
2711 hbox = gtk_hbox_new(FALSE, 5);
2712 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2713 entry = gtk_label_new("Font face to use for graph labels");
2714 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2715
f3e8440f 2716 font = gtk_font_button_new();
1cf6bca5 2717 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 2718
8577f4fd
JA
2719 box = gtk_vbox_new(FALSE, 6);
2720 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2721
2722 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 2723 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
2724 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2725 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2726
c05d9056 2727 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
8577f4fd 2728
1cf6bca5
JA
2729 box = gtk_vbox_new(FALSE, 6);
2730 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2731
2732 hbox = gtk_hbox_new(FALSE, 5);
2733 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2734 entry = gtk_label_new("Client ETA request interval (msec)");
2735 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2736
2737 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4
JA
2738 frame = gtk_frame_new("Debug logging");
2739 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2740 vbox = gtk_vbox_new(FALSE, 6);
2741 gtk_container_add(GTK_CONTAINER(frame), vbox);
2742
2743 box = gtk_hbox_new(FALSE, 6);
2744 gtk_container_add(GTK_CONTAINER(vbox), box);
2745
2746 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2747
2748 for (i = 0; i < FD_DEBUG_MAX; i++) {
2749 if (i == 7) {
2750 box = gtk_hbox_new(FALSE, 6);
2751 gtk_container_add(GTK_CONTAINER(vbox), box);
2752 }
2753
2754
2755 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2756 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2757 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2758 }
2759
46974a7d
JA
2760 gtk_widget_show_all(dialog);
2761
2762 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2763 gtk_widget_destroy(dialog);
2764 return;
2765 }
2766
2767 for (i = 0; i < FD_DEBUG_MAX; i++) {
2768 int set;
2769
2770 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2771 if (set)
2772 fio_debug |= (1UL << i);
2773 }
2774
f3e8440f 2775 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
2776 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2777 update_graph_limits();
1cf6bca5 2778 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 2779
46974a7d
JA
2780 gtk_widget_destroy(dialog);
2781}
2782
0420ba6a
JA
2783static void about_dialog(GtkWidget *w, gpointer data)
2784{
81e4ea6e
JA
2785 const char *authors[] = {
2786 "Jens Axboe <axboe@kernel.dk>",
2787 "Stephen Carmeron <stephenmcameron@gmail.com>",
2788 NULL
2789 };
84a72ed3
JA
2790 const char *license[] = {
2791 "Fio is free software; you can redistribute it and/or modify "
2792 "it under the terms of the GNU General Public License as published by "
2793 "the Free Software Foundation; either version 2 of the License, or "
2794 "(at your option) any later version.\n",
2795 "Fio is distributed in the hope that it will be useful, "
2796 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2797 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2798 "GNU General Public License for more details.\n",
2799 "You should have received a copy of the GNU General Public License "
2800 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2801 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2802 };
2803 char *license_trans;
2804
2805 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2806 license[2], "\n", NULL);
81e4ea6e 2807
0420ba6a
JA
2808 gtk_show_about_dialog(NULL,
2809 "program-name", "gfio",
2810 "comments", "Gtk2 UI for fio",
84a72ed3 2811 "license", license_trans,
81e4ea6e
JA
2812 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2813 "authors", authors,
0420ba6a 2814 "version", fio_version_string,
81e4ea6e 2815 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
2816 "logo-icon-name", "fio",
2817 /* Must be last: */
81e4ea6e 2818 "wrap-license", TRUE,
0420ba6a 2819 NULL);
84a72ed3 2820
2f99deb0 2821 g_free(license_trans);
0420ba6a
JA
2822}
2823
2824static GtkActionEntry menu_items[] = {
46974a7d 2825 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 2826 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
16ce5adf 2827 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
46974a7d 2828 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 2829 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
16ce5adf 2830 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
46974a7d
JA
2831 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2832 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2833 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 2834 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
781ccba6 2835 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
85dd01e7
JA
2836 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2837 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2838 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2839 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
46974a7d
JA
2840 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2841 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 2842};
3e47bd25 2843static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
2844
2845static const gchar *ui_string = " \
2846 <ui> \
2847 <menubar name=\"MainMenu\"> \
2848 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0 2849 <menuitem name=\"New\" action=\"NewFile\" /> \
bf64138b 2850 <menuitem name=\"Open\" action=\"OpenFile\" /> \
16ce5adf 2851 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2f99deb0 2852 <separator name=\"Separator1\"/> \
0420ba6a 2853 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 2854 <separator name=\"Separator2\"/> \
2f99deb0
JA
2855 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2856 <separator name=\"Separator3\"/> \
261f21d0
JA
2857 <placeholder name=\"FileRecentFiles\"/> \
2858 <separator name=\"Separator4\"/> \
0420ba6a
JA
2859 <menuitem name=\"Quit\" action=\"Quit\" /> \
2860 </menu> \
16ce5adf 2861 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
85dd01e7 2862 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
261f21d0 2863 <separator name=\"Separator5\"/> \
85dd01e7
JA
2864 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2865 <menuitem name=\"Send job\" action=\"SendJob\" /> \
261f21d0 2866 <separator name=\"Separator6\"/> \
85dd01e7 2867 <menuitem name=\"Start job\" action=\"StartJob\" /> \
16ce5adf 2868 </menu>\
9b260bdf 2869 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
781ccba6
JA
2870 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2871 <separator name=\"Separator7\"/> \
9b260bdf
JA
2872 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2873 </menu>\
0420ba6a
JA
2874 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2875 <menuitem name=\"About\" action=\"About\" /> \
2876 </menu> \
2877 </menubar> \
2878 </ui> \
2879";
2880
4cbe7211
JA
2881static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2882 struct gui *ui)
0420ba6a 2883{
ca664f49 2884 GtkActionGroup *action_group;
0420ba6a
JA
2885 GError *error = 0;
2886
2887 action_group = gtk_action_group_new("Menu");
4cbe7211 2888 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
2889
2890 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2891 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2892
2893 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
02421e69 2894
0420ba6a
JA
2895 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2896}
2897
2898void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2899 GtkWidget *vbox, GtkUIManager *ui_manager)
2900{
2901 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2902}
2903
c80b74b0
JA
2904static void combo_entry_changed(GtkComboBox *box, gpointer data)
2905{
2906 struct gui_entry *ge = (struct gui_entry *) data;
2907 gint index;
2908
2909 index = gtk_combo_box_get_active(box);
2910
2911 multitext_set_entry(&ge->eta.iotype, index);
2912 multitext_set_entry(&ge->eta.ioengine, index);
2913 multitext_set_entry(&ge->eta.iodepth, index);
2914}
2915
2916static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2917{
2918 struct gui_entry *ge = (struct gui_entry *) data;
2919
2920 multitext_free(&ge->eta.iotype);
2921 multitext_free(&ge->eta.ioengine);
2922 multitext_free(&ge->eta.iodepth);
2923}
2924
2f99deb0 2925static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 2926{
2f99deb0 2927 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 2928 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
0420ba6a 2929
2f99deb0 2930 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 2931
65476336
JA
2932 top_align = gtk_alignment_new(0, 0, 1, 0);
2933 top_vbox = gtk_vbox_new(FALSE, 3);
2934 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2935 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2f99deb0
JA
2936
2937 probe = gtk_frame_new("Job");
2938 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2939 probe_frame = gtk_vbox_new(FALSE, 3);
2940 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2941
2942 probe_box = gtk_hbox_new(FALSE, 3);
2943 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2944 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2945 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2946 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2947 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2948
2949 probe_box = gtk_hbox_new(FALSE, 3);
2950 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2951
3863d1ad 2952 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
c80b74b0
JA
2953 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2954 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2955 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2956 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2957 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2f99deb0
JA
2958 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2959 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2960
2961 probe_box = gtk_hbox_new(FALSE, 3);
2962 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2963 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2964 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2965 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2966 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2967
2968 /*
2969 * Only add this if we have a commit rate
2839f0c6 2970 */
2f99deb0
JA
2971#if 0
2972 probe_box = gtk_hbox_new(FALSE, 3);
2973 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 2974
2f99deb0
JA
2975 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2976 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 2977
2f99deb0
JA
2978 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2979 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2980#endif
ff1f3280 2981
2f99deb0
JA
2982 /*
2983 * Set up a drawing area and IOPS and bandwidth graphs
2984 */
2f99deb0 2985 ge->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 2986 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
57f9d28e 2987 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
2988 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2989 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2990 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2991 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2992 G_CALLBACK(on_config_drawing_area), &ge->graphs);
65476336
JA
2993 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2994 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 2995 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 2996 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 2997 ge->graphs.drawing_area);
65476336 2998 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
04cc6b77 2999
2f99deb0
JA
3000 setup_graphs(&ge->graphs);
3001
3002 /*
3003 * Set up alignments for widgets at the bottom of ui,
3004 * align bottom left, expand horizontally but not vertically
3005 */
65476336 3006 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2f99deb0 3007 ge->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
3008 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3009 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2f99deb0
JA
3010
3011 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
0420ba6a 3012
c36f98d9 3013 /*
2f99deb0
JA
3014 * Set up thread status progress bar
3015 */
3016 ge->thread_status_pb = gtk_progress_bar_new();
3017 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3018 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3019 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3020
3021
3022 return main_vbox;
3023}
3024
3025static GtkWidget *new_main_page(struct gui *ui)
3026{
3027 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 3028 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2f99deb0
JA
3029
3030 main_vbox = gtk_vbox_new(FALSE, 3);
3031
3032 /*
3033 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
3034 * align top left, expand horizontally but not vertically
3035 */
65476336
JA
3036 top_align = gtk_alignment_new(0, 0, 1, 0);
3037 top_vbox = gtk_vbox_new(FALSE, 0);
3038 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3039 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
c36f98d9 3040
2f99deb0
JA
3041 probe = gtk_frame_new("Run statistics");
3042 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
3043 probe_frame = gtk_vbox_new(FALSE, 3);
3044 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3045
3046 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 3047 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 3048 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
ca850992
JA
3049 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3050 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3051 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3052 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 3053
807f9971
JA
3054 /*
3055 * Only add this if we have a commit rate
3056 */
3057#if 0
3e47bd25
JA
3058 probe_box = gtk_hbox_new(FALSE, 3);
3059 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
3060
3061 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3062 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3063
3e47bd25
JA
3064 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3065 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 3066#endif
3e47bd25 3067
736f2dff 3068 /*
2fd3bb0e 3069 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 3070 */
2f99deb0 3071 ui->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 3072 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
57f9d28e 3073 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
3074 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3075 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3076 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3077 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3078 G_CALLBACK(on_config_drawing_area), &ui->graphs);
65476336
JA
3079 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3080 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
736f2dff 3081 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 3082 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3083 ui->graphs.drawing_area);
65476336 3084 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
e164534f 3085 TRUE, TRUE, 0);
736f2dff 3086
2f99deb0 3087 setup_graphs(&ui->graphs);
2fd3bb0e 3088
c36f98d9
SC
3089 /*
3090 * Set up alignments for widgets at the bottom of ui,
3091 * align bottom left, expand horizontally but not vertically
3092 */
65476336 3093 bottom_align = gtk_alignment_new(0, 1, 1, 0);
c36f98d9 3094 ui->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
3095 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3096 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
c36f98d9 3097
3ec62ec4
JA
3098 /*
3099 * Set up thread status progress bar
3100 */
3101 ui->thread_status_pb = gtk_progress_bar_new();
3102 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 3103 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
3104 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3105
2f99deb0
JA
3106 return main_vbox;
3107}
3108
3109static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3110 guint page, gpointer data)
3111
3112{
02421e69 3113 struct gui *ui = (struct gui *) data;
85dd01e7
JA
3114 struct gui_entry *ge;
3115
3116 if (!page) {
3117 set_job_menu_visible(ui, 0);
781ccba6 3118 set_view_results_visible(ui, 0);
85dd01e7
JA
3119 return TRUE;
3120 }
3121
3122 set_job_menu_visible(ui, 1);
38634cb1 3123 ge = get_ge_from_page(page, NULL);
85dd01e7
JA
3124 if (ge)
3125 update_button_states(ui, ge);
02421e69 3126
2f99deb0
JA
3127 return TRUE;
3128}
3129
38634cb1
JA
3130static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3131{
3132 time_t time_a = gtk_recent_info_get_visited(a);
3133 time_t time_b = gtk_recent_info_get_visited(b);
3134
3135 return time_b - time_a;
3136}
3137
3138static void add_recent_file_items(struct gui *ui)
3139{
3140 const gchar *gfio = g_get_application_name();
3141 GList *items, *item;
3142 int i = 0;
3143
3144 if (ui->recent_ui_id) {
3145 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3146 gtk_ui_manager_ensure_update(ui->uimanager);
3147 }
3148 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3149
3150 if (ui->actiongroup) {
3151 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3152 g_object_unref(ui->actiongroup);
3153 }
3154 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3155
3156 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3157
3158 items = gtk_recent_manager_get_items(ui->recentmanager);
3159 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3160
3161 for (item = items; item && item->data; item = g_list_next(item)) {
3162 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3163 gchar *action_name;
3164 const gchar *label;
3165 GtkAction *action;
3166
3167 if (!gtk_recent_info_has_application(info, gfio))
3168 continue;
3169
3170 /*
3171 * We only support local files for now
3172 */
3173 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3174 continue;
3175
3176 action_name = g_strdup_printf("RecentFile%u", i++);
3177 label = gtk_recent_info_get_display_name(info);
3178
3179 action = g_object_new(GTK_TYPE_ACTION,
3180 "name", action_name,
3181 "label", label, NULL);
3182
3183 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3184 gtk_recent_info_ref(info),
3185 (GDestroyNotify) gtk_recent_info_unref);
3186
3187
3188 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3189
3190 gtk_action_group_add_action(ui->actiongroup, action);
3191 g_object_unref(action);
3192
3193 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3194 "/MainMenu/FileMenu/FileRecentFiles",
3195 label, action_name,
3196 GTK_UI_MANAGER_MENUITEM, FALSE);
3197
3198 g_free(action_name);
3199
3200 if (i == 8)
3201 break;
3202 }
3203
3204 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3205 g_list_free(items);
3206}
3207
a6790906
JA
3208static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3209 gint x, gint y, GtkSelectionData *data,
3210 guint info, guint time)
3211{
3212 struct gui *ui = &main_ui;
3213 gchar **uris;
3214 GtkWidget *source;
3215 int i;
3216
3217 source = gtk_drag_get_source_widget(ctx);
3218 if (source && widget == gtk_widget_get_toplevel(source)) {
3219 gtk_drag_finish(ctx, FALSE, FALSE, time);
3220 return;
3221 }
3222
3223 uris = gtk_selection_data_get_uris(data);
3224 if (!uris) {
3225 gtk_drag_finish(ctx, FALSE, FALSE, time);
3226 return;
3227 }
3228
3229 i = 0;
3230 while (uris[i]) {
3231 if (do_file_open_with_tab(ui, uris[i]))
3232 break;
3233 i++;
3234 }
3235
3236 gtk_drag_finish(ctx, TRUE, FALSE, time);
3237 g_strfreev(uris);
3238}
3239
2f99deb0
JA
3240static void init_ui(int *argc, char **argv[], struct gui *ui)
3241{
3242 GtkSettings *settings;
02421e69 3243 GtkWidget *vbox;
2f99deb0
JA
3244
3245 /* Magical g*thread incantation, you just need this thread stuff.
3246 * Without it, the update that happens in gfio_update_thread_status
3247 * doesn't really happen in a timely fashion, you need expose events
3248 */
3249 if (!g_thread_supported())
3250 g_thread_init(NULL);
3251 gdk_threads_init();
3252
3253 gtk_init(argc, argv);
3254 settings = gtk_settings_get_default();
3255 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3256 g_type_init();
814479d5 3257 gdk_color_parse("white", &white);
2f99deb0
JA
3258
3259 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
814479d5 3260 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2f99deb0
JA
3261 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3262
3263 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3264 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3265
3266 ui->vbox = gtk_vbox_new(FALSE, 0);
3267 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3268
02421e69
JA
3269 ui->uimanager = gtk_ui_manager_new();
3270 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3271 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2f99deb0 3272
38634cb1
JA
3273 ui->recentmanager = gtk_recent_manager_get_default();
3274 add_recent_file_items(ui);
3275
2f99deb0
JA
3276 ui->notebook = gtk_notebook_new();
3277 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 3278 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 3279 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
3280 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3281
3282 vbox = new_main_page(ui);
a6790906
JA
3283 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3284 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3285 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
2f99deb0
JA
3286
3287 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3288
9b260bdf 3289 gfio_ui_setup_log(ui);
3ec62ec4 3290
ff1f3280
SC
3291 gtk_widget_show_all(ui->window);
3292}
3293
8232e285 3294int main(int argc, char *argv[], char *envp[])
ff1f3280 3295{
8232e285
SC
3296 if (initialize_fio(envp))
3297 return 1;
0420ba6a
JA
3298 if (fio_init_options())
3299 return 1;
a1820207 3300
2f99deb0
JA
3301 memset(&main_ui, 0, sizeof(main_ui));
3302 INIT_FLIST_HEAD(&main_ui.list);
3303
3304 init_ui(&argc, &argv, &main_ui);
5b7573ab 3305
2839f0c6 3306 gdk_threads_enter();
ff1f3280 3307 gtk_main();
2839f0c6 3308 gdk_threads_leave();
ff1f3280
SC
3309 return 0;
3310}