gfio: color code the iops/bw fields
[fio.git] / gfio.c
... / ...
CommitLineData
1/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
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 */
24#include <locale.h>
25#include <malloc.h>
26#include <string.h>
27
28#include <glib.h>
29#include <cairo.h>
30#include <gtk/gtk.h>
31
32#include "fio.h"
33#include "gfio.h"
34#include "ghelpers.h"
35#include "goptions.h"
36#include "gerror.h"
37#include "gclient.h"
38#include "graph.h"
39
40static int gfio_server_running;
41static unsigned int gfio_graph_limit = 100;
42
43GdkColor gfio_color_white;
44GdkColor gfio_color_lightyellow;
45const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
46
47typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
48
49static void connect_clicked(GtkWidget *widget, gpointer data);
50static void start_job_clicked(GtkWidget *widget, gpointer data);
51static void send_clicked(GtkWidget *widget, gpointer data);
52
53static struct button_spec {
54 const char *buttontext;
55 clickfunction f;
56 const char *tooltiptext[2];
57 const int start_sensitive;
58} buttonspeclist[] = {
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 },
77};
78
79static void setup_iops_graph(struct gfio_graphs *gg)
80{
81 struct graph *g;
82
83 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
84 graph_title(g, "IOPS (IOs/sec)");
85 graph_x_title(g, "Time (secs)");
86 gg->read_iops = graph_add_label(g, "Read IOPS");
87 gg->write_iops = graph_add_label(g, "Write IOPS");
88 gg->trim_iops = graph_add_label(g, "Trim IOPS");
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);
91 graph_set_color(g, gg->trim_iops, GFIO_IOPS_R, GFIO_IOPS_G, GFIO_IOPS_B);
92 line_graph_set_data_count_limit(g, gfio_graph_limit);
93 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
94 graph_set_graph_all_zeroes(g, 0);
95 gg->iops_graph = g;
96}
97
98static void setup_bandwidth_graph(struct gfio_graphs *gg)
99{
100 struct graph *g;
101
102 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
103 graph_title(g, "Bandwidth (bytes/sec)");
104 graph_x_title(g, "Time (secs)");
105 gg->read_bw = graph_add_label(g, "Read Bandwidth");
106 gg->write_bw = graph_add_label(g, "Write Bandwidth");
107 gg->trim_bw = graph_add_label(g, "Trim Bandwidth");
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);
110 graph_set_color(g, gg->trim_bw, GFIO_IOPS_R, GFIO_IOPS_G, GFIO_IOPS_B);
111 graph_set_base_offset(g, 1);
112 line_graph_set_data_count_limit(g, 100);
113 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
114 graph_set_graph_all_zeroes(g, 0);
115 gg->bandwidth_graph = g;
116}
117
118static void setup_graphs(struct gfio_graphs *g)
119{
120 setup_iops_graph(g);
121 setup_bandwidth_graph(g);
122}
123
124void clear_ge_ui_info(struct gui_entry *ge)
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), "");
130#if 0
131 /* should we empty it... */
132 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
133#endif
134 multitext_update_entry(&ge->eta.iotype, 0, "");
135 multitext_update_entry(&ge->eta.bs, 0, "");
136 multitext_update_entry(&ge->eta.ioengine, 0, "");
137 multitext_update_entry(&ge->eta.iodepth, 0, "");
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), "");
144}
145
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
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
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;
221
222 switch (ge->state) {
223 default:
224 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
225 /* fall through to new state */
226 case GE_STATE_NEW:
227 connect_state = 1;
228 edit_state = 1;
229 connect_str = "Connect";
230 send_state = 0;
231 start_state = 0;
232 break;
233 case GE_STATE_CONNECTED:
234 connect_state = 1;
235 edit_state = 1;
236 connect_str = "Disconnect";
237 send_state = 1;
238 start_state = 0;
239 break;
240 case GE_STATE_JOB_SENT:
241 connect_state = 1;
242 edit_state = 1;
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
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));
275
276 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
277 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
278
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);
282
283 if (ge->client && ge->client->nr_results)
284 set_view_results_visible(ui, 1);
285 else
286 set_view_results_visible(ui, 0);
287}
288
289void gfio_set_state(struct gui_entry *ge, unsigned int state)
290{
291 ge->state = state;
292 update_button_states(ge->ui, ge);
293}
294
295static void gfio_ui_setup_log(struct gui *ui)
296{
297 GtkTreeSelection *selection;
298 GtkListStore *model;
299 GtkWidget *tree_view;
300
301 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
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);
308 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
309 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
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);
314 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
315
316 ui->log_model = model;
317 ui->log_tree = tree_view;
318}
319
320static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
321 gpointer data)
322{
323 guint width = gtk_widget_get_allocated_width(w);
324 guint height = gtk_widget_get_allocated_height(w);
325 struct gfio_graphs *g = data;
326
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);
330 graph_set_position(g->bandwidth_graph, 0, 0);
331 return TRUE;
332}
333
334static void draw_graph(struct graph *g, cairo_t *cr)
335{
336 line_graph_draw(g, cr);
337 cairo_stroke(cr);
338}
339
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;
346
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);
351
352 if (text) {
353 gtk_tooltip_set_text(tooltip, text);
354 return TRUE;
355 }
356
357 return FALSE;
358}
359
360static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
361{
362 struct gfio_graphs *g = p;
363 cairo_t *cr;
364
365 cr = gdk_cairo_create(gtk_widget_get_window(w));
366
367 if (graph_has_tooltips(g->iops_graph) ||
368 graph_has_tooltips(g->bandwidth_graph)) {
369 g_object_set(w, "has-tooltip", TRUE, NULL);
370 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
371 }
372
373 cairo_set_source_rgb(cr, 0, 0, 0);
374 draw_graph(g->iops_graph, cr);
375 draw_graph(g->bandwidth_graph, cr);
376 cairo_destroy(cr);
377
378 return FALSE;
379}
380
381/*
382 * FIXME: need more handling here
383 */
384static void ge_destroy(struct gui_entry *ge)
385{
386 struct gfio_client *gc = ge->client;
387
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);
396 }
397
398 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
399
400 free(ge->job_file);
401 free(ge->host);
402 free(ge);
403}
404
405static void ge_widget_destroy(GtkWidget *w, gpointer data)
406{
407 struct gui_entry *ge = (struct gui_entry *) data;
408
409 ge_destroy(ge);
410}
411
412static void gfio_quit(struct gui *ui)
413{
414 gtk_main_quit();
415}
416
417static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
418 gpointer data)
419{
420 struct gui *ui = (struct gui *) data;
421
422 gfio_quit(ui);
423}
424
425static void *job_thread(void *arg)
426{
427 struct gui *ui = arg;
428
429 ui->handler_running = 1;
430 fio_handle_clients(&gfio_client_ops);
431 ui->handler_running = 0;
432 return NULL;
433}
434
435static int send_job_file(struct gui_entry *ge)
436{
437 struct gfio_client *gc = ge->client;
438 int ret = 0;
439
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
452 ret = fio_client_send_ini(gc->client, ge->job_file);
453 if (!ret)
454 return 0;
455
456 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
457 return 1;
458}
459
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}
468
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);
475 }
476}
477
478static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
479 gpointer data)
480{
481 struct gui_entry *ge = data;
482 struct gfio_client *gc = ge->client;
483
484 if (gc)
485 fio_start_client(gc->client);
486}
487
488static void file_open(GtkWidget *w, gpointer data);
489
490struct connection_widgets
491{
492 GtkWidget *hentry;
493 GtkWidget *combo;
494 GtkWidget *button;
495};
496
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;
503
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 */
509 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
510 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
511 uses_net = 1;
512 g_free(ctext);
513
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 }
521
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 }
529}
530
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;
537
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");
548 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
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
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");
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);
597
598 /*
599 * Connect edit signal, so we can show/not-show the auto start button
600 */
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);
603
604 gtk_widget_show_all(dialog);
605
606 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
607 gtk_widget_destroy(dialog);
608 return 1;
609 }
610
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));
613
614 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
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);
622
623 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
624
625 gtk_widget_destroy(dialog);
626 return 0;
627}
628
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{
637 struct gfio_client_options *gco;
638 struct gfio_client *gc;
639
640 gc = calloc(1, sizeof(*gc));
641 INIT_FLIST_HEAD(&gc->o_list);
642 gc->ge = ge;
643 ge->client = gc;
644 gfio_set_client(gc, client);
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));
651 INIT_FLIST_HEAD(&gco->list);
652 options_default_fill(&gco->o);
653 flist_add_tail(&gco->list, &gc->o_list);
654 gc->o_list_nr++;
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;
664
665 if (!ge->job_file)
666 file_open(widget, ge->ui);
667 if (!ge->job_file)
668 return;
669
670 gc = ge->client;
671
672 if (!gc->client) {
673 struct fio_client *client;
674
675 if (get_connection_details(ge)) {
676 gfio_report_error(ge, "Failed to get connection details\n");
677 return;
678 }
679
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;
686 }
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);
697 } else {
698 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
699 }
700 } else {
701 fio_client_terminate(gc->client);
702 gfio_set_state(ge, GE_STATE_NEW);
703 clear_ge_ui_info(ge);
704 }
705}
706
707static void send_clicked(GtkWidget *widget, gpointer data)
708{
709 struct gui_entry *ge = data;
710
711 if (send_job_file(ge))
712 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
713}
714
715static GtkWidget *new_client_page(struct gui_entry *ge);
716
717static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
718{
719 struct gui_entry *ge;
720
721 ge = malloc(sizeof(*ge));
722 memset(ge, 0, sizeof(*ge));
723 ge->state = GE_STATE_NEW;
724 ge->ui = ui;
725 return ge;
726}
727
728static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
729{
730 struct gui_entry *ge;
731
732 ge = alloc_new_gui_entry(ui);
733
734 ge->vbox = new_client_page(ge);
735 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
736
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);
739
740 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
741
742 gtk_widget_show_all(ui->window);
743 return ge;
744}
745
746static void file_new(GtkWidget *w, gpointer data)
747{
748 struct gui *ui = (struct gui *) data;
749 struct gui_entry *ge;
750
751 ge = get_new_ge_with_tab(ui, "Untitled");
752 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
753}
754
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{
762 if (!cur_page) {
763 if (created)
764 *created = 1;
765 return get_new_ge_with_tab(ui, "Untitled");
766 }
767
768 if (created)
769 *created = 0;
770
771 return g_hash_table_lookup(ui->ge_hash, &cur_page);
772}
773
774static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
775{
776 gint cur_page;
777
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);
785
786 return NULL;
787}
788
789static void file_close(GtkWidget *w, gpointer data)
790{
791 struct gui *ui = (struct gui *) data;
792 struct gui_entry *ge;
793
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;
801 }
802
803 if (g_hash_table_size(ui->ge_hash)) {
804 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
805 return;
806 }
807
808 gfio_quit(ui);
809}
810
811static void file_add_recent(struct gui *ui, const gchar *uri)
812{
813 GtkRecentData grd;
814
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");
821
822 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
823}
824
825static gchar *get_filename_from_uri(const gchar *uri)
826{
827 if (strncmp(uri, "file://", 7))
828 return strdup(uri);
829
830 return strdup(uri + 7);
831}
832
833static int do_file_open(struct gui_entry *ge, const gchar *uri)
834{
835 struct fio_client *client;
836
837 assert(!ge->job_file);
838
839 ge->job_file = get_filename_from_uri(uri);
840
841 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
842 if (client) {
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
849 gfio_client_added(ge, client);
850 file_add_recent(ge->ui, uri);
851 return 0;
852 }
853
854 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
855 free(ge->host);
856 ge->host = NULL;
857 free(ge->job_file);
858 ge->job_file = NULL;
859 return 1;
860}
861
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;
867
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;
877 }
878
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);
884
885 return 1;
886 }
887
888 ret = do_file_open(ge, uri);
889
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);
896 }
897
898 return ret;
899}
900
901static void recent_open(GtkAction *action, gpointer data)
902{
903 struct gui *ui = (struct gui *) data;
904 GtkRecentInfo *info;
905 const gchar *uri;
906
907 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
908 uri = gtk_recent_info_get_uri(info);
909
910 do_file_open_with_tab(ui, uri);
911}
912
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;
939 }
940
941 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
942
943 gtk_widget_destroy(dialog);
944
945 do_file_open_with_tab(ui, filename);
946 g_free(filename);
947}
948
949static void file_save(GtkWidget *w, gpointer data)
950{
951 struct gui *ui = data;
952 GtkWidget *dialog;
953
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);
970 }
971 gtk_widget_destroy(dialog);
972}
973
974static void view_log_destroy(GtkWidget *w, gpointer data)
975{
976 struct gui *ui = (struct gui *) data;
977
978 g_object_ref(G_OBJECT(ui->log_tree));
979 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
980 gtk_widget_destroy(w);
981 ui->log_view = NULL;
982}
983
984void gfio_view_log(struct gui *ui)
985{
986 GtkWidget *win, *scroll, *vbox, *box;
987
988 if (ui->log_view)
989 return;
990
991 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
992 gtk_window_set_title(GTK_WINDOW(win), "Log");
993 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
994
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);
1002 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
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);
1007 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
1008
1009 gtk_container_add(GTK_CONTAINER(win), vbox);
1010 gtk_widget_show_all(win);
1011}
1012
1013static void view_log(GtkWidget *w, gpointer data)
1014{
1015 struct gui *ui = (struct gui *) data;
1016
1017 gfio_view_log(ui);
1018}
1019
1020static void connect_job_entry(GtkWidget *w, gpointer data)
1021{
1022 struct gui *ui = (struct gui *) data;
1023 struct gui_entry *ge;
1024
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);
1038}
1039
1040static void edit_job_entry(GtkWidget *w, gpointer data)
1041{
1042 struct gui *ui = (struct gui *) data;
1043 struct gui_entry *ge;
1044
1045 ge = get_ge_from_cur_tab(ui);
1046 if (ge && ge->client)
1047 gopt_get_options_window(ui->window, ge->client);
1048}
1049
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
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
1078static void __update_graph_settings(struct gfio_graphs *g)
1079{
1080 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
1081 graph_set_font(g->iops_graph, gfio_graph_font);
1082 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
1083 graph_set_font(g->bandwidth_graph, gfio_graph_font);
1084}
1085
1086static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
1087{
1088 struct gui_entry *ge = (struct gui_entry *) value;
1089 GdkEvent *ev;
1090
1091 __update_graph_settings(&ge->graphs);
1092
1093 ev = gdk_event_new(GDK_EXPOSE);
1094 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1095 gdk_event_free(ev);
1096}
1097
1098static void update_graph_limits(void)
1099{
1100 struct gui *ui = &main_ui;
1101 GdkEvent *ev;
1102
1103 __update_graph_settings(&ui->graphs);
1104
1105 ev = gdk_event_new(GDK_EXPOSE);
1106 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1107 gdk_event_free(ev);
1108
1109 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
1110}
1111
1112static void preferences(GtkWidget *w, gpointer data)
1113{
1114 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1115 GtkWidget *hbox, *spin, *entry, *spin_int;
1116 struct gui *ui = (struct gui *) data;
1117 int i;
1118
1119 dialog = gtk_dialog_new_with_buttons("Preferences",
1120 GTK_WINDOW(ui->window),
1121 GTK_DIALOG_DESTROY_WITH_PARENT,
1122 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1123 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1124 NULL);
1125
1126 frame = gtk_frame_new("Graphing");
1127 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1128 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1129 vbox = gtk_vbox_new(FALSE, 6);
1130 gtk_container_add(GTK_CONTAINER(frame), vbox);
1131
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
1137 font = gtk_font_button_new_with_font(gfio_graph_font);
1138 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
1139
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);
1144 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
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
1148 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
1149
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);
1159 frame = gtk_frame_new("Debug logging");
1160 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1161 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
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
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
1197 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1198 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1199 update_graph_limits();
1200 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
1201
1202 gtk_widget_destroy(dialog);
1203}
1204
1205static void about_dialog(GtkWidget *w, gpointer data)
1206{
1207 const char *authors[] = {
1208 "Jens Axboe <axboe@kernel.dk>",
1209 "Stephen Carmeron <stephenmcameron@gmail.com>",
1210 NULL
1211 };
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);
1229
1230 gtk_show_about_dialog(NULL,
1231 "program-name", "gfio",
1232 "comments", "Gtk2 UI for fio",
1233 "license", license_trans,
1234 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1235 "authors", authors,
1236 "version", fio_version_string,
1237 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1238 "logo-icon-name", "fio",
1239 /* Must be last: */
1240 "wrap-license", TRUE,
1241 NULL);
1242
1243 g_free(license_trans);
1244}
1245
1246static GtkActionEntry menu_items[] = {
1247 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1248 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1249 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
1250 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1251 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
1252 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
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) },
1256 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1257 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
1258 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
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) },
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) },
1264};
1265static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1266
1267static const gchar *ui_string = " \
1268 <ui> \
1269 <menubar name=\"MainMenu\"> \
1270 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1271 <menuitem name=\"New\" action=\"NewFile\" /> \
1272 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1273 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1274 <separator name=\"Separator1\"/> \
1275 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1276 <separator name=\"Separator2\"/> \
1277 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1278 <separator name=\"Separator3\"/> \
1279 <placeholder name=\"FileRecentFiles\"/> \
1280 <separator name=\"Separator4\"/> \
1281 <menuitem name=\"Quit\" action=\"Quit\" /> \
1282 </menu> \
1283 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
1284 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
1285 <separator name=\"Separator5\"/> \
1286 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1287 <menuitem name=\"Send job\" action=\"SendJob\" /> \
1288 <separator name=\"Separator6\"/> \
1289 <menuitem name=\"Start job\" action=\"StartJob\" /> \
1290 </menu>\
1291 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1292 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1293 <separator name=\"Separator7\"/> \
1294 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1295 </menu>\
1296 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1297 <menuitem name=\"About\" action=\"About\" /> \
1298 </menu> \
1299 </menubar> \
1300 </ui> \
1301";
1302
1303static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1304 struct gui *ui)
1305{
1306 GtkActionGroup *action_group;
1307 GError *error = 0;
1308
1309 action_group = gtk_action_group_new("Menu");
1310 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
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));
1316
1317 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1318}
1319
1320void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1321 GtkWidget *vbox, GtkUIManager *ui_manager)
1322{
1323 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1324}
1325
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);
1334 multitext_set_entry(&ge->eta.bs, index);
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);
1344 multitext_free(&ge->eta.bs);
1345 multitext_free(&ge->eta.ioengine);
1346 multitext_free(&ge->eta.iodepth);
1347}
1348
1349static void fill_color_from_rgb(GdkColor *c, gfloat r, gfloat g, gfloat b)
1350{
1351 gint R, G, B;
1352 gchar tmp[32];
1353
1354 memset(c, 0, sizeof(*c));
1355 R = r * 255;
1356 G = g * 255;
1357 B = b * 255;
1358 sprintf(tmp, "#%02x%02x%02x", R, G, B);
1359 printf("%s\n", tmp);
1360 gdk_color_parse(tmp, c);
1361}
1362
1363static GtkWidget *new_client_page(struct gui_entry *ge)
1364{
1365 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1366 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1367 GdkColor color;
1368
1369 main_vbox = gtk_vbox_new(FALSE, 3);
1370
1371 top_align = gtk_alignment_new(0, 0, 1, 0);
1372 top_vbox = gtk_vbox_new(FALSE, 3);
1373 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1374 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1375
1376 probe = gtk_frame_new("Job");
1377 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1378 probe_frame = gtk_vbox_new(FALSE, 3);
1379 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1380
1381 probe_box = gtk_hbox_new(FALSE, 3);
1382 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1383 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1384 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1385 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1386 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1387
1388 probe_box = gtk_hbox_new(FALSE, 3);
1389 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1390
1391 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
1392 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1393 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1394 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
1395 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
1396 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1397 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
1398 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1399 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1400
1401 probe_box = gtk_hbox_new(FALSE, 3);
1402 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1403 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1404 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1405 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1406 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1407 ge->eta.trim_bw = new_info_entry_in_frame(probe_box, "Trim BW");
1408 ge->eta.trim_iops = new_info_entry_in_frame(probe_box, "IOPS");
1409
1410 fill_color_from_rgb(&color, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1411 gtk_widget_modify_text(ge->eta.read_bw, GTK_STATE_NORMAL, &color);
1412 gtk_widget_modify_text(ge->eta.read_iops, GTK_STATE_NORMAL, &color);
1413
1414 fill_color_from_rgb(&color, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1415 gtk_widget_modify_text(ge->eta.write_bw, GTK_STATE_NORMAL, &color);
1416 gtk_widget_modify_text(ge->eta.write_iops, GTK_STATE_NORMAL, &color);
1417
1418 fill_color_from_rgb(&color, GFIO_IOPS_R, GFIO_IOPS_G, GFIO_IOPS_B);
1419 gtk_widget_modify_text(ge->eta.trim_bw, GTK_STATE_NORMAL, &color);
1420 gtk_widget_modify_text(ge->eta.trim_iops, GTK_STATE_NORMAL, &color);
1421
1422 /*
1423 * Only add this if we have a commit rate
1424 */
1425#if 0
1426 probe_box = gtk_hbox_new(FALSE, 3);
1427 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1428
1429 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1430 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1431
1432 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1433 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1434#endif
1435
1436 /*
1437 * Set up a drawing area and IOPS and bandwidth graphs
1438 */
1439 ge->graphs.drawing_area = gtk_drawing_area_new();
1440 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
1441 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1442 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
1443 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
1444 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1445 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1446 G_CALLBACK(on_config_drawing_area), &ge->graphs);
1447 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1448 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1449 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1450 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1451 ge->graphs.drawing_area);
1452 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
1453
1454 setup_graphs(&ge->graphs);
1455
1456 /*
1457 * Set up alignments for widgets at the bottom of ui,
1458 * align bottom left, expand horizontally but not vertically
1459 */
1460 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1461 ge->buttonbox = gtk_hbox_new(FALSE, 0);
1462 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1463 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1464
1465 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
1466
1467 /*
1468 * Set up thread status progress bar
1469 */
1470 ge->thread_status_pb = gtk_progress_bar_new();
1471 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1472 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1473 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1474
1475
1476 return main_vbox;
1477}
1478
1479static GtkWidget *new_main_page(struct gui *ui)
1480{
1481 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
1482 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
1483
1484 main_vbox = gtk_vbox_new(FALSE, 3);
1485
1486 /*
1487 * Set up alignments for widgets at the top of ui,
1488 * align top left, expand horizontally but not vertically
1489 */
1490 top_align = gtk_alignment_new(0, 0, 1, 0);
1491 top_vbox = gtk_vbox_new(FALSE, 0);
1492 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1493 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
1494
1495 probe = gtk_frame_new("Run statistics");
1496 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1497 probe_frame = gtk_vbox_new(FALSE, 3);
1498 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1499
1500 probe_box = gtk_hbox_new(FALSE, 3);
1501 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1502 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
1503 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1504 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1505 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1506 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1507 ui->eta.trim_bw = new_info_entry_in_frame(probe_box, "Trim BW");
1508 ui->eta.trim_iops = new_info_entry_in_frame(probe_box, "IOPS");
1509
1510
1511 /*
1512 * Only add this if we have a commit rate
1513 */
1514#if 0
1515 probe_box = gtk_hbox_new(FALSE, 3);
1516 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1517
1518 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1519 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1520
1521 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1522 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1523#endif
1524
1525 /*
1526 * Set up a drawing area and IOPS and bandwidth graphs
1527 */
1528 ui->graphs.drawing_area = gtk_drawing_area_new();
1529 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
1530 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1531 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
1532 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
1533 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1534 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1535 G_CALLBACK(on_config_drawing_area), &ui->graphs);
1536 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1537 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1538 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1539 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
1540 ui->graphs.drawing_area);
1541 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
1542 TRUE, TRUE, 0);
1543
1544 setup_graphs(&ui->graphs);
1545
1546 /*
1547 * Set up alignments for widgets at the bottom of ui,
1548 * align bottom left, expand horizontally but not vertically
1549 */
1550 bottom_align = gtk_alignment_new(0, 1, 1, 0);
1551 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1552 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1553 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
1554
1555 /*
1556 * Set up thread status progress bar
1557 */
1558 ui->thread_status_pb = gtk_progress_bar_new();
1559 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1560 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1561 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1562
1563 return main_vbox;
1564}
1565
1566static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1567 guint page, gpointer data)
1568
1569{
1570 struct gui *ui = (struct gui *) data;
1571 struct gui_entry *ge;
1572
1573 if (!page) {
1574 set_job_menu_visible(ui, 0);
1575 set_view_results_visible(ui, 0);
1576 return TRUE;
1577 }
1578
1579 set_job_menu_visible(ui, 1);
1580 ge = get_ge_from_page(ui, page, NULL);
1581 if (ge)
1582 update_button_states(ui, ge);
1583
1584 return TRUE;
1585}
1586
1587static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1588{
1589 time_t time_a = gtk_recent_info_get_visited(a);
1590 time_t time_b = gtk_recent_info_get_visited(b);
1591
1592 return time_b - time_a;
1593}
1594
1595static void add_recent_file_items(struct gui *ui)
1596{
1597 const gchar *gfio = g_get_application_name();
1598 GList *items, *item;
1599 int i = 0;
1600
1601 if (ui->recent_ui_id) {
1602 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1603 gtk_ui_manager_ensure_update(ui->uimanager);
1604 }
1605 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1606
1607 if (ui->actiongroup) {
1608 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1609 g_object_unref(ui->actiongroup);
1610 }
1611 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1612
1613 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1614
1615 items = gtk_recent_manager_get_items(ui->recentmanager);
1616 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1617
1618 for (item = items; item && item->data; item = g_list_next(item)) {
1619 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1620 gchar *action_name;
1621 const gchar *label;
1622 GtkAction *action;
1623
1624 if (!gtk_recent_info_has_application(info, gfio))
1625 continue;
1626
1627 /*
1628 * We only support local files for now
1629 */
1630 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1631 continue;
1632
1633 action_name = g_strdup_printf("RecentFile%u", i++);
1634 label = gtk_recent_info_get_display_name(info);
1635
1636 action = g_object_new(GTK_TYPE_ACTION,
1637 "name", action_name,
1638 "label", label, NULL);
1639
1640 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1641 gtk_recent_info_ref(info),
1642 (GDestroyNotify) gtk_recent_info_unref);
1643
1644
1645 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1646
1647 gtk_action_group_add_action(ui->actiongroup, action);
1648 g_object_unref(action);
1649
1650 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1651 "/MainMenu/FileMenu/FileRecentFiles",
1652 label, action_name,
1653 GTK_UI_MANAGER_MENUITEM, FALSE);
1654
1655 g_free(action_name);
1656
1657 if (i == 8)
1658 break;
1659 }
1660
1661 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1662 g_list_free(items);
1663}
1664
1665static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
1666 gint x, gint y, GtkSelectionData *seldata,
1667 guint info, guint time, gpointer *data)
1668{
1669 struct gui *ui = (struct gui *) data;
1670 gchar **uris;
1671 GtkWidget *source;
1672
1673 source = gtk_drag_get_source_widget(ctx);
1674 if (source && widget == gtk_widget_get_toplevel(source)) {
1675 gtk_drag_finish(ctx, FALSE, FALSE, time);
1676 return;
1677 }
1678
1679 uris = gtk_selection_data_get_uris(seldata);
1680 if (!uris) {
1681 gtk_drag_finish(ctx, FALSE, FALSE, time);
1682 return;
1683 }
1684
1685 if (uris[0])
1686 do_file_open_with_tab(ui, uris[0]);
1687
1688 gtk_drag_finish(ctx, TRUE, FALSE, time);
1689 g_strfreev(uris);
1690}
1691
1692static void init_ui(int *argc, char **argv[], struct gui *ui)
1693{
1694 GtkSettings *settings;
1695 GtkWidget *vbox;
1696
1697 /* Magical g*thread incantation, you just need this thread stuff.
1698 * Without it, the update that happens in gfio_update_thread_status
1699 * doesn't really happen in a timely fashion, you need expose events
1700 */
1701 if (!g_thread_supported())
1702 g_thread_init(NULL);
1703 gdk_threads_init();
1704
1705 gtk_init(argc, argv);
1706 settings = gtk_settings_get_default();
1707 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1708 g_type_init();
1709 gdk_color_parse("#ffffee", &gfio_color_lightyellow);
1710 gdk_color_parse("white", &gfio_color_white);
1711
1712 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1713 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1714 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1715
1716 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1717 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
1718
1719 ui->vbox = gtk_vbox_new(FALSE, 0);
1720 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1721
1722 ui->uimanager = gtk_ui_manager_new();
1723 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1724 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
1725
1726 ui->recentmanager = gtk_recent_manager_get_default();
1727 add_recent_file_items(ui);
1728
1729 ui->notebook = gtk_notebook_new();
1730 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
1731 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
1732 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
1733 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1734
1735 vbox = new_main_page(ui);
1736 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
1737 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1738 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
1739
1740 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1741
1742 gfio_ui_setup_log(ui);
1743
1744 gtk_widget_show_all(ui->window);
1745}
1746
1747int main(int argc, char *argv[], char *envp[])
1748{
1749 if (initialize_fio(envp))
1750 return 1;
1751 if (fio_init_options())
1752 return 1;
1753
1754 gopt_init();
1755
1756 memset(&main_ui, 0, sizeof(main_ui));
1757 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
1758
1759 init_ui(&argc, &argv, &main_ui);
1760
1761 gdk_threads_enter();
1762 gtk_main();
1763 gdk_threads_leave();
1764
1765 g_hash_table_destroy(main_ui.ge_hash);
1766
1767 gopt_exit();
1768 return 0;
1769}