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