1#include <locale.h> 2#include <malloc.h> 3#include <string.h> 4 5#include <glib.h> 6#include <cairo.h> 7#include <gtk/gtk.h> 8 9#include "fio.h" 10#include "gfio.h" 11#include "ghelpers.h" 12#include "gerror.h" 13#include "parse.h" 14 15struct gopt { 16 GtkWidget *box; 17 unsigned int opt_index; 18 unsigned int opt_type; 19 gulong sig_handler; 20 struct gopt_job_view *gjv; 21 struct flist_head changed_list; 22}; 23 24struct gopt_combo { 25 struct gopt gopt; 26 GtkWidget *combo; 27}; 28 29struct gopt_int { 30 struct gopt gopt; 31 unsigned long long lastval; 32 GtkWidget *spin; 33}; 34 35struct gopt_bool { 36 struct gopt gopt; 37 GtkWidget *check; 38}; 39 40struct gopt_str { 41 struct gopt gopt; 42 GtkWidget *entry; 43}; 44 45struct gopt_str_val { 46 struct gopt gopt; 47 GtkWidget *spin; 48 GtkWidget *combo; 49 unsigned int maxindex; 50}; 51 52#define GOPT_RANGE_SPIN 4 53 54struct gopt_range { 55 struct gopt gopt; 56 GtkWidget *spins[GOPT_RANGE_SPIN]; 57}; 58 59struct gopt_str_multi { 60 struct gopt gopt; 61 GtkWidget *checks[PARSE_MAX_VP]; 62}; 63 64enum { 65 GOPT_COMBO_INT = 1, 66 GOPT_COMBO_STR, 67 GOPT_INT, 68 GOPT_BOOL, 69 GOPT_STR, 70 GOPT_STR_VAL, 71 GOPT_RANGE, 72 GOPT_STR_MULTI, 73}; 74 75struct gopt_frame_widget { 76 GtkWidget *vbox[2]; 77 unsigned int nr; 78}; 79 80struct gopt_job_view { 81 struct gopt_frame_widget g_widgets[__FIO_OPT_G_NR]; 82 GtkWidget *vboxes[__FIO_OPT_C_NR]; 83 struct gopt *gopts[FIO_MAX_OPTS]; 84 GtkWidget *dialog; 85 GtkWidget *job_combo; 86 struct gfio_client *client; 87 struct flist_head changed_list; 88 struct thread_options *o; 89 int in_job_switch; 90}; 91 92static GNode *gopt_dep_tree; 93 94static GtkWidget *gopt_get_group_frame(struct gopt_job_view *gjv, 95 GtkWidget *box, unsigned int groupmask) 96{ 97 unsigned int mask, group; 98 struct opt_group *og; 99 GtkWidget *frame, *hbox; 100 struct gopt_frame_widget *gfw; 101 102 if (!groupmask) 103 return 0; 104 105 mask = groupmask; 106 og = opt_group_cat_from_mask(&mask); 107 if (!og) 108 return NULL; 109 110 group = ffz(~groupmask); 111 gfw = &gjv->g_widgets[group]; 112 if (!gfw->vbox[0]) { 113 frame = gtk_frame_new(og->name); 114 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3); 115 hbox = gtk_hbox_new(FALSE, 0); 116 gtk_container_add(GTK_CONTAINER(frame), hbox); 117 gfw->vbox[0] = gtk_vbox_new(TRUE, 5); 118 gfw->vbox[1] = gtk_vbox_new(TRUE, 5); 119 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[0], TRUE, TRUE, 5); 120 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[1], TRUE, TRUE, 5); 121 } 122 123 hbox = gtk_hbox_new(FALSE, 3); 124 gtk_box_pack_start(GTK_BOX(gfw->vbox[gfw->nr++ & 1]), hbox, FALSE, FALSE, 5); 125 return hbox; 126} 127 128/* 129 * Mark children as invisible, if needed. 130 */ 131static void gopt_set_children_visible(struct gopt_job_view *gjv, 132 struct fio_option *parent, 133 gboolean visible) 134{ 135 GNode *child, *node; 136 137 if (parent->hide_on_set) 138 visible = !visible; 139 140 node = g_node_find(gopt_dep_tree, G_IN_ORDER, G_TRAVERSE_ALL, parent); 141 child = g_node_first_child(node); 142 while (child) { 143 struct fio_option *o = child->data; 144 struct gopt *g = o->gui_data; 145 GtkWidget *widget = g->box; 146 147 /* 148 * Recurse into child, if it also has children 149 */ 150 if (g_node_n_children(child)) 151 gopt_set_children_visible(gjv, o, visible); 152 153 gtk_widget_set_sensitive(widget, visible); 154 child = g_node_next_sibling(child); 155 } 156} 157 158static void gopt_mark_index(struct gopt_job_view *gjv, struct gopt *gopt, 159 unsigned int idx, int type) 160{ 161 INIT_FLIST_HEAD(&gopt->changed_list); 162 163 assert(!gjv->gopts[idx]); 164 gopt->opt_index = idx; 165 gopt->opt_type = type; 166 gopt->gjv = gjv; 167 gjv->gopts[idx] = gopt; 168} 169 170static void gopt_dialog_update_apply_button(struct gopt_job_view *gjv) 171{ 172 GtkDialog *dialog = GTK_DIALOG(gjv->dialog); 173 gboolean set; 174 175 set = !flist_empty(&gjv->changed_list); 176 gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_APPLY, set); 177 178 if (set) { 179 gtk_widget_set_sensitive(gjv->job_combo, 0); 180 gtk_widget_set_tooltip_text(gjv->job_combo, "Apply option changes before switching to a new job"); 181 } else { 182 gtk_widget_set_sensitive(gjv->job_combo, 1); 183 gtk_widget_set_tooltip_text(gjv->job_combo, "Change current job"); 184 } 185} 186 187static void gopt_changed(struct gopt *gopt) 188{ 189 struct gopt_job_view *gjv = gopt->gjv; 190 191 if (gjv->in_job_switch) 192 return; 193 194 /* 195 * Add to changed list. This also prevents the option from being 196 * freed when the widget is destroyed. 197 */ 198 if (flist_empty(&gopt->changed_list)) { 199 flist_add_tail(&gopt->changed_list, &gjv->changed_list); 200 gopt_dialog_update_apply_button(gjv); 201 } 202} 203 204static void gopt_str_changed(GtkEntry *entry, gpointer data) 205{ 206 struct gopt_str *s = (struct gopt_str *) data; 207 struct fio_option *o = &fio_options[s->gopt.opt_index]; 208 const gchar *text; 209 int set; 210 211 gopt_changed(&s->gopt); 212 213 text = gtk_entry_get_text(GTK_ENTRY(s->entry)); 214 set = strcmp(text, "") != 0; 215 216 gopt_set_children_visible(s->gopt.gjv, o, set); 217} 218 219static void gopt_str_destroy(GtkWidget *w, gpointer data) 220{ 221 struct gopt_str *s = (struct gopt_str *) data; 222 223 free(s); 224 gtk_widget_destroy(w); 225} 226 227static void gopt_str_store_set_val(struct gopt_str *s, const char *text) 228{ 229 if (text) 230 gtk_entry_set_text(GTK_ENTRY(s->entry), text); 231} 232 233static struct gopt *gopt_new_str_store(struct gopt_job_view *gjv, 234 struct fio_option *o, const char *text, 235 unsigned int idx) 236{ 237 struct gopt_str *s; 238 GtkWidget *label; 239 240 s = calloc(1, sizeof(*s)); 241 242 s->gopt.box = gtk_hbox_new(FALSE, 3); 243 if (!o->lname) 244 label = gtk_label_new(o->name); 245 else 246 label = gtk_label_new(o->lname); 247 248 s->entry = gtk_entry_new(); 249 gopt_mark_index(gjv, &s->gopt, idx, GOPT_STR); 250 gtk_editable_set_editable(GTK_EDITABLE(s->entry), 1); 251 252 if (text) 253 gopt_str_store_set_val(s, text); 254 else if (o->def) 255 gopt_str_store_set_val(s, o->def); 256 257 s->gopt.sig_handler = g_signal_connect(G_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s); 258 g_signal_connect(G_OBJECT(s->entry), "destroy", G_CALLBACK(gopt_str_destroy), s); 259 260 gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0); 261 gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0); 262 return &s->gopt; 263} 264 265static void gopt_combo_changed(GtkComboBox *box, gpointer data) 266{ 267 struct gopt_combo *c = (struct gopt_combo *) data; 268 struct fio_option *o = &fio_options[c->gopt.opt_index]; 269 unsigned int index; 270 271 gopt_changed(&c->gopt); 272 273 index = gtk_combo_box_get_active(GTK_COMBO_BOX(c->combo)); 274 275 gopt_set_children_visible(c->gopt.gjv, o, index); 276} 277 278static void gopt_combo_destroy(GtkWidget *w, gpointer data) 279{ 280 struct gopt_combo *c = (struct gopt_combo *) data; 281 282 free(c); 283 gtk_widget_destroy(w); 284} 285 286static struct gopt_combo *__gopt_new_combo(struct gopt_job_view *gjv, 287 struct fio_option *o, 288 unsigned int idx, int type) 289{ 290 struct gopt_combo *c; 291 GtkWidget *label; 292 293 c = calloc(1, sizeof(*c)); 294 295 c->gopt.box = gtk_hbox_new(FALSE, 3); 296 if (!o->lname) 297 label = gtk_label_new(o->name); 298 else 299 label = gtk_label_new(o->lname); 300 301 c->combo = gtk_combo_box_text_new(); 302 gopt_mark_index(gjv, &c->gopt, idx, type); 303 g_signal_connect(G_OBJECT(c->combo), "destroy", G_CALLBACK(gopt_combo_destroy), c); 304 305 gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0); 306 gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0); 307 308 return c; 309} 310 311static void gopt_combo_str_set_val(struct gopt_combo *c, const char *text) 312{ 313 struct fio_option *o = &fio_options[c->gopt.opt_index]; 314 struct value_pair *vp; 315 int i; 316 317 i = 0; 318 vp = &o->posval[0]; 319 while (vp->ival) { 320 if (!strcmp(vp->ival, text)) { 321 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), i); 322 break; 323 } 324 vp++; 325 i++; 326 } 327} 328 329static struct gopt *gopt_new_combo_str(struct gopt_job_view *gjv, 330 struct fio_option *o, const char *text, 331 unsigned int idx) 332{ 333 struct gopt_combo *c; 334 struct value_pair *vp; 335 int i, active = 0; 336 337 c = __gopt_new_combo(gjv, o, idx, GOPT_COMBO_STR); 338 339 i = 0; 340 vp = &o->posval[0]; 341 while (vp->ival) { 342 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(c->combo), vp->ival); 343 if (o->def && !strcmp(vp->ival, o->def)) 344 active = i; 345 vp++; 346 i++; 347 } 348 349 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active); 350 if (text) 351 gopt_combo_str_set_val(c, text); 352 c->gopt.sig_handler = g_signal_connect(G_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c); 353 return &c->gopt; 354} 355 356static void gopt_combo_int_set_val(struct gopt_combo *c, unsigned int ip) 357{ 358 struct fio_option *o = &fio_options[c->gopt.opt_index]; 359 struct value_pair *vp; 360 int i; 361 362 i = 0; 363 vp = &o->posval[0]; 364 while (vp->ival) { 365 if (vp->oval == ip) { 366 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), i); 367 break; 368 } 369 vp++; 370 i++; 371 } 372} 373 374static struct gopt *gopt_new_combo_int(struct gopt_job_view *gjv, 375 struct fio_option *o, unsigned int *ip, 376 unsigned int idx) 377{ 378 struct gopt_combo *c; 379 struct value_pair *vp; 380 int i, active = 0; 381 382 c = __gopt_new_combo(gjv, o, idx, GOPT_COMBO_INT); 383 384 i = 0; 385 vp = &o->posval[0]; 386 while (vp->ival) { 387 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(c->combo), vp->ival); 388 if (ip && vp->oval == *ip) 389 active = i; 390 vp++; 391 i++; 392 } 393 394 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active); 395 if (ip) 396 gopt_combo_int_set_val(c, *ip); 397 c->gopt.sig_handler = g_signal_connect(G_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c); 398 return &c->gopt; 399} 400 401static void gopt_str_multi_toggled(GtkToggleButton *button, gpointer data) 402{ 403 struct gopt_str_multi *m = (struct gopt_str_multi *) data; 404 405 gopt_changed(&m->gopt); 406} 407 408static void gopt_str_multi_destroy(GtkWidget *w, gpointer data) 409{ 410 struct gopt_str_multi *m = (struct gopt_str_multi *) data; 411 412 free(m); 413 gtk_widget_destroy(w); 414} 415 416static void gopt_str_multi_set_val(struct gopt_str_multi *m, int val) 417{ 418} 419 420static struct gopt *gopt_new_str_multi(struct gopt_job_view *gjv, 421 struct fio_option *o, unsigned int idx) 422{ 423 struct gopt_str_multi *m; 424 struct value_pair *vp; 425 GtkWidget *frame, *hbox; 426 int i; 427 428 m = calloc(1, sizeof(*m)); 429 m->gopt.box = gtk_hbox_new(FALSE, 3); 430 gopt_mark_index(gjv, &m->gopt, idx, GOPT_STR_MULTI); 431 432 if (!o->lname) 433 frame = gtk_frame_new(o->name); 434 else 435 frame = gtk_frame_new(o->lname); 436 gtk_box_pack_start(GTK_BOX(m->gopt.box), frame, FALSE, FALSE, 3); 437 438 hbox = gtk_hbox_new(FALSE, 3); 439 gtk_container_add(GTK_CONTAINER(frame), hbox); 440 441 i = 0; 442 vp = &o->posval[0]; 443 while (vp->ival) { 444 m->checks[i] = gtk_check_button_new_with_label(vp->ival); 445 gtk_widget_set_tooltip_text(m->checks[i], vp->help); 446 gtk_box_pack_start(GTK_BOX(hbox), m->checks[i], FALSE, FALSE, 3); 447 g_signal_connect(G_OBJECT(m->checks[i]), "toggled", G_CALLBACK(gopt_str_multi_toggled), m); 448 vp++; 449 i++; 450 } 451 452 gopt_str_multi_set_val(m, 0); 453 g_signal_connect(G_OBJECT(m->gopt.box), "destroy", G_CALLBACK(gopt_str_multi_destroy), m); 454 return &m->gopt; 455} 456 457static void gopt_int_changed(GtkSpinButton *spin, gpointer data) 458{ 459 struct gopt_int *i = (struct gopt_int *) data; 460 struct fio_option *o = &fio_options[i->gopt.opt_index]; 461 GtkAdjustment *adj; 462 int value, delta; 463 464 gopt_changed(&i->gopt); 465 466 adj = gtk_spin_button_get_adjustment(spin); 467 value = gtk_adjustment_get_value(adj); 468 delta = value - i->lastval; 469 i->lastval = value; 470 471 if (o->inv_opt) { 472 struct gopt *b_inv = o->inv_opt->gui_data; 473 struct gopt_int *i_inv = container_of(b_inv, struct gopt_int, gopt); 474 int cur_val; 475 476 assert(o->type == o->inv_opt->type); 477 478 cur_val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(i_inv->spin)); 479 cur_val -= delta; 480 g_signal_handler_block(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler); 481 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i_inv->spin), cur_val); 482 g_signal_handler_unblock(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler); 483 } 484} 485 486static void gopt_int_destroy(GtkWidget *w, gpointer data) 487{ 488 struct gopt_int *i = (struct gopt_int *) data; 489 490 free(i); 491 gtk_widget_destroy(w); 492} 493 494static void gopt_int_set_val(struct gopt_int *i, unsigned long long p) 495{ 496 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), p); 497 i->lastval = p; 498} 499 500static struct gopt_int *__gopt_new_int(struct gopt_job_view *gjv, 501 struct fio_option *o, 502 unsigned long long *p, unsigned int idx) 503{ 504 unsigned long long defval; 505 struct gopt_int *i; 506 guint maxval, interval; 507 GtkWidget *label; 508 509 i = calloc(1, sizeof(*i)); 510 i->gopt.box = gtk_hbox_new(FALSE, 3); 511 if (!o->lname) 512 label = gtk_label_new(o->name); 513 else 514 label = gtk_label_new(o->lname); 515 516 maxval = o->maxval; 517 if (!maxval) 518 maxval = UINT_MAX; 519 520 defval = 0; 521 if (p) 522 defval = *p; 523 else if (o->def) { 524 long long val; 525 526 check_str_bytes(o->def, &val, o); 527 defval = val; 528 } 529 530 interval = 1.0; 531 if (o->interval) 532 interval = o->interval; 533 534 i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval); 535 gopt_mark_index(gjv, &i->gopt, idx, GOPT_INT); 536 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID); 537 if (p) 538 gopt_int_set_val(i, *p); 539 else 540 gopt_int_set_val(i, defval); 541 i->gopt.sig_handler = g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i); 542 g_signal_connect(G_OBJECT(i->spin), "destroy", G_CALLBACK(gopt_int_destroy), i); 543 544 gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0); 545 gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0); 546 547 return i; 548} 549 550static struct gopt *gopt_new_int(struct gopt_job_view *gjv, 551 struct fio_option *o, unsigned int *ip, 552 unsigned int idx) 553{ 554 unsigned long long ullp; 555 struct gopt_int *i; 556 557 if (ip) { 558 ullp = *ip; 559 i = __gopt_new_int(gjv, o, &ullp, idx); 560 } else 561 i = __gopt_new_int(gjv, o, NULL, idx); 562 563 return &i->gopt; 564} 565 566static struct gopt *gopt_new_ullong(struct gopt_job_view *gjv, 567 struct fio_option *o, unsigned long long *p, 568 unsigned int idx) 569{ 570 struct gopt_int *i; 571 572 i = __gopt_new_int(gjv, o, p, idx); 573 return &i->gopt; 574} 575 576static void gopt_bool_toggled(GtkToggleButton *button, gpointer data) 577{ 578 struct gopt_bool *b = (struct gopt_bool *) data; 579 struct fio_option *o = &fio_options[b->gopt.opt_index]; 580 gboolean set; 581 582 gopt_changed(&b->gopt); 583 584 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check)); 585 586 if (o->inv_opt) { 587 struct gopt *g_inv = o->inv_opt->gui_data; 588 struct gopt_bool *b_inv = container_of(g_inv, struct gopt_bool, gopt); 589 590 assert(o->type == o->inv_opt->type); 591 592 g_signal_handler_block(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler); 593 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_inv->check), !set); 594 g_signal_handler_unblock(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler); 595 } 596 597 gopt_set_children_visible(b->gopt.gjv, o, set); 598} 599 600static void gopt_bool_destroy(GtkWidget *w, gpointer data) 601{ 602 struct gopt_bool *b = (struct gopt_bool *) data; 603 604 free(b); 605 gtk_widget_destroy(w); 606} 607 608static void gopt_bool_set_val(struct gopt_bool *b, unsigned int val) 609{ 610 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), val); 611} 612 613static struct gopt *gopt_new_bool(struct gopt_job_view *gjv, 614 struct fio_option *o, unsigned int *val, 615 unsigned int idx) 616{ 617 struct gopt_bool *b; 618 GtkWidget *label; 619 int defstate = 0; 620 621 b = calloc(1, sizeof(*b)); 622 b->gopt.box = gtk_hbox_new(FALSE, 3); 623 if (!o->lname) 624 label = gtk_label_new(o->name); 625 else 626 label = gtk_label_new(o->lname); 627 628 b->check = gtk_check_button_new(); 629 gopt_mark_index(gjv, &b->gopt, idx, GOPT_BOOL); 630 if (o->def && !strcmp(o->def, "1")) 631 defstate = 1; 632 633 if (o->neg) 634 defstate = !defstate; 635 636 if (val) 637 gopt_bool_set_val(b, *val); 638 else 639 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate); 640 b->gopt.sig_handler = g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b); 641 g_signal_connect(G_OBJECT(b->check), "destroy", G_CALLBACK(gopt_bool_destroy), b); 642 643 gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0); 644 gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0); 645 return &b->gopt; 646} 647 648/* 649 * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values. 650 * If the max is made smaller than min, adjust min down. 651 * If the min is made larger than max, adjust the max. 652 */ 653static void range_value_changed(GtkSpinButton *spin, gpointer data) 654{ 655 struct gopt_range *r = (struct gopt_range *) data; 656 int changed = -1, i; 657 gint val, mval; 658 659 gopt_changed(&r->gopt); 660 661 for (i = 0; i < GOPT_RANGE_SPIN; i++) { 662 if (GTK_SPIN_BUTTON(r->spins[i]) == spin) { 663 changed = i; 664 break; 665 } 666 } 667 668 assert(changed != -1); 669 670 /* 671 * Min changed 672 */ 673 if (changed == 0 || changed == 2) { 674 GtkWidget *mspin = r->spins[changed + 1]; 675 676 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed])); 677 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin)); 678 if (val > mval) 679 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val); 680 } else { 681 GtkWidget *mspin = r->spins[changed - 1]; 682 683 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed])); 684 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin)); 685 if (val < mval) 686 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val); 687 } 688} 689 690static void gopt_range_destroy(GtkWidget *w, gpointer data) 691{ 692 struct gopt_range *r = (struct gopt_range *) data; 693 694 free(r); 695 gtk_widget_destroy(w); 696} 697 698static void gopt_int_range_set_val(struct gopt_range *r, unsigned int *vals) 699{ 700 int i; 701 702 for (i = 0; i < GOPT_RANGE_SPIN; i++) 703 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), vals[i]); 704} 705 706static struct gopt *gopt_new_int_range(struct gopt_job_view *gjv, 707 struct fio_option *o, unsigned int **ip, 708 unsigned int idx) 709{ 710 struct gopt_range *r; 711 GtkWidget *label; 712 guint interval; 713 unsigned int defvals[GOPT_RANGE_SPIN]; 714 gint maxval; 715 int i; 716 717 r = calloc(1, sizeof(*r)); 718 r->gopt.box = gtk_hbox_new(FALSE, 3); 719 gopt_mark_index(gjv, &r->gopt, idx, GOPT_RANGE); 720 if (!o->lname) 721 label = gtk_label_new(o->name); 722 else 723 label = gtk_label_new(o->lname); 724 725 maxval = o->maxval; 726 if (!maxval) 727 maxval = INT_MAX; 728 729 memset(defvals, 0, sizeof(defvals)); 730 if (o->def) { 731 long long val; 732 733 check_str_bytes(o->def, &val, o); 734 for (i = 0; i < GOPT_RANGE_SPIN; i++) 735 defvals[i] = val; 736 } 737 738 interval = 1.0; 739 if (o->interval) 740 interval = o->interval; 741 742 for (i = 0; i < GOPT_RANGE_SPIN; i++) { 743 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval); 744 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID); 745 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0); 746 } 747 748 if (ip) 749 gopt_int_range_set_val(r, *ip); 750 else 751 gopt_int_range_set_val(r, defvals); 752 753 for (i = 0; i < GOPT_RANGE_SPIN; i++) 754 g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r); 755 756 gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0); 757 g_signal_connect(G_OBJECT(r->gopt.box), "destroy", G_CALLBACK(gopt_range_destroy), r); 758 return &r->gopt; 759} 760 761static void gopt_str_val_destroy(GtkWidget *w, gpointer data) 762{ 763 struct gopt_str_val *g = (struct gopt_str_val *) data; 764 765 free(g); 766 gtk_widget_destroy(w); 767} 768 769static void gopt_str_val_spin_wrapped(GtkSpinButton *spin, gpointer data) 770{ 771 struct gopt_str_val *g = (struct gopt_str_val *) data; 772 unsigned int val; 773 GtkAdjustment *adj; 774 gint index; 775 776 adj = gtk_spin_button_get_adjustment(spin); 777 val = gtk_adjustment_get_value(adj); 778 779 /* 780 * Can't rely on exact value, as fast changes increment >= 1 781 */ 782 if (!val) { 783 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo)); 784 if (index + 1 <= g->maxindex) { 785 val = 1; 786 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), ++index); 787 } else 788 val = 1023; 789 gtk_spin_button_set_value(spin, val); 790 } else { 791 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo)); 792 if (index) { 793 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), --index); 794 gtk_spin_button_set_value(spin, 1023); 795 } else 796 gtk_spin_button_set_value(spin, 0); 797 } 798} 799 800static void gopt_str_val_changed(GtkSpinButton *spin, gpointer data) 801{ 802 struct gopt_str_val *g = (struct gopt_str_val *) data; 803 804 gopt_changed(&g->gopt); 805} 806 807static void gopt_str_val_set_val(struct gopt_str_val *g, unsigned long long val) 808{ 809 int i = 0; 810 811 do { 812 if (!val || (val % 1024)) 813 break; 814 815 i++; 816 val /= 1024; 817 } while (1); 818 819 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), val); 820 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), i); 821} 822 823static struct gopt *gopt_new_str_val(struct gopt_job_view *gjv, 824 struct fio_option *o, 825 unsigned long long *p, unsigned int idx) 826{ 827 struct gopt_str_val *g; 828 const gchar *postfix[] = { "B", "KB", "MB", "GB", "PB", "TB", "" }; 829 GtkWidget *label; 830 int i; 831 832 g = calloc(1, sizeof(*g)); 833 g->gopt.box = gtk_hbox_new(FALSE, 3); 834 if (!o->lname) 835 label = gtk_label_new(o->name); 836 else 837 label = gtk_label_new(o->lname); 838 gopt_mark_index(gjv, &g->gopt, idx, GOPT_STR_VAL); 839 840 g->spin = gtk_spin_button_new_with_range(0.0, 1023.0, 1.0); 841 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(g->spin), GTK_UPDATE_IF_VALID); 842 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), 0); 843 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(g->spin), 1); 844 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->spin, FALSE, FALSE, 0); 845 g_signal_connect(G_OBJECT(g->spin), "wrapped", G_CALLBACK(gopt_str_val_spin_wrapped), g); 846 g_signal_connect(G_OBJECT(g->spin), "changed", G_CALLBACK(gopt_str_val_changed), g); 847 848 g->combo = gtk_combo_box_text_new(); 849 i = 0; 850 while (strlen(postfix[i])) { 851 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(g->combo), postfix[i]); 852 i++; 853 } 854 g->maxindex = i - 1; 855 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), 0); 856 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->combo, FALSE, FALSE, 0); 857 gtk_box_pack_start(GTK_BOX(g->gopt.box), label, FALSE, FALSE, 3); 858 859 if (p) 860 gopt_str_val_set_val(g, *p); 861 862 g_signal_connect(G_OBJECT(g->combo), "changed", G_CALLBACK(gopt_str_val_changed), g); 863 864 g_signal_connect(G_OBJECT(g->gopt.box), "destroy", G_CALLBACK(gopt_str_val_destroy), g); 865 return &g->gopt; 866} 867 868static void gopt_set_option(struct gopt_job_view *gjv, struct fio_option *o, 869 struct gopt *gopt, struct thread_options *to) 870{ 871 switch (o->type) { 872 case FIO_OPT_STR_VAL: { 873 unsigned long long *ullp = NULL; 874 struct gopt_str_val *g; 875 876 if (o->off1) 877 ullp = td_var(to, o, o->off1); 878 879 g = container_of(gopt, struct gopt_str_val, gopt); 880 if (ullp) 881 gopt_str_val_set_val(g, *ullp); 882 break; 883 } 884 case FIO_OPT_STR_VAL_TIME: { 885 unsigned long long *ullp = NULL; 886 struct gopt_int *i; 887 888 if (o->off1) 889 ullp = td_var(to, o, o->off1); 890 891 i = container_of(gopt, struct gopt_int, gopt); 892 if (ullp) 893 gopt_int_set_val(i, *ullp); 894 break; 895 } 896 case FIO_OPT_INT: 897 if (o->posval[0].ival) { 898 unsigned int *ip = NULL; 899 struct gopt_combo *c; 900 901 if (o->off1) 902 ip = td_var(to, o, o->off1); 903 904 c = container_of(gopt, struct gopt_combo, gopt); 905 if (ip) 906 gopt_combo_int_set_val(c, *ip); 907 } else { 908 unsigned int *ip = NULL; 909 struct gopt_int *i; 910 911 if (o->off1) 912 ip = td_var(to, o, o->off1); 913 914 i = container_of(gopt, struct gopt_int, gopt); 915 if (ip) 916 gopt_int_set_val(i, *ip); 917 } 918 break; 919 case FIO_OPT_STR_SET: 920 case FIO_OPT_BOOL: { 921 unsigned int *ip = NULL; 922 struct gopt_bool *b; 923 924 if (o->off1) 925 ip = td_var(to, o, o->off1); 926 927 b = container_of(gopt, struct gopt_bool, gopt); 928 if (ip) 929 gopt_bool_set_val(b, *ip); 930 break; 931 } 932 case FIO_OPT_STR: { 933 if (o->posval[0].ival) { 934 unsigned int *ip = NULL; 935 struct gopt_combo *c; 936 937 if (o->off1) 938 ip = td_var(to, o, o->off1); 939 940 c = container_of(gopt, struct gopt_combo, gopt); 941 if (ip) 942 gopt_combo_int_set_val(c, *ip); 943 } else { 944 struct gopt_str *s; 945 char *text = NULL; 946 947 if (o->off1) { 948 char **p = td_var(to, o, o->off1); 949 950 text = *p; 951 } 952 953 s = container_of(gopt, struct gopt_str, gopt); 954 gopt_str_store_set_val(s, text); 955 } 956 957 break; 958 } 959 case FIO_OPT_STR_STORE: { 960 struct gopt_combo *c; 961 char *text = NULL; 962 963 if (o->off1) { 964 char **p = td_var(to, o, o->off1); 965 text = *p; 966 } 967 968 if (!o->posval[0].ival) { 969 struct gopt_str *s; 970 971 s = container_of(gopt, struct gopt_str, gopt); 972 gopt_str_store_set_val(s, text); 973 break; 974 } 975 976 c = container_of(gopt, struct gopt_combo, gopt); 977 if (text) 978 gopt_combo_str_set_val(c, text); 979 break; 980 } 981 case FIO_OPT_STR_MULTI: 982 /* HANDLE ME */ 983 break; 984 case FIO_OPT_RANGE: { 985 struct gopt_range *r; 986 unsigned int *ip[4] = { td_var(to, o, o->off1), 987 td_var(to, o, o->off2), 988 td_var(to, o, o->off3), 989 td_var(to, o, o->off4) }; 990 991 r = container_of(gopt, struct gopt_range, gopt); 992 gopt_int_range_set_val(r, *ip); 993 break; 994 } 995 /* still need to handle this one */ 996 case FIO_OPT_FLOAT_LIST: 997 break; 998 case FIO_OPT_DEPRECATED: 999 break; 1000 default: 1001 printf("ignore type %u\n", o->type); 1002 break; 1003 } 1004} 1005 1006static void gopt_add_option(struct gopt_job_view *gjv, GtkWidget *hbox, 1007 struct fio_option *o, unsigned int opt_index, 1008 struct thread_options *to) 1009{ 1010 struct gopt *go = NULL; 1011 1012 switch (o->type) { 1013 case FIO_OPT_STR_VAL: { 1014 unsigned long long *ullp = NULL; 1015 1016 if (o->off1) 1017 ullp = td_var(to, o, o->off1); 1018 1019 go = gopt_new_str_val(gjv, o, ullp, opt_index); 1020 break; 1021 } 1022 case FIO_OPT_STR_VAL_TIME: { 1023 unsigned long long *ullp = NULL; 1024 1025 if (o->off1) 1026 ullp = td_var(to, o, o->off1); 1027 1028 go = gopt_new_ullong(gjv, o, ullp, opt_index); 1029 break; 1030 } 1031 case FIO_OPT_INT: 1032 if (o->posval[0].ival) { 1033 unsigned int *ip = NULL; 1034 1035 if (o->off1) 1036 ip = td_var(to, o, o->off1); 1037 1038 go = gopt_new_combo_int(gjv, o, ip, opt_index); 1039 } else { 1040 unsigned int *ip = NULL; 1041 1042 if (o->off1) 1043 ip = td_var(to, o, o->off1); 1044 1045 go = gopt_new_int(gjv, o, ip, opt_index); 1046 } 1047 break; 1048 case FIO_OPT_STR_SET: 1049 case FIO_OPT_BOOL: { 1050 unsigned int *ip = NULL; 1051 1052 if (o->off1) 1053 ip = td_var(to, o, o->off1); 1054 1055 go = gopt_new_bool(gjv, o, ip, opt_index); 1056 break; 1057 } 1058 case FIO_OPT_STR: { 1059 if (o->posval[0].ival) { 1060 unsigned int *ip = NULL; 1061 1062 if (o->off1) 1063 ip = td_var(to, o, o->off1); 1064 1065 go = gopt_new_combo_int(gjv, o, ip, opt_index); 1066 } else { 1067 /* TODO: usually ->cb, or unsigned int pointer */ 1068 go = gopt_new_str_store(gjv, o, NULL, opt_index); 1069 } 1070 1071 break; 1072 } 1073 case FIO_OPT_STR_STORE: { 1074 char *text = NULL; 1075 1076 if (o->off1) { 1077 char **p = td_var(to, o, o->off1); 1078 text = *p; 1079 } 1080 1081 if (!o->posval[0].ival) { 1082 go = gopt_new_str_store(gjv, o, text, opt_index); 1083 break; 1084 } 1085 1086 go = gopt_new_combo_str(gjv, o, text, opt_index); 1087 break; 1088 } 1089 case FIO_OPT_STR_MULTI: 1090 go = gopt_new_str_multi(gjv, o, opt_index); 1091 break; 1092 case FIO_OPT_RANGE: { 1093 unsigned int *ip[4] = { td_var(to, o, o->off1), 1094 td_var(to, o, o->off2), 1095 td_var(to, o, o->off3), 1096 td_var(to, o, o->off4) }; 1097 1098 go = gopt_new_int_range(gjv, o, ip, opt_index); 1099 break; 1100 } 1101 /* still need to handle this one */ 1102 case FIO_OPT_FLOAT_LIST: 1103 break; 1104 case FIO_OPT_DEPRECATED: 1105 break; 1106 default: 1107 printf("ignore type %u\n", o->type); 1108 break; 1109 } 1110 1111 if (go) { 1112 GtkWidget *dest; 1113 1114 if (o->help) 1115 gtk_widget_set_tooltip_text(go->box, o->help); 1116 1117 o->gui_data = go; 1118 1119 dest = gopt_get_group_frame(gjv, hbox, o->group); 1120 if (!dest) 1121 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5); 1122 else 1123 gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5); 1124 } 1125} 1126 1127static void gopt_add_options(struct gopt_job_view *gjv, 1128 struct thread_options *to) 1129{ 1130 GtkWidget *hbox = NULL; 1131 int i; 1132 1133 /* 1134 * First add all options 1135 */ 1136 for (i = 0; fio_options[i].name; i++) { 1137 struct fio_option *o = &fio_options[i]; 1138 unsigned int mask = o->category; 1139 struct opt_group *og; 1140 1141 while ((og = opt_group_from_mask(&mask)) != NULL) { 1142 GtkWidget *vbox = gjv->vboxes[ffz(~og->mask)]; 1143 1144 hbox = gtk_hbox_new(FALSE, 3); 1145 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); 1146 gopt_add_option(gjv, hbox, o, i, to); 1147 } 1148 } 1149} 1150 1151static void gopt_set_options(struct gopt_job_view *gjv, 1152 struct thread_options *to) 1153{ 1154 int i; 1155 1156 for (i = 0; fio_options[i].name; i++) { 1157 struct fio_option *o = &fio_options[i]; 1158 struct gopt *gopt = gjv->gopts[i]; 1159 1160 gopt_set_option(gjv, o, gopt, to); 1161 } 1162} 1163 1164static GtkWidget *gopt_add_tab(GtkWidget *notebook, const char *name) 1165{ 1166 GtkWidget *box, *vbox, *scroll; 1167 1168 scroll = gtk_scrolled_window_new(NULL, NULL); 1169 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); 1170 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 1171 1172 vbox = gtk_vbox_new(FALSE, 3); 1173 box = gtk_hbox_new(FALSE, 0); 1174 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); 1175 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox); 1176 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(name)); 1177 return vbox; 1178} 1179 1180static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og) 1181{ 1182 return gopt_add_tab(notebook, og->name); 1183} 1184 1185static void gopt_add_group_tabs(GtkWidget *notebook, struct gopt_job_view *gjv) 1186{ 1187 struct opt_group *og; 1188 unsigned int i; 1189 1190 i = 0; 1191 do { 1192 unsigned int mask = (1U << i); 1193 1194 og = opt_group_from_mask(&mask); 1195 if (!og) 1196 break; 1197 gjv->vboxes[i] = gopt_add_group_tab(notebook, og); 1198 i++; 1199 } while (1); 1200} 1201 1202static void gopt_handle_str_multi_changed(struct gopt_job_view *gjv, 1203 struct gopt_str_multi *m, 1204 struct fio_option *o) 1205{ 1206 unsigned int *ip = td_var(gjv->o, o, o->off1); 1207 struct value_pair *vp; 1208 gboolean set; 1209 guint val = 0; 1210 int i; 1211 1212 i = 0; 1213 vp = &o->posval[0]; 1214 while (vp->ival) { 1215 if (!m->checks[i]) 1216 break; 1217 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m->checks[i])); 1218 if (set) { 1219 if (vp->orval) 1220 val |= vp->oval; 1221 else 1222 val = vp->oval; 1223 } 1224 i++; 1225 vp++; 1226 } 1227 1228 if (o->off1) 1229 *ip = val; 1230} 1231 1232static void gopt_handle_range_changed(struct gopt_job_view *gjv, 1233 struct gopt_range *r, 1234 struct fio_option *o) 1235{ 1236 unsigned int *ip[4] = { td_var(gjv->o, o, o->off1), 1237 td_var(gjv->o, o, o->off2), 1238 td_var(gjv->o, o, o->off3), 1239 td_var(gjv->o, o, o->off4) }; 1240 gint val; 1241 int i; 1242 1243 for (i = 0; i < GOPT_RANGE_SPIN; i++) { 1244 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[i])); 1245 *ip[i] = val; 1246 } 1247} 1248 1249static void gopt_handle_str_val_changed(struct gopt_job_view *gjv, 1250 struct gopt_str_val *s, 1251 struct fio_option *o) 1252{ 1253 unsigned long long *ullp = td_var(gjv->o, o, o->off1); 1254 GtkAdjustment *adj; 1255 gint index; 1256 1257 if (!ullp) 1258 return; 1259 1260 /* 1261 * Numerical value 1262 */ 1263 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(s->spin)); 1264 *ullp = gtk_adjustment_get_value(adj); 1265 1266 /* 1267 * Multiplier 1268 */ 1269 index = gtk_combo_box_get_active(GTK_COMBO_BOX(s->combo)); 1270 while (index--) 1271 *ullp *= 1024ULL; 1272} 1273 1274static void gopt_handle_str_changed(struct gopt_job_view *gjv, 1275 struct gopt_str *s, struct fio_option *o) 1276{ 1277 char **p = td_var(gjv->o, o, o->off1); 1278 1279 if (*p) 1280 free(*p); 1281 1282 *p = strdup(gtk_entry_get_text(GTK_ENTRY(s->entry))); 1283} 1284 1285static void gopt_handle_bool_changed(struct gopt_job_view *gjv, 1286 struct gopt_bool *b, struct fio_option *o) 1287{ 1288 unsigned int *ip = td_var(gjv->o, o, o->off1); 1289 gboolean set; 1290 1291 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check)); 1292 *ip = set; 1293} 1294 1295static void gopt_handle_int_changed(struct gopt_job_view *gjv, 1296 struct gopt_int *i, struct fio_option *o) 1297{ 1298 unsigned int *ip = td_var(gjv->o, o, o->off1); 1299 GtkAdjustment *adj; 1300 guint val; 1301 1302 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(i->spin)); 1303 val = gtk_adjustment_get_value(adj); 1304 *ip = val; 1305} 1306 1307static void gopt_handle_combo_str_changed(struct gopt_job_view *gjv, 1308 struct gopt_combo *c, 1309 struct fio_option *o) 1310{ 1311 char **p = td_var(gjv->o, o, o->off1); 1312 1313 if (*p) 1314 free(*p); 1315 1316 *p = strdup(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(c->combo))); 1317} 1318 1319static void gopt_handle_combo_int_changed(struct gopt_job_view *gjv, 1320 struct gopt_combo *c, 1321 struct fio_option *o) 1322{ 1323 unsigned int *ip = td_var(gjv->o, o, o->off1); 1324 gint index; 1325 1326 index = gtk_combo_box_get_active(GTK_COMBO_BOX(c->combo)); 1327 *ip = o->posval[index].oval; 1328} 1329 1330static void gopt_handle_changed(struct gopt *gopt) 1331{ 1332 struct fio_option *o = &fio_options[gopt->opt_index]; 1333 struct gopt_job_view *gjv = gopt->gjv; 1334 1335 switch (gopt->opt_type) { 1336 case GOPT_COMBO_INT: { 1337 struct gopt_combo *c; 1338 1339 c = container_of(gopt, struct gopt_combo, gopt); 1340 gopt_handle_combo_int_changed(gjv, c, o); 1341 break; 1342 } 1343 case GOPT_COMBO_STR: { 1344 struct gopt_combo *c; 1345 1346 c = container_of(gopt, struct gopt_combo, gopt); 1347 gopt_handle_combo_str_changed(gjv, c, o); 1348 break; 1349 } 1350 case GOPT_INT: { 1351 struct gopt_int *i; 1352 1353 i = container_of(gopt, struct gopt_int, gopt); 1354 gopt_handle_int_changed(gjv, i, o); 1355 break; 1356 } 1357 case GOPT_BOOL: { 1358 struct gopt_bool *b; 1359 1360 b = container_of(gopt, struct gopt_bool, gopt); 1361 gopt_handle_bool_changed(gjv, b, o); 1362 break; 1363 } 1364 case GOPT_STR: { 1365 struct gopt_str *s; 1366 1367 s = container_of(gopt, struct gopt_str, gopt); 1368 gopt_handle_str_changed(gjv, s, o); 1369 break; 1370 } 1371 case GOPT_STR_VAL: { 1372 struct gopt_str_val *s; 1373 1374 s = container_of(gopt, struct gopt_str_val, gopt); 1375 gopt_handle_str_val_changed(gjv, s, o); 1376 break; 1377 } 1378 case GOPT_RANGE: { 1379 struct gopt_range *r; 1380 1381 r = container_of(gopt, struct gopt_range, gopt); 1382 gopt_handle_range_changed(gjv, r, o); 1383 break; 1384 } 1385 case GOPT_STR_MULTI: { 1386 struct gopt_str_multi *m; 1387 1388 m = container_of(gopt, struct gopt_str_multi, gopt); 1389 gopt_handle_str_multi_changed(gjv, m, o); 1390 break; 1391 } 1392 default: 1393 log_err("gfio: bad option type: %d\n", gopt->opt_type); 1394 break; 1395 } 1396} 1397 1398static void gopt_report_update_status(struct gopt_job_view *gjv) 1399{ 1400 struct gfio_client *gc = gjv->client; 1401 char tmp[80]; 1402 1403 sprintf(tmp, "\nCompleted with error: %d\n", gc->update_job_status); 1404 gfio_report_info(gc->ge->ui, "Update job", tmp); 1405} 1406 1407static int gopt_handle_changed_options(struct gopt_job_view *gjv) 1408{ 1409 struct gfio_client *gc = gjv->client; 1410 struct flist_head *entry; 1411 uint64_t waitid = 0; 1412 struct gopt *gopt; 1413 int ret; 1414 1415 flist_for_each(entry, &gjv->changed_list) { 1416 gopt = flist_entry(entry, struct gopt, changed_list); 1417 gopt_handle_changed(gopt); 1418 } 1419 1420 gc->update_job_status = 0; 1421 gc->update_job_done = 0; 1422 1423 ret = fio_client_update_options(gc->client, gjv->o, &waitid); 1424 if (ret) 1425 goto done; 1426 1427 ret = fio_client_wait_for_reply(gc->client, waitid); 1428 if (ret) 1429 goto done; 1430 1431 assert(gc->update_job_done); 1432 if (gc->update_job_status) 1433 goto done; 1434 1435 while (!flist_empty(&gjv->changed_list)) { 1436 gopt = flist_entry(gjv->changed_list.next, struct gopt, changed_list); 1437 flist_del_init(&gopt->changed_list); 1438 } 1439 1440done: 1441 gopt_dialog_update_apply_button(gjv); 1442 return ret; 1443} 1444 1445static gint gopt_dialog_cancel(gint response) 1446{ 1447 switch (response) { 1448 case GTK_RESPONSE_NONE: 1449 case GTK_RESPONSE_REJECT: 1450 case GTK_RESPONSE_DELETE_EVENT: 1451 case GTK_RESPONSE_CANCEL: 1452 case GTK_RESPONSE_NO: 1453 return 1; 1454 default: 1455 return 0; 1456 } 1457} 1458 1459static gint gopt_dialog_done(gint response) 1460{ 1461 switch (response) { 1462 case GTK_RESPONSE_ACCEPT: 1463 case GTK_RESPONSE_OK: 1464 case GTK_RESPONSE_YES: 1465 return 1; 1466 default: 1467 return 0; 1468 } 1469} 1470 1471static void gopt_handle_option_dialog(struct gopt_job_view *gjv) 1472{ 1473 gint response; 1474 1475 do { 1476 response = gtk_dialog_run(GTK_DIALOG(gjv->dialog)); 1477 1478 if (gopt_dialog_cancel(response) || 1479 gopt_dialog_done(response)) 1480 break; 1481 1482 /* 1483 * Apply 1484 */ 1485 gopt_handle_changed_options(gjv); 1486 gopt_report_update_status(gjv); 1487 } while (1); 1488 1489 if (gopt_dialog_cancel(response)) 1490 return; 1491 1492 gopt_handle_changed_options(gjv); 1493} 1494 1495static void gopt_job_changed(GtkComboBox *box, gpointer data) 1496{ 1497 struct gopt_job_view *gjv = (struct gopt_job_view *) data; 1498 struct gfio_client_options *gco = NULL; 1499 struct gfio_client *gc = gjv->client; 1500 struct flist_head *entry; 1501 gchar *job; 1502 1503 /* 1504 * The switch act should be sensitized appropriately, so that we 1505 * never get here with modified options. 1506 */ 1507 if (!flist_empty(&gjv->changed_list)) { 1508 gfio_report_info(gc->ge->ui, "Internal Error", "Modified options on job switch.\nThat should not be possible!\n"); 1509 return; 1510 } 1511 1512 job = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(gjv->job_combo)); 1513 flist_for_each(entry, &gc->o_list) { 1514 const char *name; 1515 1516 gco = flist_entry(entry, struct gfio_client_options, list); 1517 name = gco->o.name; 1518 if (!name || !strlen(name)) 1519 name = "Default job"; 1520 1521 if (!strcmp(name, job)) 1522 break; 1523 1524 gco = NULL; 1525 } 1526 1527 if (!gco) { 1528 gfio_report_info(gc->ge->ui, "Internal Error", "Could not find job description.\nThat should not be possible!\n"); 1529 return; 1530 } 1531 1532 gjv->in_job_switch = 1; 1533 gopt_set_options(gjv, &gco->o); 1534 gjv->in_job_switch = 0; 1535} 1536 1537void gopt_get_options_window(GtkWidget *window, struct gfio_client *gc) 1538{ 1539 GtkWidget *dialog, *notebook, *vbox, *topvbox, *combo; 1540 struct gfio_client_options *gco; 1541 struct flist_head *entry; 1542 struct gopt_job_view *gjv; 1543 1544 dialog = gtk_dialog_new_with_buttons("Fio options", 1545 GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, 1546 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, 1547 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, 1548 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); 1549 1550 combo = gtk_combo_box_text_new(); 1551 flist_for_each(entry, &gc->o_list) { 1552 struct thread_options *o; 1553 const char *name; 1554 1555 gco = flist_entry(entry, struct gfio_client_options, list); 1556 o = &gco->o; 1557 name = o->name; 1558 if (!name || !strlen(name)) 1559 name = "Default job"; 1560 1561 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), name); 1562 } 1563 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); 1564 1565 gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768); 1566 1567 topvbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); 1568 gtk_box_pack_start(GTK_BOX(topvbox), combo, FALSE, FALSE, 5); 1569 1570 vbox = gtk_vbox_new(TRUE, 5); 1571 gtk_box_pack_start(GTK_BOX(topvbox), vbox, TRUE, TRUE, 5); 1572 1573 notebook = gtk_notebook_new(); 1574 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1); 1575 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); 1576 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 5); 1577 1578 gjv = calloc(1, sizeof(*gjv)); 1579 INIT_FLIST_HEAD(&gjv->changed_list); 1580 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list); 1581 gjv->o = &gco->o; 1582 gjv->dialog = dialog; 1583 gjv->client = gc; 1584 gjv->job_combo = combo; 1585 gopt_add_group_tabs(notebook, gjv); 1586 gopt_add_options(gjv, &gco->o); 1587 gopt_dialog_update_apply_button(gjv); 1588 1589 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(gopt_job_changed), gjv); 1590 1591 gtk_widget_show_all(dialog); 1592 1593 gopt_handle_option_dialog(gjv); 1594 1595 gtk_widget_destroy(dialog); 1596 free(gjv); 1597} 1598 1599/* 1600 * Build n-ary option dependency tree 1601 */ 1602void gopt_init(void) 1603{ 1604 int i; 1605 1606 gopt_dep_tree = g_node_new(NULL); 1607 1608 for (i = 0; fio_options[i].name; i++) { 1609 struct fio_option *o = &fio_options[i]; 1610 GNode *node, *nparent; 1611 1612 /* 1613 * Insert node with either the root parent, or an 1614 * option parent. 1615 */ 1616 node = g_node_new(o); 1617 nparent = gopt_dep_tree; 1618 if (o->parent) { 1619 struct fio_option *parent; 1620 1621 parent = fio_option_find(o->parent); 1622 nparent = g_node_find(gopt_dep_tree, G_IN_ORDER, G_TRAVERSE_ALL, parent); 1623 if (!nparent) { 1624 log_err("fio: did not find parent %s for opt %s\n", o->name, o->parent); 1625 nparent = gopt_dep_tree; 1626 } 1627 } 1628 1629 g_node_insert(nparent, -1, node); 1630 } 1631} 1632 1633void gopt_exit(void) 1634{ 1635 g_node_destroy(gopt_dep_tree); 1636 gopt_dep_tree = NULL; 1637} 1638