goptions: ensure that FIO_OPT_STR_VAL doesn't exceed max multiplier
[fio.git] / goptions.c
CommitLineData
9af4a244
JA
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 "parse.h"
13
14struct gopt {
15 GtkWidget *box;
16 unsigned int opt_index;
17 unsigned int opt_type;
8f9e46ab 18 gulong sig_handler;
9af4a244
JA
19};
20
21struct gopt_combo {
22 struct gopt gopt;
23 GtkWidget *combo;
24};
25
26struct gopt_int {
27 struct gopt gopt;
90265353 28 unsigned int lastval;
9af4a244
JA
29 GtkWidget *spin;
30};
31
32struct gopt_bool {
33 struct gopt gopt;
34 GtkWidget *check;
35};
36
37struct gopt_str {
38 struct gopt gopt;
39 GtkWidget *entry;
40};
41
7fc35ff7
JA
42struct gopt_str_val {
43 struct gopt gopt;
44 GtkWidget *spin;
45 GtkWidget *combo;
481c242b 46 unsigned int maxindex;
7fc35ff7
JA
47};
48
1da10c19
JA
49#define GOPT_RANGE_SPIN 4
50
9af4a244
JA
51struct gopt_range {
52 struct gopt gopt;
1da10c19 53 GtkWidget *spins[GOPT_RANGE_SPIN];
9af4a244
JA
54};
55
7386d4ae
JA
56struct gopt_str_multi {
57 struct gopt gopt;
58 GtkWidget *checks[PARSE_MAX_VP];
59};
60
e8b0e958 61static GtkWidget *gopt_widgets[FIO_MAX_OPTS];
ec0218ff 62
e8b0e958
JA
63struct gopt_frame_widget {
64 GtkWidget *vbox[2];
65 unsigned int nr;
66};
67static struct gopt_frame_widget gopt_g_widgets[__FIO_OPT_G_NR];
ec0218ff 68
e8b0e958 69static GtkWidget *gopt_get_group_frame(GtkWidget *box, unsigned int groupmask)
ec0218ff 70{
e8b0e958
JA
71 unsigned int mask, group;
72 struct opt_group *og;
73 GtkWidget *frame, *hbox;
74 struct gopt_frame_widget *gfw;
75
76 if (!groupmask)
77 return 0;
78
79 mask = groupmask;
80 og = opt_group_cat_from_mask(&mask);
81 if (!og)
82 return NULL;
83
84 group = ffz(~groupmask);
85 gfw = &gopt_g_widgets[group];
86 if (!gfw->vbox[0]) {
87 frame = gtk_frame_new(og->name);
88 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3);
89 hbox = gtk_hbox_new(FALSE, 0);
90 gtk_container_add(GTK_CONTAINER(frame), hbox);
91 gfw->vbox[0] = gtk_vbox_new(TRUE, 5);
92 gfw->vbox[1] = gtk_vbox_new(TRUE, 5);
93 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[0], TRUE, TRUE, 5);
94 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[1], TRUE, TRUE, 5);
ec0218ff 95 }
e8b0e958
JA
96
97 hbox = gtk_hbox_new(FALSE, 3);
98 gtk_box_pack_start(GTK_BOX(gfw->vbox[gfw->nr++ & 1]), hbox, FALSE, FALSE, 5);
99 return hbox;
ec0218ff 100}
b6caa836
JA
101
102/*
103 * Mark children as invisible, if needed.
104 */
231edf61
JA
105static void gopt_set_children_visible(struct fio_option *parent,
106 gboolean visible)
b6caa836
JA
107{
108 struct fio_option *o;
109 int i;
110
111 /*
112 * This isn't super fast, but it should not be an issue. If it is, we
113 * can speed it up by caching the lookup at least. Or we can do it
114 * once, at init time.
115 */
116 for (i = 0; fio_options[i].name; i++) {
117 o = &fio_options[i];
ec0218ff 118 if (!o->parent || !o->hide)
b6caa836
JA
119 continue;
120
121 if (strcmp(parent->name, o->parent))
122 continue;
123
e8b0e958
JA
124 if (gopt_widgets[i])
125 gtk_widget_set_sensitive(gopt_widgets[i], visible);
b6caa836
JA
126 }
127}
128
129static void gopt_str_changed(GtkEntry *entry, gpointer data)
130{
131 struct gopt_str *s = (struct gopt_str *) data;
132 struct fio_option *o = &fio_options[s->gopt.opt_index];
133 const gchar *text;
134 int set;
135
136 text = gtk_entry_get_text(GTK_ENTRY(s->entry));
137 set = strcmp(text, "") != 0;
138 gopt_set_children_visible(o, set);
139}
140
ec0218ff 141static void gopt_mark_index(struct gopt *gopt, unsigned int idx)
b6caa836 142{
e8b0e958 143 assert(!gopt_widgets[idx]);
b6caa836 144 gopt->opt_index = idx;
e8b0e958 145 gopt_widgets[idx] = gopt->box;
b6caa836
JA
146}
147
231edf61
JA
148static void gopt_str_destroy(GtkWidget *w, gpointer data)
149{
150 struct gopt_str *s = (struct gopt_str *) data;
151
152 free(s);
153 gtk_widget_destroy(w);
154}
155
156static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text,
157 unsigned int idx)
9af4a244
JA
158{
159 struct gopt_str *s;
160 GtkWidget *label;
161
162 s = malloc(sizeof(*s));
a01a1bc5 163 memset(s, 0, sizeof(*s));
9af4a244
JA
164
165 s->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
166 if (!o->lname)
167 label = gtk_label_new(o->name);
168 else
169 label = gtk_label_new(o->lname);
9af4a244
JA
170
171 s->entry = gtk_entry_new();
ec0218ff 172 gopt_mark_index(&s->gopt, idx);
789f4ccd
JA
173 if (text)
174 gtk_entry_set_text(GTK_ENTRY(s->entry), text);
9af4a244 175 gtk_entry_set_editable(GTK_ENTRY(s->entry), 1);
8f9e46ab 176 s->gopt.sig_handler = g_signal_connect(GTK_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s);
231edf61 177 g_signal_connect(GTK_OBJECT(s->entry), "destroy", G_CALLBACK(gopt_str_destroy), s);
9af4a244
JA
178
179 if (o->def)
180 gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
181
182 gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
e8b0e958 183 gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
90265353 184 o->gui_data = s;
9af4a244
JA
185 return &s->gopt;
186}
187
b6caa836 188static void gopt_combo_changed(GtkComboBox *box, gpointer data)
9af4a244 189{
b6caa836
JA
190 struct gopt_combo *c = (struct gopt_combo *) data;
191 struct fio_option *o = &fio_options[c->gopt.opt_index];
192
193 printf("combo %s changed\n", o->name);
194}
195
231edf61
JA
196static void gopt_combo_destroy(GtkWidget *w, gpointer data)
197{
198 struct gopt_combo *c = (struct gopt_combo *) data;
199
200 free(c);
201 gtk_widget_destroy(w);
202}
203
204static struct gopt_combo *__gopt_new_combo(struct fio_option *o,
205 unsigned int idx)
b6caa836
JA
206{
207 struct gopt_combo *c;
9af4a244 208 GtkWidget *label;
9af4a244 209
b6caa836 210 c = malloc(sizeof(*c));
a01a1bc5 211 memset(c, 0, sizeof(*c));
9af4a244 212
b6caa836 213 c->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
214 if (!o->lname)
215 label = gtk_label_new(o->name);
216 else
217 label = gtk_label_new(o->lname);
9af4a244 218
b6caa836 219 c->combo = gtk_combo_box_new_text();
ec0218ff 220 gopt_mark_index(&c->gopt, idx);
8f9e46ab 221 c->gopt.sig_handler = g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
231edf61 222 g_signal_connect(GTK_OBJECT(c->combo), "destroy", G_CALLBACK(gopt_combo_destroy), c);
b6caa836 223
e8b0e958
JA
224 gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0);
225 gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0);
226
90265353 227 o->gui_data = c;
b6caa836 228 return c;
39f04336
JA
229}
230
231edf61
JA
231static struct gopt *gopt_new_combo_str(struct fio_option *o, const char *text,
232 unsigned int idx)
39f04336
JA
233{
234 struct gopt_combo *combo;
235 struct value_pair *vp;
236 int i, active = 0;
237
b6caa836 238 combo = __gopt_new_combo(o, idx);
39f04336 239
9af4a244
JA
240 i = 0;
241 vp = &o->posval[0];
242 while (vp->ival) {
243 gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival);
244 if (o->def && !strcmp(vp->ival, o->def))
245 active = i;
39f04336
JA
246 if (text && !strcmp(vp->ival, text))
247 active = i;
9af4a244
JA
248 vp++;
249 i++;
250 }
251
252 gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active);
253 return &combo->gopt;
254}
255
231edf61
JA
256static struct gopt *gopt_new_combo_int(struct fio_option *o, unsigned int *ip,
257 unsigned int idx)
9af4a244 258{
39f04336
JA
259 struct gopt_combo *combo;
260 struct value_pair *vp;
261 int i, active = 0;
262
b6caa836 263 combo = __gopt_new_combo(o, idx);
39f04336
JA
264
265 i = 0;
266 vp = &o->posval[0];
267 while (vp->ival) {
268 gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival);
269 if (ip && vp->oval == *ip)
270 active = i;
271 vp++;
272 i++;
273 }
274
275 gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active);
276 return &combo->gopt;
277}
278
7386d4ae
JA
279static struct gopt *gopt_new_str_multi(struct fio_option *o, unsigned int idx)
280{
281 struct gopt_str_multi *m;
282 struct value_pair *vp;
283 GtkWidget *frame, *hbox;
284 int i;
285
286 m = malloc(sizeof(*m));
a01a1bc5 287 memset(m, 0, sizeof(*m));
7386d4ae
JA
288 m->gopt.box = gtk_hbox_new(FALSE, 3);
289 gopt_mark_index(&m->gopt, idx);
290
291 if (!o->lname)
292 frame = gtk_frame_new(o->name);
293 else
294 frame = gtk_frame_new(o->lname);
295 gtk_box_pack_start(GTK_BOX(m->gopt.box), frame, FALSE, FALSE, 3);
296
297 hbox = gtk_hbox_new(FALSE, 3);
298 gtk_container_add(GTK_CONTAINER(frame), hbox);
299
300 i = 0;
301 vp = &o->posval[0];
302 while (vp->ival) {
303 m->checks[i] = gtk_check_button_new_with_label(vp->ival);
304 gtk_widget_set_tooltip_text(m->checks[i], vp->help);
305 gtk_box_pack_start(GTK_BOX(hbox), m->checks[i], FALSE, FALSE, 3);
306 vp++;
307 }
308
309 return &m->gopt;
310}
311
b6caa836
JA
312static void gopt_int_changed(GtkSpinButton *spin, gpointer data)
313{
314 struct gopt_int *i = (struct gopt_int *) data;
315 struct fio_option *o = &fio_options[i->gopt.opt_index];
90265353
JA
316 GtkAdjustment *adj;
317 int value, delta;
318
319 adj = gtk_spin_button_get_adjustment(spin);
320 value = gtk_adjustment_get_value(adj);
321 delta = value - i->lastval;
322 i->lastval = value;
323
324 if (o->inv_opt) {
325 struct gopt_int *i_inv = o->inv_opt->gui_data;
326 int cur_val;
327
a01a1bc5
JA
328 assert(o->type == o->inv_opt->type);
329
90265353
JA
330 cur_val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(i_inv->spin));
331 cur_val -= delta;
8f9e46ab 332 g_signal_handler_block(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
90265353 333 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i_inv->spin), cur_val);
8f9e46ab 334 g_signal_handler_unblock(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
90265353 335 }
b6caa836
JA
336}
337
231edf61
JA
338static void gopt_int_destroy(GtkWidget *w, gpointer data)
339{
340 struct gopt_int *i = (struct gopt_int *) data;
341
342 free(i);
343 gtk_widget_destroy(w);
344}
345
346static struct gopt_int *__gopt_new_int(struct fio_option *o,
347 unsigned long long *p, unsigned int idx)
39f04336
JA
348{
349 unsigned long long defval;
9af4a244 350 struct gopt_int *i;
20eb06bd 351 guint maxval, interval;
9af4a244
JA
352 GtkWidget *label;
353
354 i = malloc(sizeof(*i));
a01a1bc5 355 memset(i, 0, sizeof(*i));
9af4a244 356 i->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
357 if (!o->lname)
358 label = gtk_label_new(o->name);
359 else
360 label = gtk_label_new(o->lname);
9af4a244
JA
361
362 maxval = o->maxval;
363 if (!maxval)
39f04336 364 maxval = UINT_MAX;
9af4a244
JA
365
366 defval = 0;
39f04336
JA
367 if (p)
368 defval = *p;
369 else if (o->def) {
9af4a244
JA
370 long long val;
371
372 check_str_bytes(o->def, &val, NULL);
373 defval = val;
374 }
375
20eb06bd
JA
376 interval = 1.0;
377 if (o->interval)
378 interval = o->interval;
379
380 i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval);
ec0218ff 381 gopt_mark_index(&i->gopt, idx);
9af4a244
JA
382 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
383 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
90265353 384 i->lastval = defval;
8f9e46ab 385 i->gopt.sig_handler = g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
231edf61 386 g_signal_connect(G_OBJECT(i->spin), "destroy", G_CALLBACK(gopt_int_destroy), i);
9af4a244
JA
387
388 gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
e8b0e958 389 gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
b6caa836 390
90265353 391 o->gui_data = i;
b6caa836 392 return i;
9af4a244
JA
393}
394
231edf61
JA
395static struct gopt *gopt_new_int(struct fio_option *o, unsigned int *ip,
396 unsigned int idx)
39f04336
JA
397{
398 unsigned long long ullp;
b6caa836 399 struct gopt_int *i;
39f04336
JA
400
401 if (ip) {
402 ullp = *ip;
b6caa836
JA
403 i = __gopt_new_int(o, &ullp, idx);
404 } else
405 i = __gopt_new_int(o, NULL, idx);
406
407 return &i->gopt;
408}
39f04336 409
b6caa836
JA
410static struct gopt *gopt_new_ullong(struct fio_option *o, unsigned long long *p,
411 unsigned int idx)
412{
413 struct gopt_int *i;
414
415 i = __gopt_new_int(o, p, idx);
416 return &i->gopt;
39f04336
JA
417}
418
b6caa836 419static void gopt_bool_toggled(GtkToggleButton *button, gpointer data)
39f04336 420{
b6caa836
JA
421 struct gopt_bool *b = (struct gopt_bool *) data;
422 struct fio_option *o = &fio_options[b->gopt.opt_index];
423 gboolean set;
424
425 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check));
a01a1bc5
JA
426
427 if (o->inv_opt) {
428 struct gopt_bool *b_inv = o->inv_opt->gui_data;
429
430 assert(o->type == o->inv_opt->type);
431
8f9e46ab 432 g_signal_handler_block(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
a01a1bc5 433 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_inv->check), !set);
8f9e46ab 434 g_signal_handler_unblock(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
a01a1bc5
JA
435 }
436
b6caa836 437 gopt_set_children_visible(o, set);
39f04336
JA
438}
439
231edf61
JA
440static void gopt_bool_destroy(GtkWidget *w, gpointer data)
441{
442 struct gopt_bool *b = (struct gopt_bool *) data;
443
444 free(b);
445 gtk_widget_destroy(w);
446}
447
448static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val,
449 unsigned int idx)
9af4a244
JA
450{
451 struct gopt_bool *b;
452 GtkWidget *label;
453 int defstate = 0;
454
455 b = malloc(sizeof(*b));
a01a1bc5 456 memset(b, 0, sizeof(*b));
9af4a244 457 b->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
458 if (!o->lname)
459 label = gtk_label_new(o->name);
460 else
461 label = gtk_label_new(o->lname);
9af4a244
JA
462
463 b->check = gtk_check_button_new();
ec0218ff 464 gopt_mark_index(&b->gopt, idx);
39f04336
JA
465 if (val)
466 defstate = *val;
467 else if (o->def && !strcmp(o->def, "1"))
9af4a244
JA
468 defstate = 1;
469
d872fb4c
JA
470 if (o->neg)
471 defstate = !defstate;
472
9af4a244 473 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate);
8f9e46ab 474 b->gopt.sig_handler = g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b);
231edf61 475 g_signal_connect(G_OBJECT(b->check), "destroy", G_CALLBACK(gopt_bool_destroy), b);
9af4a244
JA
476
477 gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
e8b0e958 478 gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
90265353 479 o->gui_data = b;
9af4a244
JA
480 return &b->gopt;
481}
482
1da10c19
JA
483/*
484 * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values.
485 * If the max is made smaller than min, adjust min down.
486 * If the min is made larger than max, adjust the max.
487 */
488static void range_value_changed(GtkSpinButton *spin, gpointer data)
489{
490 struct gopt_range *r = (struct gopt_range *) data;
491 int changed = -1, i;
492 gint val, mval;
493
494 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
495 if (GTK_SPIN_BUTTON(r->spins[i]) == spin) {
496 changed = i;
497 break;
498 }
499 }
500
501 assert(changed != -1);
502
503 /*
504 * Min changed
505 */
506 if (changed == 0 || changed == 2) {
507 GtkWidget *mspin = r->spins[changed + 1];
508
509 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
510 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
511 if (val > mval)
512 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
513 } else {
514 GtkWidget *mspin = r->spins[changed - 1];
515
516 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
517 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
518 if (val < mval)
519 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
520 }
521}
522
231edf61
JA
523static void gopt_range_destroy(GtkWidget *w, gpointer data)
524{
525 struct gopt_range *r = (struct gopt_range *) data;
526
527 free(r);
528 gtk_widget_destroy(w);
529}
530
b6caa836
JA
531static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip,
532 unsigned int idx)
9af4a244
JA
533{
534 struct gopt_range *r;
535 gint maxval, defval;
536 GtkWidget *label;
20eb06bd 537 guint interval;
9af4a244
JA
538 int i;
539
540 r = malloc(sizeof(*r));
a01a1bc5 541 memset(r, 0, sizeof(*r));
9af4a244 542 r->gopt.box = gtk_hbox_new(FALSE, 3);
ec0218ff 543 gopt_mark_index(&r->gopt, idx);
e8b0e958
JA
544 if (!o->lname)
545 label = gtk_label_new(o->name);
546 else
547 label = gtk_label_new(o->lname);
9af4a244
JA
548
549 maxval = o->maxval;
550 if (!maxval)
551 maxval = INT_MAX;
552
553 defval = 0;
554 if (o->def) {
555 long long val;
556
557 check_str_bytes(o->def, &val, NULL);
558 defval = val;
559 }
560
20eb06bd
JA
561 interval = 1.0;
562 if (o->interval)
563 interval = o->interval;
564
1da10c19 565 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
20eb06bd 566 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval);
9af4a244 567 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID);
39f04336
JA
568 if (ip)
569 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]);
570 else
571 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval);
9af4a244
JA
572
573 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0);
1da10c19 574 g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r);
9af4a244
JA
575 }
576
e8b0e958 577 gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
231edf61 578 g_signal_connect(G_OBJECT(r->gopt.box), "destroy", G_CALLBACK(gopt_range_destroy), r);
90265353 579 o->gui_data = r;
9af4a244
JA
580 return &r->gopt;
581}
582
7fc35ff7
JA
583static void gopt_str_val_destroy(GtkWidget *w, gpointer data)
584{
585 struct gopt_str_val *g = (struct gopt_str_val *) data;
586
587 free(g);
588 gtk_widget_destroy(w);
589}
590
591static void gopt_str_val_spin_wrapped(GtkSpinButton *spin, gpointer data)
592{
593 struct gopt_str_val *g = (struct gopt_str_val *) data;
594 unsigned int val;
595 GtkAdjustment *adj;
596 gint index;
597
598 adj = gtk_spin_button_get_adjustment(spin);
599 val = gtk_adjustment_get_value(adj);
600
601 /*
602 * Can't rely on exact value, as fast changes increment >= 1
603 */
604 if (!val) {
7fc35ff7 605 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
481c242b
JA
606 if (index + 1 <= g->maxindex) {
607 val = 1;
608 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), ++index);
609 } else
610 val = 1023;
7fc35ff7
JA
611 gtk_spin_button_set_value(spin, val);
612 } else {
613 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
614 if (index) {
615 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), --index);
616 gtk_spin_button_set_value(spin, 1023);
617 } else
618 gtk_spin_button_set_value(spin, 0);
619 }
620}
621
622static struct gopt *gopt_new_str_val(struct fio_option *o,
623 unsigned long long *p, unsigned int idx)
624{
625 struct gopt_str_val *g;
626 const gchar *postfix[] = { "B", "KB", "MB", "GB", "PB", "TB", "" };
627 GtkWidget *label;
628 int i;
629
630 g = malloc(sizeof(*g));
631 memset(g, 0, sizeof(*g));
632 g->gopt.box = gtk_hbox_new(FALSE, 3);
633 if (!o->lname)
634 label = gtk_label_new(o->name);
635 else
636 label = gtk_label_new(o->lname);
637
638 g->spin = gtk_spin_button_new_with_range(0.0, 1023.0, 1.0);
639 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(g->spin), GTK_UPDATE_IF_VALID);
640 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), 0);
641 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(g->spin), 1);
642 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->spin, FALSE, FALSE, 0);
643 g_signal_connect(G_OBJECT(g->spin), "wrapped", G_CALLBACK(gopt_str_val_spin_wrapped), g);
644
645 g->combo = gtk_combo_box_new_text();
646 i = 0;
647 while (strlen(postfix[i])) {
648 gtk_combo_box_append_text(GTK_COMBO_BOX(g->combo), postfix[i]);
649 i++;
650 }
481c242b 651 g->maxindex = i - 1;
7fc35ff7
JA
652 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), 0);
653 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->combo, FALSE, FALSE, 0);
654 gtk_box_pack_start(GTK_BOX(g->gopt.box), label, FALSE, FALSE, 3);
655
656 g_signal_connect(G_OBJECT(g->gopt.box), "destroy", G_CALLBACK(gopt_str_val_destroy), g);
657 o->gui_data = g;
658 return &g->gopt;
659}
660
9af4a244 661static void gopt_add_option(GtkWidget *hbox, struct fio_option *o,
789f4ccd 662 unsigned int opt_index, struct thread_options *to)
9af4a244
JA
663{
664 struct gopt *go = NULL;
665
666 switch (o->type) {
7fc35ff7
JA
667 case FIO_OPT_STR_VAL: {
668 unsigned long long *ullp = NULL;
669
670 if (o->off1)
671 ullp = td_var(to, o->off1);
672
673 go = gopt_new_str_val(o, ullp, opt_index);
674 break;
675 }
39f04336
JA
676 case FIO_OPT_STR_VAL_TIME: {
677 unsigned long long *ullp = NULL;
678
679 if (o->off1)
680 ullp = td_var(to, o->off1);
789f4ccd 681
b6caa836 682 go = gopt_new_ullong(o, ullp, opt_index);
9af4a244 683 break;
789f4ccd 684 }
39f04336
JA
685 case FIO_OPT_INT: {
686 unsigned int *ip = NULL;
687
688 if (o->off1)
689 ip = td_var(to, o->off1);
690
b6caa836 691 go = gopt_new_int(o, ip, opt_index);
9af4a244 692 break;
39f04336 693 }
9af4a244 694 case FIO_OPT_STR_SET:
39f04336
JA
695 case FIO_OPT_BOOL: {
696 unsigned int *ip = NULL;
697
698 if (o->off1)
699 ip = td_var(to, o->off1);
700
b6caa836 701 go = gopt_new_bool(o, ip, opt_index);
39f04336
JA
702 break;
703 }
704 case FIO_OPT_STR: {
d872fb4c
JA
705 if (o->posval[0].ival) {
706 unsigned int *ip = NULL;
39f04336 707
d872fb4c
JA
708 if (o->off1)
709 ip = td_var(to, o->off1);
710
711 go = gopt_new_combo_int(o, ip, opt_index);
712 } else {
713 /* TODO: usually ->cb, or unsigned int pointer */
714 go = gopt_new_str_store(o, NULL, opt_index);
715 }
39f04336 716
9af4a244 717 break;
39f04336
JA
718 }
719 case FIO_OPT_STR_STORE: {
720 char *text = NULL;
721
d3944493 722 if (o->off1) {
39f04336
JA
723 char **p = td_var(to, o->off1);
724 text = *p;
725 }
726
9af4a244 727 if (!o->posval[0].ival) {
b6caa836 728 go = gopt_new_str_store(o, text, opt_index);
9af4a244
JA
729 break;
730 }
39f04336 731
b6caa836 732 go = gopt_new_combo_str(o, text, opt_index);
39f04336
JA
733 break;
734 }
9af4a244 735 case FIO_OPT_STR_MULTI:
7386d4ae 736 go = gopt_new_str_multi(o, opt_index);
9af4a244 737 break;
39f04336
JA
738 case FIO_OPT_RANGE: {
739 unsigned int *ip[4] = { td_var(to, o->off1),
740 td_var(to, o->off2),
741 td_var(to, o->off3),
742 td_var(to, o->off4) };
743
b6caa836 744 go = gopt_new_int_range(o, ip, opt_index);
9af4a244 745 break;
39f04336 746 }
9af4a244
JA
747 /* still need to handle this one */
748 case FIO_OPT_FLOAT_LIST:
749 break;
750 case FIO_OPT_DEPRECATED:
751 break;
752 default:
753 printf("ignore type %u\n", o->type);
754 break;
755 }
756
757 if (go) {
e8b0e958
JA
758 GtkWidget *dest;
759
9af4a244
JA
760 if (o->help)
761 gtk_widget_set_tooltip_text(go->box, o->help);
e8b0e958 762
9af4a244 763 go->opt_type = o->type;
e8b0e958
JA
764
765 dest = gopt_get_group_frame(hbox, o->group);
766 if (!dest)
767 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
768 else
769 gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5);
9af4a244
JA
770 }
771}
772
789f4ccd 773static void gopt_add_options(GtkWidget **vboxes, struct thread_options *to)
9af4a244
JA
774{
775 GtkWidget *hbox = NULL;
776 int i;
777
90265353
JA
778 /*
779 * First add all options
780 */
9af4a244
JA
781 for (i = 0; fio_options[i].name; i++) {
782 struct fio_option *o = &fio_options[i];
783 unsigned int mask = o->category;
784 struct opt_group *og;
785
786 while ((og = opt_group_from_mask(&mask)) != NULL) {
787 GtkWidget *vbox = vboxes[ffz(~og->mask)];
788
789 hbox = gtk_hbox_new(FALSE, 3);
790 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
789f4ccd 791 gopt_add_option(hbox, o, i, to);
9af4a244
JA
792 }
793 }
794}
795
796static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
797{
798 GtkWidget *box, *vbox, *scroll;
799
800 scroll = gtk_scrolled_window_new(NULL, NULL);
801 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
802 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
803
804 vbox = gtk_vbox_new(FALSE, 3);
805 box = gtk_hbox_new(FALSE, 0);
806 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
807 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
808 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(og->name));
809
810 return vbox;
811}
812
813static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
814{
815 struct opt_group *og;
ec0218ff 816 unsigned int i;
9af4a244 817
ec0218ff 818 i = 0;
9af4a244
JA
819 do {
820 unsigned int mask = (1U << i);
821
822 og = opt_group_from_mask(&mask);
823 if (!og)
824 break;
825 vbox[i] = gopt_add_group_tab(notebook, og);
826 i++;
827 } while (1);
828}
829
789f4ccd 830void gopt_get_options_window(GtkWidget *window, struct thread_options *o)
9af4a244
JA
831{
832 GtkWidget *dialog, *notebook;
e8b0e958 833 GtkWidget *vboxes[__FIO_OPT_C_NR];
9af4a244
JA
834
835 dialog = gtk_dialog_new_with_buttons("Fio options",
836 GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
837 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
838 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
839
840 gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768);
841
842 notebook = gtk_notebook_new();
843 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
844 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
845 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 5);
846
847 gopt_add_group_tabs(notebook, vboxes);
848
789f4ccd 849 gopt_add_options(vboxes, o);
9af4a244
JA
850
851 gtk_widget_show_all(dialog);
852
853 gtk_dialog_run(GTK_DIALOG(dialog));
854
855 gtk_widget_destroy(dialog);
a8baa347
SC
856 memset(gopt_widgets, 0, sizeof(gopt_widgets));
857 memset(gopt_g_widgets, 0, sizeof(gopt_g_widgets));
9af4a244 858}