backend: privatize nr_thread/nr_process
[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;
18};
19
20struct gopt_combo {
21 struct gopt gopt;
22 GtkWidget *combo;
23};
24
25struct gopt_int {
26 struct gopt gopt;
27 GtkWidget *spin;
28};
29
30struct gopt_bool {
31 struct gopt gopt;
32 GtkWidget *check;
33};
34
35struct gopt_str {
36 struct gopt gopt;
37 GtkWidget *entry;
38};
39
1da10c19
JA
40#define GOPT_RANGE_SPIN 4
41
9af4a244
JA
42struct gopt_range {
43 struct gopt gopt;
1da10c19 44 GtkWidget *spins[GOPT_RANGE_SPIN];
9af4a244
JA
45};
46
ec0218ff
JA
47struct gopt_widget {
48 struct flist_head list;
49 GtkWidget *widget;
50};
51
52static struct flist_head gopt_list[FIO_MAX_OPTS];
53
54static void __gopt_set_children_visible(unsigned int idx, gboolean visible)
55{
56 struct flist_head *entry;
57 struct gopt_widget *gw;
58
59 flist_for_each(entry, &gopt_list[idx]) {
60 gw = flist_entry(entry, struct gopt_widget, list);
61 gtk_widget_set_sensitive(gw->widget, visible);
62 }
63}
b6caa836
JA
64
65/*
66 * Mark children as invisible, if needed.
67 */
68static void gopt_set_children_visible(struct fio_option *parent, gboolean visible)
69{
70 struct fio_option *o;
71 int i;
72
73 /*
74 * This isn't super fast, but it should not be an issue. If it is, we
75 * can speed it up by caching the lookup at least. Or we can do it
76 * once, at init time.
77 */
78 for (i = 0; fio_options[i].name; i++) {
79 o = &fio_options[i];
ec0218ff 80 if (!o->parent || !o->hide)
b6caa836
JA
81 continue;
82
83 if (strcmp(parent->name, o->parent))
84 continue;
85
ec0218ff 86 __gopt_set_children_visible(i, visible);
b6caa836
JA
87 }
88}
89
90static void gopt_str_changed(GtkEntry *entry, gpointer data)
91{
92 struct gopt_str *s = (struct gopt_str *) data;
93 struct fio_option *o = &fio_options[s->gopt.opt_index];
94 const gchar *text;
95 int set;
96
97 text = gtk_entry_get_text(GTK_ENTRY(s->entry));
98 set = strcmp(text, "") != 0;
99 gopt_set_children_visible(o, set);
100}
101
ec0218ff 102static void gopt_mark_index(struct gopt *gopt, unsigned int idx)
b6caa836 103{
ec0218ff
JA
104 struct gopt_widget *gw;
105
b6caa836 106 gopt->opt_index = idx;
ec0218ff
JA
107
108 gw = malloc(sizeof(*gw));
109 gw->widget = gopt->box;
110 flist_add_tail(&gw->list, &gopt_list[idx]);
b6caa836
JA
111}
112
113static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text, unsigned int idx)
9af4a244
JA
114{
115 struct gopt_str *s;
116 GtkWidget *label;
117
118 s = malloc(sizeof(*s));
119
120 s->gopt.box = gtk_hbox_new(FALSE, 3);
121 label = gtk_label_new(o->name);
122 gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
123
124 s->entry = gtk_entry_new();
ec0218ff 125 gopt_mark_index(&s->gopt, idx);
789f4ccd
JA
126 if (text)
127 gtk_entry_set_text(GTK_ENTRY(s->entry), text);
9af4a244 128 gtk_entry_set_editable(GTK_ENTRY(s->entry), 1);
b6caa836 129 g_signal_connect(GTK_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s);
9af4a244
JA
130
131 if (o->def)
132 gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
133
134 gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
135 return &s->gopt;
136}
137
b6caa836 138static void gopt_combo_changed(GtkComboBox *box, gpointer data)
9af4a244 139{
b6caa836
JA
140 struct gopt_combo *c = (struct gopt_combo *) data;
141 struct fio_option *o = &fio_options[c->gopt.opt_index];
142
143 printf("combo %s changed\n", o->name);
144}
145
146static struct gopt_combo *__gopt_new_combo(struct fio_option *o, unsigned int idx)
147{
148 struct gopt_combo *c;
9af4a244 149 GtkWidget *label;
9af4a244 150
b6caa836 151 c = malloc(sizeof(*c));
9af4a244 152
b6caa836 153 c->gopt.box = gtk_hbox_new(FALSE, 3);
9af4a244 154 label = gtk_label_new(o->name);
b6caa836 155 gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0);
9af4a244 156
b6caa836 157 c->combo = gtk_combo_box_new_text();
ec0218ff 158 gopt_mark_index(&c->gopt, idx);
b6caa836 159 gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0);
9af4a244 160
b6caa836
JA
161 g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
162
163 return c;
39f04336
JA
164}
165
b6caa836 166static struct gopt *gopt_new_combo_str(struct fio_option *o, const char *text, unsigned int idx)
39f04336
JA
167{
168 struct gopt_combo *combo;
169 struct value_pair *vp;
170 int i, active = 0;
171
b6caa836 172 combo = __gopt_new_combo(o, idx);
39f04336 173
9af4a244
JA
174 i = 0;
175 vp = &o->posval[0];
176 while (vp->ival) {
177 gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival);
178 if (o->def && !strcmp(vp->ival, o->def))
179 active = i;
39f04336
JA
180 if (text && !strcmp(vp->ival, text))
181 active = i;
9af4a244
JA
182 vp++;
183 i++;
184 }
185
186 gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active);
187 return &combo->gopt;
188}
189
b6caa836 190static struct gopt *gopt_new_combo_int(struct fio_option *o, unsigned int *ip, unsigned int idx)
9af4a244 191{
39f04336
JA
192 struct gopt_combo *combo;
193 struct value_pair *vp;
194 int i, active = 0;
195
b6caa836 196 combo = __gopt_new_combo(o, idx);
39f04336
JA
197
198 i = 0;
199 vp = &o->posval[0];
200 while (vp->ival) {
201 gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival);
202 if (ip && vp->oval == *ip)
203 active = i;
204 vp++;
205 i++;
206 }
207
208 gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active);
209 return &combo->gopt;
210}
211
b6caa836
JA
212static void gopt_int_changed(GtkSpinButton *spin, gpointer data)
213{
214 struct gopt_int *i = (struct gopt_int *) data;
215 struct fio_option *o = &fio_options[i->gopt.opt_index];
216
217 printf("int %s changed\n", o->name);
218}
219
220static struct gopt_int *__gopt_new_int(struct fio_option *o, unsigned long long *p,
221 unsigned int idx)
39f04336
JA
222{
223 unsigned long long defval;
9af4a244 224 struct gopt_int *i;
20eb06bd 225 guint maxval, interval;
9af4a244
JA
226 GtkWidget *label;
227
228 i = malloc(sizeof(*i));
229 i->gopt.box = gtk_hbox_new(FALSE, 3);
230 label = gtk_label_new(o->name);
231 gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
232
233 maxval = o->maxval;
234 if (!maxval)
39f04336 235 maxval = UINT_MAX;
9af4a244
JA
236
237 defval = 0;
39f04336
JA
238 if (p)
239 defval = *p;
240 else if (o->def) {
9af4a244
JA
241 long long val;
242
243 check_str_bytes(o->def, &val, NULL);
244 defval = val;
245 }
246
20eb06bd
JA
247 interval = 1.0;
248 if (o->interval)
249 interval = o->interval;
250
251 i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval);
ec0218ff 252 gopt_mark_index(&i->gopt, idx);
9af4a244
JA
253 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
254 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
255
256 gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
b6caa836
JA
257
258 g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
259
260 return i;
9af4a244
JA
261}
262
b6caa836 263static struct gopt *gopt_new_int(struct fio_option *o, unsigned int *ip, unsigned int idx)
39f04336
JA
264{
265 unsigned long long ullp;
b6caa836 266 struct gopt_int *i;
39f04336
JA
267
268 if (ip) {
269 ullp = *ip;
b6caa836
JA
270 i = __gopt_new_int(o, &ullp, idx);
271 } else
272 i = __gopt_new_int(o, NULL, idx);
273
274 return &i->gopt;
275}
39f04336 276
b6caa836
JA
277static struct gopt *gopt_new_ullong(struct fio_option *o, unsigned long long *p,
278 unsigned int idx)
279{
280 struct gopt_int *i;
281
282 i = __gopt_new_int(o, p, idx);
283 return &i->gopt;
39f04336
JA
284}
285
b6caa836 286static void gopt_bool_toggled(GtkToggleButton *button, gpointer data)
39f04336 287{
b6caa836
JA
288 struct gopt_bool *b = (struct gopt_bool *) data;
289 struct fio_option *o = &fio_options[b->gopt.opt_index];
290 gboolean set;
291
292 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check));
293 gopt_set_children_visible(o, set);
39f04336
JA
294}
295
b6caa836 296static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val, unsigned int idx)
9af4a244
JA
297{
298 struct gopt_bool *b;
299 GtkWidget *label;
300 int defstate = 0;
301
302 b = malloc(sizeof(*b));
303 b->gopt.box = gtk_hbox_new(FALSE, 3);
304 label = gtk_label_new(o->name);
305 gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
306
307 b->check = gtk_check_button_new();
ec0218ff 308 gopt_mark_index(&b->gopt, idx);
39f04336
JA
309 if (val)
310 defstate = *val;
311 else if (o->def && !strcmp(o->def, "1"))
9af4a244
JA
312 defstate = 1;
313
314 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate);
b6caa836 315 g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b);
9af4a244
JA
316
317 gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
318 return &b->gopt;
319}
320
1da10c19
JA
321/*
322 * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values.
323 * If the max is made smaller than min, adjust min down.
324 * If the min is made larger than max, adjust the max.
325 */
326static void range_value_changed(GtkSpinButton *spin, gpointer data)
327{
328 struct gopt_range *r = (struct gopt_range *) data;
329 int changed = -1, i;
330 gint val, mval;
331
332 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
333 if (GTK_SPIN_BUTTON(r->spins[i]) == spin) {
334 changed = i;
335 break;
336 }
337 }
338
339 assert(changed != -1);
340
341 /*
342 * Min changed
343 */
344 if (changed == 0 || changed == 2) {
345 GtkWidget *mspin = r->spins[changed + 1];
346
347 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
348 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
349 if (val > mval)
350 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
351 } else {
352 GtkWidget *mspin = r->spins[changed - 1];
353
354 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
355 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
356 if (val < mval)
357 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
358 }
359}
360
b6caa836
JA
361static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip,
362 unsigned int idx)
9af4a244
JA
363{
364 struct gopt_range *r;
365 gint maxval, defval;
366 GtkWidget *label;
20eb06bd 367 guint interval;
9af4a244
JA
368 int i;
369
370 r = malloc(sizeof(*r));
371 r->gopt.box = gtk_hbox_new(FALSE, 3);
ec0218ff 372 gopt_mark_index(&r->gopt, idx);
9af4a244
JA
373 label = gtk_label_new(o->name);
374 gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
375
376 maxval = o->maxval;
377 if (!maxval)
378 maxval = INT_MAX;
379
380 defval = 0;
381 if (o->def) {
382 long long val;
383
384 check_str_bytes(o->def, &val, NULL);
385 defval = val;
386 }
387
20eb06bd
JA
388 interval = 1.0;
389 if (o->interval)
390 interval = o->interval;
391
1da10c19 392 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
20eb06bd 393 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval);
9af4a244 394 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID);
39f04336
JA
395 if (ip)
396 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]);
397 else
398 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval);
9af4a244
JA
399
400 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0);
1da10c19 401 g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r);
9af4a244
JA
402 }
403
404 return &r->gopt;
405}
406
407static void gopt_add_option(GtkWidget *hbox, struct fio_option *o,
789f4ccd 408 unsigned int opt_index, struct thread_options *to)
9af4a244
JA
409{
410 struct gopt *go = NULL;
411
412 switch (o->type) {
39f04336
JA
413 case FIO_OPT_STR_VAL:
414 case FIO_OPT_STR_VAL_TIME: {
415 unsigned long long *ullp = NULL;
416
417 if (o->off1)
418 ullp = td_var(to, o->off1);
789f4ccd 419
b6caa836 420 go = gopt_new_ullong(o, ullp, opt_index);
9af4a244 421 break;
789f4ccd 422 }
39f04336
JA
423 case FIO_OPT_INT: {
424 unsigned int *ip = NULL;
425
426 if (o->off1)
427 ip = td_var(to, o->off1);
428
b6caa836 429 go = gopt_new_int(o, ip, opt_index);
9af4a244 430 break;
39f04336 431 }
9af4a244 432 case FIO_OPT_STR_SET:
39f04336
JA
433 case FIO_OPT_BOOL: {
434 unsigned int *ip = NULL;
435
436 if (o->off1)
437 ip = td_var(to, o->off1);
438
b6caa836 439 go = gopt_new_bool(o, ip, opt_index);
39f04336
JA
440 break;
441 }
442 case FIO_OPT_STR: {
443 unsigned int *ip = NULL;
444
445 if (o->off1)
446 ip = td_var(to, o->off1);
447
b6caa836 448 go = gopt_new_combo_int(o, ip, opt_index);
9af4a244 449 break;
39f04336
JA
450 }
451 case FIO_OPT_STR_STORE: {
452 char *text = NULL;
453
d3944493 454 if (o->off1) {
39f04336
JA
455 char **p = td_var(to, o->off1);
456 text = *p;
457 }
458
9af4a244 459 if (!o->posval[0].ival) {
b6caa836 460 go = gopt_new_str_store(o, text, opt_index);
9af4a244
JA
461 break;
462 }
39f04336 463
b6caa836 464 go = gopt_new_combo_str(o, text, opt_index);
39f04336
JA
465 break;
466 }
9af4a244 467 case FIO_OPT_STR_MULTI:
b6caa836 468 go = gopt_new_combo_str(o, NULL, opt_index);
9af4a244 469 break;
39f04336
JA
470 case FIO_OPT_RANGE: {
471 unsigned int *ip[4] = { td_var(to, o->off1),
472 td_var(to, o->off2),
473 td_var(to, o->off3),
474 td_var(to, o->off4) };
475
b6caa836 476 go = gopt_new_int_range(o, ip, opt_index);
9af4a244 477 break;
39f04336 478 }
9af4a244
JA
479 /* still need to handle this one */
480 case FIO_OPT_FLOAT_LIST:
481 break;
482 case FIO_OPT_DEPRECATED:
483 break;
484 default:
485 printf("ignore type %u\n", o->type);
486 break;
487 }
488
489 if (go) {
490 if (o->help)
491 gtk_widget_set_tooltip_text(go->box, o->help);
492
493 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
9af4a244
JA
494 go->opt_type = o->type;
495 }
496}
497
789f4ccd 498static void gopt_add_options(GtkWidget **vboxes, struct thread_options *to)
9af4a244
JA
499{
500 GtkWidget *hbox = NULL;
501 int i;
502
503 for (i = 0; fio_options[i].name; i++) {
504 struct fio_option *o = &fio_options[i];
505 unsigned int mask = o->category;
506 struct opt_group *og;
507
508 while ((og = opt_group_from_mask(&mask)) != NULL) {
509 GtkWidget *vbox = vboxes[ffz(~og->mask)];
510
511 hbox = gtk_hbox_new(FALSE, 3);
512 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
789f4ccd 513 gopt_add_option(hbox, o, i, to);
9af4a244
JA
514 }
515 }
516}
517
518static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
519{
520 GtkWidget *box, *vbox, *scroll;
521
522 scroll = gtk_scrolled_window_new(NULL, NULL);
523 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
524 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
525
526 vbox = gtk_vbox_new(FALSE, 3);
527 box = gtk_hbox_new(FALSE, 0);
528 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
529 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
530 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(og->name));
531
532 return vbox;
533}
534
535static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
536{
537 struct opt_group *og;
ec0218ff 538 unsigned int i;
9af4a244 539
ec0218ff
JA
540 for (i = 0; i < FIO_MAX_OPTS; i++)
541 INIT_FLIST_HEAD(&gopt_list[i]);
542
543 i = 0;
9af4a244
JA
544 do {
545 unsigned int mask = (1U << i);
546
547 og = opt_group_from_mask(&mask);
548 if (!og)
549 break;
550 vbox[i] = gopt_add_group_tab(notebook, og);
551 i++;
552 } while (1);
553}
554
789f4ccd 555void gopt_get_options_window(GtkWidget *window, struct thread_options *o)
9af4a244
JA
556{
557 GtkWidget *dialog, *notebook;
558 GtkWidget *vboxes[__FIO_OPT_G_NR];
559
560 dialog = gtk_dialog_new_with_buttons("Fio options",
561 GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
562 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
563 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
564
565 gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768);
566
567 notebook = gtk_notebook_new();
568 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
569 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
570 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 5);
571
572 gopt_add_group_tabs(notebook, vboxes);
573
789f4ccd 574 gopt_add_options(vboxes, o);
9af4a244
JA
575
576 gtk_widget_show_all(dialog);
577
578 gtk_dialog_run(GTK_DIALOG(dialog));
579
580 gtk_widget_destroy(dialog);
581}