X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=goptions.c;h=547d9cf98a88ca50831b4cd793edd37d0a49baa6;hp=7f23cef753703aa1f8787ad250dbec3e0a8a9c9b;hb=90265353af8dbf1c43804996909777d4c1a5998e;hpb=789f4ccdf725e6da8bde98afd004c31e16241cfd diff --git a/goptions.c b/goptions.c index 7f23cef7..547d9cf9 100644 --- a/goptions.c +++ b/goptions.c @@ -24,6 +24,8 @@ struct gopt_combo { struct gopt_int { struct gopt gopt; + unsigned int lastval; + unsigned int in_change; GtkWidget *spin; }; @@ -37,12 +39,105 @@ struct gopt_str { GtkWidget *entry; }; +#define GOPT_RANGE_SPIN 4 + struct gopt_range { struct gopt gopt; - GtkWidget *spins[4]; + GtkWidget *spins[GOPT_RANGE_SPIN]; +}; + +struct gopt_str_multi { + struct gopt gopt; + GtkWidget *checks[PARSE_MAX_VP]; +}; + +static GtkWidget *gopt_widgets[FIO_MAX_OPTS]; + +struct gopt_frame_widget { + GtkWidget *vbox[2]; + unsigned int nr; }; +static struct gopt_frame_widget gopt_g_widgets[__FIO_OPT_G_NR]; + +static GtkWidget *gopt_get_group_frame(GtkWidget *box, unsigned int groupmask) +{ + unsigned int mask, group; + struct opt_group *og; + GtkWidget *frame, *hbox; + struct gopt_frame_widget *gfw; + + if (!groupmask) + return 0; + + mask = groupmask; + og = opt_group_cat_from_mask(&mask); + if (!og) + return NULL; + + group = ffz(~groupmask); + gfw = &gopt_g_widgets[group]; + if (!gfw->vbox[0]) { + frame = gtk_frame_new(og->name); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3); + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(frame), hbox); + gfw->vbox[0] = gtk_vbox_new(TRUE, 5); + gfw->vbox[1] = gtk_vbox_new(TRUE, 5); + gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[0], TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[1], TRUE, TRUE, 5); + } + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(gfw->vbox[gfw->nr++ & 1]), hbox, FALSE, FALSE, 5); + return hbox; +} + +/* + * Mark children as invisible, if needed. + */ +static void gopt_set_children_visible(struct fio_option *parent, gboolean visible) +{ + struct fio_option *o; + int i; + + /* + * This isn't super fast, but it should not be an issue. If it is, we + * can speed it up by caching the lookup at least. Or we can do it + * once, at init time. + */ + for (i = 0; fio_options[i].name; i++) { + o = &fio_options[i]; + if (!o->parent || !o->hide) + continue; + + if (strcmp(parent->name, o->parent)) + continue; -static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text) + if (gopt_widgets[i]) + gtk_widget_set_sensitive(gopt_widgets[i], visible); + } +} + +static void gopt_str_changed(GtkEntry *entry, gpointer data) +{ + struct gopt_str *s = (struct gopt_str *) data; + struct fio_option *o = &fio_options[s->gopt.opt_index]; + const gchar *text; + int set; + + text = gtk_entry_get_text(GTK_ENTRY(s->entry)); + set = strcmp(text, "") != 0; + gopt_set_children_visible(o, set); +} + +static void gopt_mark_index(struct gopt *gopt, unsigned int idx) +{ + assert(!gopt_widgets[idx]); + gopt->opt_index = idx; + gopt_widgets[idx] = gopt->box; +} + +static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text, unsigned int idx) { struct gopt_str *s; GtkWidget *label; @@ -50,42 +145,96 @@ static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text) s = malloc(sizeof(*s)); s->gopt.box = gtk_hbox_new(FALSE, 3); - label = gtk_label_new(o->name); - gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0); + if (!o->lname) + label = gtk_label_new(o->name); + else + label = gtk_label_new(o->lname); s->entry = gtk_entry_new(); + gopt_mark_index(&s->gopt, idx); if (text) gtk_entry_set_text(GTK_ENTRY(s->entry), text); gtk_entry_set_editable(GTK_ENTRY(s->entry), 1); + g_signal_connect(GTK_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s); if (o->def) gtk_entry_set_text(GTK_ENTRY(s->entry), o->def); gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0); + o->gui_data = s; return &s->gopt; } -static struct gopt *gopt_new_combo(struct fio_option *o) +static void gopt_combo_changed(GtkComboBox *box, gpointer data) +{ + struct gopt_combo *c = (struct gopt_combo *) data; + struct fio_option *o = &fio_options[c->gopt.opt_index]; + + printf("combo %s changed\n", o->name); +} + +static struct gopt_combo *__gopt_new_combo(struct fio_option *o, unsigned int idx) +{ + struct gopt_combo *c; + GtkWidget *label; + + c = malloc(sizeof(*c)); + + c->gopt.box = gtk_hbox_new(FALSE, 3); + if (!o->lname) + label = gtk_label_new(o->name); + else + label = gtk_label_new(o->lname); + + c->combo = gtk_combo_box_new_text(); + gopt_mark_index(&c->gopt, idx); + g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c); + + gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0); + + o->gui_data = c; + return c; +} + +static struct gopt *gopt_new_combo_str(struct fio_option *o, const char *text, unsigned int idx) { struct gopt_combo *combo; struct value_pair *vp; - GtkWidget *label; int i, active = 0; - combo = malloc(sizeof(*combo)); + combo = __gopt_new_combo(o, idx); - combo->gopt.box = gtk_hbox_new(FALSE, 3); - label = gtk_label_new(o->name); - gtk_box_pack_start(GTK_BOX(combo->gopt.box), label, FALSE, FALSE, 0); + i = 0; + vp = &o->posval[0]; + while (vp->ival) { + gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival); + if (o->def && !strcmp(vp->ival, o->def)) + active = i; + if (text && !strcmp(vp->ival, text)) + active = i; + vp++; + i++; + } - combo->combo = gtk_combo_box_new_text(); - gtk_box_pack_start(GTK_BOX(combo->gopt.box), combo->combo, FALSE, FALSE, 0); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active); + return &combo->gopt; +} + +static struct gopt *gopt_new_combo_int(struct fio_option *o, unsigned int *ip, unsigned int idx) +{ + struct gopt_combo *combo; + struct value_pair *vp; + int i, active = 0; + + combo = __gopt_new_combo(o, idx); i = 0; vp = &o->posval[0]; while (vp->ival) { gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival); - if (o->def && !strcmp(vp->ival, o->def)) + if (ip && vp->oval == *ip) active = i; vp++; i++; @@ -95,38 +244,151 @@ static struct gopt *gopt_new_combo(struct fio_option *o) return &combo->gopt; } -static struct gopt *gopt_new_int(struct fio_option *o) +static struct gopt *gopt_new_str_multi(struct fio_option *o, unsigned int idx) +{ + struct gopt_str_multi *m; + struct value_pair *vp; + GtkWidget *frame, *hbox; + int i; + + m = malloc(sizeof(*m)); + m->gopt.box = gtk_hbox_new(FALSE, 3); + gopt_mark_index(&m->gopt, idx); + + if (!o->lname) + frame = gtk_frame_new(o->name); + else + frame = gtk_frame_new(o->lname); + gtk_box_pack_start(GTK_BOX(m->gopt.box), frame, FALSE, FALSE, 3); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), hbox); + + i = 0; + vp = &o->posval[0]; + while (vp->ival) { + m->checks[i] = gtk_check_button_new_with_label(vp->ival); + gtk_widget_set_tooltip_text(m->checks[i], vp->help); + gtk_box_pack_start(GTK_BOX(hbox), m->checks[i], FALSE, FALSE, 3); + vp++; + } + + return &m->gopt; +} + +static void gopt_int_changed(GtkSpinButton *spin, gpointer data) +{ + struct gopt_int *i = (struct gopt_int *) data; + struct fio_option *o = &fio_options[i->gopt.opt_index]; + GtkAdjustment *adj; + int value, delta; + + adj = gtk_spin_button_get_adjustment(spin); + value = gtk_adjustment_get_value(adj); + delta = value - i->lastval; + i->lastval = value; + + if (o->inv_opt) { + struct gopt_int *i_inv = o->inv_opt->gui_data; + int cur_val; + + /* + * Don't recourse into notify changes. Is there a better + * way than this? We essentially want to block the update + * signal while we perform the below set_value(). + */ + if (i_inv->in_change) + return; + + i->in_change = 1; + cur_val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(i_inv->spin)); + cur_val -= delta; + gtk_spin_button_set_value(GTK_SPIN_BUTTON(i_inv->spin), cur_val); + i->in_change = 0; + } +} + +static struct gopt_int *__gopt_new_int(struct fio_option *o, unsigned long long *p, + unsigned int idx) { + unsigned long long defval; struct gopt_int *i; - gint maxval, defval; + guint maxval, interval; GtkWidget *label; i = malloc(sizeof(*i)); i->gopt.box = gtk_hbox_new(FALSE, 3); - label = gtk_label_new(o->name); - gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0); + if (!o->lname) + label = gtk_label_new(o->name); + else + label = gtk_label_new(o->lname); maxval = o->maxval; if (!maxval) - maxval = INT_MAX; + maxval = UINT_MAX; defval = 0; - if (o->def) { + if (p) + defval = *p; + else if (o->def) { long long val; check_str_bytes(o->def, &val, NULL); defval = val; } - i->spin = gtk_spin_button_new_with_range(o->minval, maxval, 1.0); + interval = 1.0; + if (o->interval) + interval = o->interval; + + i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval); + gopt_mark_index(&i->gopt, idx); gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID); gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval); + i->lastval = defval; + g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i); gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0); + + o->gui_data = i; + return i; +} + +static struct gopt *gopt_new_int(struct fio_option *o, unsigned int *ip, unsigned int idx) +{ + unsigned long long ullp; + struct gopt_int *i; + + if (ip) { + ullp = *ip; + i = __gopt_new_int(o, &ullp, idx); + } else + i = __gopt_new_int(o, NULL, idx); + + return &i->gopt; +} + +static struct gopt *gopt_new_ullong(struct fio_option *o, unsigned long long *p, + unsigned int idx) +{ + struct gopt_int *i; + + i = __gopt_new_int(o, p, idx); return &i->gopt; } -static struct gopt *gopt_new_bool(struct fio_option *o) +static void gopt_bool_toggled(GtkToggleButton *button, gpointer data) +{ + struct gopt_bool *b = (struct gopt_bool *) data; + struct fio_option *o = &fio_options[b->gopt.opt_index]; + gboolean set; + + set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check)); + gopt_set_children_visible(o, set); +} + +static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val, unsigned int idx) { struct gopt_bool *b; GtkWidget *label; @@ -134,30 +396,86 @@ static struct gopt *gopt_new_bool(struct fio_option *o) b = malloc(sizeof(*b)); b->gopt.box = gtk_hbox_new(FALSE, 3); - label = gtk_label_new(o->name); - gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0); + if (!o->lname) + label = gtk_label_new(o->name); + else + label = gtk_label_new(o->lname); b->check = gtk_check_button_new(); - if (o->def && !strcmp(o->def, "1")) + gopt_mark_index(&b->gopt, idx); + if (val) + defstate = *val; + else if (o->def && !strcmp(o->def, "1")) defstate = 1; + if (o->neg) + defstate = !defstate; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate); + g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b); gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0); + o->gui_data = b; return &b->gopt; } -static struct gopt *gopt_new_int_range(struct fio_option *o) +/* + * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values. + * If the max is made smaller than min, adjust min down. + * If the min is made larger than max, adjust the max. + */ +static void range_value_changed(GtkSpinButton *spin, gpointer data) +{ + struct gopt_range *r = (struct gopt_range *) data; + int changed = -1, i; + gint val, mval; + + for (i = 0; i < GOPT_RANGE_SPIN; i++) { + if (GTK_SPIN_BUTTON(r->spins[i]) == spin) { + changed = i; + break; + } + } + + assert(changed != -1); + + /* + * Min changed + */ + if (changed == 0 || changed == 2) { + GtkWidget *mspin = r->spins[changed + 1]; + + val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed])); + mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin)); + if (val > mval) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val); + } else { + GtkWidget *mspin = r->spins[changed - 1]; + + val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed])); + mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin)); + if (val < mval) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val); + } +} + +static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip, + unsigned int idx) { struct gopt_range *r; gint maxval, defval; GtkWidget *label; + guint interval; int i; r = malloc(sizeof(*r)); r->gopt.box = gtk_hbox_new(FALSE, 3); - label = gtk_label_new(o->name); - gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0); + gopt_mark_index(&r->gopt, idx); + if (!o->lname) + label = gtk_label_new(o->name); + else + label = gtk_label_new(o->lname); maxval = o->maxval; if (!maxval) @@ -171,14 +489,24 @@ static struct gopt *gopt_new_int_range(struct fio_option *o) defval = val; } - for (i = 0; i < 4; i++) { - r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, 1.0); + interval = 1.0; + if (o->interval) + interval = o->interval; + + for (i = 0; i < GOPT_RANGE_SPIN; i++) { + r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval); gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval); + if (ip) + gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]); + else + gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval); gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r); } + gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0); + o->gui_data = r; return &r->gopt; } @@ -188,32 +516,78 @@ static void gopt_add_option(GtkWidget *hbox, struct fio_option *o, struct gopt *go = NULL; switch (o->type) { - case FIO_OPT_STR_STORE: { - char **p = td_var(to, o->off1); + case FIO_OPT_STR_VAL: + case FIO_OPT_STR_VAL_TIME: { + unsigned long long *ullp = NULL; - go = gopt_new_str_store(o, *p); + if (o->off1) + ullp = td_var(to, o->off1); + + go = gopt_new_ullong(o, ullp, opt_index); break; } - case FIO_OPT_STR_VAL: - case FIO_OPT_STR_VAL_TIME: - case FIO_OPT_INT: - go = gopt_new_int(o); + case FIO_OPT_INT: { + unsigned int *ip = NULL; + + if (o->off1) + ip = td_var(to, o->off1); + + go = gopt_new_int(o, ip, opt_index); break; + } case FIO_OPT_STR_SET: - case FIO_OPT_BOOL: - go = gopt_new_bool(o); + case FIO_OPT_BOOL: { + unsigned int *ip = NULL; + + if (o->off1) + ip = td_var(to, o->off1); + + go = gopt_new_bool(o, ip, opt_index); break; - case FIO_OPT_STR: + } + case FIO_OPT_STR: { + if (o->posval[0].ival) { + unsigned int *ip = NULL; + + if (o->off1) + ip = td_var(to, o->off1); + + go = gopt_new_combo_int(o, ip, opt_index); + } else { + /* TODO: usually ->cb, or unsigned int pointer */ + go = gopt_new_str_store(o, NULL, opt_index); + } + + break; + } + case FIO_OPT_STR_STORE: { + char *text = NULL; + + if (o->off1) { + char **p = td_var(to, o->off1); + text = *p; + } + if (!o->posval[0].ival) { - go = gopt_new_str_store(o, NULL); + go = gopt_new_str_store(o, text, opt_index); break; } + + go = gopt_new_combo_str(o, text, opt_index); + break; + } case FIO_OPT_STR_MULTI: - go = gopt_new_combo(o); + go = gopt_new_str_multi(o, opt_index); break; - case FIO_OPT_RANGE: - go = gopt_new_int_range(o); + case FIO_OPT_RANGE: { + unsigned int *ip[4] = { td_var(to, o->off1), + td_var(to, o->off2), + td_var(to, o->off3), + td_var(to, o->off4) }; + + go = gopt_new_int_range(o, ip, opt_index); break; + } /* still need to handle this one */ case FIO_OPT_FLOAT_LIST: break; @@ -225,12 +599,18 @@ static void gopt_add_option(GtkWidget *hbox, struct fio_option *o, } if (go) { + GtkWidget *dest; + if (o->help) gtk_widget_set_tooltip_text(go->box, o->help); - - gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5); - go->opt_index = opt_index; + go->opt_type = o->type; + + dest = gopt_get_group_frame(hbox, o->group); + if (!dest) + gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5); + else + gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5); } } @@ -239,6 +619,9 @@ static void gopt_add_options(GtkWidget **vboxes, struct thread_options *to) GtkWidget *hbox = NULL; int i; + /* + * First add all options + */ for (i = 0; fio_options[i].name; i++) { struct fio_option *o = &fio_options[i]; unsigned int mask = o->category; @@ -274,8 +657,9 @@ static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og) static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox) { struct opt_group *og; - unsigned int i = 0; + unsigned int i; + i = 0; do { unsigned int mask = (1U << i); @@ -290,7 +674,7 @@ static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox) void gopt_get_options_window(GtkWidget *window, struct thread_options *o) { GtkWidget *dialog, *notebook; - GtkWidget *vboxes[__FIO_OPT_G_NR]; + GtkWidget *vboxes[__FIO_OPT_C_NR]; dialog = gtk_dialog_new_with_buttons("Fio options", GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,