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