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