arm64: ensure CPU clock retrieval issues isb()
[fio.git] / gfio.c
CommitLineData
ff1f3280
SC
1/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
3c3ed070 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
fa07eaa6 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
ff1f3280
SC
22 *
23 */
8232e285 24#include <locale.h>
cfe1ed39 25#include <stdlib.h>
6b79c80c 26#include <string.h>
cfe1ed39 27#include <libgen.h>
8232e285 28
5b7573ab 29#include <glib.h>
2fd3bb0e 30#include <cairo.h>
ff1f3280
SC
31#include <gtk/gtk.h>
32
8232e285 33#include "fio.h"
53e0e85d
JA
34#include "gfio.h"
35#include "ghelpers.h"
9af4a244 36#include "goptions.h"
41666588 37#include "gerror.h"
1252d8f2 38#include "gclient.h"
2fd3bb0e 39#include "graph.h"
8232e285 40
63a4b9cc
JA
41struct gui main_ui;
42
f1867a7f 43static bool gfio_server_running;
8577f4fd 44static unsigned int gfio_graph_limit = 100;
63a130b7 45
1252d8f2 46GdkColor gfio_color_white;
2a95f716 47GdkColor gfio_color_lightyellow;
a1e7972d 48const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
3e47bd25 49
f3074008
SC
50typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
51
3e47bd25 52static void connect_clicked(GtkWidget *widget, gpointer data);
f3074008 53static void start_job_clicked(GtkWidget *widget, gpointer data);
b9d2f30a 54static void send_clicked(GtkWidget *widget, gpointer data);
f3074008
SC
55
56static struct button_spec {
57 const char *buttontext;
58 clickfunction f;
014f4024
JA
59 const char *tooltiptext[2];
60 const int start_sensitive;
f3074008 61} buttonspeclist[] = {
53e0e85d
JA
62 {
63 .buttontext = "Connect",
64 .f = connect_clicked,
65 .tooltiptext = { "Disconnect from host", "Connect to host" },
66 .start_sensitive = 1,
67 },
68 {
69 .buttontext = "Send",
70 .f = send_clicked,
71 .tooltiptext = { "Send job description to host", NULL },
72 .start_sensitive = 0,
73 },
74 {
75 .buttontext = "Start Job",
76 .f = start_job_clicked,
77 .tooltiptext = { "Start the current job on the server", NULL },
78 .start_sensitive = 0,
79 },
e0681f3e
JA
80};
81
8dfd6071 82static void setup_iops_graph(struct gfio_graphs *gg)
2f99deb0
JA
83{
84 struct graph *g;
85
86 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
d8fbeefb 87 graph_title(g, "IOPS (IOs/sec)");
2f99deb0 88 graph_x_title(g, "Time (secs)");
8dfd6071
JA
89 gg->read_iops = graph_add_label(g, "Read IOPS");
90 gg->write_iops = graph_add_label(g, "Write IOPS");
5442cfb0 91 gg->trim_iops = graph_add_label(g, "Trim IOPS");
dbc542d4
JA
92 graph_set_color(g, gg->read_iops, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
93 graph_set_color(g, gg->write_iops, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
b78ca651 94 graph_set_color(g, gg->trim_iops, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
8577f4fd 95 line_graph_set_data_count_limit(g, gfio_graph_limit);
d8fbeefb 96 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
01a947f0 97 graph_set_graph_all_zeroes(g, 0);
75450781 98 gg->iops_graph = g;
2f99deb0
JA
99}
100
8dfd6071 101static void setup_bandwidth_graph(struct gfio_graphs *gg)
2f99deb0
JA
102{
103 struct graph *g;
104
105 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
d8fbeefb 106 graph_title(g, "Bandwidth (bytes/sec)");
2f99deb0 107 graph_x_title(g, "Time (secs)");
8dfd6071
JA
108 gg->read_bw = graph_add_label(g, "Read Bandwidth");
109 gg->write_bw = graph_add_label(g, "Write Bandwidth");
5442cfb0 110 gg->trim_bw = graph_add_label(g, "Trim Bandwidth");
dbc542d4
JA
111 graph_set_color(g, gg->read_bw, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
112 graph_set_color(g, gg->write_bw, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
b78ca651 113 graph_set_color(g, gg->trim_bw, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
d8fbeefb 114 graph_set_base_offset(g, 1);
2f99deb0 115 line_graph_set_data_count_limit(g, 100);
d8fbeefb 116 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
01a947f0 117 graph_set_graph_all_zeroes(g, 0);
75450781 118 gg->bandwidth_graph = g;
2f99deb0
JA
119}
120
121static void setup_graphs(struct gfio_graphs *g)
122{
8dfd6071
JA
123 setup_iops_graph(g);
124 setup_bandwidth_graph(g);
2f99deb0
JA
125}
126
1252d8f2 127void clear_ge_ui_info(struct gui_entry *ge)
2f99deb0
JA
128{
129 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
130 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
131 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
132 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
3863d1ad
JA
133#if 0
134 /* should we empty it... */
2f99deb0 135 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
3863d1ad 136#endif
c80b74b0 137 multitext_update_entry(&ge->eta.iotype, 0, "");
99d633af 138 multitext_update_entry(&ge->eta.bs, 0, "");
c80b74b0
JA
139 multitext_update_entry(&ge->eta.ioengine, 0, "");
140 multitext_update_entry(&ge->eta.iodepth, 0, "");
2f99deb0
JA
141 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
142 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
143 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
144 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
145 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
146 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
8663ea65
JA
147}
148
781ccba6
JA
149static void set_menu_entry_text(struct gui *ui, const char *path,
150 const char *text)
151{
152 GtkWidget *w;
153
154 w = gtk_ui_manager_get_widget(ui->uimanager, path);
155 if (w)
156 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
157 else
158 fprintf(stderr, "gfio: can't find path %s\n", path);
159}
160
161
162static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
163{
164 GtkWidget *w;
165
166 w = gtk_ui_manager_get_widget(ui->uimanager, path);
167 if (w)
168 gtk_widget_set_sensitive(w, show);
169 else
170 fprintf(stderr, "gfio: can't find path %s\n", path);
171}
172
173static void set_job_menu_visible(struct gui *ui, int visible)
174{
175 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
176}
177
178static void set_view_results_visible(struct gui *ui, int visible)
179{
180 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
181}
182
014f4024
JA
183static const char *get_button_tooltip(struct button_spec *s, int sensitive)
184{
185 if (s->tooltiptext[sensitive])
186 return s->tooltiptext[sensitive];
187
188 return s->tooltiptext[0];
189}
190
191static GtkWidget *add_button(GtkWidget *buttonbox,
192 struct button_spec *buttonspec, gpointer data)
193{
194 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
195 gboolean sens = buttonspec->start_sensitive;
196
197 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
198 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
199
200 sens = buttonspec->start_sensitive;
201 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
202 gtk_widget_set_sensitive(button, sens);
203
204 return button;
205}
206
207static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
208 int nbuttons)
209{
210 int i;
211
212 for (i = 0; i < nbuttons; i++)
213 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
214}
215
85dd01e7
JA
216/*
217 * Update sensitivity of job buttons and job menu items, based on the
218 * state of the client.
219 */
220static void update_button_states(struct gui *ui, struct gui_entry *ge)
221{
222 unsigned int connect_state, send_state, start_state, edit_state;
223 const char *connect_str = NULL;
85dd01e7
JA
224
225 switch (ge->state) {
1252d8f2
JA
226 default:
227 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
63a4b9cc 228 /* fall-through */
85dd01e7
JA
229 case GE_STATE_NEW:
230 connect_state = 1;
9af4a244 231 edit_state = 1;
85dd01e7
JA
232 connect_str = "Connect";
233 send_state = 0;
234 start_state = 0;
235 break;
236 case GE_STATE_CONNECTED:
237 connect_state = 1;
9af4a244 238 edit_state = 1;
85dd01e7
JA
239 connect_str = "Disconnect";
240 send_state = 1;
241 start_state = 0;
242 break;
243 case GE_STATE_JOB_SENT:
244 connect_state = 1;
9af4a244 245 edit_state = 1;
85dd01e7
JA
246 connect_str = "Disconnect";
247 send_state = 0;
248 start_state = 1;
249 break;
250 case GE_STATE_JOB_STARTED:
251 connect_state = 1;
252 edit_state = 1;
253 connect_str = "Disconnect";
254 send_state = 0;
255 start_state = 1;
256 break;
257 case GE_STATE_JOB_RUNNING:
258 connect_state = 1;
259 edit_state = 0;
260 connect_str = "Disconnect";
261 send_state = 0;
262 start_state = 0;
263 break;
264 case GE_STATE_JOB_DONE:
265 connect_state = 1;
266 edit_state = 0;
267 connect_str = "Connect";
268 send_state = 0;
269 start_state = 0;
270 break;
271 }
272
53e0e85d
JA
273 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
274 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
275 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
276 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
277 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
85dd01e7 278
781ccba6
JA
279 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
280 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
85dd01e7 281
781ccba6
JA
282 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
283 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
284 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
85dd01e7 285
781ccba6
JA
286 if (ge->client && ge->client->nr_results)
287 set_view_results_visible(ui, 1);
288 else
289 set_view_results_visible(ui, 0);
85dd01e7
JA
290}
291
1252d8f2 292void gfio_set_state(struct gui_entry *ge, unsigned int state)
85dd01e7
JA
293{
294 ge->state = state;
295 update_button_states(ge->ui, ge);
296}
297
9b260bdf
JA
298static void gfio_ui_setup_log(struct gui *ui)
299{
300 GtkTreeSelection *selection;
301 GtkListStore *model;
302 GtkWidget *tree_view;
303
3ad04399 304 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
9b260bdf
JA
305
306 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
307 gtk_widget_set_can_focus(tree_view, FALSE);
308
309 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
310 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
661f741a
JA
311 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
312 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
9b260bdf
JA
313
314 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
315 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
316 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
f095d563 317 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
9b260bdf
JA
318
319 ui->log_model = model;
320 ui->log_tree = tree_view;
321}
322
1252d8f2
JA
323static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
324 gpointer data)
c57f254c 325{
cd0d2db8
JA
326 guint width = gtk_widget_get_allocated_width(w);
327 guint height = gtk_widget_get_allocated_height(w);
1252d8f2 328 struct gfio_graphs *g = data;
c57f254c 329
cd0d2db8
JA
330 graph_set_size(g->iops_graph, width / 2.0, height);
331 graph_set_position(g->iops_graph, width / 2.0, 0.0);
332 graph_set_size(g->bandwidth_graph, width / 2.0, height);
1252d8f2
JA
333 graph_set_position(g->bandwidth_graph, 0, 0);
334 return TRUE;
c57f254c
SC
335}
336
1252d8f2 337static void draw_graph(struct graph *g, cairo_t *cr)
a269790c 338{
1252d8f2
JA
339 line_graph_draw(g, cr);
340 cairo_stroke(cr);
341}
a269790c 342
1252d8f2
JA
343static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
344 gboolean keyboard_mode, GtkTooltip *tooltip,
345 gpointer data)
346{
347 struct gfio_graphs *g = data;
348 const char *text = NULL;
a269790c 349
1252d8f2
JA
350 if (graph_contains_xy(g->iops_graph, x, y))
351 text = graph_find_tooltip(g->iops_graph, x, y);
352 else if (graph_contains_xy(g->bandwidth_graph, x, y))
353 text = graph_find_tooltip(g->bandwidth_graph, x, y);
a269790c 354
1252d8f2
JA
355 if (text) {
356 gtk_tooltip_set_text(tooltip, text);
357 return TRUE;
e0681f3e 358 }
a269790c 359
1252d8f2 360 return FALSE;
a269790c
JA
361}
362
1252d8f2 363static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
c57f254c 364{
1252d8f2 365 struct gfio_graphs *g = p;
c57f254c
SC
366 cairo_t *cr;
367
cd0d2db8 368 cr = gdk_cairo_create(gtk_widget_get_window(w));
1252d8f2
JA
369
370 if (graph_has_tooltips(g->iops_graph) ||
371 graph_has_tooltips(g->bandwidth_graph)) {
c57f254c 372 g_object_set(w, "has-tooltip", TRUE, NULL);
1252d8f2 373 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
c57f254c 374 }
1252d8f2 375
c57f254c 376 cairo_set_source_rgb(cr, 0, 0, 0);
1252d8f2
JA
377 draw_graph(g->iops_graph, cr);
378 draw_graph(g->bandwidth_graph, cr);
c57f254c
SC
379 cairo_destroy(cr);
380
381 return FALSE;
382}
383
1252d8f2
JA
384/*
385 * FIXME: need more handling here
386 */
387static void ge_destroy(struct gui_entry *ge)
c57f254c 388{
1252d8f2 389 struct gfio_client *gc = ge->client;
c57f254c 390
470cdbb7
JA
391 if (gc) {
392 if (gc->client) {
393 if (ge->state >= GE_STATE_CONNECTED)
394 fio_client_terminate(gc->client);
395
396 fio_put_client(gc->client);
397 }
398 free(gc);
a269790c
JA
399 }
400
b98ab718
JA
401 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
402
1252d8f2
JA
403 free(ge->job_file);
404 free(ge->host);
1252d8f2 405 free(ge);
a269790c
JA
406}
407
1252d8f2 408static void ge_widget_destroy(GtkWidget *w, gpointer data)
3650a3ca 409{
1252d8f2 410 struct gui_entry *ge = (struct gui_entry *) data;
3650a3ca 411
1252d8f2 412 ge_destroy(ge);
3650a3ca
JA
413}
414
1252d8f2 415static void gfio_quit(struct gui *ui)
3650a3ca 416{
3c3ed070 417 gtk_main_quit();
1252d8f2 418}
3650a3ca 419
1252d8f2 420static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
3c3ed070 421 gpointer data)
1252d8f2
JA
422{
423 struct gui *ui = (struct gui *) data;
3650a3ca 424
1252d8f2
JA
425 gfio_quit(ui);
426}
3650a3ca 427
1252d8f2
JA
428static void *job_thread(void *arg)
429{
430 struct gui *ui = arg;
3650a3ca 431
1252d8f2
JA
432 ui->handler_running = 1;
433 fio_handle_clients(&gfio_client_ops);
434 ui->handler_running = 0;
435 return NULL;
436}
ca850992 437
1252d8f2
JA
438static int send_job_file(struct gui_entry *ge)
439{
440 struct gfio_client *gc = ge->client;
441 int ret = 0;
ca850992 442
753e9e64
JA
443 /*
444 * Prune old options, we are expecting the return options
445 * when the job file is parsed remotely and returned to us.
446 */
447 while (!flist_empty(&gc->o_list)) {
448 struct gfio_client_options *gco;
449
9342d5f8 450 gco = flist_first_entry(&gc->o_list, struct gfio_client_options, list);
753e9e64
JA
451 flist_del(&gco->list);
452 free(gco);
453 }
454
bb781643 455 ret = fio_client_send_ini(gc->client, ge->job_file, false);
1252d8f2
JA
456 if (!ret)
457 return 0;
ca850992 458
1252d8f2
JA
459 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
460 return 1;
461}
ca850992 462
1252d8f2
JA
463static void *server_thread(void *arg)
464{
8267ddba 465 fio_server_create_sk_key();
f1867a7f
JA
466 is_backend = true;
467 gfio_server_running = true;
1252d8f2 468 fio_start_server(NULL);
f1867a7f 469 gfio_server_running = false;
8267ddba 470 fio_server_destroy_sk_key();
1252d8f2
JA
471 return NULL;
472}
ca850992 473
1252d8f2
JA
474static void gfio_start_server(struct gui *ui)
475{
476 if (!gfio_server_running) {
f1867a7f 477 gfio_server_running = true;
1252d8f2
JA
478 pthread_create(&ui->server_t, NULL, server_thread, NULL);
479 pthread_detach(ui->server_t);
2b089892 480 }
3650a3ca
JA
481}
482
1252d8f2 483static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
3c3ed070 484 gpointer data)
09d574e3 485{
1252d8f2
JA
486 struct gui_entry *ge = data;
487 struct gfio_client *gc = ge->client;
09d574e3 488
1252d8f2
JA
489 if (gc)
490 fio_start_client(gc->client);
09d574e3
JA
491}
492
1252d8f2 493static void file_open(GtkWidget *w, gpointer data);
e5bd1347 494
1252d8f2
JA
495struct connection_widgets
496{
497 GtkWidget *hentry;
498 GtkWidget *combo;
499 GtkWidget *button;
500};
e5bd1347 501
1252d8f2
JA
502static void hostname_cb(GtkEntry *entry, gpointer data)
503{
504 struct connection_widgets *cw = data;
505 int uses_net = 0, is_localhost = 0;
506 const gchar *text;
507 gchar *ctext;
e5bd1347 508
1252d8f2
JA
509 /*
510 * Check whether to display the 'auto start backend' box
511 * or not. Show it if we are a localhost and using network,
512 * or using a socket.
513 */
f762cef9 514 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
1252d8f2
JA
515 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
516 uses_net = 1;
517 g_free(ctext);
e5bd1347 518
1252d8f2
JA
519 if (uses_net) {
520 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
521 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
522 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
523 !strcmp(text, "ip6-loopback"))
524 is_localhost = 1;
525 }
e5bd1347 526
1252d8f2
JA
527 if (!uses_net || is_localhost) {
528 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
529 gtk_widget_set_sensitive(cw->button, 1);
530 } else {
531 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
532 gtk_widget_set_sensitive(cw->button, 0);
533 }
b0500ccb 534}
661f741a 535
b0500ccb
JA
536static int get_connection_details(struct gui_entry *ge)
537{
538 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
539 struct connection_widgets cw;
540 struct gui *ui = ge->ui;
541 char *typeentry;
2e33101f 542
b0500ccb
JA
543 if (ge->host)
544 return 0;
545
546 dialog = gtk_dialog_new_with_buttons("Connection details",
547 GTK_WINDOW(ui->window),
548 GTK_DIALOG_DESTROY_WITH_PARENT,
549 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
550 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
551
552 frame = gtk_frame_new("Hostname / socket name");
f762cef9 553 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
b0500ccb
JA
554 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
555
556 box = gtk_vbox_new(FALSE, 6);
557 gtk_container_add(GTK_CONTAINER(frame), box);
558
559 hbox = gtk_hbox_new(TRUE, 10);
560 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
561 cw.hentry = gtk_entry_new();
562 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
563 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
564
565 frame = gtk_frame_new("Port");
566 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
567 box = gtk_vbox_new(FALSE, 10);
568 gtk_container_add(GTK_CONTAINER(frame), box);
569
570 hbox = gtk_hbox_new(TRUE, 4);
571 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
572 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
573
574 frame = gtk_frame_new("Type");
575 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
576 box = gtk_vbox_new(FALSE, 10);
577 gtk_container_add(GTK_CONTAINER(frame), box);
578
579 hbox = gtk_hbox_new(TRUE, 4);
580 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
581
f762cef9
JA
582 cw.combo = gtk_combo_box_text_new();
583 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
584 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
585 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
b0500ccb
JA
586 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
587
588 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
589
590 frame = gtk_frame_new("Options");
591 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
592 box = gtk_vbox_new(FALSE, 10);
593 gtk_container_add(GTK_CONTAINER(frame), box);
594
595 hbox = gtk_hbox_new(TRUE, 4);
596 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
597
598 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
599 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
600 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.");
601 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2e33101f 602
b0500ccb
JA
603 /*
604 * Connect edit signal, so we can show/not-show the auto start button
605 */
cd972fdd
JA
606 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
607 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2e33101f 608
b0500ccb 609 gtk_widget_show_all(dialog);
2e33101f 610
b0500ccb 611 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1252d8f2 612 gtk_widget_destroy(dialog);
b0500ccb 613 return 1;
1252d8f2 614 }
2e33101f 615
b0500ccb
JA
616 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
617 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2e33101f 618
f762cef9 619 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
b0500ccb
JA
620 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
621 ge->type = Fio_client_ipv4;
622 else if (!strncmp(typeentry, "IPv6", 4))
623 ge->type = Fio_client_ipv6;
624 else
625 ge->type = Fio_client_socket;
626 g_free(typeentry);
2e33101f 627
b0500ccb 628 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2e33101f 629
b0500ccb
JA
630 gtk_widget_destroy(dialog);
631 return 0;
632}
1252d8f2 633
b0500ccb
JA
634static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
635{
636 gc->client = fio_get_client(client);
637 client->client_data = gc;
638}
639
640static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
641{
cf3d8241 642 struct gfio_client_options *gco;
b0500ccb
JA
643 struct gfio_client *gc;
644
cf3d8241
JA
645 gc = calloc(1, sizeof(*gc));
646 INIT_FLIST_HEAD(&gc->o_list);
b0500ccb
JA
647 gc->ge = ge;
648 ge->client = gc;
649 gfio_set_client(gc, client);
cf3d8241
JA
650
651 /*
652 * Just add a default set of options, need to consider how best
653 * to handle this
654 */
655 gco = calloc(1, sizeof(*gco));
753e9e64 656 INIT_FLIST_HEAD(&gco->list);
cf3d8241
JA
657 options_default_fill(&gco->o);
658 flist_add_tail(&gco->list, &gc->o_list);
753e9e64 659 gc->o_list_nr++;
b0500ccb
JA
660}
661
5721d270
JA
662static void gfio_clear_graph_data(struct gfio_graphs *g)
663{
664 graph_clear_values(g->iops_graph);
665 graph_clear_values(g->bandwidth_graph);
666}
667
b0500ccb
JA
668static void connect_clicked(GtkWidget *widget, gpointer data)
669{
670 struct gui_entry *ge = data;
671 struct gfio_client *gc = ge->client;
672
673 if (ge->state == GE_STATE_NEW) {
674 int ret;
19998dbc 675
b0500ccb
JA
676 if (!ge->job_file)
677 file_open(widget, ge->ui);
678 if (!ge->job_file)
679 return;
2e33101f 680
b0500ccb 681 gc = ge->client;
2e33101f 682
b0500ccb
JA
683 if (!gc->client) {
684 struct fio_client *client;
19998dbc 685
b0500ccb
JA
686 if (get_connection_details(ge)) {
687 gfio_report_error(ge, "Failed to get connection details\n");
688 return;
1252d8f2 689 }
2e33101f 690
b0500ccb
JA
691 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
692 if (!client) {
693 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
694 free(ge->host);
695 ge->host = NULL;
696 return;
1252d8f2 697 }
b0500ccb
JA
698 gfio_set_client(gc, client);
699 }
700
701 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
702 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
703 ret = fio_client_connect(gc->client);
704 if (!ret) {
705 if (!ge->ui->handler_running)
706 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
707 gfio_set_state(ge, GE_STATE_CONNECTED);
5721d270 708 gfio_clear_graph_data(&ge->graphs);
1252d8f2 709 } else {
b0500ccb 710 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2e33101f 711 }
b0500ccb
JA
712 } else {
713 fio_client_terminate(gc->client);
714 gfio_set_state(ge, GE_STATE_NEW);
715 clear_ge_ui_info(ge);
2e33101f 716 }
b0500ccb 717}
2e33101f 718
b0500ccb
JA
719static void send_clicked(GtkWidget *widget, gpointer data)
720{
721 struct gui_entry *ge = data;
2e33101f 722
b0500ccb
JA
723 if (send_job_file(ge))
724 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
725}
62bc937f 726
b0500ccb 727static GtkWidget *new_client_page(struct gui_entry *ge);
62bc937f 728
b0500ccb
JA
729static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
730{
731 struct gui_entry *ge;
a7a42ce1 732
b0500ccb
JA
733 ge = malloc(sizeof(*ge));
734 memset(ge, 0, sizeof(*ge));
735 ge->state = GE_STATE_NEW;
b0500ccb
JA
736 ge->ui = ui;
737 return ge;
738}
a7a42ce1 739
b0500ccb
JA
740static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
741{
742 struct gui_entry *ge;
a7a42ce1 743
b0500ccb 744 ge = alloc_new_gui_entry(ui);
a7a42ce1 745
b0500ccb
JA
746 ge->vbox = new_client_page(ge);
747 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
a7a42ce1 748
b0500ccb
JA
749 ge->page_label = gtk_label_new(name);
750 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
b9f3c7ed 751
b98ab718
JA
752 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
753
b0500ccb
JA
754 gtk_widget_show_all(ui->window);
755 return ge;
756}
b9f3c7ed 757
b0500ccb
JA
758static void file_new(GtkWidget *w, gpointer data)
759{
760 struct gui *ui = (struct gui *) data;
761 struct gui_entry *ge;
62bc937f 762
b0500ccb
JA
763 ge = get_new_ge_with_tab(ui, "Untitled");
764 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
765}
a7a42ce1 766
b0500ccb
JA
767/*
768 * Return the 'ge' corresponding to the tab. If the active tab is the
769 * main tab, open a new tab.
770 */
771static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
772 int *created)
773{
b0500ccb 774 if (!cur_page) {
1252d8f2 775 if (created)
b0500ccb
JA
776 *created = 1;
777 return get_new_ge_with_tab(ui, "Untitled");
778 }
b9f3c7ed 779
b0500ccb
JA
780 if (created)
781 *created = 0;
a7a42ce1 782
b98ab718 783 return g_hash_table_lookup(ui->ge_hash, &cur_page);
b0500ccb 784}
e0681f3e 785
b0500ccb
JA
786static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
787{
788 gint cur_page;
e0681f3e 789
b0500ccb
JA
790 /*
791 * Main tab is tab 0, so any current page other than 0 holds
792 * a ge entry.
793 */
794 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
795 if (cur_page)
796 return get_ge_from_page(ui, cur_page, NULL);
0cf3ece0 797
b0500ccb
JA
798 return NULL;
799}
0cf3ece0 800
b0500ccb
JA
801static void file_close(GtkWidget *w, gpointer data)
802{
803 struct gui *ui = (struct gui *) data;
804 struct gui_entry *ge;
2f99deb0 805
b0500ccb
JA
806 /*
807 * Can't close the main tab
808 */
809 ge = get_ge_from_cur_tab(ui);
810 if (ge) {
811 gtk_widget_destroy(ge->vbox);
812 return;
1252d8f2 813 }
16ce5adf 814
b98ab718 815 if (g_hash_table_size(ui->ge_hash)) {
b0500ccb
JA
816 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
817 return;
38634cb1
JA
818 }
819
b0500ccb
JA
820 gfio_quit(ui);
821}
2f99deb0 822
b0500ccb
JA
823static void file_add_recent(struct gui *ui, const gchar *uri)
824{
825 GtkRecentData grd;
2f99deb0 826
b0500ccb
JA
827 memset(&grd, 0, sizeof(grd));
828 grd.display_name = strdup("gfio");
829 grd.description = strdup("Fio job file");
830 grd.mime_type = strdup(GFIO_MIME);
831 grd.app_name = strdup(g_get_application_name());
832 grd.app_exec = strdup("gfio %f/%u");
85dd01e7 833
b0500ccb
JA
834 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
835}
85dd01e7 836
b0500ccb
JA
837static gchar *get_filename_from_uri(const gchar *uri)
838{
839 if (strncmp(uri, "file://", 7))
840 return strdup(uri);
16ce5adf 841
b0500ccb
JA
842 return strdup(uri + 7);
843}
16ce5adf 844
b0500ccb
JA
845static int do_file_open(struct gui_entry *ge, const gchar *uri)
846{
847 struct fio_client *client;
f5c6726e 848
b0500ccb 849 assert(!ge->job_file);
38634cb1 850
b0500ccb 851 ge->job_file = get_filename_from_uri(uri);
38634cb1 852
b0500ccb
JA
853 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
854 if (client) {
832fde79
JA
855 char *label = strdup(uri);
856
857 basename(label);
858 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
859 free(label);
860
b0500ccb
JA
861 gfio_client_added(ge, client);
862 file_add_recent(ge->ui, uri);
863 return 0;
864 }
38634cb1 865
b0500ccb
JA
866 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
867 free(ge->host);
868 ge->host = NULL;
b98ab718
JA
869 free(ge->job_file);
870 ge->job_file = NULL;
b0500ccb
JA
871 return 1;
872}
38634cb1 873
b0500ccb
JA
874static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
875{
876 struct gui_entry *ge;
877 gint cur_page;
878 int ret, ge_is_new = 0;
2f99deb0 879
b0500ccb
JA
880 /*
881 * Creates new tab if current tab is the main window, or the
882 * current tab already has a client.
883 */
884 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
885 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
886 if (ge->client) {
887 ge = get_new_ge_with_tab(ui, "Untitled");
888 ge_is_new = 1;
38634cb1 889 }
2f99deb0 890
b0500ccb
JA
891 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
892
893 if (get_connection_details(ge)) {
894 if (ge_is_new)
895 gtk_widget_destroy(ge->vbox);
3c3ed070 896
b0500ccb
JA
897 return 1;
898 }
a6790906 899
b0500ccb 900 ret = do_file_open(ge, uri);
a6790906 901
b0500ccb
JA
902 if (!ret) {
903 if (ge->server_start)
904 gfio_start_server(ui);
905 } else {
906 if (ge_is_new)
907 gtk_widget_destroy(ge->vbox);
a6790906
JA
908 }
909
b0500ccb
JA
910 return ret;
911}
a6790906 912
b0500ccb
JA
913static void recent_open(GtkAction *action, gpointer data)
914{
915 struct gui *ui = (struct gui *) data;
916 GtkRecentInfo *info;
917 const gchar *uri;
0420ba6a 918
b0500ccb
JA
919 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
920 uri = gtk_recent_info_get_uri(info);
0420ba6a 921
b0500ccb
JA
922 do_file_open_with_tab(ui, uri);
923}
a7a42ce1 924
b0500ccb
JA
925static void file_open(GtkWidget *w, gpointer data)
926{
927 struct gui *ui = data;
928 GtkWidget *dialog;
929 GtkFileFilter *filter;
930 gchar *filename;
931
932 dialog = gtk_file_chooser_dialog_new("Open File",
933 GTK_WINDOW(ui->window),
934 GTK_FILE_CHOOSER_ACTION_OPEN,
935 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
936 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
937 NULL);
938 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
939
940 filter = gtk_file_filter_new();
941 gtk_file_filter_add_pattern(filter, "*.fio");
942 gtk_file_filter_add_pattern(filter, "*.job");
943 gtk_file_filter_add_pattern(filter, "*.ini");
944 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
945 gtk_file_filter_set_name(filter, "Fio job file");
946 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
947
948 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
949 gtk_widget_destroy(dialog);
950 return;
1252d8f2 951 }
0420ba6a 952
b0500ccb 953 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
0420ba6a 954
b0500ccb 955 gtk_widget_destroy(dialog);
0420ba6a 956
b0500ccb
JA
957 do_file_open_with_tab(ui, filename);
958 g_free(filename);
959}
0420ba6a 960
b0500ccb
JA
961static void file_save(GtkWidget *w, gpointer data)
962{
963 struct gui *ui = data;
964 GtkWidget *dialog;
0420ba6a 965
b0500ccb
JA
966 dialog = gtk_file_chooser_dialog_new("Save File",
967 GTK_WINDOW(ui->window),
968 GTK_FILE_CHOOSER_ACTION_SAVE,
969 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
970 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
971 NULL);
972
973 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
974 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
975
976 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
977 char *filename;
978
979 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
980 // save_job_file(filename);
981 g_free(filename);
0420ba6a 982 }
b0500ccb
JA
983 gtk_widget_destroy(dialog);
984}
0420ba6a 985
9b260bdf
JA
986static void view_log_destroy(GtkWidget *w, gpointer data)
987{
988 struct gui *ui = (struct gui *) data;
989
f762cef9 990 g_object_ref(G_OBJECT(ui->log_tree));
9b260bdf
JA
991 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
992 gtk_widget_destroy(w);
4cbe7211 993 ui->log_view = NULL;
9b260bdf
JA
994}
995
1252d8f2 996void gfio_view_log(struct gui *ui)
9b260bdf 997{
4cbe7211 998 GtkWidget *win, *scroll, *vbox, *box;
9b260bdf 999
4cbe7211
JA
1000 if (ui->log_view)
1001 return;
1002
1003 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 1004 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 1005 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 1006
4cbe7211
JA
1007 scroll = gtk_scrolled_window_new(NULL, NULL);
1008
1009 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1010
1011 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1012
1013 box = gtk_hbox_new(TRUE, 0);
f762cef9 1014 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
4cbe7211
JA
1015 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1016 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1017
1018 vbox = gtk_vbox_new(TRUE, 5);
f762cef9 1019 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
9b260bdf 1020
4cbe7211 1021 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
1022 gtk_widget_show_all(win);
1023}
1024
1252d8f2
JA
1025static void view_log(GtkWidget *w, gpointer data)
1026{
1027 struct gui *ui = (struct gui *) data;
1028
1029 gfio_view_log(ui);
1030}
1031
85dd01e7
JA
1032static void connect_job_entry(GtkWidget *w, gpointer data)
1033{
1034 struct gui *ui = (struct gui *) data;
1035 struct gui_entry *ge;
3c3ed070 1036
85dd01e7
JA
1037 ge = get_ge_from_cur_tab(ui);
1038 if (ge)
1039 connect_clicked(w, ge);
1040}
1041
1042static void send_job_entry(GtkWidget *w, gpointer data)
1043{
1044 struct gui *ui = (struct gui *) data;
1045 struct gui_entry *ge;
1046
1047 ge = get_ge_from_cur_tab(ui);
1048 if (ge)
1049 send_clicked(w, ge);
85dd01e7
JA
1050}
1051
1052static void edit_job_entry(GtkWidget *w, gpointer data)
16ce5adf 1053{
9af4a244 1054 struct gui *ui = (struct gui *) data;
789f4ccd 1055 struct gui_entry *ge;
9af4a244 1056
789f4ccd
JA
1057 ge = get_ge_from_cur_tab(ui);
1058 if (ge && ge->client)
cf3d8241 1059 gopt_get_options_window(ui->window, ge->client);
16ce5adf
JA
1060}
1061
85dd01e7
JA
1062static void start_job_entry(GtkWidget *w, gpointer data)
1063{
1064 struct gui *ui = (struct gui *) data;
1065 struct gui_entry *ge;
1066
1067 ge = get_ge_from_cur_tab(ui);
1068 if (ge)
1069 start_job_clicked(w, ge);
1070}
1071
781ccba6
JA
1072static void view_results(GtkWidget *w, gpointer data)
1073{
1074 struct gui *ui = (struct gui *) data;
1075 struct gfio_client *gc;
1076 struct gui_entry *ge;
1077
1078 ge = get_ge_from_cur_tab(ui);
1079 if (!ge)
1080 return;
1081
1082 if (ge->results_window)
1083 return;
1084
1085 gc = ge->client;
1086 if (gc && gc->nr_results)
1087 gfio_display_end_results(gc);
1088}
1089
a1e7972d 1090static void __update_graph_settings(struct gfio_graphs *g)
8577f4fd
JA
1091{
1092 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
a1e7972d 1093 graph_set_font(g->iops_graph, gfio_graph_font);
8577f4fd 1094 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
a1e7972d 1095 graph_set_font(g->bandwidth_graph, gfio_graph_font);
8577f4fd
JA
1096}
1097
a1e7972d 1098static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
b98ab718
JA
1099{
1100 struct gui_entry *ge = (struct gui_entry *) value;
a1e7972d 1101 GdkEvent *ev;
b98ab718 1102
a1e7972d
JA
1103 __update_graph_settings(&ge->graphs);
1104
1105 ev = gdk_event_new(GDK_EXPOSE);
b6ab6a31 1106 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
a1e7972d 1107 gdk_event_free(ev);
b98ab718
JA
1108}
1109
8577f4fd
JA
1110static void update_graph_limits(void)
1111{
b98ab718 1112 struct gui *ui = &main_ui;
a1e7972d
JA
1113 GdkEvent *ev;
1114
1115 __update_graph_settings(&ui->graphs);
8577f4fd 1116
a1e7972d 1117 ev = gdk_event_new(GDK_EXPOSE);
b6ab6a31 1118 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
a1e7972d 1119 gdk_event_free(ev);
8577f4fd 1120
a1e7972d 1121 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
8577f4fd
JA
1122}
1123
46974a7d
JA
1124static void preferences(GtkWidget *w, gpointer data)
1125{
f3e8440f 1126 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 1127 GtkWidget *hbox, *spin, *entry, *spin_int;
6e02ad6c 1128 struct gui *ui = (struct gui *) data;
46974a7d
JA
1129 int i;
1130
1131 dialog = gtk_dialog_new_with_buttons("Preferences",
6e02ad6c 1132 GTK_WINDOW(ui->window),
46974a7d
JA
1133 GTK_DIALOG_DESTROY_WITH_PARENT,
1134 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1135 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1136 NULL);
1137
8577f4fd 1138 frame = gtk_frame_new("Graphing");
f762cef9
JA
1139 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1140 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
f3e8440f
JA
1141 vbox = gtk_vbox_new(FALSE, 6);
1142 gtk_container_add(GTK_CONTAINER(frame), vbox);
1143
1cf6bca5
JA
1144 hbox = gtk_hbox_new(FALSE, 5);
1145 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1146 entry = gtk_label_new("Font face to use for graph labels");
1147 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1148
a1e7972d 1149 font = gtk_font_button_new_with_font(gfio_graph_font);
1cf6bca5 1150 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 1151
8577f4fd
JA
1152 box = gtk_vbox_new(FALSE, 6);
1153 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1154
1155 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 1156 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
1157 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1158 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1159
c05d9056 1160 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
8577f4fd 1161
1cf6bca5
JA
1162 box = gtk_vbox_new(FALSE, 6);
1163 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1164
1165 hbox = gtk_hbox_new(FALSE, 5);
1166 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1167 entry = gtk_label_new("Client ETA request interval (msec)");
1168 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1169
1170 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4 1171 frame = gtk_frame_new("Debug logging");
f762cef9
JA
1172 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1173 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
a31d9fa4
JA
1174 vbox = gtk_vbox_new(FALSE, 6);
1175 gtk_container_add(GTK_CONTAINER(frame), vbox);
1176
1177 box = gtk_hbox_new(FALSE, 6);
1178 gtk_container_add(GTK_CONTAINER(vbox), box);
1179
1180 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1181
1182 for (i = 0; i < FD_DEBUG_MAX; i++) {
1183 if (i == 7) {
1184 box = gtk_hbox_new(FALSE, 6);
1185 gtk_container_add(GTK_CONTAINER(vbox), box);
1186 }
1187
1188
1189 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1190 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1191 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1192 }
1193
46974a7d
JA
1194 gtk_widget_show_all(dialog);
1195
1196 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1197 gtk_widget_destroy(dialog);
1198 return;
1199 }
1200
1201 for (i = 0; i < FD_DEBUG_MAX; i++) {
1202 int set;
1203
1204 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1205 if (set)
1206 fio_debug |= (1UL << i);
1207 }
1208
f3e8440f 1209 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
1210 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1211 update_graph_limits();
1cf6bca5 1212 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 1213
46974a7d
JA
1214 gtk_widget_destroy(dialog);
1215}
1216
0420ba6a
JA
1217static void about_dialog(GtkWidget *w, gpointer data)
1218{
81e4ea6e
JA
1219 const char *authors[] = {
1220 "Jens Axboe <axboe@kernel.dk>",
5921cea2 1221 "Stephen Cameron <stephenmcameron@gmail.com>",
81e4ea6e
JA
1222 NULL
1223 };
84a72ed3
JA
1224 const char *license[] = {
1225 "Fio is free software; you can redistribute it and/or modify "
1226 "it under the terms of the GNU General Public License as published by "
1227 "the Free Software Foundation; either version 2 of the License, or "
1228 "(at your option) any later version.\n",
1229 "Fio is distributed in the hope that it will be useful, "
1230 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1231 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1232 "GNU General Public License for more details.\n",
1233 "You should have received a copy of the GNU General Public License "
1234 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1235 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1236 };
1237 char *license_trans;
1238
1239 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1240 license[2], "\n", NULL);
81e4ea6e 1241
0420ba6a
JA
1242 gtk_show_about_dialog(NULL,
1243 "program-name", "gfio",
1244 "comments", "Gtk2 UI for fio",
84a72ed3 1245 "license", license_trans,
ec9a7e69 1246 "website", "http://git.kernel.dk/cgit/fio/",
81e4ea6e 1247 "authors", authors,
0420ba6a 1248 "version", fio_version_string,
403bb450 1249 "copyright", "© 2012-2017 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
1250 "logo-icon-name", "fio",
1251 /* Must be last: */
81e4ea6e 1252 "wrap-license", TRUE,
0420ba6a 1253 NULL);
84a72ed3 1254
2f99deb0 1255 g_free(license_trans);
0420ba6a
JA
1256}
1257
1258static GtkActionEntry menu_items[] = {
46974a7d 1259 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 1260 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
16ce5adf 1261 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
46974a7d 1262 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 1263 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
16ce5adf 1264 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
46974a7d
JA
1265 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1266 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1267 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 1268 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
781ccba6 1269 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
bc271d87 1270 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
85dd01e7
JA
1271 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1272 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1273 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
46974a7d
JA
1274 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1275 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 1276};
59f94d26 1277static gint nmenu_items = FIO_ARRAY_SIZE(menu_items);
0420ba6a
JA
1278
1279static const gchar *ui_string = " \
1280 <ui> \
1281 <menubar name=\"MainMenu\"> \
1282 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0 1283 <menuitem name=\"New\" action=\"NewFile\" /> \
bf64138b 1284 <menuitem name=\"Open\" action=\"OpenFile\" /> \
16ce5adf 1285 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2f99deb0 1286 <separator name=\"Separator1\"/> \
0420ba6a 1287 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 1288 <separator name=\"Separator2\"/> \
2f99deb0
JA
1289 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1290 <separator name=\"Separator3\"/> \
261f21d0
JA
1291 <placeholder name=\"FileRecentFiles\"/> \
1292 <separator name=\"Separator4\"/> \
0420ba6a
JA
1293 <menuitem name=\"Quit\" action=\"Quit\" /> \
1294 </menu> \
16ce5adf 1295 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
85dd01e7 1296 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
261f21d0 1297 <separator name=\"Separator5\"/> \
85dd01e7
JA
1298 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1299 <menuitem name=\"Send job\" action=\"SendJob\" /> \
261f21d0 1300 <separator name=\"Separator6\"/> \
85dd01e7 1301 <menuitem name=\"Start job\" action=\"StartJob\" /> \
16ce5adf 1302 </menu>\
9b260bdf 1303 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
781ccba6
JA
1304 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1305 <separator name=\"Separator7\"/> \
9b260bdf
JA
1306 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1307 </menu>\
0420ba6a
JA
1308 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1309 <menuitem name=\"About\" action=\"About\" /> \
1310 </menu> \
1311 </menubar> \
1312 </ui> \
1313";
1314
4cbe7211
JA
1315static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1316 struct gui *ui)
0420ba6a 1317{
ca664f49 1318 GtkActionGroup *action_group;
0420ba6a
JA
1319 GError *error = 0;
1320
1321 action_group = gtk_action_group_new("Menu");
4cbe7211 1322 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
1323
1324 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1325 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1326
1327 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
02421e69 1328
0420ba6a
JA
1329 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1330}
1331
1332void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1252d8f2 1333 GtkWidget *vbox, GtkUIManager *ui_manager)
0420ba6a 1334{
1252d8f2 1335 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
0420ba6a
JA
1336}
1337
c80b74b0
JA
1338static void combo_entry_changed(GtkComboBox *box, gpointer data)
1339{
1340 struct gui_entry *ge = (struct gui_entry *) data;
1341 gint index;
1342
1343 index = gtk_combo_box_get_active(box);
1344
1345 multitext_set_entry(&ge->eta.iotype, index);
99d633af 1346 multitext_set_entry(&ge->eta.bs, index);
c80b74b0
JA
1347 multitext_set_entry(&ge->eta.ioengine, index);
1348 multitext_set_entry(&ge->eta.iodepth, index);
1349}
1350
1351static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1352{
1353 struct gui_entry *ge = (struct gui_entry *) data;
1354
1355 multitext_free(&ge->eta.iotype);
99d633af 1356 multitext_free(&ge->eta.bs);
c80b74b0
JA
1357 multitext_free(&ge->eta.ioengine);
1358 multitext_free(&ge->eta.iodepth);
1359}
1360
2f99deb0 1361static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 1362{
2f99deb0 1363 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 1364 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
0420ba6a 1365
2f99deb0 1366 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 1367
65476336
JA
1368 top_align = gtk_alignment_new(0, 0, 1, 0);
1369 top_vbox = gtk_vbox_new(FALSE, 3);
1370 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1371 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2f99deb0
JA
1372
1373 probe = gtk_frame_new("Job");
1374 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1375 probe_frame = gtk_vbox_new(FALSE, 3);
1376 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1377
1378 probe_box = gtk_hbox_new(FALSE, 3);
1379 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1380 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1381 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1382 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1383 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1384
1385 probe_box = gtk_hbox_new(FALSE, 3);
1386 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1387
3863d1ad 1388 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
c80b74b0
JA
1389 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1390 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1391 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
5921cea2 1392 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write/Trim)");
c80b74b0
JA
1393 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1394 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2f99deb0
JA
1395 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1396 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1397
1398 probe_box = gtk_hbox_new(FALSE, 3);
1399 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
b78ca651 1400 ge->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
5921cea2 1401 ge->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "Read IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
b78ca651 1402 ge->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
5921cea2 1403 ge->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "Write IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
b78ca651 1404 ge->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
5921cea2 1405 ge->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "Trim IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
dbc542d4 1406
2f99deb0
JA
1407 /*
1408 * Only add this if we have a commit rate
2839f0c6 1409 */
2f99deb0
JA
1410#if 0
1411 probe_box = gtk_hbox_new(FALSE, 3);
1412 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 1413
2f99deb0
JA
1414 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1415 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 1416
2f99deb0
JA
1417 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1418 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1419#endif
ff1f3280 1420
2f99deb0
JA
1421 /*
1422 * Set up a drawing area and IOPS and bandwidth graphs
1423 */
2f99deb0 1424 ge->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 1425 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
57f9d28e 1426 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2a95f716 1427 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
b6ab6a31 1428 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
2f99deb0
JA
1429 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1430 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1431 G_CALLBACK(on_config_drawing_area), &ge->graphs);
65476336
JA
1432 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1433 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 1434 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 1435 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 1436 ge->graphs.drawing_area);
65476336 1437 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
04cc6b77 1438
2f99deb0
JA
1439 setup_graphs(&ge->graphs);
1440
1441 /*
3c3ed070 1442 * Set up alignments for widgets at the bottom of ui,
2f99deb0
JA
1443 * align bottom left, expand horizontally but not vertically
1444 */
65476336 1445 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2f99deb0 1446 ge->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
1447 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1448 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2f99deb0 1449
59f94d26 1450 add_buttons(ge, buttonspeclist, FIO_ARRAY_SIZE(buttonspeclist));
0420ba6a 1451
c36f98d9 1452 /*
2f99deb0
JA
1453 * Set up thread status progress bar
1454 */
1455 ge->thread_status_pb = gtk_progress_bar_new();
1456 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1457 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1458 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1459
1460
1461 return main_vbox;
1462}
1463
1464static GtkWidget *new_main_page(struct gui *ui)
1465{
1466 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 1467 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2f99deb0
JA
1468
1469 main_vbox = gtk_vbox_new(FALSE, 3);
1470
1471 /*
1472 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
1473 * align top left, expand horizontally but not vertically
1474 */
65476336
JA
1475 top_align = gtk_alignment_new(0, 0, 1, 0);
1476 top_vbox = gtk_vbox_new(FALSE, 0);
1477 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1478 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
c36f98d9 1479
2f99deb0
JA
1480 probe = gtk_frame_new("Run statistics");
1481 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
1482 probe_frame = gtk_vbox_new(FALSE, 3);
1483 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1484
1485 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 1486 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 1487 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
b78ca651
JA
1488 ui->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1489 ui->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1490 ui->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1491 ui->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1492 ui->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1493 ui->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
3e47bd25 1494
807f9971
JA
1495 /*
1496 * Only add this if we have a commit rate
1497 */
1498#if 0
3e47bd25
JA
1499 probe_box = gtk_hbox_new(FALSE, 3);
1500 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
1501
1502 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1503 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1504
3e47bd25
JA
1505 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1506 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 1507#endif
3e47bd25 1508
736f2dff 1509 /*
2fd3bb0e 1510 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 1511 */
2f99deb0 1512 ui->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 1513 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
57f9d28e 1514 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2a95f716 1515 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
b6ab6a31 1516 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
2f99deb0
JA
1517 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1518 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1519 G_CALLBACK(on_config_drawing_area), &ui->graphs);
65476336
JA
1520 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1521 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
736f2dff 1522 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 1523 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 1524 ui->graphs.drawing_area);
65476336 1525 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
e164534f 1526 TRUE, TRUE, 0);
736f2dff 1527
2f99deb0 1528 setup_graphs(&ui->graphs);
2fd3bb0e 1529
c36f98d9 1530 /*
3c3ed070 1531 * Set up alignments for widgets at the bottom of ui,
c36f98d9
SC
1532 * align bottom left, expand horizontally but not vertically
1533 */
65476336 1534 bottom_align = gtk_alignment_new(0, 1, 1, 0);
c36f98d9 1535 ui->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
1536 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1537 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
c36f98d9 1538
3ec62ec4
JA
1539 /*
1540 * Set up thread status progress bar
1541 */
1542 ui->thread_status_pb = gtk_progress_bar_new();
1543 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 1544 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
1545 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1546
2f99deb0
JA
1547 return main_vbox;
1548}
1549
1550static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1551 guint page, gpointer data)
1552
1553{
02421e69 1554 struct gui *ui = (struct gui *) data;
85dd01e7
JA
1555 struct gui_entry *ge;
1556
1557 if (!page) {
1558 set_job_menu_visible(ui, 0);
781ccba6 1559 set_view_results_visible(ui, 0);
85dd01e7
JA
1560 return TRUE;
1561 }
1562
1563 set_job_menu_visible(ui, 1);
6e02ad6c 1564 ge = get_ge_from_page(ui, page, NULL);
85dd01e7
JA
1565 if (ge)
1566 update_button_states(ui, ge);
02421e69 1567
2f99deb0
JA
1568 return TRUE;
1569}
1570
38634cb1
JA
1571static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1572{
1573 time_t time_a = gtk_recent_info_get_visited(a);
1574 time_t time_b = gtk_recent_info_get_visited(b);
1575
1576 return time_b - time_a;
1577}
1578
1579static void add_recent_file_items(struct gui *ui)
1580{
1581 const gchar *gfio = g_get_application_name();
1582 GList *items, *item;
1583 int i = 0;
1584
1585 if (ui->recent_ui_id) {
1586 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1587 gtk_ui_manager_ensure_update(ui->uimanager);
1588 }
1589 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1590
1591 if (ui->actiongroup) {
1592 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1593 g_object_unref(ui->actiongroup);
1594 }
1595 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1596
1597 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1598
1599 items = gtk_recent_manager_get_items(ui->recentmanager);
1600 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1601
1602 for (item = items; item && item->data; item = g_list_next(item)) {
1603 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1604 gchar *action_name;
1605 const gchar *label;
1606 GtkAction *action;
1607
1608 if (!gtk_recent_info_has_application(info, gfio))
1609 continue;
1610
1611 /*
1612 * We only support local files for now
1613 */
1614 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1615 continue;
1616
1617 action_name = g_strdup_printf("RecentFile%u", i++);
1618 label = gtk_recent_info_get_display_name(info);
1619
1620 action = g_object_new(GTK_TYPE_ACTION,
1621 "name", action_name,
1622 "label", label, NULL);
1623
1624 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1625 gtk_recent_info_ref(info),
1626 (GDestroyNotify) gtk_recent_info_unref);
1627
1628
1629 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1630
1631 gtk_action_group_add_action(ui->actiongroup, action);
1632 g_object_unref(action);
1633
1634 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1635 "/MainMenu/FileMenu/FileRecentFiles",
1636 label, action_name,
1637 GTK_UI_MANAGER_MENUITEM, FALSE);
1638
1639 g_free(action_name);
1640
1641 if (i == 8)
1642 break;
1643 }
1644
1645 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1646 g_list_free(items);
1647}
1648
a6790906 1649static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
6e02ad6c
JA
1650 gint x, gint y, GtkSelectionData *seldata,
1651 guint info, guint time, gpointer *data)
a6790906 1652{
6e02ad6c 1653 struct gui *ui = (struct gui *) data;
a6790906
JA
1654 gchar **uris;
1655 GtkWidget *source;
a6790906
JA
1656
1657 source = gtk_drag_get_source_widget(ctx);
1658 if (source && widget == gtk_widget_get_toplevel(source)) {
1659 gtk_drag_finish(ctx, FALSE, FALSE, time);
1660 return;
1661 }
1662
6e02ad6c 1663 uris = gtk_selection_data_get_uris(seldata);
a6790906
JA
1664 if (!uris) {
1665 gtk_drag_finish(ctx, FALSE, FALSE, time);
1666 return;
1667 }
1668
0cf3ece0
JA
1669 if (uris[0])
1670 do_file_open_with_tab(ui, uris[0]);
a6790906
JA
1671
1672 gtk_drag_finish(ctx, TRUE, FALSE, time);
1673 g_strfreev(uris);
1674}
1675
2f99deb0
JA
1676static void init_ui(int *argc, char **argv[], struct gui *ui)
1677{
1678 GtkSettings *settings;
02421e69 1679 GtkWidget *vbox;
2f99deb0
JA
1680
1681 /* Magical g*thread incantation, you just need this thread stuff.
1682 * Without it, the update that happens in gfio_update_thread_status
1683 * doesn't really happen in a timely fashion, you need expose events
1684 */
f1c4b372 1685#if !GLIB_CHECK_VERSION(2, 31, 0)
2f99deb0
JA
1686 if (!g_thread_supported())
1687 g_thread_init(NULL);
b11bd8d8
JA
1688#endif
1689
2f99deb0
JA
1690 gdk_threads_init();
1691
1692 gtk_init(argc, argv);
1693 settings = gtk_settings_get_default();
1694 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
c05da82c 1695#if !GLIB_CHECK_VERSION(2, 36, 0)
2f99deb0 1696 g_type_init();
c05da82c 1697#endif
0950e832 1698 gdk_color_parse("#fffff4", &gfio_color_lightyellow);
1252d8f2 1699 gdk_color_parse("white", &gfio_color_white);
3c3ed070 1700
2f99deb0 1701 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
814479d5 1702 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2f99deb0
JA
1703 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1704
6e02ad6c
JA
1705 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1706 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
2f99deb0
JA
1707
1708 ui->vbox = gtk_vbox_new(FALSE, 0);
1709 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1710
02421e69
JA
1711 ui->uimanager = gtk_ui_manager_new();
1712 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1713 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2f99deb0 1714
38634cb1
JA
1715 ui->recentmanager = gtk_recent_manager_get_default();
1716 add_recent_file_items(ui);
1717
2f99deb0
JA
1718 ui->notebook = gtk_notebook_new();
1719 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 1720 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 1721 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
1722 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1723
1724 vbox = new_main_page(ui);
0cf3ece0 1725 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
a6790906
JA
1726 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1727 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
2f99deb0
JA
1728
1729 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1730
9b260bdf 1731 gfio_ui_setup_log(ui);
3ec62ec4 1732
ff1f3280
SC
1733 gtk_widget_show_all(ui->window);
1734}
1735
8232e285 1736int main(int argc, char *argv[], char *envp[])
ff1f3280 1737{
8232e285
SC
1738 if (initialize_fio(envp))
1739 return 1;
0420ba6a
JA
1740 if (fio_init_options())
1741 return 1;
a1820207 1742
753e9e64
JA
1743 gopt_init();
1744
2f99deb0 1745 memset(&main_ui, 0, sizeof(main_ui));
b98ab718 1746 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
2f99deb0
JA
1747
1748 init_ui(&argc, &argv, &main_ui);
5b7573ab 1749
2839f0c6 1750 gdk_threads_enter();
ff1f3280 1751 gtk_main();
2839f0c6 1752 gdk_threads_leave();
b98ab718
JA
1753
1754 g_hash_table_destroy(main_ui.ge_hash);
753e9e64
JA
1755
1756 gopt_exit();
ff1f3280
SC
1757 return 0;
1758}