blob: f7ba3038fafe1e7a9007394542f6d75160ed8278 [file] [log] [blame]
Jens Axboe9af4a242012-03-16 10:13:49 +01001#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;
Jens Axboe8f9e46a2012-03-19 22:01:20 +010018 gulong sig_handler;
Jens Axboe9af4a242012-03-16 10:13:49 +010019};
20
21struct gopt_combo {
22 struct gopt gopt;
23 GtkWidget *combo;
24};
25
26struct gopt_int {
27 struct gopt gopt;
Jens Axboe90265352012-03-19 20:29:44 +010028 unsigned int lastval;
Jens Axboe9af4a242012-03-16 10:13:49 +010029 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
Jens Axboe7fc35ff2012-03-20 09:32:35 +010042struct gopt_str_val {
43 struct gopt gopt;
44 GtkWidget *spin;
45 GtkWidget *combo;
Jens Axboe481c2422012-03-20 09:49:49 +010046 unsigned int maxindex;
Jens Axboe7fc35ff2012-03-20 09:32:35 +010047};
48
Jens Axboe1da10c12012-03-16 19:41:01 +010049#define GOPT_RANGE_SPIN 4
50
Jens Axboe9af4a242012-03-16 10:13:49 +010051struct gopt_range {
52 struct gopt gopt;
Jens Axboe1da10c12012-03-16 19:41:01 +010053 GtkWidget *spins[GOPT_RANGE_SPIN];
Jens Axboe9af4a242012-03-16 10:13:49 +010054};
55
Jens Axboe7386d4a2012-03-19 18:36:29 +010056struct gopt_str_multi {
57 struct gopt gopt;
58 GtkWidget *checks[PARSE_MAX_VP];
59};
60
Jens Axboee8b0e952012-03-19 14:37:08 +010061static GtkWidget *gopt_widgets[FIO_MAX_OPTS];
62
63struct gopt_frame_widget {
64 GtkWidget *vbox[2];
65 unsigned int nr;
Jens Axboeec0218f2012-03-16 21:45:05 +010066};
Jens Axboee8b0e952012-03-19 14:37:08 +010067static struct gopt_frame_widget gopt_g_widgets[__FIO_OPT_G_NR];
Jens Axboeec0218f2012-03-16 21:45:05 +010068
Jens Axboee8b0e952012-03-19 14:37:08 +010069static GtkWidget *gopt_get_group_frame(GtkWidget *box, unsigned int groupmask)
Jens Axboeec0218f2012-03-16 21:45:05 +010070{
Jens Axboee8b0e952012-03-19 14:37:08 +010071 unsigned int mask, group;
72 struct opt_group *og;
73 GtkWidget *frame, *hbox;
74 struct gopt_frame_widget *gfw;
Jens Axboeec0218f2012-03-16 21:45:05 +010075
Jens Axboee8b0e952012-03-19 14:37:08 +010076 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);
Jens Axboeec0218f2012-03-16 21:45:05 +010095 }
Jens Axboee8b0e952012-03-19 14:37:08 +010096
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;
Jens Axboeec0218f2012-03-16 21:45:05 +0100100}
Jens Axboeb6caa832012-03-16 21:22:56 +0100101
102/*
103 * Mark children as invisible, if needed.
104 */
Jens Axboe231edf62012-03-20 08:31:29 +0100105static void gopt_set_children_visible(struct fio_option *parent,
106 gboolean visible)
Jens Axboeb6caa832012-03-16 21:22:56 +0100107{
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];
Jens Axboeec0218f2012-03-16 21:45:05 +0100118 if (!o->parent || !o->hide)
Jens Axboeb6caa832012-03-16 21:22:56 +0100119 continue;
120
121 if (strcmp(parent->name, o->parent))
122 continue;
123
Jens Axboee8b0e952012-03-19 14:37:08 +0100124 if (gopt_widgets[i])
125 gtk_widget_set_sensitive(gopt_widgets[i], visible);
Jens Axboeb6caa832012-03-16 21:22:56 +0100126 }
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
Jens Axboeec0218f2012-03-16 21:45:05 +0100141static void gopt_mark_index(struct gopt *gopt, unsigned int idx)
Jens Axboeb6caa832012-03-16 21:22:56 +0100142{
Jens Axboee8b0e952012-03-19 14:37:08 +0100143 assert(!gopt_widgets[idx]);
Jens Axboeb6caa832012-03-16 21:22:56 +0100144 gopt->opt_index = idx;
Jens Axboee8b0e952012-03-19 14:37:08 +0100145 gopt_widgets[idx] = gopt->box;
Jens Axboeb6caa832012-03-16 21:22:56 +0100146}
147
Jens Axboe231edf62012-03-20 08:31:29 +0100148static 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)
Jens Axboe9af4a242012-03-16 10:13:49 +0100158{
159 struct gopt_str *s;
160 GtkWidget *label;
161
162 s = malloc(sizeof(*s));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100163 memset(s, 0, sizeof(*s));
Jens Axboe9af4a242012-03-16 10:13:49 +0100164
165 s->gopt.box = gtk_hbox_new(FALSE, 3);
Jens Axboee8b0e952012-03-19 14:37:08 +0100166 if (!o->lname)
167 label = gtk_label_new(o->name);
168 else
169 label = gtk_label_new(o->lname);
Jens Axboe9af4a242012-03-16 10:13:49 +0100170
171 s->entry = gtk_entry_new();
Jens Axboeec0218f2012-03-16 21:45:05 +0100172 gopt_mark_index(&s->gopt, idx);
Jens Axboe789f4cc2012-03-16 14:56:44 +0100173 if (text)
174 gtk_entry_set_text(GTK_ENTRY(s->entry), text);
Jens Axboe9af4a242012-03-16 10:13:49 +0100175 gtk_entry_set_editable(GTK_ENTRY(s->entry), 1);
176
177 if (o->def)
178 gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
179
Jens Axboe4fbcd652012-03-20 10:05:38 +0100180 s->gopt.sig_handler = g_signal_connect(GTK_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s);
181 g_signal_connect(GTK_OBJECT(s->entry), "destroy", G_CALLBACK(gopt_str_destroy), s);
182
Jens Axboe9af4a242012-03-16 10:13:49 +0100183 gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
Jens Axboee8b0e952012-03-19 14:37:08 +0100184 gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
Jens Axboe90265352012-03-19 20:29:44 +0100185 o->gui_data = s;
Jens Axboe9af4a242012-03-16 10:13:49 +0100186 return &s->gopt;
187}
188
Jens Axboeb6caa832012-03-16 21:22:56 +0100189static void gopt_combo_changed(GtkComboBox *box, gpointer data)
Jens Axboe9af4a242012-03-16 10:13:49 +0100190{
Jens Axboeb6caa832012-03-16 21:22:56 +0100191 struct gopt_combo *c = (struct gopt_combo *) data;
192 struct fio_option *o = &fio_options[c->gopt.opt_index];
Jens Axboe9af4a242012-03-16 10:13:49 +0100193
Jens Axboeb6caa832012-03-16 21:22:56 +0100194 printf("combo %s changed\n", o->name);
Jens Axboe39f04332012-03-16 16:34:06 +0100195}
196
Jens Axboe231edf62012-03-20 08:31:29 +0100197static void gopt_combo_destroy(GtkWidget *w, gpointer data)
198{
199 struct gopt_combo *c = (struct gopt_combo *) data;
200
201 free(c);
202 gtk_widget_destroy(w);
203}
204
205static struct gopt_combo *__gopt_new_combo(struct fio_option *o,
206 unsigned int idx)
Jens Axboeb6caa832012-03-16 21:22:56 +0100207{
208 struct gopt_combo *c;
209 GtkWidget *label;
210
211 c = malloc(sizeof(*c));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100212 memset(c, 0, sizeof(*c));
Jens Axboeb6caa832012-03-16 21:22:56 +0100213
214 c->gopt.box = gtk_hbox_new(FALSE, 3);
Jens Axboee8b0e952012-03-19 14:37:08 +0100215 if (!o->lname)
216 label = gtk_label_new(o->name);
217 else
218 label = gtk_label_new(o->lname);
Jens Axboeb6caa832012-03-16 21:22:56 +0100219
220 c->combo = gtk_combo_box_new_text();
Jens Axboeec0218f2012-03-16 21:45:05 +0100221 gopt_mark_index(&c->gopt, idx);
Jens Axboe231edf62012-03-20 08:31:29 +0100222 g_signal_connect(GTK_OBJECT(c->combo), "destroy", G_CALLBACK(gopt_combo_destroy), c);
Jens Axboeb6caa832012-03-16 21:22:56 +0100223
Jens Axboee8b0e952012-03-19 14:37:08 +0100224 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
Jens Axboe90265352012-03-19 20:29:44 +0100227 o->gui_data = c;
Jens Axboeb6caa832012-03-16 21:22:56 +0100228 return c;
229}
230
Jens Axboe231edf62012-03-20 08:31:29 +0100231static struct gopt *gopt_new_combo_str(struct fio_option *o, const char *text,
232 unsigned int idx)
Jens Axboe39f04332012-03-16 16:34:06 +0100233{
Jens Axboe4fbcd652012-03-20 10:05:38 +0100234 struct gopt_combo *c;
Jens Axboe39f04332012-03-16 16:34:06 +0100235 struct value_pair *vp;
236 int i, active = 0;
237
Jens Axboe4fbcd652012-03-20 10:05:38 +0100238 c = __gopt_new_combo(o, idx);
Jens Axboe39f04332012-03-16 16:34:06 +0100239
Jens Axboe9af4a242012-03-16 10:13:49 +0100240 i = 0;
241 vp = &o->posval[0];
242 while (vp->ival) {
Jens Axboe4fbcd652012-03-20 10:05:38 +0100243 gtk_combo_box_append_text(GTK_COMBO_BOX(c->combo), vp->ival);
Jens Axboe9af4a242012-03-16 10:13:49 +0100244 if (o->def && !strcmp(vp->ival, o->def))
245 active = i;
Jens Axboe39f04332012-03-16 16:34:06 +0100246 if (text && !strcmp(vp->ival, text))
247 active = i;
Jens Axboe9af4a242012-03-16 10:13:49 +0100248 vp++;
249 i++;
250 }
251
Jens Axboe4fbcd652012-03-20 10:05:38 +0100252 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active);
253 c->gopt.sig_handler = g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
254 return &c->gopt;
Jens Axboe9af4a242012-03-16 10:13:49 +0100255}
256
Jens Axboe231edf62012-03-20 08:31:29 +0100257static struct gopt *gopt_new_combo_int(struct fio_option *o, unsigned int *ip,
258 unsigned int idx)
Jens Axboe9af4a242012-03-16 10:13:49 +0100259{
Jens Axboe4fbcd652012-03-20 10:05:38 +0100260 struct gopt_combo *c;
Jens Axboe39f04332012-03-16 16:34:06 +0100261 struct value_pair *vp;
262 int i, active = 0;
263
Jens Axboe4fbcd652012-03-20 10:05:38 +0100264 c = __gopt_new_combo(o, idx);
Jens Axboe39f04332012-03-16 16:34:06 +0100265
266 i = 0;
267 vp = &o->posval[0];
268 while (vp->ival) {
Jens Axboe4fbcd652012-03-20 10:05:38 +0100269 gtk_combo_box_append_text(GTK_COMBO_BOX(c->combo), vp->ival);
Jens Axboe39f04332012-03-16 16:34:06 +0100270 if (ip && vp->oval == *ip)
271 active = i;
272 vp++;
273 i++;
274 }
275
Jens Axboe4fbcd652012-03-20 10:05:38 +0100276 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active);
277 c->gopt.sig_handler = g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
278 return &c->gopt;
Jens Axboe39f04332012-03-16 16:34:06 +0100279}
280
Jens Axboe7386d4a2012-03-19 18:36:29 +0100281static struct gopt *gopt_new_str_multi(struct fio_option *o, unsigned int idx)
282{
283 struct gopt_str_multi *m;
284 struct value_pair *vp;
285 GtkWidget *frame, *hbox;
286 int i;
287
288 m = malloc(sizeof(*m));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100289 memset(m, 0, sizeof(*m));
Jens Axboe7386d4a2012-03-19 18:36:29 +0100290 m->gopt.box = gtk_hbox_new(FALSE, 3);
291 gopt_mark_index(&m->gopt, idx);
292
293 if (!o->lname)
294 frame = gtk_frame_new(o->name);
295 else
296 frame = gtk_frame_new(o->lname);
297 gtk_box_pack_start(GTK_BOX(m->gopt.box), frame, FALSE, FALSE, 3);
298
299 hbox = gtk_hbox_new(FALSE, 3);
300 gtk_container_add(GTK_CONTAINER(frame), hbox);
301
302 i = 0;
303 vp = &o->posval[0];
304 while (vp->ival) {
305 m->checks[i] = gtk_check_button_new_with_label(vp->ival);
306 gtk_widget_set_tooltip_text(m->checks[i], vp->help);
307 gtk_box_pack_start(GTK_BOX(hbox), m->checks[i], FALSE, FALSE, 3);
308 vp++;
309 }
310
311 return &m->gopt;
312}
313
Jens Axboeb6caa832012-03-16 21:22:56 +0100314static void gopt_int_changed(GtkSpinButton *spin, gpointer data)
315{
316 struct gopt_int *i = (struct gopt_int *) data;
317 struct fio_option *o = &fio_options[i->gopt.opt_index];
Jens Axboe90265352012-03-19 20:29:44 +0100318 GtkAdjustment *adj;
319 int value, delta;
Jens Axboeb6caa832012-03-16 21:22:56 +0100320
Jens Axboe90265352012-03-19 20:29:44 +0100321 adj = gtk_spin_button_get_adjustment(spin);
322 value = gtk_adjustment_get_value(adj);
323 delta = value - i->lastval;
324 i->lastval = value;
325
326 if (o->inv_opt) {
327 struct gopt_int *i_inv = o->inv_opt->gui_data;
328 int cur_val;
329
Jens Axboea01a1bc2012-03-19 21:13:01 +0100330 assert(o->type == o->inv_opt->type);
331
Jens Axboe90265352012-03-19 20:29:44 +0100332 cur_val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(i_inv->spin));
333 cur_val -= delta;
Jens Axboe8f9e46a2012-03-19 22:01:20 +0100334 g_signal_handler_block(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
Jens Axboe90265352012-03-19 20:29:44 +0100335 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i_inv->spin), cur_val);
Jens Axboe8f9e46a2012-03-19 22:01:20 +0100336 g_signal_handler_unblock(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
Jens Axboe90265352012-03-19 20:29:44 +0100337 }
Jens Axboeb6caa832012-03-16 21:22:56 +0100338}
339
Jens Axboe231edf62012-03-20 08:31:29 +0100340static void gopt_int_destroy(GtkWidget *w, gpointer data)
341{
342 struct gopt_int *i = (struct gopt_int *) data;
343
344 free(i);
345 gtk_widget_destroy(w);
346}
347
348static struct gopt_int *__gopt_new_int(struct fio_option *o,
349 unsigned long long *p, unsigned int idx)
Jens Axboe39f04332012-03-16 16:34:06 +0100350{
351 unsigned long long defval;
Jens Axboe9af4a242012-03-16 10:13:49 +0100352 struct gopt_int *i;
Jens Axboe20eb06b2012-03-16 19:57:23 +0100353 guint maxval, interval;
Jens Axboe9af4a242012-03-16 10:13:49 +0100354 GtkWidget *label;
355
356 i = malloc(sizeof(*i));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100357 memset(i, 0, sizeof(*i));
Jens Axboe9af4a242012-03-16 10:13:49 +0100358 i->gopt.box = gtk_hbox_new(FALSE, 3);
Jens Axboee8b0e952012-03-19 14:37:08 +0100359 if (!o->lname)
360 label = gtk_label_new(o->name);
361 else
362 label = gtk_label_new(o->lname);
Jens Axboe9af4a242012-03-16 10:13:49 +0100363
364 maxval = o->maxval;
365 if (!maxval)
Jens Axboe39f04332012-03-16 16:34:06 +0100366 maxval = UINT_MAX;
Jens Axboe9af4a242012-03-16 10:13:49 +0100367
368 defval = 0;
Jens Axboe39f04332012-03-16 16:34:06 +0100369 if (p)
370 defval = *p;
371 else if (o->def) {
Jens Axboe9af4a242012-03-16 10:13:49 +0100372 long long val;
373
374 check_str_bytes(o->def, &val, NULL);
375 defval = val;
376 }
377
Jens Axboe20eb06b2012-03-16 19:57:23 +0100378 interval = 1.0;
379 if (o->interval)
380 interval = o->interval;
381
382 i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval);
Jens Axboeec0218f2012-03-16 21:45:05 +0100383 gopt_mark_index(&i->gopt, idx);
Jens Axboe9af4a242012-03-16 10:13:49 +0100384 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
385 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
Jens Axboe90265352012-03-19 20:29:44 +0100386 i->lastval = defval;
Jens Axboe8f9e46a2012-03-19 22:01:20 +0100387 i->gopt.sig_handler = g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
Jens Axboe231edf62012-03-20 08:31:29 +0100388 g_signal_connect(G_OBJECT(i->spin), "destroy", G_CALLBACK(gopt_int_destroy), i);
Jens Axboe9af4a242012-03-16 10:13:49 +0100389
390 gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
Jens Axboee8b0e952012-03-19 14:37:08 +0100391 gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
Jens Axboeb6caa832012-03-16 21:22:56 +0100392
Jens Axboe90265352012-03-19 20:29:44 +0100393 o->gui_data = i;
Jens Axboeb6caa832012-03-16 21:22:56 +0100394 return i;
Jens Axboe9af4a242012-03-16 10:13:49 +0100395}
396
Jens Axboe231edf62012-03-20 08:31:29 +0100397static struct gopt *gopt_new_int(struct fio_option *o, unsigned int *ip,
398 unsigned int idx)
Jens Axboe39f04332012-03-16 16:34:06 +0100399{
400 unsigned long long ullp;
Jens Axboeb6caa832012-03-16 21:22:56 +0100401 struct gopt_int *i;
Jens Axboe39f04332012-03-16 16:34:06 +0100402
403 if (ip) {
404 ullp = *ip;
Jens Axboeb6caa832012-03-16 21:22:56 +0100405 i = __gopt_new_int(o, &ullp, idx);
406 } else
407 i = __gopt_new_int(o, NULL, idx);
Jens Axboe39f04332012-03-16 16:34:06 +0100408
Jens Axboeb6caa832012-03-16 21:22:56 +0100409 return &i->gopt;
Jens Axboe39f04332012-03-16 16:34:06 +0100410}
411
Jens Axboeb6caa832012-03-16 21:22:56 +0100412static struct gopt *gopt_new_ullong(struct fio_option *o, unsigned long long *p,
413 unsigned int idx)
Jens Axboe39f04332012-03-16 16:34:06 +0100414{
Jens Axboeb6caa832012-03-16 21:22:56 +0100415 struct gopt_int *i;
416
417 i = __gopt_new_int(o, p, idx);
418 return &i->gopt;
Jens Axboe39f04332012-03-16 16:34:06 +0100419}
420
Jens Axboeb6caa832012-03-16 21:22:56 +0100421static void gopt_bool_toggled(GtkToggleButton *button, gpointer data)
422{
423 struct gopt_bool *b = (struct gopt_bool *) data;
424 struct fio_option *o = &fio_options[b->gopt.opt_index];
425 gboolean set;
426
427 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100428
429 if (o->inv_opt) {
430 struct gopt_bool *b_inv = o->inv_opt->gui_data;
431
432 assert(o->type == o->inv_opt->type);
433
Jens Axboe8f9e46a2012-03-19 22:01:20 +0100434 g_signal_handler_block(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
Jens Axboea01a1bc2012-03-19 21:13:01 +0100435 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_inv->check), !set);
Jens Axboe8f9e46a2012-03-19 22:01:20 +0100436 g_signal_handler_unblock(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
Jens Axboea01a1bc2012-03-19 21:13:01 +0100437 }
438
Jens Axboeb6caa832012-03-16 21:22:56 +0100439 gopt_set_children_visible(o, set);
440}
441
Jens Axboe231edf62012-03-20 08:31:29 +0100442static void gopt_bool_destroy(GtkWidget *w, gpointer data)
443{
444 struct gopt_bool *b = (struct gopt_bool *) data;
445
446 free(b);
447 gtk_widget_destroy(w);
448}
449
450static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val,
451 unsigned int idx)
Jens Axboe9af4a242012-03-16 10:13:49 +0100452{
453 struct gopt_bool *b;
454 GtkWidget *label;
455 int defstate = 0;
456
457 b = malloc(sizeof(*b));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100458 memset(b, 0, sizeof(*b));
Jens Axboe9af4a242012-03-16 10:13:49 +0100459 b->gopt.box = gtk_hbox_new(FALSE, 3);
Jens Axboee8b0e952012-03-19 14:37:08 +0100460 if (!o->lname)
461 label = gtk_label_new(o->name);
462 else
463 label = gtk_label_new(o->lname);
Jens Axboe9af4a242012-03-16 10:13:49 +0100464
465 b->check = gtk_check_button_new();
Jens Axboeec0218f2012-03-16 21:45:05 +0100466 gopt_mark_index(&b->gopt, idx);
Jens Axboe39f04332012-03-16 16:34:06 +0100467 if (val)
468 defstate = *val;
469 else if (o->def && !strcmp(o->def, "1"))
Jens Axboe9af4a242012-03-16 10:13:49 +0100470 defstate = 1;
471
Jens Axboed872fb42012-03-19 19:17:49 +0100472 if (o->neg)
473 defstate = !defstate;
474
Jens Axboe9af4a242012-03-16 10:13:49 +0100475 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate);
Jens Axboe8f9e46a2012-03-19 22:01:20 +0100476 b->gopt.sig_handler = g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b);
Jens Axboe231edf62012-03-20 08:31:29 +0100477 g_signal_connect(G_OBJECT(b->check), "destroy", G_CALLBACK(gopt_bool_destroy), b);
Jens Axboe9af4a242012-03-16 10:13:49 +0100478
479 gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
Jens Axboee8b0e952012-03-19 14:37:08 +0100480 gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
Jens Axboe90265352012-03-19 20:29:44 +0100481 o->gui_data = b;
Jens Axboe9af4a242012-03-16 10:13:49 +0100482 return &b->gopt;
483}
484
Jens Axboe1da10c12012-03-16 19:41:01 +0100485/*
486 * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values.
487 * If the max is made smaller than min, adjust min down.
488 * If the min is made larger than max, adjust the max.
489 */
490static void range_value_changed(GtkSpinButton *spin, gpointer data)
491{
492 struct gopt_range *r = (struct gopt_range *) data;
493 int changed = -1, i;
494 gint val, mval;
495
496 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
497 if (GTK_SPIN_BUTTON(r->spins[i]) == spin) {
498 changed = i;
499 break;
500 }
501 }
502
503 assert(changed != -1);
504
505 /*
506 * Min changed
507 */
508 if (changed == 0 || changed == 2) {
509 GtkWidget *mspin = r->spins[changed + 1];
510
511 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
512 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
513 if (val > mval)
514 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
515 } else {
516 GtkWidget *mspin = r->spins[changed - 1];
517
518 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
519 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
520 if (val < mval)
521 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
522 }
523}
524
Jens Axboe231edf62012-03-20 08:31:29 +0100525static void gopt_range_destroy(GtkWidget *w, gpointer data)
526{
527 struct gopt_range *r = (struct gopt_range *) data;
528
529 free(r);
530 gtk_widget_destroy(w);
531}
532
Jens Axboeb6caa832012-03-16 21:22:56 +0100533static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip,
534 unsigned int idx)
Jens Axboe9af4a242012-03-16 10:13:49 +0100535{
536 struct gopt_range *r;
537 gint maxval, defval;
538 GtkWidget *label;
Jens Axboe20eb06b2012-03-16 19:57:23 +0100539 guint interval;
Jens Axboe9af4a242012-03-16 10:13:49 +0100540 int i;
541
542 r = malloc(sizeof(*r));
Jens Axboea01a1bc2012-03-19 21:13:01 +0100543 memset(r, 0, sizeof(*r));
Jens Axboe9af4a242012-03-16 10:13:49 +0100544 r->gopt.box = gtk_hbox_new(FALSE, 3);
Jens Axboeec0218f2012-03-16 21:45:05 +0100545 gopt_mark_index(&r->gopt, idx);
Jens Axboee8b0e952012-03-19 14:37:08 +0100546 if (!o->lname)
547 label = gtk_label_new(o->name);
548 else
549 label = gtk_label_new(o->lname);
Jens Axboe9af4a242012-03-16 10:13:49 +0100550
551 maxval = o->maxval;
552 if (!maxval)
553 maxval = INT_MAX;
554
555 defval = 0;
556 if (o->def) {
557 long long val;
558
559 check_str_bytes(o->def, &val, NULL);
560 defval = val;
561 }
562
Jens Axboe20eb06b2012-03-16 19:57:23 +0100563 interval = 1.0;
564 if (o->interval)
565 interval = o->interval;
566
Jens Axboe1da10c12012-03-16 19:41:01 +0100567 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
Jens Axboe20eb06b2012-03-16 19:57:23 +0100568 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval);
Jens Axboe9af4a242012-03-16 10:13:49 +0100569 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID);
Jens Axboe39f04332012-03-16 16:34:06 +0100570 if (ip)
571 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]);
572 else
573 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval);
Jens Axboe9af4a242012-03-16 10:13:49 +0100574
575 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0);
Jens Axboe1da10c12012-03-16 19:41:01 +0100576 g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r);
Jens Axboe9af4a242012-03-16 10:13:49 +0100577 }
578
Jens Axboee8b0e952012-03-19 14:37:08 +0100579 gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
Jens Axboe231edf62012-03-20 08:31:29 +0100580 g_signal_connect(G_OBJECT(r->gopt.box), "destroy", G_CALLBACK(gopt_range_destroy), r);
Jens Axboe90265352012-03-19 20:29:44 +0100581 o->gui_data = r;
Jens Axboe9af4a242012-03-16 10:13:49 +0100582 return &r->gopt;
583}
584
Jens Axboe7fc35ff2012-03-20 09:32:35 +0100585static void gopt_str_val_destroy(GtkWidget *w, gpointer data)
586{
587 struct gopt_str_val *g = (struct gopt_str_val *) data;
588
589 free(g);
590 gtk_widget_destroy(w);
591}
592
593static void gopt_str_val_spin_wrapped(GtkSpinButton *spin, gpointer data)
594{
595 struct gopt_str_val *g = (struct gopt_str_val *) data;
596 unsigned int val;
597 GtkAdjustment *adj;
598 gint index;
599
600 adj = gtk_spin_button_get_adjustment(spin);
601 val = gtk_adjustment_get_value(adj);
602
603 /*
604 * Can't rely on exact value, as fast changes increment >= 1
605 */
606 if (!val) {
Jens Axboe7fc35ff2012-03-20 09:32:35 +0100607 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
Jens Axboe481c2422012-03-20 09:49:49 +0100608 if (index + 1 <= g->maxindex) {
609 val = 1;
610 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), ++index);
611 } else
612 val = 1023;
Jens Axboe7fc35ff2012-03-20 09:32:35 +0100613 gtk_spin_button_set_value(spin, val);
614 } else {
615 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
616 if (index) {
617 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), --index);
618 gtk_spin_button_set_value(spin, 1023);
619 } else
620 gtk_spin_button_set_value(spin, 0);
621 }
622}
623
624static struct gopt *gopt_new_str_val(struct fio_option *o,
625 unsigned long long *p, unsigned int idx)
626{
627 struct gopt_str_val *g;
628 const gchar *postfix[] = { "B", "KB", "MB", "GB", "PB", "TB", "" };
629 GtkWidget *label;
630 int i;
631
632 g = malloc(sizeof(*g));
633 memset(g, 0, sizeof(*g));
634 g->gopt.box = gtk_hbox_new(FALSE, 3);
635 if (!o->lname)
636 label = gtk_label_new(o->name);
637 else
638 label = gtk_label_new(o->lname);
639
640 g->spin = gtk_spin_button_new_with_range(0.0, 1023.0, 1.0);
641 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(g->spin), GTK_UPDATE_IF_VALID);
642 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), 0);
643 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(g->spin), 1);
644 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->spin, FALSE, FALSE, 0);
645 g_signal_connect(G_OBJECT(g->spin), "wrapped", G_CALLBACK(gopt_str_val_spin_wrapped), g);
646
647 g->combo = gtk_combo_box_new_text();
648 i = 0;
649 while (strlen(postfix[i])) {
650 gtk_combo_box_append_text(GTK_COMBO_BOX(g->combo), postfix[i]);
651 i++;
652 }
Jens Axboe481c2422012-03-20 09:49:49 +0100653 g->maxindex = i - 1;
Jens Axboe7fc35ff2012-03-20 09:32:35 +0100654 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), 0);
655 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->combo, FALSE, FALSE, 0);
656 gtk_box_pack_start(GTK_BOX(g->gopt.box), label, FALSE, FALSE, 3);
657
658 g_signal_connect(G_OBJECT(g->gopt.box), "destroy", G_CALLBACK(gopt_str_val_destroy), g);
659 o->gui_data = g;
660 return &g->gopt;
661}
662
Jens Axboe9af4a242012-03-16 10:13:49 +0100663static void gopt_add_option(GtkWidget *hbox, struct fio_option *o,
Jens Axboe789f4cc2012-03-16 14:56:44 +0100664 unsigned int opt_index, struct thread_options *to)
Jens Axboe9af4a242012-03-16 10:13:49 +0100665{
666 struct gopt *go = NULL;
667
668 switch (o->type) {
Jens Axboe7fc35ff2012-03-20 09:32:35 +0100669 case FIO_OPT_STR_VAL: {
670 unsigned long long *ullp = NULL;
671
672 if (o->off1)
673 ullp = td_var(to, o->off1);
674
675 go = gopt_new_str_val(o, ullp, opt_index);
676 break;
677 }
Jens Axboe39f04332012-03-16 16:34:06 +0100678 case FIO_OPT_STR_VAL_TIME: {
679 unsigned long long *ullp = NULL;
Jens Axboe789f4cc2012-03-16 14:56:44 +0100680
Jens Axboe39f04332012-03-16 16:34:06 +0100681 if (o->off1)
682 ullp = td_var(to, o->off1);
683
Jens Axboeb6caa832012-03-16 21:22:56 +0100684 go = gopt_new_ullong(o, ullp, opt_index);
Jens Axboe9af4a242012-03-16 10:13:49 +0100685 break;
Jens Axboe789f4cc2012-03-16 14:56:44 +0100686 }
Jens Axboe39f04332012-03-16 16:34:06 +0100687 case FIO_OPT_INT: {
688 unsigned int *ip = NULL;
689
690 if (o->off1)
691 ip = td_var(to, o->off1);
692
Jens Axboeb6caa832012-03-16 21:22:56 +0100693 go = gopt_new_int(o, ip, opt_index);
Jens Axboe9af4a242012-03-16 10:13:49 +0100694 break;
Jens Axboe39f04332012-03-16 16:34:06 +0100695 }
Jens Axboe9af4a242012-03-16 10:13:49 +0100696 case FIO_OPT_STR_SET:
Jens Axboe39f04332012-03-16 16:34:06 +0100697 case FIO_OPT_BOOL: {
698 unsigned int *ip = NULL;
699
700 if (o->off1)
701 ip = td_var(to, o->off1);
702
Jens Axboeb6caa832012-03-16 21:22:56 +0100703 go = gopt_new_bool(o, ip, opt_index);
Jens Axboe9af4a242012-03-16 10:13:49 +0100704 break;
Jens Axboe39f04332012-03-16 16:34:06 +0100705 }
706 case FIO_OPT_STR: {
Jens Axboed872fb42012-03-19 19:17:49 +0100707 if (o->posval[0].ival) {
708 unsigned int *ip = NULL;
Jens Axboe39f04332012-03-16 16:34:06 +0100709
Jens Axboed872fb42012-03-19 19:17:49 +0100710 if (o->off1)
711 ip = td_var(to, o->off1);
Jens Axboe39f04332012-03-16 16:34:06 +0100712
Jens Axboed872fb42012-03-19 19:17:49 +0100713 go = gopt_new_combo_int(o, ip, opt_index);
714 } else {
715 /* TODO: usually ->cb, or unsigned int pointer */
716 go = gopt_new_str_store(o, NULL, opt_index);
717 }
718
Jens Axboe39f04332012-03-16 16:34:06 +0100719 break;
720 }
721 case FIO_OPT_STR_STORE: {
722 char *text = NULL;
723
Jens Axboed3944492012-03-16 18:53:32 +0100724 if (o->off1) {
Jens Axboe39f04332012-03-16 16:34:06 +0100725 char **p = td_var(to, o->off1);
726 text = *p;
727 }
728
Jens Axboe9af4a242012-03-16 10:13:49 +0100729 if (!o->posval[0].ival) {
Jens Axboeb6caa832012-03-16 21:22:56 +0100730 go = gopt_new_str_store(o, text, opt_index);
Jens Axboe9af4a242012-03-16 10:13:49 +0100731 break;
732 }
Jens Axboe39f04332012-03-16 16:34:06 +0100733
Jens Axboeb6caa832012-03-16 21:22:56 +0100734 go = gopt_new_combo_str(o, text, opt_index);
Jens Axboe39f04332012-03-16 16:34:06 +0100735 break;
736 }
Jens Axboe9af4a242012-03-16 10:13:49 +0100737 case FIO_OPT_STR_MULTI:
Jens Axboe7386d4a2012-03-19 18:36:29 +0100738 go = gopt_new_str_multi(o, opt_index);
Jens Axboe9af4a242012-03-16 10:13:49 +0100739 break;
Jens Axboe39f04332012-03-16 16:34:06 +0100740 case FIO_OPT_RANGE: {
741 unsigned int *ip[4] = { td_var(to, o->off1),
742 td_var(to, o->off2),
743 td_var(to, o->off3),
744 td_var(to, o->off4) };
745
Jens Axboeb6caa832012-03-16 21:22:56 +0100746 go = gopt_new_int_range(o, ip, opt_index);
Jens Axboe9af4a242012-03-16 10:13:49 +0100747 break;
Jens Axboe39f04332012-03-16 16:34:06 +0100748 }
Jens Axboe9af4a242012-03-16 10:13:49 +0100749 /* still need to handle this one */
750 case FIO_OPT_FLOAT_LIST:
751 break;
752 case FIO_OPT_DEPRECATED:
753 break;
754 default:
755 printf("ignore type %u\n", o->type);
756 break;
757 }
758
759 if (go) {
Jens Axboee8b0e952012-03-19 14:37:08 +0100760 GtkWidget *dest;
761
Jens Axboe9af4a242012-03-16 10:13:49 +0100762 if (o->help)
763 gtk_widget_set_tooltip_text(go->box, o->help);
Jens Axboee8b0e952012-03-19 14:37:08 +0100764
Jens Axboe9af4a242012-03-16 10:13:49 +0100765 go->opt_type = o->type;
Jens Axboee8b0e952012-03-19 14:37:08 +0100766
767 dest = gopt_get_group_frame(hbox, o->group);
768 if (!dest)
769 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
770 else
771 gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5);
Jens Axboe9af4a242012-03-16 10:13:49 +0100772 }
773}
774
Jens Axboe789f4cc2012-03-16 14:56:44 +0100775static void gopt_add_options(GtkWidget **vboxes, struct thread_options *to)
Jens Axboe9af4a242012-03-16 10:13:49 +0100776{
777 GtkWidget *hbox = NULL;
778 int i;
779
Jens Axboe90265352012-03-19 20:29:44 +0100780 /*
781 * First add all options
782 */
Jens Axboe9af4a242012-03-16 10:13:49 +0100783 for (i = 0; fio_options[i].name; i++) {
784 struct fio_option *o = &fio_options[i];
785 unsigned int mask = o->category;
786 struct opt_group *og;
787
788 while ((og = opt_group_from_mask(&mask)) != NULL) {
789 GtkWidget *vbox = vboxes[ffz(~og->mask)];
790
791 hbox = gtk_hbox_new(FALSE, 3);
792 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
Jens Axboe789f4cc2012-03-16 14:56:44 +0100793 gopt_add_option(hbox, o, i, to);
Jens Axboe9af4a242012-03-16 10:13:49 +0100794 }
795 }
796}
797
798static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
799{
800 GtkWidget *box, *vbox, *scroll;
801
802 scroll = gtk_scrolled_window_new(NULL, NULL);
803 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
804 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
805
806 vbox = gtk_vbox_new(FALSE, 3);
807 box = gtk_hbox_new(FALSE, 0);
808 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
809 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
810 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(og->name));
811
812 return vbox;
813}
814
815static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
816{
817 struct opt_group *og;
Jens Axboeec0218f2012-03-16 21:45:05 +0100818 unsigned int i;
Jens Axboe9af4a242012-03-16 10:13:49 +0100819
Jens Axboeec0218f2012-03-16 21:45:05 +0100820 i = 0;
Jens Axboe9af4a242012-03-16 10:13:49 +0100821 do {
822 unsigned int mask = (1U << i);
823
824 og = opt_group_from_mask(&mask);
825 if (!og)
826 break;
827 vbox[i] = gopt_add_group_tab(notebook, og);
828 i++;
829 } while (1);
830}
831
Jens Axboe789f4cc2012-03-16 14:56:44 +0100832void gopt_get_options_window(GtkWidget *window, struct thread_options *o)
Jens Axboe9af4a242012-03-16 10:13:49 +0100833{
834 GtkWidget *dialog, *notebook;
Jens Axboee8b0e952012-03-19 14:37:08 +0100835 GtkWidget *vboxes[__FIO_OPT_C_NR];
Jens Axboe9af4a242012-03-16 10:13:49 +0100836
837 dialog = gtk_dialog_new_with_buttons("Fio options",
838 GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
839 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
840 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
841
842 gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768);
843
844 notebook = gtk_notebook_new();
845 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
846 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
847 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 5);
848
849 gopt_add_group_tabs(notebook, vboxes);
850
Jens Axboe789f4cc2012-03-16 14:56:44 +0100851 gopt_add_options(vboxes, o);
Jens Axboe9af4a242012-03-16 10:13:49 +0100852
853 gtk_widget_show_all(dialog);
854
855 gtk_dialog_run(GTK_DIALOG(dialog));
856
857 gtk_widget_destroy(dialog);
Stephen M. Camerona8baa342012-03-20 07:10:47 +0100858 memset(gopt_widgets, 0, sizeof(gopt_widgets));
859 memset(gopt_g_widgets, 0, sizeof(gopt_g_widgets));
Jens Axboe9af4a242012-03-16 10:13:49 +0100860}