Home | History | Annotate | Download | only in fio
      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 
     15 struct 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 
     24 struct gopt_combo {
     25 	struct gopt gopt;
     26 	GtkWidget *combo;
     27 };
     28 
     29 struct gopt_int {
     30 	struct gopt gopt;
     31 	unsigned long long lastval;
     32 	GtkWidget *spin;
     33 };
     34 
     35 struct gopt_bool {
     36 	struct gopt gopt;
     37 	GtkWidget *check;
     38 };
     39 
     40 struct gopt_str {
     41 	struct gopt gopt;
     42 	GtkWidget *entry;
     43 };
     44 
     45 struct 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 
     54 struct gopt_range {
     55 	struct gopt gopt;
     56 	GtkWidget *spins[GOPT_RANGE_SPIN];
     57 };
     58 
     59 struct gopt_str_multi {
     60 	struct gopt gopt;
     61 	GtkWidget *checks[PARSE_MAX_VP];
     62 };
     63 
     64 enum {
     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 
     75 struct gopt_frame_widget {
     76 	GtkWidget *vbox[2];
     77 	unsigned int nr;
     78 };
     79 
     80 struct 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 
     92 static GNode *gopt_dep_tree;
     93 
     94 static 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  */
    131 static 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 
    158 static 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 
    170 static 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 
    187 static 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 
    204 static 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 
    219 static 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 
    227 static 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 
    233 static 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 
    265 static 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 
    278 static 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 
    286 static 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 
    311 static 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 
    329 static 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 
    356 static 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 
    374 static 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 
    401 static 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 
    408 static 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 
    416 static void gopt_str_multi_set_val(struct gopt_str_multi *m, int val)
    417 {
    418 }
    419 
    420 static 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 
    457 static 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 
    486 static 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 
    494 static 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 
    500 static 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 
    550 static 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 
    566 static 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 
    576 static 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 
    600 static 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 
    608 static 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 
    613 static 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  */
    653 static 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 
    690 static 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 
    698 static 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 
    706 static 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 
    761 static 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 
    769 static 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 
    800 static 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 
    807 static 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 
    823 static 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 
    868 static 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 
   1006 static 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 
   1127 static 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 
   1151 static 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 
   1164 static 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 
   1180 static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
   1181 {
   1182 	return gopt_add_tab(notebook, og->name);
   1183 }
   1184 
   1185 static 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 
   1202 static 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 
   1232 static 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 
   1249 static 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 
   1274 static 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 
   1285 static 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 
   1295 static 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 
   1307 static 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 
   1319 static 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 
   1330 static 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 
   1398 static 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 
   1407 static 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 
   1440 done:
   1441 	gopt_dialog_update_apply_button(gjv);
   1442 	return ret;
   1443 }
   1444 
   1445 static 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 
   1459 static 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 
   1471 static 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 
   1495 static 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 
   1537 void 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  */
   1602 void 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 
   1633 void gopt_exit(void)
   1634 {
   1635 	g_node_destroy(gopt_dep_tree);
   1636 	gopt_dep_tree = NULL;
   1637 }
   1638