gfio: reload ge->gc after clicking connect force the file open dialog
[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
d3b70f32
JA
2094 gc = ge->client;
2095
2f99deb0
JA
2096 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2097 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
c724926b
JA
2098 ret = fio_client_connect(gc->client);
2099 if (!ret) {
a9eccde4
JA
2100 if (!ge->ui->handler_running)
2101 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
85dd01e7 2102 gfio_set_state(ge, GE_STATE_CONNECTED);
c724926b
JA
2103 } else {
2104 GError *error;
2105
2106 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2107 report_error(error);
2108 g_error_free(error);
69406b92 2109 }
df06f220 2110 } else {
2f99deb0 2111 fio_client_terminate(gc->client);
85dd01e7 2112 gfio_set_state(ge, GE_STATE_NEW);
2f99deb0 2113 clear_ge_ui_info(ge);
df06f220 2114 }
3e47bd25
JA
2115}
2116
b9d2f30a
JA
2117static void send_clicked(GtkWidget *widget, gpointer data)
2118{
2f99deb0 2119 struct gui_entry *ge = data;
b9d2f30a 2120
2f99deb0 2121 if (send_job_files(ge)) {
c724926b
JA
2122 GError *error;
2123
2124 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);
2125 report_error(error);
2126 g_error_free(error);
2127
2f99deb0 2128 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a 2129 }
b9d2f30a
JA
2130}
2131
0420ba6a
JA
2132static void on_info_bar_response(GtkWidget *widget, gint response,
2133 gpointer data)
2134{
2f99deb0
JA
2135 struct gui *ui = &main_ui;
2136
0420ba6a
JA
2137 if (response == GTK_RESPONSE_OK) {
2138 gtk_widget_destroy(widget);
2f99deb0 2139 ui->error_info_bar = NULL;
0420ba6a
JA
2140 }
2141}
2142
df06f220 2143void report_error(GError *error)
0420ba6a 2144{
2f99deb0
JA
2145 struct gui *ui = &main_ui;
2146
2147 if (ui->error_info_bar == NULL) {
2148 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
0420ba6a
JA
2149 GTK_RESPONSE_OK,
2150 NULL);
2f99deb0
JA
2151 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2152 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
0420ba6a
JA
2153 GTK_MESSAGE_ERROR);
2154
2f99deb0
JA
2155 ui->error_label = gtk_label_new(error->message);
2156 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2157 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
0420ba6a 2158
2f99deb0
JA
2159 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2160 gtk_widget_show_all(ui->vbox);
0420ba6a
JA
2161 } else {
2162 char buffer[256];
2163 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2f99deb0 2164 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
0420ba6a
JA
2165 }
2166}
2167
62bc937f
JA
2168struct connection_widgets
2169{
2170 GtkWidget *hentry;
2171 GtkWidget *combo;
2172 GtkWidget *button;
2173};
2174
2175static void hostname_cb(GtkEntry *entry, gpointer data)
2176{
2177 struct connection_widgets *cw = data;
2178 int uses_net = 0, is_localhost = 0;
2179 const gchar *text;
2180 gchar *ctext;
2181
2182 /*
2183 * Check whether to display the 'auto start backend' box
2184 * or not. Show it if we are a localhost and using network,
2185 * or using a socket.
2186 */
2187 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2188 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2189 uses_net = 1;
2190 g_free(ctext);
2191
2192 if (uses_net) {
2193 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2194 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2195 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2196 !strcmp(text, "ip6-loopback"))
2197 is_localhost = 1;
2198 }
2199
2200 if (!uses_net || is_localhost) {
2201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2202 gtk_widget_set_sensitive(cw->button, 1);
2203 } else {
2204 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2205 gtk_widget_set_sensitive(cw->button, 0);
2206 }
2207}
2208
b9f3c7ed
JA
2209static int get_connection_details(char **host, int *port, int *type,
2210 int *server_start)
a7a42ce1 2211{
62bc937f
JA
2212 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2213 struct connection_widgets cw;
a7a42ce1
JA
2214 char *typeentry;
2215
2216 dialog = gtk_dialog_new_with_buttons("Connection details",
2f99deb0 2217 GTK_WINDOW(main_ui.window),
a7a42ce1
JA
2218 GTK_DIALOG_DESTROY_WITH_PARENT,
2219 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2220 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2221
2222 frame = gtk_frame_new("Hostname / socket name");
f129909e
JA
2223 /* gtk_dialog_get_content_area() is 2.14 and newer */
2224 vbox = GTK_DIALOG(dialog)->vbox;
a7a42ce1
JA
2225 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2226
2227 box = gtk_vbox_new(FALSE, 6);
2228 gtk_container_add(GTK_CONTAINER(frame), box);
2229
2230 hbox = gtk_hbox_new(TRUE, 10);
2231 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
62bc937f
JA
2232 cw.hentry = gtk_entry_new();
2233 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2234 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
a7a42ce1
JA
2235
2236 frame = gtk_frame_new("Port");
2237 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2238 box = gtk_vbox_new(FALSE, 10);
2239 gtk_container_add(GTK_CONTAINER(frame), box);
2240
2241 hbox = gtk_hbox_new(TRUE, 4);
2242 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2243 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2244
2245 frame = gtk_frame_new("Type");
2246 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2247 box = gtk_vbox_new(FALSE, 10);
2248 gtk_container_add(GTK_CONTAINER(frame), box);
2249
2250 hbox = gtk_hbox_new(TRUE, 4);
2251 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2252
62bc937f
JA
2253 cw.combo = gtk_combo_box_new_text();
2254 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2255 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2256 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2257 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
a7a42ce1 2258
62bc937f 2259 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
a7a42ce1 2260
b9f3c7ed
JA
2261 frame = gtk_frame_new("Options");
2262 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2263 box = gtk_vbox_new(FALSE, 10);
2264 gtk_container_add(GTK_CONTAINER(frame), box);
2265
2266 hbox = gtk_hbox_new(TRUE, 4);
2267 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2268
62bc937f
JA
2269 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2270 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2271 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.");
2272 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2273
2274 /*
2275 * Connect edit signal, so we can show/not-show the auto start button
2276 */
2277 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2278 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
b9f3c7ed 2279
a7a42ce1
JA
2280 gtk_widget_show_all(dialog);
2281
2282 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2283 gtk_widget_destroy(dialog);
2284 return 1;
2285 }
2286
62bc937f 2287 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
a7a42ce1
JA
2288 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2289
62bc937f 2290 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
a7a42ce1
JA
2291 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2292 *type = Fio_client_ipv4;
2293 else if (!strncmp(typeentry, "IPv6", 4))
2294 *type = Fio_client_ipv6;
2295 else
2296 *type = Fio_client_socket;
2297 g_free(typeentry);
2298
62bc937f 2299 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
b9f3c7ed 2300
a7a42ce1
JA
2301 gtk_widget_destroy(dialog);
2302 return 0;
2303}
2304
2f99deb0 2305static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
e0681f3e
JA
2306{
2307 struct gfio_client *gc;
2308
2309 gc = malloc(sizeof(*gc));
2310 memset(gc, 0, sizeof(*gc));
2f99deb0 2311 gc->ge = ge;
343cb4a9 2312 gc->client = fio_get_client(client);
b9d2f30a 2313
2f99deb0 2314 ge->client = gc;
e0681f3e
JA
2315
2316 client->client_data = gc;
2317}
2318
2f99deb0
JA
2319static GtkWidget *new_client_page(struct gui_entry *ge);
2320
2321static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2322{
2323 struct gui_entry *ge;
2324
2325 ge = malloc(sizeof(*ge));
2326 memset(ge, 0, sizeof(*ge));
85dd01e7 2327 ge->state = GE_STATE_NEW;
2f99deb0
JA
2328 INIT_FLIST_HEAD(&ge->list);
2329 flist_add_tail(&ge->list, &ui->list);
2330 ge->ui = ui;
2331 return ge;
2332}
2333
2f99deb0
JA
2334static struct gui_entry *get_new_ge_with_tab(const char *name)
2335{
2336 struct gui_entry *ge;
2337
2338 ge = alloc_new_gui_entry(&main_ui);
2339
2340 ge->vbox = new_client_page(ge);
0fd18982 2341 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2f99deb0
JA
2342
2343 ge->page_label = gtk_label_new(name);
2344 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2345
2346 gtk_widget_show_all(main_ui.window);
2347 return ge;
2348}
2349
2350static void file_new(GtkWidget *w, gpointer data)
2351{
16ce5adf
JA
2352 struct gui *ui = (struct gui *) data;
2353 struct gui_entry *ge;
2354
2355 ge = get_new_ge_with_tab("Untitled");
2356 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2f99deb0
JA
2357}
2358
2359/*
2360 * Return the 'ge' corresponding to the tab. If the active tab is the
2361 * main tab, open a new tab.
2362 */
38634cb1 2363static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2f99deb0
JA
2364{
2365 struct flist_head *entry;
2366 struct gui_entry *ge;
2367
38634cb1
JA
2368 if (!cur_page) {
2369 if (created)
2370 *created = 1;
2f99deb0 2371 return get_new_ge_with_tab("Untitled");
38634cb1
JA
2372 }
2373
2374 if (created)
2375 *created = 0;
2f99deb0
JA
2376
2377 flist_for_each(entry, &main_ui.list) {
2378 ge = flist_entry(entry, struct gui_entry, list);
2379 if (ge->page_num == cur_page)
2380 return ge;
2381 }
2382
2383 return NULL;
2384}
2385
85dd01e7 2386static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
16ce5adf 2387{
16ce5adf
JA
2388 gint cur_page;
2389
2390 /*
85dd01e7
JA
2391 * Main tab is tab 0, so any current page other than 0 holds
2392 * a ge entry.
16ce5adf
JA
2393 */
2394 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
85dd01e7 2395 if (cur_page)
38634cb1 2396 return get_ge_from_page(cur_page, NULL);
85dd01e7
JA
2397
2398 return NULL;
2399}
2400
2401static void file_close(GtkWidget *w, gpointer data)
2402{
2403 struct gui *ui = (struct gui *) data;
2404 struct gui_entry *ge;
16ce5adf 2405
85dd01e7
JA
2406 /*
2407 * Can't close the main tab
2408 */
2409 ge = get_ge_from_cur_tab(ui);
2410 if (ge) {
16ce5adf
JA
2411 gtk_widget_destroy(ge->vbox);
2412 return;
2413 }
2414
f5c6726e
JA
2415 if (!flist_empty(&ui->list)) {
2416 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2417 return;
2418 }
2419
0fd18982 2420 gfio_quit(ui);
16ce5adf
JA
2421}
2422
38634cb1
JA
2423static void file_add_recent(struct gui *ui, const gchar *uri)
2424{
a217ba7d
JA
2425 GtkRecentData grd;
2426
2427 memset(&grd, 0, sizeof(grd));
2428 grd.display_name = strdup("gfio");
2429 grd.description = strdup("Fio job file");
2430 grd.mime_type = strdup(GFIO_MIME);
2431 grd.app_name = strdup(g_get_application_name());
2432 grd.app_exec = strdup("gfio %f/%u");
2433
2434 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
38634cb1
JA
2435}
2436
2437static gchar *get_filename_from_uri(const gchar *uri)
2438{
2439 if (strncmp(uri, "file://", 7))
2440 return strdup(uri);
2441
2442 return strdup(uri + 7);
2443}
2444
2445static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2446 int type, int port)
2447{
2448 struct fio_client *client;
2449 gchar *filename;
2450
2451 filename = get_filename_from_uri(uri);
2452
2453 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2454 ge->job_files[ge->nr_job_files] = strdup(filename);
2455 ge->nr_job_files++;
2456
2457 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2458 if (!client) {
2459 GError *error;
2460
2461 error = g_error_new(g_quark_from_string("fio"), 1,
2462 "Failed to add client %s", host);
2463 report_error(error);
2464 g_error_free(error);
2465 return 1;
2466 }
2467
2468 gfio_client_added(ge, client);
2469 file_add_recent(ge->ui, uri);
2470 return 0;
2471}
2472
a6790906 2473static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
0420ba6a 2474{
a6790906 2475 int port, type, server_start;
2f99deb0
JA
2476 struct gui_entry *ge;
2477 gint cur_page;
38634cb1 2478 char *host;
a6790906 2479 int ret, ge_is_new = 0;
2f99deb0
JA
2480
2481 /*
2482 * Creates new tab if current tab is the main window, or the
2483 * current tab already has a client.
2484 */
2485 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
38634cb1
JA
2486 ge = get_ge_from_page(cur_page, &ge_is_new);
2487 if (ge->client) {
2f99deb0 2488 ge = get_new_ge_with_tab("Untitled");
38634cb1
JA
2489 ge_is_new = 1;
2490 }
2f99deb0
JA
2491
2492 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
0420ba6a 2493
a6790906
JA
2494 if (get_connection_details(&host, &port, &type, &server_start)) {
2495 if (ge_is_new)
2496 gtk_widget_destroy(ge->vbox);
2497
2498 return 1;
2499 }
2500
2501 ret = do_file_open(ge, uri, host, type, port);
2502
2503 free(host);
2504
2505 if (!ret) {
2506 if (server_start)
2507 gfio_start_server();
2508 } else {
2509 if (ge_is_new)
2510 gtk_widget_destroy(ge->vbox);
2511 }
2512
2513 return ret;
2514}
2515
2516static void recent_open(GtkAction *action, gpointer data)
2517{
2518 struct gui *ui = (struct gui *) data;
2519 GtkRecentInfo *info;
2520 const gchar *uri;
2521
2522 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2523 uri = gtk_recent_info_get_uri(info);
2524
2525 do_file_open_with_tab(ui, uri);
2526}
2527
2528static void file_open(GtkWidget *w, gpointer data)
2529{
2530 struct gui *ui = data;
2531 GtkWidget *dialog;
2532 GSList *filenames, *fn_glist;
2533 GtkFileFilter *filter;
2534
0420ba6a 2535 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 2536 GTK_WINDOW(ui->window),
0420ba6a
JA
2537 GTK_FILE_CHOOSER_ACTION_OPEN,
2538 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2539 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2540 NULL);
2541 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2542
2543 filter = gtk_file_filter_new();
2544 gtk_file_filter_add_pattern(filter, "*.fio");
2545 gtk_file_filter_add_pattern(filter, "*.job");
2d262990 2546 gtk_file_filter_add_pattern(filter, "*.ini");
38634cb1 2547 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
0420ba6a
JA
2548 gtk_file_filter_set_name(filter, "Fio job file");
2549 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2550
2551 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2552 gtk_widget_destroy(dialog);
2553 return;
2554 }
2555
2556 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
2557
2558 gtk_widget_destroy(dialog);
2559
0420ba6a
JA
2560 filenames = fn_glist;
2561 while (filenames != NULL) {
a6790906
JA
2562 if (do_file_open_with_tab(ui, filenames->data))
2563 break;
0420ba6a
JA
2564 filenames = g_slist_next(filenames);
2565 }
63a130b7 2566
0420ba6a 2567 g_slist_free(fn_glist);
0420ba6a
JA
2568}
2569
2570static void file_save(GtkWidget *w, gpointer data)
2571{
63a130b7 2572 struct gui *ui = data;
0420ba6a
JA
2573 GtkWidget *dialog;
2574
2575 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 2576 GTK_WINDOW(ui->window),
0420ba6a
JA
2577 GTK_FILE_CHOOSER_ACTION_SAVE,
2578 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2579 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2580 NULL);
2581
2582 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2583 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2584
2585 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2586 char *filename;
2587
2588 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2589 // save_job_file(filename);
2590 g_free(filename);
2591 }
2592 gtk_widget_destroy(dialog);
2593}
2594
9b260bdf
JA
2595static void view_log_destroy(GtkWidget *w, gpointer data)
2596{
2597 struct gui *ui = (struct gui *) data;
2598
2599 gtk_widget_ref(ui->log_tree);
2600 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2601 gtk_widget_destroy(w);
4cbe7211 2602 ui->log_view = NULL;
9b260bdf
JA
2603}
2604
2605static void view_log(GtkWidget *w, gpointer data)
2606{
4cbe7211
JA
2607 GtkWidget *win, *scroll, *vbox, *box;
2608 struct gui *ui = (struct gui *) data;
9b260bdf 2609
4cbe7211
JA
2610 if (ui->log_view)
2611 return;
2612
2613 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 2614 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 2615 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 2616
4cbe7211
JA
2617 scroll = gtk_scrolled_window_new(NULL, NULL);
2618
2619 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2620
2621 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2622
2623 box = gtk_hbox_new(TRUE, 0);
2624 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2625 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2626 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2627
2628 vbox = gtk_vbox_new(TRUE, 5);
2629 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 2630
4cbe7211 2631 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
2632 gtk_widget_show_all(win);
2633}
2634
85dd01e7
JA
2635static void connect_job_entry(GtkWidget *w, gpointer data)
2636{
2637 struct gui *ui = (struct gui *) data;
2638 struct gui_entry *ge;
2639
2640 ge = get_ge_from_cur_tab(ui);
2641 if (ge)
2642 connect_clicked(w, ge);
2643}
2644
2645static void send_job_entry(GtkWidget *w, gpointer data)
2646{
2647 struct gui *ui = (struct gui *) data;
2648 struct gui_entry *ge;
2649
2650 ge = get_ge_from_cur_tab(ui);
2651 if (ge)
2652 send_clicked(w, ge);
2653
2654}
2655
2656static void edit_job_entry(GtkWidget *w, gpointer data)
16ce5adf
JA
2657{
2658}
2659
85dd01e7
JA
2660static void start_job_entry(GtkWidget *w, gpointer data)
2661{
2662 struct gui *ui = (struct gui *) data;
2663 struct gui_entry *ge;
2664
2665 ge = get_ge_from_cur_tab(ui);
2666 if (ge)
2667 start_job_clicked(w, ge);
2668}
2669
781ccba6
JA
2670static void view_results(GtkWidget *w, gpointer data)
2671{
2672 struct gui *ui = (struct gui *) data;
2673 struct gfio_client *gc;
2674 struct gui_entry *ge;
2675
2676 ge = get_ge_from_cur_tab(ui);
2677 if (!ge)
2678 return;
2679
2680 if (ge->results_window)
2681 return;
2682
2683 gc = ge->client;
2684 if (gc && gc->nr_results)
2685 gfio_display_end_results(gc);
2686}
2687
8577f4fd
JA
2688static void __update_graph_limits(struct gfio_graphs *g)
2689{
2690 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2691 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2692}
2693
2694static void update_graph_limits(void)
2695{
2696 struct flist_head *entry;
2697 struct gui_entry *ge;
2698
2699 __update_graph_limits(&main_ui.graphs);
2700
2701 flist_for_each(entry, &main_ui.list) {
2702 ge = flist_entry(entry, struct gui_entry, list);
2703 __update_graph_limits(&ge->graphs);
2704 }
2705}
2706
46974a7d
JA
2707static void preferences(GtkWidget *w, gpointer data)
2708{
f3e8440f 2709 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 2710 GtkWidget *hbox, *spin, *entry, *spin_int;
46974a7d
JA
2711 int i;
2712
2713 dialog = gtk_dialog_new_with_buttons("Preferences",
2f99deb0 2714 GTK_WINDOW(main_ui.window),
46974a7d
JA
2715 GTK_DIALOG_DESTROY_WITH_PARENT,
2716 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2717 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2718 NULL);
2719
8577f4fd 2720 frame = gtk_frame_new("Graphing");
f3e8440f
JA
2721 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2722 vbox = gtk_vbox_new(FALSE, 6);
2723 gtk_container_add(GTK_CONTAINER(frame), vbox);
2724
1cf6bca5
JA
2725 hbox = gtk_hbox_new(FALSE, 5);
2726 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2727 entry = gtk_label_new("Font face to use for graph labels");
2728 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2729
f3e8440f 2730 font = gtk_font_button_new();
1cf6bca5 2731 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 2732
8577f4fd
JA
2733 box = gtk_vbox_new(FALSE, 6);
2734 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2735
2736 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 2737 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
2738 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2739 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2740
c05d9056 2741 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
8577f4fd 2742
1cf6bca5
JA
2743 box = gtk_vbox_new(FALSE, 6);
2744 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2745
2746 hbox = gtk_hbox_new(FALSE, 5);
2747 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2748 entry = gtk_label_new("Client ETA request interval (msec)");
2749 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2750
2751 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4
JA
2752 frame = gtk_frame_new("Debug logging");
2753 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2754 vbox = gtk_vbox_new(FALSE, 6);
2755 gtk_container_add(GTK_CONTAINER(frame), vbox);
2756
2757 box = gtk_hbox_new(FALSE, 6);
2758 gtk_container_add(GTK_CONTAINER(vbox), box);
2759
2760 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2761
2762 for (i = 0; i < FD_DEBUG_MAX; i++) {
2763 if (i == 7) {
2764 box = gtk_hbox_new(FALSE, 6);
2765 gtk_container_add(GTK_CONTAINER(vbox), box);
2766 }
2767
2768
2769 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2770 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2771 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2772 }
2773
46974a7d
JA
2774 gtk_widget_show_all(dialog);
2775
2776 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2777 gtk_widget_destroy(dialog);
2778 return;
2779 }
2780
2781 for (i = 0; i < FD_DEBUG_MAX; i++) {
2782 int set;
2783
2784 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2785 if (set)
2786 fio_debug |= (1UL << i);
2787 }
2788
f3e8440f 2789 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
2790 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2791 update_graph_limits();
1cf6bca5 2792 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 2793
46974a7d
JA
2794 gtk_widget_destroy(dialog);
2795}
2796
0420ba6a
JA
2797static void about_dialog(GtkWidget *w, gpointer data)
2798{
81e4ea6e
JA
2799 const char *authors[] = {
2800 "Jens Axboe <axboe@kernel.dk>",
2801 "Stephen Carmeron <stephenmcameron@gmail.com>",
2802 NULL
2803 };
84a72ed3
JA
2804 const char *license[] = {
2805 "Fio is free software; you can redistribute it and/or modify "
2806 "it under the terms of the GNU General Public License as published by "
2807 "the Free Software Foundation; either version 2 of the License, or "
2808 "(at your option) any later version.\n",
2809 "Fio is distributed in the hope that it will be useful, "
2810 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2811 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2812 "GNU General Public License for more details.\n",
2813 "You should have received a copy of the GNU General Public License "
2814 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2815 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2816 };
2817 char *license_trans;
2818
2819 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2820 license[2], "\n", NULL);
81e4ea6e 2821
0420ba6a
JA
2822 gtk_show_about_dialog(NULL,
2823 "program-name", "gfio",
2824 "comments", "Gtk2 UI for fio",
84a72ed3 2825 "license", license_trans,
81e4ea6e
JA
2826 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2827 "authors", authors,
0420ba6a 2828 "version", fio_version_string,
81e4ea6e 2829 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
2830 "logo-icon-name", "fio",
2831 /* Must be last: */
81e4ea6e 2832 "wrap-license", TRUE,
0420ba6a 2833 NULL);
84a72ed3 2834
2f99deb0 2835 g_free(license_trans);
0420ba6a
JA
2836}
2837
2838static GtkActionEntry menu_items[] = {
46974a7d 2839 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 2840 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
16ce5adf 2841 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
46974a7d 2842 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 2843 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
16ce5adf 2844 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
46974a7d
JA
2845 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2846 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2847 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 2848 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
781ccba6 2849 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
85dd01e7
JA
2850 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2851 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2852 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2853 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
46974a7d
JA
2854 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2855 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 2856};
3e47bd25 2857static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
2858
2859static const gchar *ui_string = " \
2860 <ui> \
2861 <menubar name=\"MainMenu\"> \
2862 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0 2863 <menuitem name=\"New\" action=\"NewFile\" /> \
bf64138b 2864 <menuitem name=\"Open\" action=\"OpenFile\" /> \
16ce5adf 2865 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2f99deb0 2866 <separator name=\"Separator1\"/> \
0420ba6a 2867 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 2868 <separator name=\"Separator2\"/> \
2f99deb0
JA
2869 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2870 <separator name=\"Separator3\"/> \
261f21d0
JA
2871 <placeholder name=\"FileRecentFiles\"/> \
2872 <separator name=\"Separator4\"/> \
0420ba6a
JA
2873 <menuitem name=\"Quit\" action=\"Quit\" /> \
2874 </menu> \
16ce5adf 2875 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
85dd01e7 2876 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
261f21d0 2877 <separator name=\"Separator5\"/> \
85dd01e7
JA
2878 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2879 <menuitem name=\"Send job\" action=\"SendJob\" /> \
261f21d0 2880 <separator name=\"Separator6\"/> \
85dd01e7 2881 <menuitem name=\"Start job\" action=\"StartJob\" /> \
16ce5adf 2882 </menu>\
9b260bdf 2883 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
781ccba6
JA
2884 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2885 <separator name=\"Separator7\"/> \
9b260bdf
JA
2886 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2887 </menu>\
0420ba6a
JA
2888 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2889 <menuitem name=\"About\" action=\"About\" /> \
2890 </menu> \
2891 </menubar> \
2892 </ui> \
2893";
2894
4cbe7211
JA
2895static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2896 struct gui *ui)
0420ba6a 2897{
ca664f49 2898 GtkActionGroup *action_group;
0420ba6a
JA
2899 GError *error = 0;
2900
2901 action_group = gtk_action_group_new("Menu");
4cbe7211 2902 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
2903
2904 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2905 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2906
2907 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
02421e69 2908
0420ba6a
JA
2909 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2910}
2911
2912void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2913 GtkWidget *vbox, GtkUIManager *ui_manager)
2914{
2915 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2916}
2917
c80b74b0
JA
2918static void combo_entry_changed(GtkComboBox *box, gpointer data)
2919{
2920 struct gui_entry *ge = (struct gui_entry *) data;
2921 gint index;
2922
2923 index = gtk_combo_box_get_active(box);
2924
2925 multitext_set_entry(&ge->eta.iotype, index);
2926 multitext_set_entry(&ge->eta.ioengine, index);
2927 multitext_set_entry(&ge->eta.iodepth, index);
2928}
2929
2930static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2931{
2932 struct gui_entry *ge = (struct gui_entry *) data;
2933
2934 multitext_free(&ge->eta.iotype);
2935 multitext_free(&ge->eta.ioengine);
2936 multitext_free(&ge->eta.iodepth);
2937}
2938
2f99deb0 2939static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 2940{
2f99deb0 2941 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 2942 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
0420ba6a 2943
2f99deb0 2944 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 2945
65476336
JA
2946 top_align = gtk_alignment_new(0, 0, 1, 0);
2947 top_vbox = gtk_vbox_new(FALSE, 3);
2948 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2949 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2f99deb0
JA
2950
2951 probe = gtk_frame_new("Job");
2952 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2953 probe_frame = gtk_vbox_new(FALSE, 3);
2954 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2955
2956 probe_box = gtk_hbox_new(FALSE, 3);
2957 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2958 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2959 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2960 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2961 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2962
2963 probe_box = gtk_hbox_new(FALSE, 3);
2964 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2965
3863d1ad 2966 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
c80b74b0
JA
2967 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2968 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2969 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2970 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2971 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2f99deb0
JA
2972 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2973 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2974
2975 probe_box = gtk_hbox_new(FALSE, 3);
2976 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2977 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2978 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2979 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2980 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2981
2982 /*
2983 * Only add this if we have a commit rate
2839f0c6 2984 */
2f99deb0
JA
2985#if 0
2986 probe_box = gtk_hbox_new(FALSE, 3);
2987 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 2988
2f99deb0
JA
2989 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2990 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 2991
2f99deb0
JA
2992 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2993 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2994#endif
ff1f3280 2995
2f99deb0
JA
2996 /*
2997 * Set up a drawing area and IOPS and bandwidth graphs
2998 */
2f99deb0 2999 ge->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 3000 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
57f9d28e 3001 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
3002 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3003 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3004 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3005 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3006 G_CALLBACK(on_config_drawing_area), &ge->graphs);
65476336
JA
3007 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3008 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3009 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 3010 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3011 ge->graphs.drawing_area);
65476336 3012 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
04cc6b77 3013
2f99deb0
JA
3014 setup_graphs(&ge->graphs);
3015
3016 /*
3017 * Set up alignments for widgets at the bottom of ui,
3018 * align bottom left, expand horizontally but not vertically
3019 */
65476336 3020 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2f99deb0 3021 ge->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
3022 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3023 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2f99deb0
JA
3024
3025 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
0420ba6a 3026
c36f98d9 3027 /*
2f99deb0
JA
3028 * Set up thread status progress bar
3029 */
3030 ge->thread_status_pb = gtk_progress_bar_new();
3031 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3032 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3033 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3034
3035
3036 return main_vbox;
3037}
3038
3039static GtkWidget *new_main_page(struct gui *ui)
3040{
3041 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 3042 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2f99deb0
JA
3043
3044 main_vbox = gtk_vbox_new(FALSE, 3);
3045
3046 /*
3047 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
3048 * align top left, expand horizontally but not vertically
3049 */
65476336
JA
3050 top_align = gtk_alignment_new(0, 0, 1, 0);
3051 top_vbox = gtk_vbox_new(FALSE, 0);
3052 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3053 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
c36f98d9 3054
2f99deb0
JA
3055 probe = gtk_frame_new("Run statistics");
3056 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
3057 probe_frame = gtk_vbox_new(FALSE, 3);
3058 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3059
3060 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 3061 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 3062 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
ca850992
JA
3063 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3064 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3065 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3066 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 3067
807f9971
JA
3068 /*
3069 * Only add this if we have a commit rate
3070 */
3071#if 0
3e47bd25
JA
3072 probe_box = gtk_hbox_new(FALSE, 3);
3073 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
3074
3075 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3076 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3077
3e47bd25
JA
3078 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3079 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 3080#endif
3e47bd25 3081
736f2dff 3082 /*
2fd3bb0e 3083 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 3084 */
2f99deb0 3085 ui->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 3086 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
57f9d28e 3087 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
3088 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3089 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3090 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3091 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3092 G_CALLBACK(on_config_drawing_area), &ui->graphs);
65476336
JA
3093 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3094 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
736f2dff 3095 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 3096 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3097 ui->graphs.drawing_area);
65476336 3098 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
e164534f 3099 TRUE, TRUE, 0);
736f2dff 3100
2f99deb0 3101 setup_graphs(&ui->graphs);
2fd3bb0e 3102
c36f98d9
SC
3103 /*
3104 * Set up alignments for widgets at the bottom of ui,
3105 * align bottom left, expand horizontally but not vertically
3106 */
65476336 3107 bottom_align = gtk_alignment_new(0, 1, 1, 0);
c36f98d9 3108 ui->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
3109 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3110 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
c36f98d9 3111
3ec62ec4
JA
3112 /*
3113 * Set up thread status progress bar
3114 */
3115 ui->thread_status_pb = gtk_progress_bar_new();
3116 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 3117 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
3118 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3119
2f99deb0
JA
3120 return main_vbox;
3121}
3122
3123static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3124 guint page, gpointer data)
3125
3126{
02421e69 3127 struct gui *ui = (struct gui *) data;
85dd01e7
JA
3128 struct gui_entry *ge;
3129
3130 if (!page) {
3131 set_job_menu_visible(ui, 0);
781ccba6 3132 set_view_results_visible(ui, 0);
85dd01e7
JA
3133 return TRUE;
3134 }
3135
3136 set_job_menu_visible(ui, 1);
38634cb1 3137 ge = get_ge_from_page(page, NULL);
85dd01e7
JA
3138 if (ge)
3139 update_button_states(ui, ge);
02421e69 3140
2f99deb0
JA
3141 return TRUE;
3142}
3143
38634cb1
JA
3144static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3145{
3146 time_t time_a = gtk_recent_info_get_visited(a);
3147 time_t time_b = gtk_recent_info_get_visited(b);
3148
3149 return time_b - time_a;
3150}
3151
3152static void add_recent_file_items(struct gui *ui)
3153{
3154 const gchar *gfio = g_get_application_name();
3155 GList *items, *item;
3156 int i = 0;
3157
3158 if (ui->recent_ui_id) {
3159 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3160 gtk_ui_manager_ensure_update(ui->uimanager);
3161 }
3162 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3163
3164 if (ui->actiongroup) {
3165 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3166 g_object_unref(ui->actiongroup);
3167 }
3168 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3169
3170 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3171
3172 items = gtk_recent_manager_get_items(ui->recentmanager);
3173 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3174
3175 for (item = items; item && item->data; item = g_list_next(item)) {
3176 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3177 gchar *action_name;
3178 const gchar *label;
3179 GtkAction *action;
3180
3181 if (!gtk_recent_info_has_application(info, gfio))
3182 continue;
3183
3184 /*
3185 * We only support local files for now
3186 */
3187 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3188 continue;
3189
3190 action_name = g_strdup_printf("RecentFile%u", i++);
3191 label = gtk_recent_info_get_display_name(info);
3192
3193 action = g_object_new(GTK_TYPE_ACTION,
3194 "name", action_name,
3195 "label", label, NULL);
3196
3197 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3198 gtk_recent_info_ref(info),
3199 (GDestroyNotify) gtk_recent_info_unref);
3200
3201
3202 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3203
3204 gtk_action_group_add_action(ui->actiongroup, action);
3205 g_object_unref(action);
3206
3207 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3208 "/MainMenu/FileMenu/FileRecentFiles",
3209 label, action_name,
3210 GTK_UI_MANAGER_MENUITEM, FALSE);
3211
3212 g_free(action_name);
3213
3214 if (i == 8)
3215 break;
3216 }
3217
3218 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3219 g_list_free(items);
3220}
3221
a6790906
JA
3222static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3223 gint x, gint y, GtkSelectionData *data,
3224 guint info, guint time)
3225{
3226 struct gui *ui = &main_ui;
3227 gchar **uris;
3228 GtkWidget *source;
3229 int i;
3230
3231 source = gtk_drag_get_source_widget(ctx);
3232 if (source && widget == gtk_widget_get_toplevel(source)) {
3233 gtk_drag_finish(ctx, FALSE, FALSE, time);
3234 return;
3235 }
3236
3237 uris = gtk_selection_data_get_uris(data);
3238 if (!uris) {
3239 gtk_drag_finish(ctx, FALSE, FALSE, time);
3240 return;
3241 }
3242
3243 i = 0;
3244 while (uris[i]) {
3245 if (do_file_open_with_tab(ui, uris[i]))
3246 break;
3247 i++;
3248 }
3249
3250 gtk_drag_finish(ctx, TRUE, FALSE, time);
3251 g_strfreev(uris);
3252}
3253
2f99deb0
JA
3254static void init_ui(int *argc, char **argv[], struct gui *ui)
3255{
3256 GtkSettings *settings;
02421e69 3257 GtkWidget *vbox;
2f99deb0
JA
3258
3259 /* Magical g*thread incantation, you just need this thread stuff.
3260 * Without it, the update that happens in gfio_update_thread_status
3261 * doesn't really happen in a timely fashion, you need expose events
3262 */
3263 if (!g_thread_supported())
3264 g_thread_init(NULL);
3265 gdk_threads_init();
3266
3267 gtk_init(argc, argv);
3268 settings = gtk_settings_get_default();
3269 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3270 g_type_init();
814479d5 3271 gdk_color_parse("white", &white);
2f99deb0
JA
3272
3273 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
814479d5 3274 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2f99deb0
JA
3275 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3276
3277 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3278 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3279
3280 ui->vbox = gtk_vbox_new(FALSE, 0);
3281 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3282
02421e69
JA
3283 ui->uimanager = gtk_ui_manager_new();
3284 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3285 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2f99deb0 3286
38634cb1
JA
3287 ui->recentmanager = gtk_recent_manager_get_default();
3288 add_recent_file_items(ui);
3289
2f99deb0
JA
3290 ui->notebook = gtk_notebook_new();
3291 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 3292 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 3293 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
3294 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3295
3296 vbox = new_main_page(ui);
a6790906
JA
3297 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3298 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3299 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
2f99deb0
JA
3300
3301 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3302
9b260bdf 3303 gfio_ui_setup_log(ui);
3ec62ec4 3304
ff1f3280
SC
3305 gtk_widget_show_all(ui->window);
3306}
3307
8232e285 3308int main(int argc, char *argv[], char *envp[])
ff1f3280 3309{
8232e285
SC
3310 if (initialize_fio(envp))
3311 return 1;
0420ba6a
JA
3312 if (fio_init_options())
3313 return 1;
a1820207 3314
2f99deb0
JA
3315 memset(&main_ui, 0, sizeof(main_ui));
3316 INIT_FLIST_HEAD(&main_ui.list);
3317
3318 init_ui(&argc, &argv, &main_ui);
5b7573ab 3319
2839f0c6 3320 gdk_threads_enter();
ff1f3280 3321 gtk_main();
2839f0c6 3322 gdk_threads_leave();
ff1f3280
SC
3323 return 0;
3324}