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