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