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