gfio: make 'light yellow' color a little lighter
[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
447 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list);
448 flist_del(&gco->list);
449 free(gco);
450 }
451
1252d8f2
JA
452 ret = fio_client_send_ini(gc->client, ge->job_file);
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{
462 is_backend = 1;
463 gfio_server_running = 1;
464 fio_start_server(NULL);
465 gfio_server_running = 0;
466 return NULL;
467}
ca850992 468
1252d8f2
JA
469static void gfio_start_server(struct gui *ui)
470{
471 if (!gfio_server_running) {
472 gfio_server_running = 1;
473 pthread_create(&ui->server_t, NULL, server_thread, NULL);
474 pthread_detach(ui->server_t);
2b089892 475 }
3650a3ca
JA
476}
477
1252d8f2 478static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
3c3ed070 479 gpointer data)
09d574e3 480{
1252d8f2
JA
481 struct gui_entry *ge = data;
482 struct gfio_client *gc = ge->client;
09d574e3 483
1252d8f2
JA
484 if (gc)
485 fio_start_client(gc->client);
09d574e3
JA
486}
487
1252d8f2 488static void file_open(GtkWidget *w, gpointer data);
e5bd1347 489
1252d8f2
JA
490struct connection_widgets
491{
492 GtkWidget *hentry;
493 GtkWidget *combo;
494 GtkWidget *button;
495};
e5bd1347 496
1252d8f2
JA
497static void hostname_cb(GtkEntry *entry, gpointer data)
498{
499 struct connection_widgets *cw = data;
500 int uses_net = 0, is_localhost = 0;
501 const gchar *text;
502 gchar *ctext;
e5bd1347 503
1252d8f2
JA
504 /*
505 * Check whether to display the 'auto start backend' box
506 * or not. Show it if we are a localhost and using network,
507 * or using a socket.
508 */
f762cef9 509 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
1252d8f2
JA
510 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
511 uses_net = 1;
512 g_free(ctext);
e5bd1347 513
1252d8f2
JA
514 if (uses_net) {
515 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
516 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
517 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
518 !strcmp(text, "ip6-loopback"))
519 is_localhost = 1;
520 }
e5bd1347 521
1252d8f2
JA
522 if (!uses_net || is_localhost) {
523 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
524 gtk_widget_set_sensitive(cw->button, 1);
525 } else {
526 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
527 gtk_widget_set_sensitive(cw->button, 0);
528 }
b0500ccb 529}
661f741a 530
b0500ccb
JA
531static int get_connection_details(struct gui_entry *ge)
532{
533 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
534 struct connection_widgets cw;
535 struct gui *ui = ge->ui;
536 char *typeentry;
2e33101f 537
b0500ccb
JA
538 if (ge->host)
539 return 0;
540
541 dialog = gtk_dialog_new_with_buttons("Connection details",
542 GTK_WINDOW(ui->window),
543 GTK_DIALOG_DESTROY_WITH_PARENT,
544 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
545 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
546
547 frame = gtk_frame_new("Hostname / socket name");
f762cef9 548 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
b0500ccb
JA
549 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
550
551 box = gtk_vbox_new(FALSE, 6);
552 gtk_container_add(GTK_CONTAINER(frame), box);
553
554 hbox = gtk_hbox_new(TRUE, 10);
555 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
556 cw.hentry = gtk_entry_new();
557 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
558 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
559
560 frame = gtk_frame_new("Port");
561 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
562 box = gtk_vbox_new(FALSE, 10);
563 gtk_container_add(GTK_CONTAINER(frame), box);
564
565 hbox = gtk_hbox_new(TRUE, 4);
566 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
567 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
568
569 frame = gtk_frame_new("Type");
570 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
571 box = gtk_vbox_new(FALSE, 10);
572 gtk_container_add(GTK_CONTAINER(frame), box);
573
574 hbox = gtk_hbox_new(TRUE, 4);
575 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
576
f762cef9
JA
577 cw.combo = gtk_combo_box_text_new();
578 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
579 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
580 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
b0500ccb
JA
581 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
582
583 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
584
585 frame = gtk_frame_new("Options");
586 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
587 box = gtk_vbox_new(FALSE, 10);
588 gtk_container_add(GTK_CONTAINER(frame), box);
589
590 hbox = gtk_hbox_new(TRUE, 4);
591 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
592
593 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
594 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
595 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.");
596 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2e33101f 597
b0500ccb
JA
598 /*
599 * Connect edit signal, so we can show/not-show the auto start button
600 */
cd972fdd
JA
601 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
602 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2e33101f 603
b0500ccb 604 gtk_widget_show_all(dialog);
2e33101f 605
b0500ccb 606 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1252d8f2 607 gtk_widget_destroy(dialog);
b0500ccb 608 return 1;
1252d8f2 609 }
2e33101f 610
b0500ccb
JA
611 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
612 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2e33101f 613
f762cef9 614 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
b0500ccb
JA
615 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
616 ge->type = Fio_client_ipv4;
617 else if (!strncmp(typeentry, "IPv6", 4))
618 ge->type = Fio_client_ipv6;
619 else
620 ge->type = Fio_client_socket;
621 g_free(typeentry);
2e33101f 622
b0500ccb 623 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2e33101f 624
b0500ccb
JA
625 gtk_widget_destroy(dialog);
626 return 0;
627}
1252d8f2 628
b0500ccb
JA
629static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
630{
631 gc->client = fio_get_client(client);
632 client->client_data = gc;
633}
634
635static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
636{
cf3d8241 637 struct gfio_client_options *gco;
b0500ccb
JA
638 struct gfio_client *gc;
639
cf3d8241
JA
640 gc = calloc(1, sizeof(*gc));
641 INIT_FLIST_HEAD(&gc->o_list);
b0500ccb
JA
642 gc->ge = ge;
643 ge->client = gc;
644 gfio_set_client(gc, client);
cf3d8241
JA
645
646 /*
647 * Just add a default set of options, need to consider how best
648 * to handle this
649 */
650 gco = calloc(1, sizeof(*gco));
753e9e64 651 INIT_FLIST_HEAD(&gco->list);
cf3d8241
JA
652 options_default_fill(&gco->o);
653 flist_add_tail(&gco->list, &gc->o_list);
753e9e64 654 gc->o_list_nr++;
b0500ccb
JA
655}
656
657static void connect_clicked(GtkWidget *widget, gpointer data)
658{
659 struct gui_entry *ge = data;
660 struct gfio_client *gc = ge->client;
661
662 if (ge->state == GE_STATE_NEW) {
663 int ret;
19998dbc 664
b0500ccb
JA
665 if (!ge->job_file)
666 file_open(widget, ge->ui);
667 if (!ge->job_file)
668 return;
2e33101f 669
b0500ccb 670 gc = ge->client;
2e33101f 671
b0500ccb
JA
672 if (!gc->client) {
673 struct fio_client *client;
19998dbc 674
b0500ccb
JA
675 if (get_connection_details(ge)) {
676 gfio_report_error(ge, "Failed to get connection details\n");
677 return;
1252d8f2 678 }
2e33101f 679
b0500ccb
JA
680 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
681 if (!client) {
682 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
683 free(ge->host);
684 ge->host = NULL;
685 return;
1252d8f2 686 }
b0500ccb
JA
687 gfio_set_client(gc, client);
688 }
689
690 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
691 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
692 ret = fio_client_connect(gc->client);
693 if (!ret) {
694 if (!ge->ui->handler_running)
695 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
696 gfio_set_state(ge, GE_STATE_CONNECTED);
1252d8f2 697 } else {
b0500ccb 698 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2e33101f 699 }
b0500ccb
JA
700 } else {
701 fio_client_terminate(gc->client);
702 gfio_set_state(ge, GE_STATE_NEW);
703 clear_ge_ui_info(ge);
2e33101f 704 }
b0500ccb 705}
2e33101f 706
b0500ccb
JA
707static void send_clicked(GtkWidget *widget, gpointer data)
708{
709 struct gui_entry *ge = data;
2e33101f 710
b0500ccb
JA
711 if (send_job_file(ge))
712 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
713}
62bc937f 714
b0500ccb 715static GtkWidget *new_client_page(struct gui_entry *ge);
62bc937f 716
b0500ccb
JA
717static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
718{
719 struct gui_entry *ge;
a7a42ce1 720
b0500ccb
JA
721 ge = malloc(sizeof(*ge));
722 memset(ge, 0, sizeof(*ge));
723 ge->state = GE_STATE_NEW;
b0500ccb
JA
724 ge->ui = ui;
725 return ge;
726}
a7a42ce1 727
b0500ccb
JA
728static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
729{
730 struct gui_entry *ge;
a7a42ce1 731
b0500ccb 732 ge = alloc_new_gui_entry(ui);
a7a42ce1 733
b0500ccb
JA
734 ge->vbox = new_client_page(ge);
735 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
a7a42ce1 736
b0500ccb
JA
737 ge->page_label = gtk_label_new(name);
738 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
b9f3c7ed 739
b98ab718
JA
740 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
741
b0500ccb
JA
742 gtk_widget_show_all(ui->window);
743 return ge;
744}
b9f3c7ed 745
b0500ccb
JA
746static void file_new(GtkWidget *w, gpointer data)
747{
748 struct gui *ui = (struct gui *) data;
749 struct gui_entry *ge;
62bc937f 750
b0500ccb
JA
751 ge = get_new_ge_with_tab(ui, "Untitled");
752 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
753}
a7a42ce1 754
b0500ccb
JA
755/*
756 * Return the 'ge' corresponding to the tab. If the active tab is the
757 * main tab, open a new tab.
758 */
759static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
760 int *created)
761{
b0500ccb 762 if (!cur_page) {
1252d8f2 763 if (created)
b0500ccb
JA
764 *created = 1;
765 return get_new_ge_with_tab(ui, "Untitled");
766 }
b9f3c7ed 767
b0500ccb
JA
768 if (created)
769 *created = 0;
a7a42ce1 770
b98ab718 771 return g_hash_table_lookup(ui->ge_hash, &cur_page);
b0500ccb 772}
e0681f3e 773
b0500ccb
JA
774static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
775{
776 gint cur_page;
e0681f3e 777
b0500ccb
JA
778 /*
779 * Main tab is tab 0, so any current page other than 0 holds
780 * a ge entry.
781 */
782 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
783 if (cur_page)
784 return get_ge_from_page(ui, cur_page, NULL);
0cf3ece0 785
b0500ccb
JA
786 return NULL;
787}
0cf3ece0 788
b0500ccb
JA
789static void file_close(GtkWidget *w, gpointer data)
790{
791 struct gui *ui = (struct gui *) data;
792 struct gui_entry *ge;
2f99deb0 793
b0500ccb
JA
794 /*
795 * Can't close the main tab
796 */
797 ge = get_ge_from_cur_tab(ui);
798 if (ge) {
799 gtk_widget_destroy(ge->vbox);
800 return;
1252d8f2 801 }
16ce5adf 802
b98ab718 803 if (g_hash_table_size(ui->ge_hash)) {
b0500ccb
JA
804 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
805 return;
38634cb1
JA
806 }
807
b0500ccb
JA
808 gfio_quit(ui);
809}
2f99deb0 810
b0500ccb
JA
811static void file_add_recent(struct gui *ui, const gchar *uri)
812{
813 GtkRecentData grd;
2f99deb0 814
b0500ccb
JA
815 memset(&grd, 0, sizeof(grd));
816 grd.display_name = strdup("gfio");
817 grd.description = strdup("Fio job file");
818 grd.mime_type = strdup(GFIO_MIME);
819 grd.app_name = strdup(g_get_application_name());
820 grd.app_exec = strdup("gfio %f/%u");
85dd01e7 821
b0500ccb
JA
822 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
823}
85dd01e7 824
b0500ccb
JA
825static gchar *get_filename_from_uri(const gchar *uri)
826{
827 if (strncmp(uri, "file://", 7))
828 return strdup(uri);
16ce5adf 829
b0500ccb
JA
830 return strdup(uri + 7);
831}
16ce5adf 832
b0500ccb
JA
833static int do_file_open(struct gui_entry *ge, const gchar *uri)
834{
835 struct fio_client *client;
f5c6726e 836
b0500ccb 837 assert(!ge->job_file);
38634cb1 838
b0500ccb 839 ge->job_file = get_filename_from_uri(uri);
38634cb1 840
b0500ccb
JA
841 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
842 if (client) {
832fde79
JA
843 char *label = strdup(uri);
844
845 basename(label);
846 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
847 free(label);
848
b0500ccb
JA
849 gfio_client_added(ge, client);
850 file_add_recent(ge->ui, uri);
851 return 0;
852 }
38634cb1 853
b0500ccb
JA
854 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
855 free(ge->host);
856 ge->host = NULL;
b98ab718
JA
857 free(ge->job_file);
858 ge->job_file = NULL;
b0500ccb
JA
859 return 1;
860}
38634cb1 861
b0500ccb
JA
862static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
863{
864 struct gui_entry *ge;
865 gint cur_page;
866 int ret, ge_is_new = 0;
2f99deb0 867
b0500ccb
JA
868 /*
869 * Creates new tab if current tab is the main window, or the
870 * current tab already has a client.
871 */
872 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
873 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
874 if (ge->client) {
875 ge = get_new_ge_with_tab(ui, "Untitled");
876 ge_is_new = 1;
38634cb1 877 }
2f99deb0 878
b0500ccb
JA
879 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
880
881 if (get_connection_details(ge)) {
882 if (ge_is_new)
883 gtk_widget_destroy(ge->vbox);
3c3ed070 884
b0500ccb
JA
885 return 1;
886 }
a6790906 887
b0500ccb 888 ret = do_file_open(ge, uri);
a6790906 889
b0500ccb
JA
890 if (!ret) {
891 if (ge->server_start)
892 gfio_start_server(ui);
893 } else {
894 if (ge_is_new)
895 gtk_widget_destroy(ge->vbox);
a6790906
JA
896 }
897
b0500ccb
JA
898 return ret;
899}
a6790906 900
b0500ccb
JA
901static void recent_open(GtkAction *action, gpointer data)
902{
903 struct gui *ui = (struct gui *) data;
904 GtkRecentInfo *info;
905 const gchar *uri;
0420ba6a 906
b0500ccb
JA
907 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
908 uri = gtk_recent_info_get_uri(info);
0420ba6a 909
b0500ccb
JA
910 do_file_open_with_tab(ui, uri);
911}
a7a42ce1 912
b0500ccb
JA
913static void file_open(GtkWidget *w, gpointer data)
914{
915 struct gui *ui = data;
916 GtkWidget *dialog;
917 GtkFileFilter *filter;
918 gchar *filename;
919
920 dialog = gtk_file_chooser_dialog_new("Open File",
921 GTK_WINDOW(ui->window),
922 GTK_FILE_CHOOSER_ACTION_OPEN,
923 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
924 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
925 NULL);
926 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
927
928 filter = gtk_file_filter_new();
929 gtk_file_filter_add_pattern(filter, "*.fio");
930 gtk_file_filter_add_pattern(filter, "*.job");
931 gtk_file_filter_add_pattern(filter, "*.ini");
932 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
933 gtk_file_filter_set_name(filter, "Fio job file");
934 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
935
936 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
937 gtk_widget_destroy(dialog);
938 return;
1252d8f2 939 }
0420ba6a 940
b0500ccb 941 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
0420ba6a 942
b0500ccb 943 gtk_widget_destroy(dialog);
0420ba6a 944
b0500ccb
JA
945 do_file_open_with_tab(ui, filename);
946 g_free(filename);
947}
0420ba6a 948
b0500ccb
JA
949static void file_save(GtkWidget *w, gpointer data)
950{
951 struct gui *ui = data;
952 GtkWidget *dialog;
0420ba6a 953
b0500ccb
JA
954 dialog = gtk_file_chooser_dialog_new("Save File",
955 GTK_WINDOW(ui->window),
956 GTK_FILE_CHOOSER_ACTION_SAVE,
957 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
958 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
959 NULL);
960
961 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
962 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
963
964 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
965 char *filename;
966
967 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
968 // save_job_file(filename);
969 g_free(filename);
0420ba6a 970 }
b0500ccb
JA
971 gtk_widget_destroy(dialog);
972}
0420ba6a 973
9b260bdf
JA
974static void view_log_destroy(GtkWidget *w, gpointer data)
975{
976 struct gui *ui = (struct gui *) data;
977
f762cef9 978 g_object_ref(G_OBJECT(ui->log_tree));
9b260bdf
JA
979 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
980 gtk_widget_destroy(w);
4cbe7211 981 ui->log_view = NULL;
9b260bdf
JA
982}
983
1252d8f2 984void gfio_view_log(struct gui *ui)
9b260bdf 985{
4cbe7211 986 GtkWidget *win, *scroll, *vbox, *box;
9b260bdf 987
4cbe7211
JA
988 if (ui->log_view)
989 return;
990
991 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 992 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 993 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 994
4cbe7211
JA
995 scroll = gtk_scrolled_window_new(NULL, NULL);
996
997 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
998
999 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1000
1001 box = gtk_hbox_new(TRUE, 0);
f762cef9 1002 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
4cbe7211
JA
1003 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1004 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1005
1006 vbox = gtk_vbox_new(TRUE, 5);
f762cef9 1007 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
9b260bdf 1008
4cbe7211 1009 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
1010 gtk_widget_show_all(win);
1011}
1012
1252d8f2
JA
1013static void view_log(GtkWidget *w, gpointer data)
1014{
1015 struct gui *ui = (struct gui *) data;
1016
1017 gfio_view_log(ui);
1018}
1019
85dd01e7
JA
1020static void connect_job_entry(GtkWidget *w, gpointer data)
1021{
1022 struct gui *ui = (struct gui *) data;
1023 struct gui_entry *ge;
3c3ed070 1024
85dd01e7
JA
1025 ge = get_ge_from_cur_tab(ui);
1026 if (ge)
1027 connect_clicked(w, ge);
1028}
1029
1030static void send_job_entry(GtkWidget *w, gpointer data)
1031{
1032 struct gui *ui = (struct gui *) data;
1033 struct gui_entry *ge;
1034
1035 ge = get_ge_from_cur_tab(ui);
1036 if (ge)
1037 send_clicked(w, ge);
85dd01e7
JA
1038}
1039
1040static void edit_job_entry(GtkWidget *w, gpointer data)
16ce5adf 1041{
9af4a244 1042 struct gui *ui = (struct gui *) data;
789f4ccd 1043 struct gui_entry *ge;
9af4a244 1044
789f4ccd
JA
1045 ge = get_ge_from_cur_tab(ui);
1046 if (ge && ge->client)
cf3d8241 1047 gopt_get_options_window(ui->window, ge->client);
16ce5adf
JA
1048}
1049
85dd01e7
JA
1050static void start_job_entry(GtkWidget *w, gpointer data)
1051{
1052 struct gui *ui = (struct gui *) data;
1053 struct gui_entry *ge;
1054
1055 ge = get_ge_from_cur_tab(ui);
1056 if (ge)
1057 start_job_clicked(w, ge);
1058}
1059
781ccba6
JA
1060static void view_results(GtkWidget *w, gpointer data)
1061{
1062 struct gui *ui = (struct gui *) data;
1063 struct gfio_client *gc;
1064 struct gui_entry *ge;
1065
1066 ge = get_ge_from_cur_tab(ui);
1067 if (!ge)
1068 return;
1069
1070 if (ge->results_window)
1071 return;
1072
1073 gc = ge->client;
1074 if (gc && gc->nr_results)
1075 gfio_display_end_results(gc);
1076}
1077
a1e7972d 1078static void __update_graph_settings(struct gfio_graphs *g)
8577f4fd
JA
1079{
1080 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
a1e7972d 1081 graph_set_font(g->iops_graph, gfio_graph_font);
8577f4fd 1082 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
a1e7972d 1083 graph_set_font(g->bandwidth_graph, gfio_graph_font);
8577f4fd
JA
1084}
1085
a1e7972d 1086static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
b98ab718
JA
1087{
1088 struct gui_entry *ge = (struct gui_entry *) value;
a1e7972d 1089 GdkEvent *ev;
b98ab718 1090
a1e7972d
JA
1091 __update_graph_settings(&ge->graphs);
1092
1093 ev = gdk_event_new(GDK_EXPOSE);
b6ab6a31 1094 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
a1e7972d 1095 gdk_event_free(ev);
b98ab718
JA
1096}
1097
8577f4fd
JA
1098static void update_graph_limits(void)
1099{
b98ab718 1100 struct gui *ui = &main_ui;
a1e7972d
JA
1101 GdkEvent *ev;
1102
1103 __update_graph_settings(&ui->graphs);
8577f4fd 1104
a1e7972d 1105 ev = gdk_event_new(GDK_EXPOSE);
b6ab6a31 1106 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
a1e7972d 1107 gdk_event_free(ev);
8577f4fd 1108
a1e7972d 1109 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
8577f4fd
JA
1110}
1111
46974a7d
JA
1112static void preferences(GtkWidget *w, gpointer data)
1113{
f3e8440f 1114 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 1115 GtkWidget *hbox, *spin, *entry, *spin_int;
6e02ad6c 1116 struct gui *ui = (struct gui *) data;
46974a7d
JA
1117 int i;
1118
1119 dialog = gtk_dialog_new_with_buttons("Preferences",
6e02ad6c 1120 GTK_WINDOW(ui->window),
46974a7d
JA
1121 GTK_DIALOG_DESTROY_WITH_PARENT,
1122 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1123 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1124 NULL);
1125
8577f4fd 1126 frame = gtk_frame_new("Graphing");
f762cef9
JA
1127 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1128 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
f3e8440f
JA
1129 vbox = gtk_vbox_new(FALSE, 6);
1130 gtk_container_add(GTK_CONTAINER(frame), vbox);
1131
1cf6bca5
JA
1132 hbox = gtk_hbox_new(FALSE, 5);
1133 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1134 entry = gtk_label_new("Font face to use for graph labels");
1135 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1136
a1e7972d 1137 font = gtk_font_button_new_with_font(gfio_graph_font);
1cf6bca5 1138 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 1139
8577f4fd
JA
1140 box = gtk_vbox_new(FALSE, 6);
1141 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1142
1143 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 1144 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
1145 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1146 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1147
c05d9056 1148 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
8577f4fd 1149
1cf6bca5
JA
1150 box = gtk_vbox_new(FALSE, 6);
1151 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1152
1153 hbox = gtk_hbox_new(FALSE, 5);
1154 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1155 entry = gtk_label_new("Client ETA request interval (msec)");
1156 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1157
1158 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4 1159 frame = gtk_frame_new("Debug logging");
f762cef9
JA
1160 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1161 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
a31d9fa4
JA
1162 vbox = gtk_vbox_new(FALSE, 6);
1163 gtk_container_add(GTK_CONTAINER(frame), vbox);
1164
1165 box = gtk_hbox_new(FALSE, 6);
1166 gtk_container_add(GTK_CONTAINER(vbox), box);
1167
1168 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1169
1170 for (i = 0; i < FD_DEBUG_MAX; i++) {
1171 if (i == 7) {
1172 box = gtk_hbox_new(FALSE, 6);
1173 gtk_container_add(GTK_CONTAINER(vbox), box);
1174 }
1175
1176
1177 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1178 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1179 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1180 }
1181
46974a7d
JA
1182 gtk_widget_show_all(dialog);
1183
1184 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1185 gtk_widget_destroy(dialog);
1186 return;
1187 }
1188
1189 for (i = 0; i < FD_DEBUG_MAX; i++) {
1190 int set;
1191
1192 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1193 if (set)
1194 fio_debug |= (1UL << i);
1195 }
1196
f3e8440f 1197 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
1198 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1199 update_graph_limits();
1cf6bca5 1200 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 1201
46974a7d
JA
1202 gtk_widget_destroy(dialog);
1203}
1204
0420ba6a
JA
1205static void about_dialog(GtkWidget *w, gpointer data)
1206{
81e4ea6e
JA
1207 const char *authors[] = {
1208 "Jens Axboe <axboe@kernel.dk>",
1209 "Stephen Carmeron <stephenmcameron@gmail.com>",
1210 NULL
1211 };
84a72ed3
JA
1212 const char *license[] = {
1213 "Fio is free software; you can redistribute it and/or modify "
1214 "it under the terms of the GNU General Public License as published by "
1215 "the Free Software Foundation; either version 2 of the License, or "
1216 "(at your option) any later version.\n",
1217 "Fio is distributed in the hope that it will be useful, "
1218 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1219 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1220 "GNU General Public License for more details.\n",
1221 "You should have received a copy of the GNU General Public License "
1222 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1223 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1224 };
1225 char *license_trans;
1226
1227 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1228 license[2], "\n", NULL);
81e4ea6e 1229
0420ba6a
JA
1230 gtk_show_about_dialog(NULL,
1231 "program-name", "gfio",
1232 "comments", "Gtk2 UI for fio",
84a72ed3 1233 "license", license_trans,
81e4ea6e
JA
1234 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1235 "authors", authors,
0420ba6a 1236 "version", fio_version_string,
81e4ea6e 1237 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
1238 "logo-icon-name", "fio",
1239 /* Must be last: */
81e4ea6e 1240 "wrap-license", TRUE,
0420ba6a 1241 NULL);
84a72ed3 1242
2f99deb0 1243 g_free(license_trans);
0420ba6a
JA
1244}
1245
1246static GtkActionEntry menu_items[] = {
46974a7d 1247 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 1248 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
16ce5adf 1249 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
46974a7d 1250 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 1251 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
16ce5adf 1252 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
46974a7d
JA
1253 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1254 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1255 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 1256 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
781ccba6 1257 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
bc271d87 1258 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
85dd01e7
JA
1259 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1260 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1261 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
46974a7d
JA
1262 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1263 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 1264};
3e47bd25 1265static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
1266
1267static const gchar *ui_string = " \
1268 <ui> \
1269 <menubar name=\"MainMenu\"> \
1270 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0 1271 <menuitem name=\"New\" action=\"NewFile\" /> \
bf64138b 1272 <menuitem name=\"Open\" action=\"OpenFile\" /> \
16ce5adf 1273 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2f99deb0 1274 <separator name=\"Separator1\"/> \
0420ba6a 1275 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 1276 <separator name=\"Separator2\"/> \
2f99deb0
JA
1277 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1278 <separator name=\"Separator3\"/> \
261f21d0
JA
1279 <placeholder name=\"FileRecentFiles\"/> \
1280 <separator name=\"Separator4\"/> \
0420ba6a
JA
1281 <menuitem name=\"Quit\" action=\"Quit\" /> \
1282 </menu> \
16ce5adf 1283 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
85dd01e7 1284 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
261f21d0 1285 <separator name=\"Separator5\"/> \
85dd01e7
JA
1286 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1287 <menuitem name=\"Send job\" action=\"SendJob\" /> \
261f21d0 1288 <separator name=\"Separator6\"/> \
85dd01e7 1289 <menuitem name=\"Start job\" action=\"StartJob\" /> \
16ce5adf 1290 </menu>\
9b260bdf 1291 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
781ccba6
JA
1292 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1293 <separator name=\"Separator7\"/> \
9b260bdf
JA
1294 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1295 </menu>\
0420ba6a
JA
1296 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1297 <menuitem name=\"About\" action=\"About\" /> \
1298 </menu> \
1299 </menubar> \
1300 </ui> \
1301";
1302
4cbe7211
JA
1303static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1304 struct gui *ui)
0420ba6a 1305{
ca664f49 1306 GtkActionGroup *action_group;
0420ba6a
JA
1307 GError *error = 0;
1308
1309 action_group = gtk_action_group_new("Menu");
4cbe7211 1310 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
1311
1312 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1313 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1314
1315 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
02421e69 1316
0420ba6a
JA
1317 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1318}
1319
1320void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1252d8f2 1321 GtkWidget *vbox, GtkUIManager *ui_manager)
0420ba6a 1322{
1252d8f2 1323 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
0420ba6a
JA
1324}
1325
c80b74b0
JA
1326static void combo_entry_changed(GtkComboBox *box, gpointer data)
1327{
1328 struct gui_entry *ge = (struct gui_entry *) data;
1329 gint index;
1330
1331 index = gtk_combo_box_get_active(box);
1332
1333 multitext_set_entry(&ge->eta.iotype, index);
99d633af 1334 multitext_set_entry(&ge->eta.bs, index);
c80b74b0
JA
1335 multitext_set_entry(&ge->eta.ioengine, index);
1336 multitext_set_entry(&ge->eta.iodepth, index);
1337}
1338
1339static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1340{
1341 struct gui_entry *ge = (struct gui_entry *) data;
1342
1343 multitext_free(&ge->eta.iotype);
99d633af 1344 multitext_free(&ge->eta.bs);
c80b74b0
JA
1345 multitext_free(&ge->eta.ioengine);
1346 multitext_free(&ge->eta.iodepth);
1347}
1348
2f99deb0 1349static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 1350{
2f99deb0 1351 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 1352 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
0420ba6a 1353
2f99deb0 1354 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 1355
65476336
JA
1356 top_align = gtk_alignment_new(0, 0, 1, 0);
1357 top_vbox = gtk_vbox_new(FALSE, 3);
1358 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1359 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2f99deb0
JA
1360
1361 probe = gtk_frame_new("Job");
1362 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1363 probe_frame = gtk_vbox_new(FALSE, 3);
1364 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1365
1366 probe_box = gtk_hbox_new(FALSE, 3);
1367 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1368 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1369 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1370 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1371 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1372
1373 probe_box = gtk_hbox_new(FALSE, 3);
1374 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1375
3863d1ad 1376 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
c80b74b0
JA
1377 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1378 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1379 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
99d633af 1380 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
c80b74b0
JA
1381 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1382 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2f99deb0
JA
1383 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1384 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1385
1386 probe_box = gtk_hbox_new(FALSE, 3);
1387 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
b78ca651
JA
1388 ge->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1389 ge->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1390 ge->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1391 ge->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1392 ge->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1393 ge->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
dbc542d4 1394
2f99deb0
JA
1395 /*
1396 * Only add this if we have a commit rate
2839f0c6 1397 */
2f99deb0
JA
1398#if 0
1399 probe_box = gtk_hbox_new(FALSE, 3);
1400 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 1401
2f99deb0
JA
1402 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1403 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 1404
2f99deb0
JA
1405 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1406 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1407#endif
ff1f3280 1408
2f99deb0
JA
1409 /*
1410 * Set up a drawing area and IOPS and bandwidth graphs
1411 */
2f99deb0 1412 ge->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 1413 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
57f9d28e 1414 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2a95f716 1415 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
b6ab6a31 1416 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
2f99deb0
JA
1417 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1418 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1419 G_CALLBACK(on_config_drawing_area), &ge->graphs);
65476336
JA
1420 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1421 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 1422 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 1423 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 1424 ge->graphs.drawing_area);
65476336 1425 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
04cc6b77 1426
2f99deb0
JA
1427 setup_graphs(&ge->graphs);
1428
1429 /*
3c3ed070 1430 * Set up alignments for widgets at the bottom of ui,
2f99deb0
JA
1431 * align bottom left, expand horizontally but not vertically
1432 */
65476336 1433 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2f99deb0 1434 ge->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
1435 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1436 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2f99deb0 1437
05775438 1438 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
0420ba6a 1439
c36f98d9 1440 /*
2f99deb0
JA
1441 * Set up thread status progress bar
1442 */
1443 ge->thread_status_pb = gtk_progress_bar_new();
1444 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1445 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1446 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1447
1448
1449 return main_vbox;
1450}
1451
1452static GtkWidget *new_main_page(struct gui *ui)
1453{
1454 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
65476336 1455 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2f99deb0
JA
1456
1457 main_vbox = gtk_vbox_new(FALSE, 3);
1458
1459 /*
1460 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
1461 * align top left, expand horizontally but not vertically
1462 */
65476336
JA
1463 top_align = gtk_alignment_new(0, 0, 1, 0);
1464 top_vbox = gtk_vbox_new(FALSE, 0);
1465 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1466 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
c36f98d9 1467
2f99deb0
JA
1468 probe = gtk_frame_new("Run statistics");
1469 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
1470 probe_frame = gtk_vbox_new(FALSE, 3);
1471 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1472
1473 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 1474 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 1475 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
b78ca651
JA
1476 ui->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1477 ui->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1478 ui->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1479 ui->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1480 ui->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1481 ui->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
3e47bd25 1482
807f9971
JA
1483 /*
1484 * Only add this if we have a commit rate
1485 */
1486#if 0
3e47bd25
JA
1487 probe_box = gtk_hbox_new(FALSE, 3);
1488 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
1489
1490 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1491 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1492
3e47bd25
JA
1493 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1494 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 1495#endif
3e47bd25 1496
736f2dff 1497 /*
2fd3bb0e 1498 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 1499 */
2f99deb0 1500 ui->graphs.drawing_area = gtk_drawing_area_new();
2f99deb0 1501 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
57f9d28e 1502 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2a95f716 1503 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
b6ab6a31 1504 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
2f99deb0
JA
1505 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1506 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1507 G_CALLBACK(on_config_drawing_area), &ui->graphs);
65476336
JA
1508 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1509 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
736f2dff 1510 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
65476336 1511 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2f99deb0 1512 ui->graphs.drawing_area);
65476336 1513 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
e164534f 1514 TRUE, TRUE, 0);
736f2dff 1515
2f99deb0 1516 setup_graphs(&ui->graphs);
2fd3bb0e 1517
c36f98d9 1518 /*
3c3ed070 1519 * Set up alignments for widgets at the bottom of ui,
c36f98d9
SC
1520 * align bottom left, expand horizontally but not vertically
1521 */
65476336 1522 bottom_align = gtk_alignment_new(0, 1, 1, 0);
c36f98d9 1523 ui->buttonbox = gtk_hbox_new(FALSE, 0);
65476336
JA
1524 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1525 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
c36f98d9 1526
3ec62ec4
JA
1527 /*
1528 * Set up thread status progress bar
1529 */
1530 ui->thread_status_pb = gtk_progress_bar_new();
1531 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 1532 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
1533 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1534
2f99deb0
JA
1535 return main_vbox;
1536}
1537
1538static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1539 guint page, gpointer data)
1540
1541{
02421e69 1542 struct gui *ui = (struct gui *) data;
85dd01e7
JA
1543 struct gui_entry *ge;
1544
1545 if (!page) {
1546 set_job_menu_visible(ui, 0);
781ccba6 1547 set_view_results_visible(ui, 0);
85dd01e7
JA
1548 return TRUE;
1549 }
1550
1551 set_job_menu_visible(ui, 1);
6e02ad6c 1552 ge = get_ge_from_page(ui, page, NULL);
85dd01e7
JA
1553 if (ge)
1554 update_button_states(ui, ge);
02421e69 1555
2f99deb0
JA
1556 return TRUE;
1557}
1558
38634cb1
JA
1559static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1560{
1561 time_t time_a = gtk_recent_info_get_visited(a);
1562 time_t time_b = gtk_recent_info_get_visited(b);
1563
1564 return time_b - time_a;
1565}
1566
1567static void add_recent_file_items(struct gui *ui)
1568{
1569 const gchar *gfio = g_get_application_name();
1570 GList *items, *item;
1571 int i = 0;
1572
1573 if (ui->recent_ui_id) {
1574 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1575 gtk_ui_manager_ensure_update(ui->uimanager);
1576 }
1577 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1578
1579 if (ui->actiongroup) {
1580 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1581 g_object_unref(ui->actiongroup);
1582 }
1583 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1584
1585 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1586
1587 items = gtk_recent_manager_get_items(ui->recentmanager);
1588 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1589
1590 for (item = items; item && item->data; item = g_list_next(item)) {
1591 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1592 gchar *action_name;
1593 const gchar *label;
1594 GtkAction *action;
1595
1596 if (!gtk_recent_info_has_application(info, gfio))
1597 continue;
1598
1599 /*
1600 * We only support local files for now
1601 */
1602 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1603 continue;
1604
1605 action_name = g_strdup_printf("RecentFile%u", i++);
1606 label = gtk_recent_info_get_display_name(info);
1607
1608 action = g_object_new(GTK_TYPE_ACTION,
1609 "name", action_name,
1610 "label", label, NULL);
1611
1612 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1613 gtk_recent_info_ref(info),
1614 (GDestroyNotify) gtk_recent_info_unref);
1615
1616
1617 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1618
1619 gtk_action_group_add_action(ui->actiongroup, action);
1620 g_object_unref(action);
1621
1622 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1623 "/MainMenu/FileMenu/FileRecentFiles",
1624 label, action_name,
1625 GTK_UI_MANAGER_MENUITEM, FALSE);
1626
1627 g_free(action_name);
1628
1629 if (i == 8)
1630 break;
1631 }
1632
1633 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1634 g_list_free(items);
1635}
1636
a6790906 1637static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
6e02ad6c
JA
1638 gint x, gint y, GtkSelectionData *seldata,
1639 guint info, guint time, gpointer *data)
a6790906 1640{
6e02ad6c 1641 struct gui *ui = (struct gui *) data;
a6790906
JA
1642 gchar **uris;
1643 GtkWidget *source;
a6790906
JA
1644
1645 source = gtk_drag_get_source_widget(ctx);
1646 if (source && widget == gtk_widget_get_toplevel(source)) {
1647 gtk_drag_finish(ctx, FALSE, FALSE, time);
1648 return;
1649 }
1650
6e02ad6c 1651 uris = gtk_selection_data_get_uris(seldata);
a6790906
JA
1652 if (!uris) {
1653 gtk_drag_finish(ctx, FALSE, FALSE, time);
1654 return;
1655 }
1656
0cf3ece0
JA
1657 if (uris[0])
1658 do_file_open_with_tab(ui, uris[0]);
a6790906
JA
1659
1660 gtk_drag_finish(ctx, TRUE, FALSE, time);
1661 g_strfreev(uris);
1662}
1663
2f99deb0
JA
1664static void init_ui(int *argc, char **argv[], struct gui *ui)
1665{
1666 GtkSettings *settings;
02421e69 1667 GtkWidget *vbox;
2f99deb0
JA
1668
1669 /* Magical g*thread incantation, you just need this thread stuff.
1670 * Without it, the update that happens in gfio_update_thread_status
1671 * doesn't really happen in a timely fashion, you need expose events
1672 */
1673 if (!g_thread_supported())
1674 g_thread_init(NULL);
1675 gdk_threads_init();
1676
1677 gtk_init(argc, argv);
1678 settings = gtk_settings_get_default();
1679 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1680 g_type_init();
0950e832 1681 gdk_color_parse("#fffff4", &gfio_color_lightyellow);
1252d8f2 1682 gdk_color_parse("white", &gfio_color_white);
3c3ed070 1683
2f99deb0 1684 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
814479d5 1685 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2f99deb0
JA
1686 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1687
6e02ad6c
JA
1688 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1689 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
2f99deb0
JA
1690
1691 ui->vbox = gtk_vbox_new(FALSE, 0);
1692 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1693
02421e69
JA
1694 ui->uimanager = gtk_ui_manager_new();
1695 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1696 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2f99deb0 1697
38634cb1
JA
1698 ui->recentmanager = gtk_recent_manager_get_default();
1699 add_recent_file_items(ui);
1700
2f99deb0
JA
1701 ui->notebook = gtk_notebook_new();
1702 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 1703 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 1704 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
1705 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1706
1707 vbox = new_main_page(ui);
0cf3ece0 1708 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
a6790906
JA
1709 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1710 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
2f99deb0
JA
1711
1712 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1713
9b260bdf 1714 gfio_ui_setup_log(ui);
3ec62ec4 1715
ff1f3280
SC
1716 gtk_widget_show_all(ui->window);
1717}
1718
8232e285 1719int main(int argc, char *argv[], char *envp[])
ff1f3280 1720{
8232e285
SC
1721 if (initialize_fio(envp))
1722 return 1;
0420ba6a
JA
1723 if (fio_init_options())
1724 return 1;
a1820207 1725
753e9e64
JA
1726 gopt_init();
1727
2f99deb0 1728 memset(&main_ui, 0, sizeof(main_ui));
b98ab718 1729 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
2f99deb0
JA
1730
1731 init_ui(&argc, &argv, &main_ui);
5b7573ab 1732
2839f0c6 1733 gdk_threads_enter();
ff1f3280 1734 gtk_main();
2839f0c6 1735 gdk_threads_leave();
b98ab718
JA
1736
1737 g_hash_table_destroy(main_ui.ge_hash);
753e9e64
JA
1738
1739 gopt_exit();
ff1f3280
SC
1740 return 0;
1741}