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