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