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