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