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