gfio: put disk utilization tab in a scrolled window
[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
f0602d78
JA
1292static GtkWidget *get_scrolled_window(gint border_width)
1293{
1294 GtkWidget *scroll;
1295
1296 scroll = gtk_scrolled_window_new(NULL, NULL);
1297 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1298 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1299
1300 return scroll;
1301}
1302
1303static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1304{
1305 GtkWidget *vbox, *box, *scroll, *res_notebook;
1306
1307 if (ge->disk_util_vbox)
1308 return ge->disk_util_vbox;
1309
1310 scroll = get_scrolled_window(5);
1311 vbox = gtk_vbox_new(FALSE, 3);
1312 box = gtk_hbox_new(FALSE, 0);
1313 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1314
1315 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1316 res_notebook = get_results_window(ge);
1317
1318 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1319 ge->disk_util_vbox = box;
1320 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1321
1322 return ge->disk_util_vbox;
1323}
1324
7da23b48
JA
1325static int __gfio_disk_util_show(GtkWidget *res_notebook,
1326 struct gfio_client *gc, struct cmd_du_pdu *p)
1327{
f0602d78 1328 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
7da23b48
JA
1329 struct gui_entry *ge = gc->ge;
1330 double util;
1331 char tmp[16];
1332
f0602d78 1333 util_vbox = gfio_disk_util_get_vbox(ge);
7da23b48
JA
1334
1335 vbox = gtk_vbox_new(FALSE, 3);
f0602d78 1336 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
7da23b48
JA
1337
1338 frame = gtk_frame_new((char *) p->dus.name);
1339 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1340
1341 box = gtk_vbox_new(FALSE, 3);
1342 gtk_container_add(GTK_CONTAINER(frame), box);
1343
1344 frame = gtk_frame_new("Read");
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, "IOs");
1349 entry_set_int_value(entry, p->dus.ios[0]);
1350 entry = new_info_entry_in_frame(vbox, "Merges");
1351 entry_set_int_value(entry, p->dus.merges[0]);
1352 entry = new_info_entry_in_frame(vbox, "Sectors");
1353 entry_set_int_value(entry, p->dus.sectors[0]);
1354 entry = new_info_entry_in_frame(vbox, "Ticks");
1355 entry_set_int_value(entry, p->dus.ticks[0]);
1356
1357 frame = gtk_frame_new("Write");
1358 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1359 vbox = gtk_hbox_new(TRUE, 3);
1360 gtk_container_add(GTK_CONTAINER(frame), vbox);
1361 entry = new_info_entry_in_frame(vbox, "IOs");
1362 entry_set_int_value(entry, p->dus.ios[1]);
1363 entry = new_info_entry_in_frame(vbox, "Merges");
1364 entry_set_int_value(entry, p->dus.merges[1]);
1365 entry = new_info_entry_in_frame(vbox, "Sectors");
1366 entry_set_int_value(entry, p->dus.sectors[1]);
1367 entry = new_info_entry_in_frame(vbox, "Ticks");
1368 entry_set_int_value(entry, p->dus.ticks[1]);
1369
1370 frame = gtk_frame_new("Shared");
1371 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1372 vbox = gtk_hbox_new(TRUE, 3);
1373 gtk_container_add(GTK_CONTAINER(frame), vbox);
1374 entry = new_info_entry_in_frame(vbox, "IO ticks");
1375 entry_set_int_value(entry, p->dus.io_ticks);
1376 entry = new_info_entry_in_frame(vbox, "Time in queue");
1377 entry_set_int_value(entry, p->dus.time_in_queue);
1378
1379 util = 0.0;
1380 if (p->dus.msec)
1381 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1382 if (util > 100.0)
1383 util = 100.0;
1384
1385 sprintf(tmp, "%3.2f%%", util);
1386 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1387 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1388
1389 gtk_widget_show_all(ge->results_window);
1390 return 0;
1391}
1392
1393static int gfio_disk_util_show(struct gfio_client *gc)
1394{
1395 struct gui_entry *ge = gc->ge;
1396 GtkWidget *res_notebook;
1397 int i;
1398
1399 if (!gc->nr_du)
1400 return 1;
1401
1402 res_notebook = get_results_window(ge);
1403
1404 for (i = 0; i < gc->nr_du; i++) {
1405 struct cmd_du_pdu *p = &gc->du[i];
1406
1407 __gfio_disk_util_show(res_notebook, gc, p);
1408 }
1409
1410 gtk_widget_show_all(ge->results_window);
1411 return 0;
1412}
1413
781ccba6
JA
1414static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1415 struct group_run_stats *rs)
3650a3ca 1416{
781ccba6 1417 unsigned int nr = gc->nr_results;
3650a3ca 1418
781ccba6
JA
1419 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1420 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1421 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1422 gc->nr_results++;
1423}
3650a3ca 1424
781ccba6
JA
1425static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1426 struct thread_stat *ts,
1427 struct group_run_stats *rs)
1428{
1429 GtkWidget *box, *vbox, *entry, *scroll;
3650a3ca 1430
b01329d0
JA
1431 scroll = gtk_scrolled_window_new(NULL, NULL);
1432 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1433 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1434
3650a3ca 1435 vbox = gtk_vbox_new(FALSE, 3);
3650a3ca 1436
b01329d0
JA
1437 box = gtk_hbox_new(FALSE, 0);
1438 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1439
1440 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
3650a3ca 1441
781ccba6 1442 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
f9d40b48 1443
3650a3ca
JA
1444 entry = new_info_entry_in_frame(box, "Name");
1445 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1446 if (strlen(ts->description)) {
1447 entry = new_info_entry_in_frame(box, "Description");
1448 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1449 }
1450 entry = new_info_entry_in_frame(box, "Group ID");
1451 entry_set_int_value(entry, ts->groupid);
1452 entry = new_info_entry_in_frame(box, "Jobs");
1453 entry_set_int_value(entry, ts->members);
6b79c80c 1454 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
3650a3ca
JA
1455 entry_set_int_value(entry, ts->error);
1456 entry = new_info_entry_in_frame(box, "PID");
1457 entry_set_int_value(entry, ts->pid);
1458
1459 if (ts->io_bytes[DDIR_READ])
c57f254c 1460 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
3650a3ca 1461 if (ts->io_bytes[DDIR_WRITE])
c57f254c 1462 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
3650a3ca 1463
09d574e3 1464 gfio_show_latency_buckets(gc, vbox, ts);
2e33101f
JA
1465 gfio_show_cpu_usage(vbox, ts);
1466 gfio_show_io_depths(vbox, ts);
781ccba6
JA
1467}
1468
1469static void gfio_display_end_results(struct gfio_client *gc)
1470{
60d0929e
JA
1471 struct gui_entry *ge = gc->ge;
1472 GtkWidget *res_notebook;
781ccba6
JA
1473 int i;
1474
60d0929e 1475 res_notebook = get_results_window(ge);
781ccba6
JA
1476
1477 for (i = 0; i < gc->nr_results; i++) {
1478 struct end_results *e = &gc->results[i];
1479
60d0929e 1480 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
781ccba6 1481 }
e5bd1347 1482
7da23b48
JA
1483 if (gfio_disk_util_show(gc))
1484 gtk_widget_show_all(ge->results_window);
781ccba6
JA
1485}
1486
1487static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1488 struct group_run_stats *rs)
1489{
1490 struct gfio_client *gc = client->client_data;
0ed83bce 1491 struct gui_entry *ge = gc->ge;
781ccba6
JA
1492
1493 gfio_add_end_results(gc, ts, rs);
1494
1495 gdk_threads_enter();
0ed83bce
JA
1496 if (ge->results_window)
1497 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1498 else
1499 gfio_display_end_results(gc);
3650a3ca
JA
1500 gdk_threads_leave();
1501}
1502
084d1c6f 1503static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 1504{
9b260bdf 1505 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
2f99deb0 1506 struct gui *ui = &main_ui;
9b260bdf
JA
1507 GtkTreeIter iter;
1508 struct tm *tm;
1509 time_t sec;
1510 char tmp[64], timebuf[80];
1511
1512 sec = p->log_sec;
1513 tm = localtime(&sec);
1514 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1515 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
736f2dff 1516
736f2dff 1517 gdk_threads_enter();
9b260bdf 1518
2f99deb0
JA
1519 gtk_list_store_append(ui->log_model, &iter);
1520 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1521 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1522 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1523 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
9b260bdf 1524
6b79c80c 1525 if (p->level == FIO_LOG_ERR)
2f99deb0 1526 view_log(NULL, (gpointer) ui);
6b79c80c 1527
736f2dff 1528 gdk_threads_leave();
a1820207
SC
1529}
1530
1531static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1532{
e0681f3e
JA
1533 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1534 struct gfio_client *gc = client->client_data;
0ed83bce 1535 struct gui_entry *ge = gc->ge;
7da23b48 1536 unsigned int nr = gc->nr_du;
e0681f3e 1537
7da23b48
JA
1538 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1539 memcpy(&gc->du[nr], p, sizeof(*p));
1540 gc->nr_du++;
e0681f3e 1541
7da23b48 1542 gdk_threads_enter();
0ed83bce
JA
1543 if (ge->results_window)
1544 __gfio_disk_util_show(ge->results_notebook, gc, p);
1545 else
1546 gfio_disk_util_show(gc);
0050e5f2 1547 gdk_threads_leave();
a1820207
SC
1548}
1549
3650a3ca
JA
1550extern int sum_stat_clients;
1551extern struct thread_stat client_ts;
1552extern struct group_run_stats client_gs;
1553
1554static int sum_stat_nr;
1555
89e5fad9
JA
1556static void gfio_thread_status_op(struct fio_client *client,
1557 struct fio_net_cmd *cmd)
a1820207 1558{
3650a3ca
JA
1559 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1560
1561 gfio_display_ts(client, &p->ts, &p->rs);
1562
1563 if (sum_stat_clients == 1)
1564 return;
1565
1566 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1567 sum_group_stats(&client_gs, &p->rs);
1568
1569 client_ts.members++;
2f122b13 1570 client_ts.thread_number = p->ts.thread_number;
3650a3ca
JA
1571 client_ts.groupid = p->ts.groupid;
1572
1573 if (++sum_stat_nr == sum_stat_clients) {
1574 strcpy(client_ts.name, "All clients");
1575 gfio_display_ts(client, &client_ts, &client_gs);
1576 }
a1820207
SC
1577}
1578
89e5fad9
JA
1579static void gfio_group_stats_op(struct fio_client *client,
1580 struct fio_net_cmd *cmd)
a1820207 1581{
98ceabd6 1582 /* We're ignoring group stats for now */
a1820207
SC
1583}
1584
2f99deb0
JA
1585static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1586 gpointer data)
3ea48b88 1587{
2f99deb0
JA
1588 struct gfio_graphs *g = data;
1589
57f9d28e
SC
1590 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1591 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1592 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1593 graph_set_position(g->bandwidth_graph, 0, 0);
3ea48b88
SC
1594 return TRUE;
1595}
1596
57f9d28e
SC
1597static void draw_graph(struct graph *g, cairo_t *cr)
1598{
1599 line_graph_draw(g, cr);
1600 cairo_stroke(cr);
1601}
1602
93e2db2b
JA
1603static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1604 gboolean keyboard_mode, GtkTooltip *tooltip,
1605 gpointer data)
1606{
1607 struct gfio_graphs *g = data;
1608 const char *text = NULL;
1609
1610 if (graph_contains_xy(g->iops_graph, x, y))
1611 text = graph_find_tooltip(g->iops_graph, x, y);
1612 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1613 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1614
1615 if (text) {
1616 gtk_tooltip_set_text(tooltip, text);
1617 return TRUE;
1618 }
1619
1620 return FALSE;
1621}
1622
2fd3bb0e
JA
1623static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1624{
2f99deb0 1625 struct gfio_graphs *g = p;
2fd3bb0e
JA
1626 cairo_t *cr;
1627
1628 cr = gdk_cairo_create(w->window);
93e2db2b
JA
1629
1630 if (graph_has_tooltips(g->iops_graph) ||
1631 graph_has_tooltips(g->bandwidth_graph)) {
1632 g_object_set(w, "has-tooltip", TRUE, NULL);
1633 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1634 }
1635
2fd3bb0e 1636 cairo_set_source_rgb(cr, 0, 0, 0);
57f9d28e
SC
1637 draw_graph(g->iops_graph, cr);
1638 draw_graph(g->bandwidth_graph, cr);
2fd3bb0e
JA
1639 cairo_destroy(cr);
1640
1641 return FALSE;
1642}
1643
2f99deb0
JA
1644/*
1645 * Client specific ETA
1646 */
1647static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
3e47bd25 1648{
2f99deb0
JA
1649 struct gfio_client *gc = client->client_data;
1650 struct gui_entry *ge = gc->ge;
3e47bd25
JA
1651 static int eta_good;
1652 char eta_str[128];
1653 char output[256];
1654 char tmp[32];
1655 double perc = 0.0;
1656 int i2p = 0;
1657
0050e5f2
JA
1658 gdk_threads_enter();
1659
3e47bd25
JA
1660 eta_str[0] = '\0';
1661 output[0] = '\0';
1662
1663 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1664 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1665 eta_to_str(eta_str, je->eta_sec);
1666 }
1667
1668 sprintf(tmp, "%u", je->nr_running);
2f99deb0 1669 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
3e47bd25 1670 sprintf(tmp, "%u", je->files_open);
2f99deb0 1671 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
3e47bd25
JA
1672
1673#if 0
1674 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1675 if (je->m_rate || je->t_rate) {
1676 char *tr, *mr;
1677
1678 mr = num2str(je->m_rate, 4, 0, i2p);
1679 tr = num2str(je->t_rate, 4, 0, i2p);
2f99deb0 1680 gtk_entry_set_text(GTK_ENTRY(ge->eta);
3e47bd25
JA
1681 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1682 free(tr);
1683 free(mr);
1684 } else if (je->m_iops || je->t_iops)
1685 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 1686
2f99deb0
JA
1687 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1688 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1689 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1690 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
3e47bd25
JA
1691#endif
1692
1693 if (je->eta_sec != INT_MAX && je->nr_running) {
1694 char *iops_str[2];
1695 char *rate_str[2];
1696
1697 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1698 strcpy(output, "-.-% done");
1699 else {
1700 eta_good = 1;
1701 perc *= 100.0;
1702 sprintf(output, "%3.1f%% done", perc);
1703 }
1704
1705 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1706 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1707
1708 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1709 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1710
2f99deb0
JA
1711 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1712 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1713 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1714 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
3e47bd25 1715
93e2db2b
JA
1716 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1717 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1718 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1719 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
2f99deb0
JA
1720
1721 free(rate_str[0]);
1722 free(rate_str[1]);
1723 free(iops_str[0]);
1724 free(iops_str[1]);
1725 }
1726
1727 if (eta_str[0]) {
1728 char *dst = output + strlen(output);
1729
1730 sprintf(dst, " - %s", eta_str);
1731 }
1732
9988ca70 1733 gfio_update_thread_status(ge, output, perc);
2f99deb0
JA
1734 gdk_threads_leave();
1735}
1736
1737/*
1738 * Update ETA in main window for all clients
1739 */
1740static void gfio_update_all_eta(struct jobs_eta *je)
1741{
1742 struct gui *ui = &main_ui;
1743 static int eta_good;
1744 char eta_str[128];
1745 char output[256];
1746 double perc = 0.0;
1747 int i2p = 0;
1748
1749 gdk_threads_enter();
1750
1751 eta_str[0] = '\0';
1752 output[0] = '\0';
1753
1754 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1755 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1756 eta_to_str(eta_str, je->eta_sec);
1757 }
1758
1759#if 0
1760 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1761 if (je->m_rate || je->t_rate) {
1762 char *tr, *mr;
1763
1764 mr = num2str(je->m_rate, 4, 0, i2p);
1765 tr = num2str(je->t_rate, 4, 0, i2p);
1766 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1767 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1768 free(tr);
1769 free(mr);
1770 } else if (je->m_iops || je->t_iops)
1771 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1772
1773 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1774 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1775 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1776 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1777#endif
1778
3863d1ad
JA
1779 entry_set_int_value(ui->eta.jobs, je->nr_running);
1780
2f99deb0
JA
1781 if (je->eta_sec != INT_MAX && je->nr_running) {
1782 char *iops_str[2];
1783 char *rate_str[2];
1784
1785 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1786 strcpy(output, "-.-% done");
1787 else {
1788 eta_good = 1;
1789 perc *= 100.0;
1790 sprintf(output, "%3.1f%% done", perc);
1791 }
1792
1793 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1794 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1795
1796 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1797 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1798
1799 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1800 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1801 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1802 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1803
93e2db2b
JA
1804 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1805 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1806 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1807 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
2fd3bb0e 1808
3e47bd25
JA
1809 free(rate_str[0]);
1810 free(rate_str[1]);
1811 free(iops_str[0]);
1812 free(iops_str[1]);
1813 }
1814
1815 if (eta_str[0]) {
1816 char *dst = output + strlen(output);
1817
1818 sprintf(dst, " - %s", eta_str);
1819 }
1820
9988ca70 1821 gfio_update_thread_status_all(output, perc);
0050e5f2 1822 gdk_threads_leave();
3e47bd25
JA
1823}
1824
a1820207
SC
1825static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1826{
843ad237 1827 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
88f6e7ad 1828 struct gfio_client *gc = client->client_data;
2f99deb0 1829 struct gui_entry *ge = gc->ge;
843ad237
JA
1830 const char *os, *arch;
1831 char buf[64];
1832
1833 os = fio_get_os_string(probe->os);
1834 if (!os)
1835 os = "unknown";
1836
1837 arch = fio_get_arch_string(probe->arch);
1838 if (!arch)
1839 os = "unknown";
1840
1841 if (!client->name)
1842 client->name = strdup((char *) probe->hostname);
1843
0050e5f2
JA
1844 gdk_threads_enter();
1845
2f99deb0
JA
1846 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1847 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1848 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
843ad237 1849 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
2f99deb0 1850 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
88f6e7ad 1851
85dd01e7 1852 gfio_set_state(ge, GE_STATE_CONNECTED);
0050e5f2
JA
1853
1854 gdk_threads_leave();
a1820207
SC
1855}
1856
9988ca70
JA
1857static void gfio_update_thread_status(struct gui_entry *ge,
1858 char *status_message, double perc)
1859{
1860 static char message[100];
1861 const char *m = message;
1862
1863 strncpy(message, status_message, sizeof(message) - 1);
1864 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1865 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1866 gtk_widget_queue_draw(main_ui.window);
1867}
1868
1869static void gfio_update_thread_status_all(char *status_message, double perc)
5b7573ab 1870{
2f99deb0 1871 struct gui *ui = &main_ui;
5b7573ab
SC
1872 static char message[100];
1873 const char *m = message;
1874
1875 strncpy(message, status_message, sizeof(message) - 1);
2f99deb0
JA
1876 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1877 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1878 gtk_widget_queue_draw(ui->window);
5b7573ab
SC
1879}
1880
35c0ba7f 1881static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
3ec62ec4 1882{
e0681f3e 1883 struct gfio_client *gc = client->client_data;
3ec62ec4 1884
0050e5f2 1885 gdk_threads_enter();
85dd01e7 1886 gfio_set_state(gc->ge, GE_STATE_NEW);
0050e5f2 1887 gdk_threads_leave();
3ec62ec4
JA
1888}
1889
807f9971
JA
1890static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1891{
1892 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
e0681f3e 1893 struct gfio_client *gc = client->client_data;
dcaeb606 1894 struct thread_options *o = &gc->o;
2f99deb0 1895 struct gui_entry *ge = gc->ge;
807f9971 1896 char tmp[8];
807f9971 1897
2f122b13
JA
1898 p->thread_number = le32_to_cpu(p->thread_number);
1899 p->groupid = le32_to_cpu(p->groupid);
dcaeb606 1900 convert_thread_options_to_cpu(o, &p->top);
807f9971 1901
0050e5f2
JA
1902 gdk_threads_enter();
1903
2f99deb0
JA
1904 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1905
3863d1ad
JA
1906 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1907 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1908
c80b74b0
JA
1909 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1910 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
807f9971 1911
dcaeb606 1912 sprintf(tmp, "%u", o->iodepth);
c80b74b0
JA
1913 multitext_add_entry(&ge->eta.iodepth, tmp);
1914
1915 multitext_set_entry(&ge->eta.iotype, 0);
1916 multitext_set_entry(&ge->eta.ioengine, 0);
1917 multitext_set_entry(&ge->eta.iodepth, 0);
0050e5f2 1918
85dd01e7
JA
1919 gfio_set_state(ge, GE_STATE_JOB_SENT);
1920
0050e5f2 1921 gdk_threads_leave();
807f9971
JA
1922}
1923
ed727a46
JA
1924static void gfio_client_timed_out(struct fio_client *client)
1925{
e0681f3e 1926 struct gfio_client *gc = client->client_data;
ed727a46
JA
1927 char buf[256];
1928
1929 gdk_threads_enter();
1930
85dd01e7 1931 gfio_set_state(gc->ge, GE_STATE_NEW);
2f99deb0 1932 clear_ge_ui_info(gc->ge);
ed727a46
JA
1933
1934 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
16ce5adf 1935 show_info_dialog(gc->ge->ui, "Network timeout", buf);
ed727a46
JA
1936
1937 gdk_threads_leave();
1938}
1939
6b79c80c
JA
1940static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1941{
1942 struct gfio_client *gc = client->client_data;
1943
1944 gdk_threads_enter();
1945
85dd01e7 1946 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
6b79c80c
JA
1947
1948 if (gc->err_entry)
1949 entry_set_int_value(gc->err_entry, client->error);
1950
1951 gdk_threads_leave();
1952}
1953
85dd01e7
JA
1954static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1955{
1956 struct gfio_client *gc = client->client_data;
1957
1958 gdk_threads_enter();
1959 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1960 gdk_threads_leave();
1961}
1962
1963static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1964{
1965 struct gfio_client *gc = client->client_data;
1966
1967 gdk_threads_enter();
1968 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1969 gdk_threads_leave();
1970}
1971
1b42725f
JA
1972static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1973{
284b1e65 1974 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1b42725f
JA
1975 free(pdu);
1976}
1977
a1820207 1978struct client_ops gfio_client_ops = {
35c0ba7f 1979 .text = gfio_text_op,
0420ba6a
JA
1980 .disk_util = gfio_disk_util_op,
1981 .thread_status = gfio_thread_status_op,
1982 .group_stats = gfio_group_stats_op,
2f99deb0
JA
1983 .jobs_eta = gfio_update_client_eta,
1984 .eta = gfio_update_all_eta,
0420ba6a 1985 .probe = gfio_probe_op,
3ec62ec4 1986 .quit = gfio_quit_op,
807f9971 1987 .add_job = gfio_add_job_op,
ed727a46 1988 .timed_out = gfio_client_timed_out,
6b79c80c 1989 .stop = gfio_client_stop,
85dd01e7
JA
1990 .start = gfio_client_start,
1991 .job_start = gfio_client_job_start,
1b42725f 1992 .iolog = gfio_client_iolog,
6433ee05 1993 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
3ec62ec4 1994 .stay_connected = 1,
46bcd498 1995 .client_type = FIO_CLIENT_TYPE_GUI,
a1820207
SC
1996};
1997
0fd18982
JA
1998/*
1999 * FIXME: need more handling here
2000 */
2001static void ge_destroy(struct gui_entry *ge)
2002{
2003 struct gfio_client *gc = ge->client;
2004
2005 if (gc && gc->client) {
2006 if (ge->state >= GE_STATE_CONNECTED)
2007 fio_client_terminate(gc->client);
2008
2009 fio_put_client(gc->client);
2010 }
2011
2012 flist_del(&ge->list);
2013 free(ge);
2014}
2015
2016static void ge_widget_destroy(GtkWidget *w, gpointer data)
2017{
0fd18982
JA
2018}
2019
2020static void gfio_quit(struct gui *ui)
2021{
2022 struct gui_entry *ge;
2023
2024 while (!flist_empty(&ui->list)) {
2025 ge = flist_entry(ui->list.next, struct gui_entry, list);
2026 ge_destroy(ge);
2027 }
2028
2029 gtk_main_quit();
2030}
2031
ff1f3280
SC
2032static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2033 __attribute__((unused)) gpointer data)
2034{
0fd18982 2035 gfio_quit(data);
ff1f3280
SC
2036}
2037
25927259
SC
2038static void *job_thread(void *arg)
2039{
a9eccde4
JA
2040 struct gui *ui = arg;
2041
2042 ui->handler_running = 1;
25927259 2043 fio_handle_clients(&gfio_client_ops);
a9eccde4 2044 ui->handler_running = 0;
25927259
SC
2045 return NULL;
2046}
2047
2f99deb0 2048static int send_job_files(struct gui_entry *ge)
60f6b330 2049{
9988ca70 2050 struct gfio_client *gc = ge->client;
441013b4 2051 int i, ret = 0;
0420ba6a 2052
2f99deb0 2053 for (i = 0; i < ge->nr_job_files; i++) {
9988ca70 2054 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
c724926b
JA
2055 if (ret < 0) {
2056 GError *error;
2057
2058 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2059 report_error(error);
2060 g_error_free(error);
2061 break;
2062 } else if (ret)
441013b4
JA
2063 break;
2064
2f99deb0
JA
2065 free(ge->job_files[i]);
2066 ge->job_files[i] = NULL;
441013b4 2067 }
2f99deb0
JA
2068 while (i < ge->nr_job_files) {
2069 free(ge->job_files[i]);
2070 ge->job_files[i] = NULL;
441013b4 2071 i++;
0420ba6a
JA
2072 }
2073
2c77d831
JA
2074 free(ge->job_files);
2075 ge->job_files = NULL;
3af45201 2076 ge->nr_job_files = 0;
441013b4 2077 return ret;
60f6b330
SC
2078}
2079
63a130b7
JA
2080static void *server_thread(void *arg)
2081{
2082 is_backend = 1;
2083 gfio_server_running = 1;
2084 fio_start_server(NULL);
2085 gfio_server_running = 0;
2086 return NULL;
2087}
2088
2f99deb0 2089static void gfio_start_server(void)
63a130b7 2090{
2f99deb0
JA
2091 struct gui *ui = &main_ui;
2092
63a130b7
JA
2093 if (!gfio_server_running) {
2094 gfio_server_running = 1;
2095 pthread_create(&ui->server_t, NULL, server_thread, NULL);
e34f6ad7 2096 pthread_detach(ui->server_t);
63a130b7
JA
2097 }
2098}
2099
f3074008 2100static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 2101 gpointer data)
f3074008 2102{
2f99deb0
JA
2103 struct gui_entry *ge = data;
2104 struct gfio_client *gc = ge->client;
25927259 2105
78cb2fe5
JA
2106 if (gc)
2107 fio_start_client(gc->client);
f3074008
SC
2108}
2109
df06f220
JA
2110static void file_open(GtkWidget *w, gpointer data);
2111
2112static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 2113{
2f99deb0
JA
2114 struct gui_entry *ge = data;
2115 struct gfio_client *gc = ge->client;
3ec62ec4 2116
85dd01e7 2117 if (ge->state == GE_STATE_NEW) {
c724926b
JA
2118 int ret;
2119
2f99deb0 2120 if (!ge->nr_job_files)
cf4b0443 2121 file_open(widget, ge->ui);
2f99deb0
JA
2122 if (!ge->nr_job_files)
2123 return;
2124
d3b70f32
JA
2125 gc = ge->client;
2126
2f99deb0
JA
2127 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2128 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
c724926b
JA
2129 ret = fio_client_connect(gc->client);
2130 if (!ret) {
a9eccde4
JA
2131 if (!ge->ui->handler_running)
2132 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
85dd01e7 2133 gfio_set_state(ge, GE_STATE_CONNECTED);
c724926b
JA
2134 } else {
2135 GError *error;
2136
2137 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2138 report_error(error);
2139 g_error_free(error);
69406b92 2140 }
df06f220 2141 } else {
2f99deb0 2142 fio_client_terminate(gc->client);
85dd01e7 2143 gfio_set_state(ge, GE_STATE_NEW);
2f99deb0 2144 clear_ge_ui_info(ge);
df06f220 2145 }
3e47bd25
JA
2146}
2147
b9d2f30a
JA
2148static void send_clicked(GtkWidget *widget, gpointer data)
2149{
2f99deb0 2150 struct gui_entry *ge = data;
b9d2f30a 2151
2f99deb0 2152 if (send_job_files(ge)) {
c724926b
JA
2153 GError *error;
2154
2155 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);
2156 report_error(error);
2157 g_error_free(error);
2158
2f99deb0 2159 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a 2160 }
b9d2f30a
JA
2161}
2162
0420ba6a
JA
2163static void on_info_bar_response(GtkWidget *widget, gint response,
2164 gpointer data)
2165{
2f99deb0
JA
2166 struct gui *ui = &main_ui;
2167
0420ba6a
JA
2168 if (response == GTK_RESPONSE_OK) {
2169 gtk_widget_destroy(widget);
2f99deb0 2170 ui->error_info_bar = NULL;
0420ba6a
JA
2171 }
2172}
2173
df06f220 2174void report_error(GError *error)
0420ba6a 2175{
2f99deb0
JA
2176 struct gui *ui = &main_ui;
2177
2178 if (ui->error_info_bar == NULL) {
2179 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
0420ba6a
JA
2180 GTK_RESPONSE_OK,
2181 NULL);
2f99deb0
JA
2182 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2183 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
0420ba6a
JA
2184 GTK_MESSAGE_ERROR);
2185
2f99deb0
JA
2186 ui->error_label = gtk_label_new(error->message);
2187 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2188 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
0420ba6a 2189
2f99deb0
JA
2190 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2191 gtk_widget_show_all(ui->vbox);
0420ba6a
JA
2192 } else {
2193 char buffer[256];
2194 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2f99deb0 2195 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
0420ba6a
JA
2196 }
2197}
2198
62bc937f
JA
2199struct connection_widgets
2200{
2201 GtkWidget *hentry;
2202 GtkWidget *combo;
2203 GtkWidget *button;
2204};
2205
2206static void hostname_cb(GtkEntry *entry, gpointer data)
2207{
2208 struct connection_widgets *cw = data;
2209 int uses_net = 0, is_localhost = 0;
2210 const gchar *text;
2211 gchar *ctext;
2212
2213 /*
2214 * Check whether to display the 'auto start backend' box
2215 * or not. Show it if we are a localhost and using network,
2216 * or using a socket.
2217 */
2218 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2219 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2220 uses_net = 1;
2221 g_free(ctext);
2222
2223 if (uses_net) {
2224 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2225 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2226 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2227 !strcmp(text, "ip6-loopback"))
2228 is_localhost = 1;
2229 }
2230
2231 if (!uses_net || is_localhost) {
2232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2233 gtk_widget_set_sensitive(cw->button, 1);
2234 } else {
2235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2236 gtk_widget_set_sensitive(cw->button, 0);
2237 }
2238}
2239
b9f3c7ed
JA
2240static int get_connection_details(char **host, int *port, int *type,
2241 int *server_start)
a7a42ce1 2242{
62bc937f
JA
2243 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2244 struct connection_widgets cw;
a7a42ce1
JA
2245 char *typeentry;
2246
2247 dialog = gtk_dialog_new_with_buttons("Connection details",
2f99deb0 2248 GTK_WINDOW(main_ui.window),
a7a42ce1
JA
2249 GTK_DIALOG_DESTROY_WITH_PARENT,
2250 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2251 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2252
2253 frame = gtk_frame_new("Hostname / socket name");
f129909e
JA
2254 /* gtk_dialog_get_content_area() is 2.14 and newer */
2255 vbox = GTK_DIALOG(dialog)->vbox;
a7a42ce1
JA
2256 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2257
2258 box = gtk_vbox_new(FALSE, 6);
2259 gtk_container_add(GTK_CONTAINER(frame), box);
2260
2261 hbox = gtk_hbox_new(TRUE, 10);
2262 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
62bc937f
JA
2263 cw.hentry = gtk_entry_new();
2264 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2265 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
a7a42ce1
JA
2266
2267 frame = gtk_frame_new("Port");
2268 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2269 box = gtk_vbox_new(FALSE, 10);
2270 gtk_container_add(GTK_CONTAINER(frame), box);
2271
2272 hbox = gtk_hbox_new(TRUE, 4);
2273 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2274 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2275
2276 frame = gtk_frame_new("Type");
2277 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2278 box = gtk_vbox_new(FALSE, 10);
2279 gtk_container_add(GTK_CONTAINER(frame), box);
2280
2281 hbox = gtk_hbox_new(TRUE, 4);
2282 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2283
62bc937f
JA
2284 cw.combo = gtk_combo_box_new_text();
2285 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2286 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2287 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2288 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
a7a42ce1 2289
62bc937f 2290 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
a7a42ce1 2291
b9f3c7ed
JA
2292 frame = gtk_frame_new("Options");
2293 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2294 box = gtk_vbox_new(FALSE, 10);
2295 gtk_container_add(GTK_CONTAINER(frame), box);
2296
2297 hbox = gtk_hbox_new(TRUE, 4);
2298 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2299
62bc937f
JA
2300 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2301 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2302 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.");
2303 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2304
2305 /*
2306 * Connect edit signal, so we can show/not-show the auto start button
2307 */
2308 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2309 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
b9f3c7ed 2310
a7a42ce1
JA
2311 gtk_widget_show_all(dialog);
2312
2313 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2314 gtk_widget_destroy(dialog);
2315 return 1;
2316 }
2317
62bc937f 2318 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
a7a42ce1
JA
2319 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2320
62bc937f 2321 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
a7a42ce1
JA
2322 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2323 *type = Fio_client_ipv4;
2324 else if (!strncmp(typeentry, "IPv6", 4))
2325 *type = Fio_client_ipv6;
2326 else
2327 *type = Fio_client_socket;
2328 g_free(typeentry);
2329
62bc937f 2330 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
b9f3c7ed 2331
a7a42ce1
JA
2332 gtk_widget_destroy(dialog);
2333 return 0;
2334}
2335
2f99deb0 2336static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
e0681f3e
JA
2337{
2338 struct gfio_client *gc;
2339
2340 gc = malloc(sizeof(*gc));
2341 memset(gc, 0, sizeof(*gc));
2f99deb0 2342 gc->ge = ge;
343cb4a9 2343 gc->client = fio_get_client(client);
b9d2f30a 2344
2f99deb0 2345 ge->client = gc;
e0681f3e
JA
2346
2347 client->client_data = gc;
2348}
2349
2f99deb0
JA
2350static GtkWidget *new_client_page(struct gui_entry *ge);
2351
2352static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2353{
2354 struct gui_entry *ge;
2355
2356 ge = malloc(sizeof(*ge));
2357 memset(ge, 0, sizeof(*ge));
85dd01e7 2358 ge->state = GE_STATE_NEW;
2f99deb0
JA
2359 INIT_FLIST_HEAD(&ge->list);
2360 flist_add_tail(&ge->list, &ui->list);
2361 ge->ui = ui;
2362 return ge;
2363}
2364
2f99deb0
JA
2365static struct gui_entry *get_new_ge_with_tab(const char *name)
2366{
2367 struct gui_entry *ge;
2368
2369 ge = alloc_new_gui_entry(&main_ui);
2370
2371 ge->vbox = new_client_page(ge);
0fd18982 2372 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2f99deb0
JA
2373
2374 ge->page_label = gtk_label_new(name);
2375 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2376
2377 gtk_widget_show_all(main_ui.window);
2378 return ge;
2379}
2380
2381static void file_new(GtkWidget *w, gpointer data)
2382{
16ce5adf
JA
2383 struct gui *ui = (struct gui *) data;
2384 struct gui_entry *ge;
2385
2386 ge = get_new_ge_with_tab("Untitled");
2387 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2f99deb0
JA
2388}
2389
2390/*
2391 * Return the 'ge' corresponding to the tab. If the active tab is the
2392 * main tab, open a new tab.
2393 */
38634cb1 2394static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2f99deb0
JA
2395{
2396 struct flist_head *entry;
2397 struct gui_entry *ge;
2398
38634cb1
JA
2399 if (!cur_page) {
2400 if (created)
2401 *created = 1;
2f99deb0 2402 return get_new_ge_with_tab("Untitled");
38634cb1
JA
2403 }
2404
2405 if (created)
2406 *created = 0;
2f99deb0
JA
2407
2408 flist_for_each(entry, &main_ui.list) {
2409 ge = flist_entry(entry, struct gui_entry, list);
2410 if (ge->page_num == cur_page)
2411 return ge;
2412 }
2413
2414 return NULL;
2415}
2416
85dd01e7 2417static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
16ce5adf 2418{
16ce5adf
JA
2419 gint cur_page;
2420
2421 /*
85dd01e7
JA
2422 * Main tab is tab 0, so any current page other than 0 holds
2423 * a ge entry.
16ce5adf
JA
2424 */
2425 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
85dd01e7 2426 if (cur_page)
38634cb1 2427 return get_ge_from_page(cur_page, NULL);
85dd01e7
JA
2428
2429 return NULL;
2430}
2431
2432static void file_close(GtkWidget *w, gpointer data)
2433{
2434 struct gui *ui = (struct gui *) data;
2435 struct gui_entry *ge;
16ce5adf 2436
85dd01e7
JA
2437 /*
2438 * Can't close the main tab
2439 */
2440 ge = get_ge_from_cur_tab(ui);
2441 if (ge) {
16ce5adf
JA
2442 gtk_widget_destroy(ge->vbox);
2443 return;
2444 }
2445
f5c6726e
JA
2446 if (!flist_empty(&ui->list)) {
2447 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2448 return;
2449 }
2450
0fd18982 2451 gfio_quit(ui);
16ce5adf
JA
2452}
2453
38634cb1
JA
2454static void file_add_recent(struct gui *ui, const gchar *uri)
2455{
a217ba7d
JA
2456 GtkRecentData grd;
2457
2458 memset(&grd, 0, sizeof(grd));
2459 grd.display_name = strdup("gfio");
2460 grd.description = strdup("Fio job file");
2461 grd.mime_type = strdup(GFIO_MIME);
2462 grd.app_name = strdup(g_get_application_name());
2463 grd.app_exec = strdup("gfio %f/%u");
2464
2465 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
38634cb1
JA
2466}
2467
2468static gchar *get_filename_from_uri(const gchar *uri)
2469{
2470 if (strncmp(uri, "file://", 7))
2471 return strdup(uri);
2472
2473 return strdup(uri + 7);
2474}
2475
2476static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2477 int type, int port)
2478{
2479 struct fio_client *client;
2480 gchar *filename;
2481
2482 filename = get_filename_from_uri(uri);
2483
2484 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2485 ge->job_files[ge->nr_job_files] = strdup(filename);
2486 ge->nr_job_files++;
2487
2488 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2489 if (!client) {
2490 GError *error;
2491
2492 error = g_error_new(g_quark_from_string("fio"), 1,
2493 "Failed to add client %s", host);
2494 report_error(error);
2495 g_error_free(error);
2496 return 1;
2497 }
2498
2499 gfio_client_added(ge, client);
2500 file_add_recent(ge->ui, uri);
2501 return 0;
2502}
2503
a6790906 2504static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
0420ba6a 2505{
a6790906 2506 int port, type, server_start;
2f99deb0
JA
2507 struct gui_entry *ge;
2508 gint cur_page;
38634cb1 2509 char *host;
a6790906 2510 int ret, ge_is_new = 0;
2f99deb0
JA
2511
2512 /*
2513 * Creates new tab if current tab is the main window, or the
2514 * current tab already has a client.
2515 */
2516 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
38634cb1
JA
2517 ge = get_ge_from_page(cur_page, &ge_is_new);
2518 if (ge->client) {
2f99deb0 2519 ge = get_new_ge_with_tab("Untitled");
38634cb1
JA
2520 ge_is_new = 1;
2521 }
2f99deb0
JA
2522
2523 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
0420ba6a 2524
a6790906
JA
2525 if (get_connection_details(&host, &port, &type, &server_start)) {
2526 if (ge_is_new)
2527 gtk_widget_destroy(ge->vbox);
2528
2529 return 1;
2530 }
2531
2532 ret = do_file_open(ge, uri, host, type, port);
2533
2534 free(host);
2535
2536 if (!ret) {
2537 if (server_start)
2538 gfio_start_server();
2539 } else {
2540 if (ge_is_new)
2541 gtk_widget_destroy(ge->vbox);
2542 }
2543
2544 return ret;
2545}
2546
2547static void recent_open(GtkAction *action, gpointer data)
2548{
2549 struct gui *ui = (struct gui *) data;
2550 GtkRecentInfo *info;
2551 const gchar *uri;
2552
2553 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2554 uri = gtk_recent_info_get_uri(info);
2555
2556 do_file_open_with_tab(ui, uri);
2557}
2558
2559static void file_open(GtkWidget *w, gpointer data)
2560{
2561 struct gui *ui = data;
2562 GtkWidget *dialog;
2563 GSList *filenames, *fn_glist;
2564 GtkFileFilter *filter;
2565
0420ba6a 2566 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 2567 GTK_WINDOW(ui->window),
0420ba6a
JA
2568 GTK_FILE_CHOOSER_ACTION_OPEN,
2569 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2570 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2571 NULL);
2572 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2573
2574 filter = gtk_file_filter_new();
2575 gtk_file_filter_add_pattern(filter, "*.fio");
2576 gtk_file_filter_add_pattern(filter, "*.job");
2d262990 2577 gtk_file_filter_add_pattern(filter, "*.ini");
38634cb1 2578 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
0420ba6a
JA
2579 gtk_file_filter_set_name(filter, "Fio job file");
2580 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2581
2582 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2583 gtk_widget_destroy(dialog);
2584 return;
2585 }
2586
2587 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
2588
2589 gtk_widget_destroy(dialog);
2590
0420ba6a
JA
2591 filenames = fn_glist;
2592 while (filenames != NULL) {
a6790906
JA
2593 if (do_file_open_with_tab(ui, filenames->data))
2594 break;
0420ba6a
JA
2595 filenames = g_slist_next(filenames);
2596 }
63a130b7 2597
0420ba6a 2598 g_slist_free(fn_glist);
0420ba6a
JA
2599}
2600
2601static void file_save(GtkWidget *w, gpointer data)
2602{
63a130b7 2603 struct gui *ui = data;
0420ba6a
JA
2604 GtkWidget *dialog;
2605
2606 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 2607 GTK_WINDOW(ui->window),
0420ba6a
JA
2608 GTK_FILE_CHOOSER_ACTION_SAVE,
2609 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2610 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2611 NULL);
2612
2613 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2614 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2615
2616 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2617 char *filename;
2618
2619 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2620 // save_job_file(filename);
2621 g_free(filename);
2622 }
2623 gtk_widget_destroy(dialog);
2624}
2625
9b260bdf
JA
2626static void view_log_destroy(GtkWidget *w, gpointer data)
2627{
2628 struct gui *ui = (struct gui *) data;
2629
2630 gtk_widget_ref(ui->log_tree);
2631 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2632 gtk_widget_destroy(w);
4cbe7211 2633 ui->log_view = NULL;
9b260bdf
JA
2634}
2635
2636static void view_log(GtkWidget *w, gpointer data)
2637{
4cbe7211
JA
2638 GtkWidget *win, *scroll, *vbox, *box;
2639 struct gui *ui = (struct gui *) data;
9b260bdf 2640
4cbe7211
JA
2641 if (ui->log_view)
2642 return;
2643
2644 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 2645 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 2646 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 2647
4cbe7211
JA
2648 scroll = gtk_scrolled_window_new(NULL, NULL);
2649
2650 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2651
2652 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2653
2654 box = gtk_hbox_new(TRUE, 0);
2655 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2656 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2657 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2658
2659 vbox = gtk_vbox_new(TRUE, 5);
2660 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 2661
4cbe7211 2662 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
2663 gtk_widget_show_all(win);
2664}
2665
85dd01e7
JA
2666static void connect_job_entry(GtkWidget *w, gpointer data)
2667{
2668 struct gui *ui = (struct gui *) data;
2669 struct gui_entry *ge;
2670
2671 ge = get_ge_from_cur_tab(ui);
2672 if (ge)
2673 connect_clicked(w, ge);
2674}
2675
2676static void send_job_entry(GtkWidget *w, gpointer data)
2677{
2678 struct gui *ui = (struct gui *) data;
2679 struct gui_entry *ge;
2680
2681 ge = get_ge_from_cur_tab(ui);
2682 if (ge)
2683 send_clicked(w, ge);
2684
2685}
2686
2687static void edit_job_entry(GtkWidget *w, gpointer data)
16ce5adf
JA
2688{
2689}
2690
85dd01e7
JA
2691static void start_job_entry(GtkWidget *w, gpointer data)
2692{
2693 struct gui *ui = (struct gui *) data;
2694 struct gui_entry *ge;
2695
2696 ge = get_ge_from_cur_tab(ui);
2697 if (ge)
2698 start_job_clicked(w, ge);
2699}
2700
781ccba6
JA
2701static void view_results(GtkWidget *w, gpointer data)
2702{
2703 struct gui *ui = (struct gui *) data;
2704 struct gfio_client *gc;
2705 struct gui_entry *ge;
2706
2707 ge = get_ge_from_cur_tab(ui);
2708 if (!ge)
2709 return;
2710
2711 if (ge->results_window)
2712 return;
2713
2714 gc = ge->client;
2715 if (gc && gc->nr_results)
2716 gfio_display_end_results(gc);
2717}
2718
8577f4fd
JA
2719static void __update_graph_limits(struct gfio_graphs *g)
2720{
2721 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2722 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2723}
2724
2725static void update_graph_limits(void)
2726{
2727 struct flist_head *entry;
2728 struct gui_entry *ge;
2729
2730 __update_graph_limits(&main_ui.graphs);
2731
2732 flist_for_each(entry, &main_ui.list) {
2733 ge = flist_entry(entry, struct gui_entry, list);
2734 __update_graph_limits(&ge->graphs);
2735 }
2736}
2737
46974a7d
JA
2738static void preferences(GtkWidget *w, gpointer data)
2739{
f3e8440f 2740 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 2741 GtkWidget *hbox, *spin, *entry, *spin_int;
46974a7d
JA
2742 int i;
2743
2744 dialog = gtk_dialog_new_with_buttons("Preferences",
2f99deb0 2745 GTK_WINDOW(main_ui.window),
46974a7d
JA
2746 GTK_DIALOG_DESTROY_WITH_PARENT,
2747 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2748 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2749 NULL);
2750
8577f4fd 2751 frame = gtk_frame_new("Graphing");
f3e8440f
JA
2752 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2753 vbox = gtk_vbox_new(FALSE, 6);
2754 gtk_container_add(GTK_CONTAINER(frame), vbox);
2755
1cf6bca5
JA
2756 hbox = gtk_hbox_new(FALSE, 5);
2757 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2758 entry = gtk_label_new("Font face to use for graph labels");
2759 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2760
f3e8440f 2761 font = gtk_font_button_new();
1cf6bca5 2762 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 2763
8577f4fd
JA
2764 box = gtk_vbox_new(FALSE, 6);
2765 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2766
2767 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 2768 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
2769 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2770 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2771
c05d9056 2772 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
8577f4fd 2773
1cf6bca5
JA
2774 box = gtk_vbox_new(FALSE, 6);
2775 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2776
2777 hbox = gtk_hbox_new(FALSE, 5);
2778 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2779 entry = gtk_label_new("Client ETA request interval (msec)");
2780 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2781
2782 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4
JA
2783 frame = gtk_frame_new("Debug logging");
2784 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2785 vbox = gtk_vbox_new(FALSE, 6);
2786 gtk_container_add(GTK_CONTAINER(frame), vbox);
2787
2788 box = gtk_hbox_new(FALSE, 6);
2789 gtk_container_add(GTK_CONTAINER(vbox), box);
2790
2791 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2792
2793 for (i = 0; i < FD_DEBUG_MAX; i++) {
2794 if (i == 7) {
2795 box = gtk_hbox_new(FALSE, 6);
2796 gtk_container_add(GTK_CONTAINER(vbox), box);
2797 }
2798
2799
2800 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2801 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2802 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2803 }
2804
46974a7d
JA
2805 gtk_widget_show_all(dialog);
2806
2807 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2808 gtk_widget_destroy(dialog);
2809 return;
2810 }
2811
2812 for (i = 0; i < FD_DEBUG_MAX; i++) {
2813 int set;
2814
2815 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2816 if (set)
2817 fio_debug |= (1UL << i);
2818 }
2819
f3e8440f 2820 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
2821 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2822 update_graph_limits();
1cf6bca5 2823 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 2824
46974a7d
JA
2825 gtk_widget_destroy(dialog);
2826}
2827
0420ba6a
JA
2828static void about_dialog(GtkWidget *w, gpointer data)
2829{
81e4ea6e
JA
2830 const char *authors[] = {
2831 "Jens Axboe <axboe@kernel.dk>",
2832 "Stephen Carmeron <stephenmcameron@gmail.com>",
2833 NULL
2834 };
84a72ed3
JA
2835 const char *license[] = {
2836 "Fio is free software; you can redistribute it and/or modify "
2837 "it under the terms of the GNU General Public License as published by "
2838 "the Free Software Foundation; either version 2 of the License, or "
2839 "(at your option) any later version.\n",
2840 "Fio is distributed in the hope that it will be useful, "
2841 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2842 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2843 "GNU General Public License for more details.\n",
2844 "You should have received a copy of the GNU General Public License "
2845 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2846 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2847 };
2848 char *license_trans;
2849
2850 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2851 license[2], "\n", NULL);
81e4ea6e 2852
0420ba6a
JA
2853 gtk_show_about_dialog(NULL,
2854 "program-name", "gfio",
2855 "comments", "Gtk2 UI for fio",
84a72ed3 2856 "license", license_trans,
81e4ea6e
JA
2857 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2858 "authors", authors,
0420ba6a 2859 "version", fio_version_string,
81e4ea6e 2860 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
2861 "logo-icon-name", "fio",
2862 /* Must be last: */
81e4ea6e 2863 "wrap-license", TRUE,
0420ba6a 2864 NULL);
84a72ed3 2865
2f99deb0 2866 g_free(license_trans);
0420ba6a
JA
2867}
2868
2869static GtkActionEntry menu_items[] = {
46974a7d 2870 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 2871 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
16ce5adf 2872 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
46974a7d 2873 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 2874 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
16ce5adf 2875 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
46974a7d
JA
2876 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2877 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2878 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 2879 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
781ccba6 2880 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
85dd01e7
JA
2881 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2882 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2883 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2884 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
46974a7d
JA
2885 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2886 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 2887};
3e47bd25 2888static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
2889
2890static const gchar *ui_string = " \
2891 <ui> \
2892 <menubar name=\"MainMenu\"> \
2893 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0 2894 <menuitem name=\"New\" action=\"NewFile\" /> \
bf64138b 2895 <menuitem name=\"Open\" action=\"OpenFile\" /> \
16ce5adf 2896 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2f99deb0 2897 <separator name=\"Separator1\"/> \
0420ba6a 2898 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 2899 <separator name=\"Separator2\"/> \
2f99deb0
JA
2900 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2901 <separator name=\"Separator3\"/> \
261f21d0
JA
2902 <placeholder name=\"FileRecentFiles\"/> \
2903 <separator name=\"Separator4\"/> \
0420ba6a
JA
2904 <menuitem name=\"Quit\" action=\"Quit\" /> \
2905 </menu> \
16ce5adf 2906 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
85dd01e7 2907 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
261f21d0 2908 <separator name=\"Separator5\"/> \
85dd01e7
JA
2909 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2910 <menuitem name=\"Send job\" action=\"SendJob\" /> \
261f21d0 2911 <separator name=\"Separator6\"/> \
85dd01e7 2912 <menuitem name=\"Start job\" action=\"StartJob\" /> \
16ce5adf 2913 </menu>\
9b260bdf 2914 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
781ccba6
JA
2915 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2916 <separator name=\"Separator7\"/> \
9b260bdf
JA
2917 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2918 </menu>\
0420ba6a
JA
2919 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2920 <menuitem name=\"About\" action=\"About\" /> \
2921 </menu> \
2922 </menubar> \
2923 </ui> \
2924";
2925
4cbe7211
JA
2926static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2927 struct gui *ui)
0420ba6a 2928{
ca664f49 2929 GtkActionGroup *action_group;
0420ba6a
JA
2930 GError *error = 0;
2931
2932 action_group = gtk_action_group_new("Menu");
4cbe7211 2933 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
2934
2935 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2936 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2937
2938 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
02421e69 2939
0420ba6a
JA
2940 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2941}
2942
2943void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2944 GtkWidget *vbox, GtkUIManager *ui_manager)
2945{
2946 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2947}
2948
c80b74b0
JA
2949static void combo_entry_changed(GtkComboBox *box, gpointer data)
2950{
2951 struct gui_entry *ge = (struct gui_entry *) data;
2952 gint index;
2953
2954 index = gtk_combo_box_get_active(box);
2955
2956 multitext_set_entry(&ge->eta.iotype, index);
2957 multitext_set_entry(&ge->eta.ioengine, index);
2958 multitext_set_entry(&ge->eta.iodepth, index);
2959}
2960
2961static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2962{
2963 struct gui_entry *ge = (struct gui_entry *) data;
2964
2965 multitext_free(&ge->eta.iotype);
2966 multitext_free(&ge->eta.ioengine);
2967 multitext_free(&ge->eta.iodepth);
2968}
2969
2f99deb0 2970static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 2971{
2f99deb0 2972 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 2973 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
0420ba6a 2974
2f99deb0 2975 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 2976
65476336
JA
2977 top_align = gtk_alignment_new(0, 0, 1, 0);
2978 top_vbox = gtk_vbox_new(FALSE, 3);
2979 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2980 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2f99deb0
JA
2981
2982 probe = gtk_frame_new("Job");
2983 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2984 probe_frame = gtk_vbox_new(FALSE, 3);
2985 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2986
2987 probe_box = gtk_hbox_new(FALSE, 3);
2988 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2989 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2990 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2991 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2992 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2993
2994 probe_box = gtk_hbox_new(FALSE, 3);
2995 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2996
3863d1ad 2997 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
c80b74b0
JA
2998 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2999 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
3000 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
3001 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
3002 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2f99deb0
JA
3003 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
3004 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3005
3006 probe_box = gtk_hbox_new(FALSE, 3);
3007 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3008 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3009 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3010 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3011 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3012
3013 /*
3014 * Only add this if we have a commit rate
2839f0c6 3015 */
2f99deb0
JA
3016#if 0
3017 probe_box = gtk_hbox_new(FALSE, 3);
3018 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 3019
2f99deb0
JA
3020 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3021 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 3022
2f99deb0
JA
3023 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3024 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3025#endif
ff1f3280 3026
2f99deb0
JA
3027 /*
3028 * Set up a drawing area and IOPS and bandwidth graphs
3029 */
2f99deb0 3030 ge->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 3031 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
57f9d28e 3032 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
3033 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3034 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3035 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3036 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3037 G_CALLBACK(on_config_drawing_area), &ge->graphs);
65476336
JA
3038 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3039 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3040 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 3041 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3042 ge->graphs.drawing_area);
65476336 3043 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
04cc6b77 3044
2f99deb0
JA
3045 setup_graphs(&ge->graphs);
3046
3047 /*
3048 * Set up alignments for widgets at the bottom of ui,
3049 * align bottom left, expand horizontally but not vertically
3050 */
65476336 3051 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2f99deb0 3052 ge->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
3053 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3054 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2f99deb0
JA
3055
3056 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
0420ba6a 3057
c36f98d9 3058 /*
2f99deb0
JA
3059 * Set up thread status progress bar
3060 */
3061 ge->thread_status_pb = gtk_progress_bar_new();
3062 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3063 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3064 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3065
3066
3067 return main_vbox;
3068}
3069
3070static GtkWidget *new_main_page(struct gui *ui)
3071{
3072 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 3073 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2f99deb0
JA
3074
3075 main_vbox = gtk_vbox_new(FALSE, 3);
3076
3077 /*
3078 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
3079 * align top left, expand horizontally but not vertically
3080 */
65476336
JA
3081 top_align = gtk_alignment_new(0, 0, 1, 0);
3082 top_vbox = gtk_vbox_new(FALSE, 0);
3083 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3084 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
c36f98d9 3085
2f99deb0
JA
3086 probe = gtk_frame_new("Run statistics");
3087 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
3088 probe_frame = gtk_vbox_new(FALSE, 3);
3089 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3090
3091 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 3092 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 3093 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
ca850992
JA
3094 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3095 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3096 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3097 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 3098
807f9971
JA
3099 /*
3100 * Only add this if we have a commit rate
3101 */
3102#if 0
3e47bd25
JA
3103 probe_box = gtk_hbox_new(FALSE, 3);
3104 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
3105
3106 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3107 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3108
3e47bd25
JA
3109 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3110 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 3111#endif
3e47bd25 3112
736f2dff 3113 /*
2fd3bb0e 3114 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 3115 */
2f99deb0 3116 ui->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 3117 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
57f9d28e 3118 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2f99deb0
JA
3119 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3120 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3121 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3122 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3123 G_CALLBACK(on_config_drawing_area), &ui->graphs);
65476336
JA
3124 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3125 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
736f2dff 3126 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 3127 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 3128 ui->graphs.drawing_area);
65476336 3129 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
e164534f 3130 TRUE, TRUE, 0);
736f2dff 3131
2f99deb0 3132 setup_graphs(&ui->graphs);
2fd3bb0e 3133
c36f98d9
SC
3134 /*
3135 * Set up alignments for widgets at the bottom of ui,
3136 * align bottom left, expand horizontally but not vertically
3137 */
65476336 3138 bottom_align = gtk_alignment_new(0, 1, 1, 0);
c36f98d9 3139 ui->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
3140 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3141 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
c36f98d9 3142
3ec62ec4
JA
3143 /*
3144 * Set up thread status progress bar
3145 */
3146 ui->thread_status_pb = gtk_progress_bar_new();
3147 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 3148 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
3149 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3150
2f99deb0
JA
3151 return main_vbox;
3152}
3153
3154static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3155 guint page, gpointer data)
3156
3157{
02421e69 3158 struct gui *ui = (struct gui *) data;
85dd01e7
JA
3159 struct gui_entry *ge;
3160
3161 if (!page) {
3162 set_job_menu_visible(ui, 0);
781ccba6 3163 set_view_results_visible(ui, 0);
85dd01e7
JA
3164 return TRUE;
3165 }
3166
3167 set_job_menu_visible(ui, 1);
38634cb1 3168 ge = get_ge_from_page(page, NULL);
85dd01e7
JA
3169 if (ge)
3170 update_button_states(ui, ge);
02421e69 3171
2f99deb0
JA
3172 return TRUE;
3173}
3174
38634cb1
JA
3175static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3176{
3177 time_t time_a = gtk_recent_info_get_visited(a);
3178 time_t time_b = gtk_recent_info_get_visited(b);
3179
3180 return time_b - time_a;
3181}
3182
3183static void add_recent_file_items(struct gui *ui)
3184{
3185 const gchar *gfio = g_get_application_name();
3186 GList *items, *item;
3187 int i = 0;
3188
3189 if (ui->recent_ui_id) {
3190 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3191 gtk_ui_manager_ensure_update(ui->uimanager);
3192 }
3193 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3194
3195 if (ui->actiongroup) {
3196 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3197 g_object_unref(ui->actiongroup);
3198 }
3199 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3200
3201 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3202
3203 items = gtk_recent_manager_get_items(ui->recentmanager);
3204 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3205
3206 for (item = items; item && item->data; item = g_list_next(item)) {
3207 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3208 gchar *action_name;
3209 const gchar *label;
3210 GtkAction *action;
3211
3212 if (!gtk_recent_info_has_application(info, gfio))
3213 continue;
3214
3215 /*
3216 * We only support local files for now
3217 */
3218 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3219 continue;
3220
3221 action_name = g_strdup_printf("RecentFile%u", i++);
3222 label = gtk_recent_info_get_display_name(info);
3223
3224 action = g_object_new(GTK_TYPE_ACTION,
3225 "name", action_name,
3226 "label", label, NULL);
3227
3228 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3229 gtk_recent_info_ref(info),
3230 (GDestroyNotify) gtk_recent_info_unref);
3231
3232
3233 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3234
3235 gtk_action_group_add_action(ui->actiongroup, action);
3236 g_object_unref(action);
3237
3238 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3239 "/MainMenu/FileMenu/FileRecentFiles",
3240 label, action_name,
3241 GTK_UI_MANAGER_MENUITEM, FALSE);
3242
3243 g_free(action_name);
3244
3245 if (i == 8)
3246 break;
3247 }
3248
3249 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3250 g_list_free(items);
3251}
3252
a6790906
JA
3253static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3254 gint x, gint y, GtkSelectionData *data,
3255 guint info, guint time)
3256{
3257 struct gui *ui = &main_ui;
3258 gchar **uris;
3259 GtkWidget *source;
3260 int i;
3261
3262 source = gtk_drag_get_source_widget(ctx);
3263 if (source && widget == gtk_widget_get_toplevel(source)) {
3264 gtk_drag_finish(ctx, FALSE, FALSE, time);
3265 return;
3266 }
3267
3268 uris = gtk_selection_data_get_uris(data);
3269 if (!uris) {
3270 gtk_drag_finish(ctx, FALSE, FALSE, time);
3271 return;
3272 }
3273
3274 i = 0;
3275 while (uris[i]) {
3276 if (do_file_open_with_tab(ui, uris[i]))
3277 break;
3278 i++;
3279 }
3280
3281 gtk_drag_finish(ctx, TRUE, FALSE, time);
3282 g_strfreev(uris);
3283}
3284
2f99deb0
JA
3285static void init_ui(int *argc, char **argv[], struct gui *ui)
3286{
3287 GtkSettings *settings;
02421e69 3288 GtkWidget *vbox;
2f99deb0
JA
3289
3290 /* Magical g*thread incantation, you just need this thread stuff.
3291 * Without it, the update that happens in gfio_update_thread_status
3292 * doesn't really happen in a timely fashion, you need expose events
3293 */
3294 if (!g_thread_supported())
3295 g_thread_init(NULL);
3296 gdk_threads_init();
3297
3298 gtk_init(argc, argv);
3299 settings = gtk_settings_get_default();
3300 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3301 g_type_init();
814479d5 3302 gdk_color_parse("white", &white);
2f99deb0
JA
3303
3304 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
814479d5 3305 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2f99deb0
JA
3306 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3307
3308 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3309 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3310
3311 ui->vbox = gtk_vbox_new(FALSE, 0);
3312 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3313
02421e69
JA
3314 ui->uimanager = gtk_ui_manager_new();
3315 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3316 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2f99deb0 3317
38634cb1
JA
3318 ui->recentmanager = gtk_recent_manager_get_default();
3319 add_recent_file_items(ui);
3320
2f99deb0
JA
3321 ui->notebook = gtk_notebook_new();
3322 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 3323 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 3324 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
3325 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3326
3327 vbox = new_main_page(ui);
a6790906
JA
3328 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3329 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3330 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
2f99deb0
JA
3331
3332 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3333
9b260bdf 3334 gfio_ui_setup_log(ui);
3ec62ec4 3335
ff1f3280
SC
3336 gtk_widget_show_all(ui->window);
3337}
3338
8232e285 3339int main(int argc, char *argv[], char *envp[])
ff1f3280 3340{
8232e285
SC
3341 if (initialize_fio(envp))
3342 return 1;
0420ba6a
JA
3343 if (fio_init_options())
3344 return 1;
a1820207 3345
2f99deb0
JA
3346 memset(&main_ui, 0, sizeof(main_ui));
3347 INIT_FLIST_HEAD(&main_ui.list);
3348
3349 init_ui(&argc, &argv, &main_ui);
5b7573ab 3350
2839f0c6 3351 gdk_threads_enter();
ff1f3280 3352 gtk_main();
2839f0c6 3353 gdk_threads_leave();
ff1f3280
SC
3354 return 0;
3355}