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