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