16 unsigned int opt_index;
17 unsigned int opt_type;
19 struct gopt_job_view *gjv;
20 struct flist_head changed_list;
48 unsigned int maxindex;
51 #define GOPT_RANGE_SPIN 4
55 GtkWidget *spins[GOPT_RANGE_SPIN];
58 struct gopt_str_multi {
60 GtkWidget *checks[PARSE_MAX_VP];
74 struct gopt_frame_widget {
79 struct gopt_job_view {
80 struct flist_head list;
81 struct gopt_frame_widget g_widgets[__FIO_OPT_G_NR];
82 GtkWidget *widgets[FIO_MAX_OPTS];
83 GtkWidget *vboxes[__FIO_OPT_C_NR];
84 struct flist_head changed_list;
85 struct thread_options *o;
88 static GNode *gopt_dep_tree;
90 static GtkWidget *gopt_get_group_frame(struct gopt_job_view *gjv,
91 GtkWidget *box, unsigned int groupmask)
93 unsigned int mask, group;
95 GtkWidget *frame, *hbox;
96 struct gopt_frame_widget *gfw;
102 og = opt_group_cat_from_mask(&mask);
106 group = ffz(~groupmask);
107 gfw = &gjv->g_widgets[group];
109 frame = gtk_frame_new(og->name);
110 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3);
111 hbox = gtk_hbox_new(FALSE, 0);
112 gtk_container_add(GTK_CONTAINER(frame), hbox);
113 gfw->vbox[0] = gtk_vbox_new(TRUE, 5);
114 gfw->vbox[1] = gtk_vbox_new(TRUE, 5);
115 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[0], TRUE, TRUE, 5);
116 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[1], TRUE, TRUE, 5);
119 hbox = gtk_hbox_new(FALSE, 3);
120 gtk_box_pack_start(GTK_BOX(gfw->vbox[gfw->nr++ & 1]), hbox, FALSE, FALSE, 5);
125 * Mark children as invisible, if needed.
127 static void gopt_set_children_visible(struct gopt_job_view *gjv,
128 struct fio_option *parent,
133 if (parent->hide_on_set)
136 node = g_node_find(gopt_dep_tree, G_IN_ORDER, G_TRAVERSE_ALL, parent);
137 child = g_node_first_child(node);
139 struct fio_option *o = child->data;
140 struct gopt *g = o->gui_data;
143 * Recurse into child, if it also has children
145 if (g_node_n_children(child))
146 gopt_set_children_visible(gjv, o, visible);
148 if (gjv->widgets[g->opt_index])
149 gtk_widget_set_sensitive(gjv->widgets[g->opt_index], visible);
151 child = g_node_next_sibling(child);
155 static void gopt_mark_index(struct gopt_job_view *gjv, struct gopt *gopt,
156 unsigned int idx, int type)
158 INIT_FLIST_HEAD(&gopt->changed_list);
159 g_object_ref(G_OBJECT(gopt->box));
161 assert(!gjv->widgets[idx]);
162 gopt->opt_index = idx;
163 gopt->opt_type = type;
165 gjv->widgets[idx] = gopt->box;
168 static void gopt_changed(struct gopt *gopt)
170 struct gopt_job_view *gjv = gopt->gjv;
173 * Add to changed list. This also prevents the option from being
174 * freed when the widget is destroyed.
176 if (flist_empty(&gopt->changed_list))
177 flist_add_tail(&gopt->changed_list, &gjv->changed_list);
180 static void gopt_str_changed(GtkEntry *entry, gpointer data)
182 struct gopt_str *s = (struct gopt_str *) data;
183 struct fio_option *o = &fio_options[s->gopt.opt_index];
187 gopt_changed(&s->gopt);
189 text = gtk_entry_get_text(GTK_ENTRY(s->entry));
190 set = strcmp(text, "") != 0;
192 gopt_set_children_visible(s->gopt.gjv, o, set);
195 static void gopt_str_destroy(GtkWidget *w, gpointer data)
197 struct gopt_str *s = (struct gopt_str *) data;
199 if (flist_empty(&s->gopt.changed_list))
202 gtk_widget_destroy(w);
205 static struct gopt *gopt_new_str_store(struct gopt_job_view *gjv,
206 struct fio_option *o, const char *text,
212 s = calloc(1, sizeof(*s));
214 s->gopt.box = gtk_hbox_new(FALSE, 3);
216 label = gtk_label_new(o->name);
218 label = gtk_label_new(o->lname);
220 s->entry = gtk_entry_new();
221 gopt_mark_index(gjv, &s->gopt, idx, GOPT_STR);
223 gtk_entry_set_text(GTK_ENTRY(s->entry), text);
224 gtk_editable_set_editable(GTK_EDITABLE(s->entry), 1);
227 gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
229 s->gopt.sig_handler = g_signal_connect(G_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s);
230 g_signal_connect(G_OBJECT(s->entry), "destroy", G_CALLBACK(gopt_str_destroy), s);
232 gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
233 gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
237 static void gopt_combo_changed(GtkComboBox *box, gpointer data)
239 struct gopt_combo *c = (struct gopt_combo *) data;
240 struct fio_option *o = &fio_options[c->gopt.opt_index];
243 gopt_changed(&c->gopt);
245 index = gtk_combo_box_get_active(GTK_COMBO_BOX(c->combo));
247 gopt_set_children_visible(c->gopt.gjv, o, index);
250 static void gopt_combo_destroy(GtkWidget *w, gpointer data)
252 struct gopt_combo *c = (struct gopt_combo *) data;
254 if (flist_empty(&c->gopt.changed_list))
257 gtk_widget_destroy(w);
260 static struct gopt_combo *__gopt_new_combo(struct gopt_job_view *gjv,
261 struct fio_option *o,
262 unsigned int idx, int type)
264 struct gopt_combo *c;
267 c = calloc(1, sizeof(*c));
269 c->gopt.box = gtk_hbox_new(FALSE, 3);
271 label = gtk_label_new(o->name);
273 label = gtk_label_new(o->lname);
275 c->combo = gtk_combo_box_text_new();
276 gopt_mark_index(gjv, &c->gopt, idx, type);
277 g_signal_connect(G_OBJECT(c->combo), "destroy", G_CALLBACK(gopt_combo_destroy), c);
279 gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0);
280 gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0);
285 static struct gopt *gopt_new_combo_str(struct gopt_job_view *gjv,
286 struct fio_option *o, const char *text,
289 struct gopt_combo *c;
290 struct value_pair *vp;
293 c = __gopt_new_combo(gjv, o, idx, GOPT_COMBO_STR);
298 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(c->combo), vp->ival);
299 if (o->def && !strcmp(vp->ival, o->def))
301 if (text && !strcmp(vp->ival, text))
307 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active);
308 c->gopt.sig_handler = g_signal_connect(G_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
312 static struct gopt *gopt_new_combo_int(struct gopt_job_view *gjv,
313 struct fio_option *o, unsigned int *ip,
316 struct gopt_combo *c;
317 struct value_pair *vp;
320 c = __gopt_new_combo(gjv, o, idx, GOPT_COMBO_INT);
325 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(c->combo), vp->ival);
326 if (ip && vp->oval == *ip)
332 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active);
333 c->gopt.sig_handler = g_signal_connect(G_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
337 static void gopt_str_multi_toggled(GtkToggleButton *button, gpointer data)
339 struct gopt_str_multi *m = (struct gopt_str_multi *) data;
341 gopt_changed(&m->gopt);
344 static struct gopt *gopt_new_str_multi(struct gopt_job_view *gjv,
345 struct fio_option *o, unsigned int idx)
347 struct gopt_str_multi *m;
348 struct value_pair *vp;
349 GtkWidget *frame, *hbox;
352 m = calloc(1, sizeof(*m));
353 m->gopt.box = gtk_hbox_new(FALSE, 3);
354 gopt_mark_index(gjv, &m->gopt, idx, GOPT_STR_MULTI);
357 frame = gtk_frame_new(o->name);
359 frame = gtk_frame_new(o->lname);
360 gtk_box_pack_start(GTK_BOX(m->gopt.box), frame, FALSE, FALSE, 3);
362 hbox = gtk_hbox_new(FALSE, 3);
363 gtk_container_add(GTK_CONTAINER(frame), hbox);
368 m->checks[i] = gtk_check_button_new_with_label(vp->ival);
369 gtk_widget_set_tooltip_text(m->checks[i], vp->help);
370 gtk_box_pack_start(GTK_BOX(hbox), m->checks[i], FALSE, FALSE, 3);
371 g_signal_connect(G_OBJECT(m->checks[i]), "toggled", G_CALLBACK(gopt_str_multi_toggled), m);
379 static void gopt_int_changed(GtkSpinButton *spin, gpointer data)
381 struct gopt_int *i = (struct gopt_int *) data;
382 struct fio_option *o = &fio_options[i->gopt.opt_index];
386 gopt_changed(&i->gopt);
388 adj = gtk_spin_button_get_adjustment(spin);
389 value = gtk_adjustment_get_value(adj);
390 delta = value - i->lastval;
394 struct gopt *b_inv = o->inv_opt->gui_data;
395 struct gopt_int *i_inv = container_of(b_inv, struct gopt_int, gopt);
398 assert(o->type == o->inv_opt->type);
400 cur_val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(i_inv->spin));
402 g_signal_handler_block(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
403 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i_inv->spin), cur_val);
404 g_signal_handler_unblock(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
408 static void gopt_int_destroy(GtkWidget *w, gpointer data)
410 struct gopt_int *i = (struct gopt_int *) data;
412 if (flist_empty(&i->gopt.changed_list))
415 gtk_widget_destroy(w);
418 static struct gopt_int *__gopt_new_int(struct gopt_job_view *gjv,
419 struct fio_option *o,
420 unsigned long long *p, unsigned int idx)
422 unsigned long long defval;
424 guint maxval, interval;
427 i = calloc(1, sizeof(*i));
428 i->gopt.box = gtk_hbox_new(FALSE, 3);
430 label = gtk_label_new(o->name);
432 label = gtk_label_new(o->lname);
444 check_str_bytes(o->def, &val, NULL);
450 interval = o->interval;
452 i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval);
453 gopt_mark_index(gjv, &i->gopt, idx, GOPT_INT);
454 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
455 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
457 i->gopt.sig_handler = g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
458 g_signal_connect(G_OBJECT(i->spin), "destroy", G_CALLBACK(gopt_int_destroy), i);
460 gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
461 gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
466 static struct gopt *gopt_new_int(struct gopt_job_view *gjv,
467 struct fio_option *o, unsigned int *ip,
470 unsigned long long ullp;
475 i = __gopt_new_int(gjv, o, &ullp, idx);
477 i = __gopt_new_int(gjv, o, NULL, idx);
482 static struct gopt *gopt_new_ullong(struct gopt_job_view *gjv,
483 struct fio_option *o, unsigned long long *p,
488 i = __gopt_new_int(gjv, o, p, idx);
492 static void gopt_bool_toggled(GtkToggleButton *button, gpointer data)
494 struct gopt_bool *b = (struct gopt_bool *) data;
495 struct fio_option *o = &fio_options[b->gopt.opt_index];
498 gopt_changed(&b->gopt);
500 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check));
503 struct gopt *g_inv = o->inv_opt->gui_data;
504 struct gopt_bool *b_inv = container_of(g_inv, struct gopt_bool, gopt);
506 assert(o->type == o->inv_opt->type);
508 g_signal_handler_block(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
509 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_inv->check), !set);
510 g_signal_handler_unblock(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
513 gopt_set_children_visible(b->gopt.gjv, o, set);
516 static void gopt_bool_destroy(GtkWidget *w, gpointer data)
518 struct gopt_bool *b = (struct gopt_bool *) data;
520 if (flist_empty(&b->gopt.changed_list))
523 gtk_widget_destroy(w);
526 static struct gopt *gopt_new_bool(struct gopt_job_view *gjv,
527 struct fio_option *o, unsigned int *val,
534 b = calloc(1, sizeof(*b));
535 b->gopt.box = gtk_hbox_new(FALSE, 3);
537 label = gtk_label_new(o->name);
539 label = gtk_label_new(o->lname);
541 b->check = gtk_check_button_new();
542 gopt_mark_index(gjv, &b->gopt, idx, GOPT_BOOL);
545 else if (o->def && !strcmp(o->def, "1"))
549 defstate = !defstate;
551 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate);
552 b->gopt.sig_handler = g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b);
553 g_signal_connect(G_OBJECT(b->check), "destroy", G_CALLBACK(gopt_bool_destroy), b);
555 gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
556 gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
561 * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values.
562 * If the max is made smaller than min, adjust min down.
563 * If the min is made larger than max, adjust the max.
565 static void range_value_changed(GtkSpinButton *spin, gpointer data)
567 struct gopt_range *r = (struct gopt_range *) data;
571 gopt_changed(&r->gopt);
573 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
574 if (GTK_SPIN_BUTTON(r->spins[i]) == spin) {
580 assert(changed != -1);
585 if (changed == 0 || changed == 2) {
586 GtkWidget *mspin = r->spins[changed + 1];
588 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
589 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
591 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
593 GtkWidget *mspin = r->spins[changed - 1];
595 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
596 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
598 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
602 static void gopt_range_destroy(GtkWidget *w, gpointer data)
604 struct gopt_range *r = (struct gopt_range *) data;
606 if (flist_empty(&r->gopt.changed_list))
609 gtk_widget_destroy(w);
612 static struct gopt *gopt_new_int_range(struct gopt_job_view *gjv,
613 struct fio_option *o, unsigned int **ip,
616 struct gopt_range *r;
622 r = calloc(1, sizeof(*r));
623 r->gopt.box = gtk_hbox_new(FALSE, 3);
624 gopt_mark_index(gjv, &r->gopt, idx, GOPT_RANGE);
626 label = gtk_label_new(o->name);
628 label = gtk_label_new(o->lname);
638 check_str_bytes(o->def, &val, NULL);
644 interval = o->interval;
646 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
647 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval);
648 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID);
650 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]);
652 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval);
654 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0);
655 g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r);
658 gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
659 g_signal_connect(G_OBJECT(r->gopt.box), "destroy", G_CALLBACK(gopt_range_destroy), r);
663 static void gopt_str_val_destroy(GtkWidget *w, gpointer data)
665 struct gopt_str_val *g = (struct gopt_str_val *) data;
667 if (flist_empty(&g->gopt.changed_list))
670 gtk_widget_destroy(w);
673 static void gopt_str_val_spin_wrapped(GtkSpinButton *spin, gpointer data)
675 struct gopt_str_val *g = (struct gopt_str_val *) data;
680 adj = gtk_spin_button_get_adjustment(spin);
681 val = gtk_adjustment_get_value(adj);
684 * Can't rely on exact value, as fast changes increment >= 1
687 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
688 if (index + 1 <= g->maxindex) {
690 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), ++index);
693 gtk_spin_button_set_value(spin, val);
695 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
697 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), --index);
698 gtk_spin_button_set_value(spin, 1023);
700 gtk_spin_button_set_value(spin, 0);
704 static void gopt_str_val_changed(GtkSpinButton *spin, gpointer data)
706 struct gopt_str_val *g = (struct gopt_str_val *) data;
708 gopt_changed(&g->gopt);
711 static struct gopt *gopt_new_str_val(struct gopt_job_view *gjv,
712 struct fio_option *o,
713 unsigned long long *p, unsigned int idx)
715 struct gopt_str_val *g;
716 const gchar *postfix[] = { "B", "KB", "MB", "GB", "PB", "TB", "" };
717 unsigned long long val;
721 g = calloc(1, sizeof(*g));
722 g->gopt.box = gtk_hbox_new(FALSE, 3);
724 label = gtk_label_new(o->name);
726 label = gtk_label_new(o->lname);
727 gopt_mark_index(gjv, &g->gopt, idx, GOPT_STR_VAL);
729 g->spin = gtk_spin_button_new_with_range(0.0, 1023.0, 1.0);
730 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(g->spin), GTK_UPDATE_IF_VALID);
731 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), 0);
732 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(g->spin), 1);
733 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->spin, FALSE, FALSE, 0);
734 g_signal_connect(G_OBJECT(g->spin), "wrapped", G_CALLBACK(gopt_str_val_spin_wrapped), g);
735 g_signal_connect(G_OBJECT(g->spin), "changed", G_CALLBACK(gopt_str_val_changed), g);
737 g->combo = gtk_combo_box_text_new();
739 while (strlen(postfix[i])) {
740 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(g->combo), postfix[i]);
744 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), 0);
745 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->combo, FALSE, FALSE, 0);
746 gtk_box_pack_start(GTK_BOX(g->gopt.box), label, FALSE, FALSE, 3);
755 if (!val || (val % 1024))
762 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), val);
763 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), i);
766 g_signal_connect(G_OBJECT(g->combo), "changed", G_CALLBACK(gopt_str_val_changed), g);
768 g_signal_connect(G_OBJECT(g->gopt.box), "destroy", G_CALLBACK(gopt_str_val_destroy), g);
772 static void gopt_add_option(struct gopt_job_view *gjv, GtkWidget *hbox,
773 struct fio_option *o, unsigned int opt_index,
774 struct thread_options *to)
776 struct gopt *go = NULL;
779 case FIO_OPT_STR_VAL: {
780 unsigned long long *ullp = NULL;
783 ullp = td_var(to, o->off1);
785 go = gopt_new_str_val(gjv, o, ullp, opt_index);
788 case FIO_OPT_STR_VAL_TIME: {
789 unsigned long long *ullp = NULL;
792 ullp = td_var(to, o->off1);
794 go = gopt_new_ullong(gjv, o, ullp, opt_index);
798 unsigned int *ip = NULL;
801 ip = td_var(to, o->off1);
803 go = gopt_new_int(gjv, o, ip, opt_index);
806 case FIO_OPT_STR_SET:
808 unsigned int *ip = NULL;
811 ip = td_var(to, o->off1);
813 go = gopt_new_bool(gjv, o, ip, opt_index);
817 if (o->posval[0].ival) {
818 unsigned int *ip = NULL;
821 ip = td_var(to, o->off1);
823 go = gopt_new_combo_int(gjv, o, ip, opt_index);
825 /* TODO: usually ->cb, or unsigned int pointer */
826 go = gopt_new_str_store(gjv, o, NULL, opt_index);
831 case FIO_OPT_STR_STORE: {
835 char **p = td_var(to, o->off1);
839 if (!o->posval[0].ival) {
840 go = gopt_new_str_store(gjv, o, text, opt_index);
844 go = gopt_new_combo_str(gjv, o, text, opt_index);
847 case FIO_OPT_STR_MULTI:
848 go = gopt_new_str_multi(gjv, o, opt_index);
850 case FIO_OPT_RANGE: {
851 unsigned int *ip[4] = { td_var(to, o->off1),
854 td_var(to, o->off4) };
856 go = gopt_new_int_range(gjv, o, ip, opt_index);
859 /* still need to handle this one */
860 case FIO_OPT_FLOAT_LIST:
862 case FIO_OPT_DEPRECATED:
865 printf("ignore type %u\n", o->type);
873 gtk_widget_set_tooltip_text(go->box, o->help);
877 dest = gopt_get_group_frame(gjv, hbox, o->group);
879 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
881 gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5);
885 static void gopt_add_options(struct gopt_job_view *gjv,
886 struct thread_options *to)
888 GtkWidget *hbox = NULL;
892 * First add all options
894 for (i = 0; fio_options[i].name; i++) {
895 struct fio_option *o = &fio_options[i];
896 unsigned int mask = o->category;
897 struct opt_group *og;
899 while ((og = opt_group_from_mask(&mask)) != NULL) {
900 GtkWidget *vbox = gjv->vboxes[ffz(~og->mask)];
902 hbox = gtk_hbox_new(FALSE, 3);
903 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
904 gopt_add_option(gjv, hbox, o, i, to);
909 static GtkWidget *gopt_add_tab(GtkWidget *notebook, const char *name)
911 GtkWidget *box, *vbox, *scroll;
913 scroll = gtk_scrolled_window_new(NULL, NULL);
914 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
915 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
917 vbox = gtk_vbox_new(FALSE, 3);
918 box = gtk_hbox_new(FALSE, 0);
919 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
920 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
921 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(name));
925 static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
927 return gopt_add_tab(notebook, og->name);
930 static void gopt_add_group_tabs(GtkWidget *notebook, struct gopt_job_view *gjv)
932 struct opt_group *og;
937 unsigned int mask = (1U << i);
939 og = opt_group_from_mask(&mask);
942 gjv->vboxes[i] = gopt_add_group_tab(notebook, og);
947 static void gopt_handle_str_multi_changed(struct gopt_job_view *gjv,
948 struct gopt_str_multi *m,
949 struct fio_option *o)
951 unsigned int *ip = td_var(gjv->o, o->off1);
952 struct value_pair *vp;
962 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m->checks[i]));
977 static void gopt_handle_range_changed(struct gopt_job_view *gjv,
978 struct gopt_range *r,
979 struct fio_option *o)
981 unsigned int *ip[4] = { td_var(gjv->o, o->off1),
982 td_var(gjv->o, o->off2),
983 td_var(gjv->o, o->off3),
984 td_var(gjv->o, o->off4) };
988 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
989 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[i]));
994 static void gopt_handle_str_val_changed(struct gopt_job_view *gjv,
995 struct gopt_str_val *s,
996 struct fio_option *o)
998 unsigned long long *ullp = td_var(gjv->o, o->off1);
1008 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(s->spin));
1009 *ullp = gtk_adjustment_get_value(adj);
1014 index = gtk_combo_box_get_active(GTK_COMBO_BOX(s->combo));
1019 static void gopt_handle_str_changed(struct gopt_job_view *gjv,
1020 struct gopt_str *s, struct fio_option *o)
1022 char **p = td_var(gjv->o, o->off1);
1027 *p = strdup(gtk_entry_get_text(GTK_ENTRY(s->entry)));
1030 static void gopt_handle_bool_changed(struct gopt_job_view *gjv,
1031 struct gopt_bool *b, struct fio_option *o)
1033 unsigned int *ip = td_var(gjv->o, o->off1);
1036 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check));
1040 static void gopt_handle_int_changed(struct gopt_job_view *gjv,
1041 struct gopt_int *i, struct fio_option *o)
1043 unsigned int *ip = td_var(gjv->o, o->off1);
1047 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(i->spin));
1048 val = gtk_adjustment_get_value(adj);
1052 static void gopt_handle_combo_str_changed(struct gopt_job_view *gjv,
1053 struct gopt_combo *c,
1054 struct fio_option *o)
1056 char **p = td_var(gjv->o, o->off1);
1061 *p = strdup(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(c->combo)));
1064 static void gopt_handle_combo_int_changed(struct gopt_job_view *gjv,
1065 struct gopt_combo *c,
1066 struct fio_option *o)
1068 unsigned int *ip = td_var(gjv->o, o->off1);
1071 index = gtk_combo_box_get_active(GTK_COMBO_BOX(c->combo));
1072 *ip = o->posval[index].oval;
1075 static void gopt_handle_changed(struct gopt *gopt)
1077 struct fio_option *o = &fio_options[gopt->opt_index];
1078 struct gopt_job_view *gjv = gopt->gjv;
1079 void *to_free = NULL;
1081 switch (gopt->opt_type) {
1082 case GOPT_COMBO_INT: {
1083 struct gopt_combo *c;
1085 c = container_of(gopt, struct gopt_combo, gopt);
1086 gopt_handle_combo_int_changed(gjv, c, o);
1090 case GOPT_COMBO_STR: {
1091 struct gopt_combo *c;
1093 c = container_of(gopt, struct gopt_combo, gopt);
1094 gopt_handle_combo_str_changed(gjv, c, o);
1101 i = container_of(gopt, struct gopt_int, gopt);
1102 gopt_handle_int_changed(gjv, i, o);
1107 struct gopt_bool *b;
1109 b = container_of(gopt, struct gopt_bool, gopt);
1110 gopt_handle_bool_changed(gjv, b, o);
1117 s = container_of(gopt, struct gopt_str, gopt);
1118 gopt_handle_str_changed(gjv, s, o);
1122 case GOPT_STR_VAL: {
1123 struct gopt_str_val *s;
1125 s = container_of(gopt, struct gopt_str_val, gopt);
1126 gopt_handle_str_val_changed(gjv, s, o);
1131 struct gopt_range *r;
1133 r = container_of(gopt, struct gopt_range, gopt);
1134 gopt_handle_range_changed(gjv, r, o);
1138 case GOPT_STR_MULTI: {
1139 struct gopt_str_multi *m;
1141 m = container_of(gopt, struct gopt_str_multi, gopt);
1142 gopt_handle_str_multi_changed(gjv, m, o);
1147 log_err("gfio: bad option type %s/%d\n", gopt->opt_type);
1151 g_object_unref(G_OBJECT(gopt->box));
1155 static void gopt_handle_changed_options(struct gopt_job_view *gjv)
1159 while (!flist_empty(&gjv->changed_list)) {
1160 gopt = flist_entry(gjv->changed_list.next, struct gopt, changed_list);
1161 flist_del(&gopt->changed_list);
1162 gopt_handle_changed(gopt);
1166 void gopt_get_options_window(GtkWidget *window, struct gfio_client *gc)
1168 GtkWidget *dialog, *notebook, *topnotebook, *vbox;
1169 struct gfio_client_options *gco;
1170 struct thread_options *o;
1171 struct flist_head *entry;
1172 struct gopt_job_view *gjv;
1173 FLIST_HEAD(gjv_list);
1176 * Just choose the first item, we need to make each options
1177 * entry the main notebook, with the below options view as
1180 assert(!flist_empty(&gc->o_list));
1182 dialog = gtk_dialog_new_with_buttons("Fio options",
1183 GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
1184 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1185 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1187 gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768);
1189 topnotebook = gtk_notebook_new();
1190 gtk_notebook_set_scrollable(GTK_NOTEBOOK(topnotebook), 1);
1191 gtk_notebook_popup_enable(GTK_NOTEBOOK(topnotebook));
1192 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1193 gtk_box_pack_start(GTK_BOX(vbox), topnotebook, TRUE, TRUE, 5);
1195 flist_for_each(entry, &gc->o_list) {
1198 gco = flist_entry(entry, struct gfio_client_options, list);
1201 if (!name || !strlen(name))
1202 name = "Default job";
1204 vbox = gopt_add_tab(topnotebook, name);
1206 notebook = gtk_notebook_new();
1207 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1208 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1209 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 5);
1211 gjv = calloc(1, sizeof(*gjv));
1212 INIT_FLIST_HEAD(&gjv->list);
1213 INIT_FLIST_HEAD(&gjv->changed_list);
1215 flist_add_tail(&gjv->list, &gjv_list);
1216 gopt_add_group_tabs(notebook, gjv);
1217 gopt_add_options(gjv, o);
1220 gtk_widget_show_all(dialog);
1222 gtk_dialog_run(GTK_DIALOG(dialog));
1224 while (!flist_empty(&gjv_list)) {
1225 gjv = flist_entry(gjv_list.next, struct gopt_job_view, list);
1227 gopt_handle_changed_options(gjv);
1229 flist_del(&gjv->list);
1233 gtk_widget_destroy(dialog);
1237 * Build n-ary option dependency tree
1239 void gopt_init(void)
1243 gopt_dep_tree = g_node_new(NULL);
1245 for (i = 0; fio_options[i].name; i++) {
1246 struct fio_option *o = &fio_options[i];
1247 GNode *node, *nparent;
1250 * Insert node with either the root parent, or an
1253 node = g_node_new(o);
1254 nparent = gopt_dep_tree;
1256 struct fio_option *parent;
1258 parent = fio_option_find(o->parent);
1259 nparent = g_node_find(gopt_dep_tree, G_IN_ORDER, G_TRAVERSE_ALL, parent);
1261 log_err("fio: did not find parent %s for opt %s\n", o->name, o->parent);
1262 nparent = gopt_dep_tree;
1266 g_node_insert(nparent, -1, node);
1270 void gopt_exit(void)
1272 g_node_destroy(gopt_dep_tree);
1273 gopt_dep_tree = NULL;