Store specific client ops in the fio_client structure
[fio.git] / gfio.c
CommitLineData
ff1f3280
SC
1/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 *
6 * The license below covers all files distributed with fio unless otherwise
7 * noted in the file itself.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
8232e285 23#include <locale.h>
60f6b330 24#include <malloc.h>
8232e285 25
5b7573ab 26#include <glib.h>
ff1f3280
SC
27#include <gtk/gtk.h>
28
8232e285
SC
29#include "fio.h"
30
3e47bd25
JA
31static void gfio_update_thread_status(char *status_message, double perc);
32
f3074008
SC
33#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
3e47bd25 37static void connect_clicked(GtkWidget *widget, gpointer data);
f3074008
SC
38static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40static struct button_spec {
41 const char *buttontext;
42 clickfunction f;
43 const char *tooltiptext;
3e47bd25 44 const int start_insensitive;
f3074008 45} buttonspeclist[] = {
3e47bd25
JA
46#define CONNECT_BUTTON 0
47#define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
f3074008
SC
49 { "Start Job",
50 start_job_clicked,
3e47bd25 51 "Send current fio job to fio server to be executed", 1 },
f3074008
SC
52};
53
843ad237
JA
54struct probe_widget {
55 GtkWidget *hostname;
56 GtkWidget *os;
57 GtkWidget *arch;
58 GtkWidget *fio_ver;
59};
60
3e47bd25 61struct eta_widget {
807f9971
JA
62 GtkWidget *name;
63 GtkWidget *iotype;
64 GtkWidget *ioengine;
65 GtkWidget *iodepth;
3e47bd25
JA
66 GtkWidget *jobs;
67 GtkWidget *files;
68 GtkWidget *read_bw;
69 GtkWidget *read_iops;
70 GtkWidget *cr_bw;
71 GtkWidget *cr_iops;
72 GtkWidget *write_bw;
73 GtkWidget *write_iops;
74 GtkWidget *cw_bw;
75 GtkWidget *cw_iops;
76};
77
ff1f3280
SC
78struct gui {
79 GtkWidget *window;
5b7573ab 80 GtkWidget *vbox;
c36f98d9
SC
81 GtkWidget *topvbox;
82 GtkWidget *topalign;
83 GtkWidget *bottomalign;
04cc6b77 84 GtkWidget *thread_status_pb;
f3074008
SC
85 GtkWidget *buttonbox;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
736f2dff
SC
87 GtkWidget *scrolled_window;
88 GtkWidget *textview;
0420ba6a
JA
89 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
736f2dff 91 GtkTextBuffer *text;
843ad237 92 struct probe_widget probe;
3e47bd25 93 struct eta_widget eta;
3ec62ec4 94 int connected;
25927259 95 pthread_t t;
0420ba6a 96
3ec62ec4 97 struct fio_client *client;
0420ba6a
JA
98 int nr_job_files;
99 char **job_files;
5b7573ab 100} ui;
ff1f3280 101
8663ea65
JA
102static void clear_ui_info(struct gui *ui)
103{
104 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
105 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
106 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
107 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
108 gtk_label_set_text(GTK_LABEL(ui->eta.name), "");
109 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), "");
110 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), "");
111 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), "");
112 gtk_label_set_text(GTK_LABEL(ui->eta.jobs), "");
113 gtk_label_set_text(GTK_LABEL(ui->eta.files), "");
114 gtk_label_set_text(GTK_LABEL(ui->eta.read_bw), "");
115 gtk_label_set_text(GTK_LABEL(ui->eta.read_iops), "");
116 gtk_label_set_text(GTK_LABEL(ui->eta.write_bw), "");
117 gtk_label_set_text(GTK_LABEL(ui->eta.write_iops), "");
118}
119
3ec62ec4
JA
120static void gfio_set_connected(struct gui *ui, int connected)
121{
122 if (connected) {
123 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
124 ui->connected = 1;
125 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
126 } else {
127 ui->connected = 0;
128 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
129 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
130 }
131}
132
084d1c6f 133static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 134{
807f9971 135#if 0
736f2dff
SC
136 GtkTextBuffer *buffer;
137 GtkTextIter end;
138
139 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
140 gdk_threads_enter();
141 gtk_text_buffer_get_end_iter(buffer, &end);
142 gtk_text_buffer_insert(buffer, &end, buf, -1);
143 gdk_threads_leave();
144 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
145 &end, 0.0, FALSE, 0.0,0.0);
807f9971 146#else
084d1c6f 147 fio_client_ops.text_op(client, cmd);
807f9971 148#endif
a1820207
SC
149}
150
151static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
152{
153 printf("gfio_disk_util_op called\n");
154 fio_client_ops.disk_util(client, cmd);
155}
156
157static void gfio_thread_status_op(struct fio_net_cmd *cmd)
158{
159 printf("gfio_thread_status_op called\n");
160 fio_client_ops.thread_status(cmd);
161}
162
163static void gfio_group_stats_op(struct fio_net_cmd *cmd)
164{
165 printf("gfio_group_stats_op called\n");
166 fio_client_ops.group_stats(cmd);
167}
168
3e47bd25
JA
169static void gfio_update_eta(struct jobs_eta *je)
170{
171 static int eta_good;
172 char eta_str[128];
173 char output[256];
174 char tmp[32];
175 double perc = 0.0;
176 int i2p = 0;
177
178 eta_str[0] = '\0';
179 output[0] = '\0';
180
181 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
182 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
183 eta_to_str(eta_str, je->eta_sec);
184 }
185
186 sprintf(tmp, "%u", je->nr_running);
187 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
188 sprintf(tmp, "%u", je->files_open);
189 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
190
191#if 0
192 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
193 if (je->m_rate || je->t_rate) {
194 char *tr, *mr;
195
196 mr = num2str(je->m_rate, 4, 0, i2p);
197 tr = num2str(je->t_rate, 4, 0, i2p);
198 gtk_label_set_text(GTK_LABEL(ui.eta.
199 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
200 free(tr);
201 free(mr);
202 } else if (je->m_iops || je->t_iops)
203 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 204
3e47bd25
JA
205 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
206 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
207 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
208 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
209#endif
210
211 if (je->eta_sec != INT_MAX && je->nr_running) {
212 char *iops_str[2];
213 char *rate_str[2];
214
215 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
216 strcpy(output, "-.-% done");
217 else {
218 eta_good = 1;
219 perc *= 100.0;
220 sprintf(output, "%3.1f%% done", perc);
221 }
222
223 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
224 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
225
226 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
227 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
228
229 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
230 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
231 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
232 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
233
234 free(rate_str[0]);
235 free(rate_str[1]);
236 free(iops_str[0]);
237 free(iops_str[1]);
238 }
239
240 if (eta_str[0]) {
241 char *dst = output + strlen(output);
242
243 sprintf(dst, " - %s", eta_str);
244 }
245
246 gfio_update_thread_status(output, perc);
247}
248
a1820207
SC
249static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
250{
843ad237
JA
251 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
252 const char *os, *arch;
253 char buf[64];
254
255 os = fio_get_os_string(probe->os);
256 if (!os)
257 os = "unknown";
258
259 arch = fio_get_arch_string(probe->arch);
260 if (!arch)
261 os = "unknown";
262
263 if (!client->name)
264 client->name = strdup((char *) probe->hostname);
265
266 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
267 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
268 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
269 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
270 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
a1820207
SC
271}
272
04cc6b77 273static void gfio_update_thread_status(char *status_message, double perc)
5b7573ab
SC
274{
275 static char message[100];
276 const char *m = message;
277
278 strncpy(message, status_message, sizeof(message) - 1);
04cc6b77
SC
279 gtk_progress_bar_set_text(
280 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
281 gtk_progress_bar_set_fraction(
282 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
5b7573ab
SC
283 gdk_threads_enter();
284 gtk_widget_queue_draw(ui.window);
285 gdk_threads_leave();
286}
287
3ec62ec4
JA
288static void gfio_quit_op(struct fio_client *client)
289{
290 struct gui *ui = client->client_data;
291
292 gfio_set_connected(ui, 0);
293}
294
807f9971
JA
295static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
296{
297 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
298 struct gui *ui = client->client_data;
299 char tmp[8];
300 int i;
301
302 p->iodepth = le32_to_cpu(p->iodepth);
303 p->rw = le32_to_cpu(p->rw);
304
305 for (i = 0; i < 2; i++) {
306 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
307 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
308 }
309
310 p->numjobs = le32_to_cpu(p->numjobs);
311 p->group_reporting = le32_to_cpu(p->group_reporting);
312
313 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
314 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
315 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
316
317 sprintf(tmp, "%u", p->iodepth);
318 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
319}
320
ed727a46
JA
321static void gfio_client_timed_out(struct fio_client *client)
322{
323 struct gui *ui = client->client_data;
324 GtkWidget *dialog, *label, *content;
325 char buf[256];
326
327 gdk_threads_enter();
328
329 gfio_set_connected(ui, 0);
88432651 330 clear_ui_info(ui);
ed727a46
JA
331
332 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
333
334 dialog = gtk_dialog_new_with_buttons("Timed out!",
335 GTK_WINDOW(ui->window),
336 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
337 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
338
339 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
340 label = gtk_label_new((const gchar *) buf);
341 gtk_container_add(GTK_CONTAINER(content), label);
342 gtk_widget_show_all(dialog);
343 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
344
345 gtk_dialog_run(GTK_DIALOG(dialog));
346 gtk_widget_destroy(dialog);
347
348 gdk_threads_leave();
349}
350
a1820207 351struct client_ops gfio_client_ops = {
0420ba6a
JA
352 .text_op = gfio_text_op,
353 .disk_util = gfio_disk_util_op,
354 .thread_status = gfio_thread_status_op,
355 .group_stats = gfio_group_stats_op,
a5276616 356 .eta = gfio_update_eta,
0420ba6a 357 .probe = gfio_probe_op,
3ec62ec4 358 .quit = gfio_quit_op,
807f9971 359 .add_job = gfio_add_job_op,
ed727a46 360 .timed_out = gfio_client_timed_out,
3ec62ec4 361 .stay_connected = 1,
a1820207
SC
362};
363
ff1f3280
SC
364static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
365 __attribute__((unused)) gpointer data)
366{
367 gtk_main_quit();
368}
369
25927259
SC
370static void *job_thread(void *arg)
371{
25927259 372 fio_handle_clients(&gfio_client_ops);
25927259
SC
373 return NULL;
374}
375
0420ba6a 376static int send_job_files(struct gui *ui)
60f6b330 377{
441013b4 378 int i, ret = 0;
0420ba6a
JA
379
380 for (i = 0; i < ui->nr_job_files; i++) {
381 ret = fio_clients_send_ini(ui->job_files[i]);
441013b4
JA
382 if (ret)
383 break;
384
0420ba6a
JA
385 free(ui->job_files[i]);
386 ui->job_files[i] = NULL;
441013b4
JA
387 }
388 while (i < ui->nr_job_files) {
389 free(ui->job_files[i]);
390 ui->job_files[i] = NULL;
391 i++;
0420ba6a
JA
392 }
393
441013b4 394 return ret;
60f6b330
SC
395}
396
3ec62ec4 397static void start_job_thread(struct gui *ui)
25927259 398{
0420ba6a 399 if (send_job_files(ui)) {
60f6b330 400 printf("Yeah, I didn't really like those options too much.\n");
60f6b330
SC
401 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
402 return;
403 }
25927259
SC
404}
405
a7a42ce1
JA
406static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
407{
408 GtkWidget *label_widget;
409 GtkWidget *frame;
410
411 frame = gtk_frame_new(label);
412 label_widget = gtk_label_new(NULL);
413 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
414 gtk_container_add(GTK_CONTAINER(frame), label_widget);
415
416 return label_widget;
417}
418
a7a42ce1
JA
419static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
420{
421 GtkWidget *button, *box;
422
423 box = gtk_hbox_new(FALSE, 3);
424 gtk_container_add(GTK_CONTAINER(hbox), box);
425
426 button = gtk_spin_button_new_with_range(min, max, 1.0);
427 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
428
429 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
430 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
431
432 return button;
433}
434
f3074008 435static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 436 gpointer data)
f3074008 437{
25927259
SC
438 struct gui *ui = data;
439
25927259 440 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
3ec62ec4 441 start_job_thread(ui);
f3074008
SC
442}
443
df06f220
JA
444static void file_open(GtkWidget *w, gpointer data);
445
446static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 447{
3ec62ec4
JA
448 struct gui *ui = data;
449
450 if (!ui->connected) {
df06f220
JA
451 if (!ui->nr_job_files)
452 file_open(widget, data);
8663ea65 453 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
3ec62ec4
JA
454 fio_clients_connect();
455 pthread_create(&ui->t, NULL, job_thread, NULL);
456 gfio_set_connected(ui, 1);
df06f220
JA
457 } else {
458 fio_clients_terminate();
3ec62ec4 459 gfio_set_connected(ui, 0);
88432651 460 clear_ui_info(ui);
df06f220 461 }
3e47bd25
JA
462}
463
f3074008
SC
464static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
465 struct button_spec *buttonspec)
466{
467 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
468 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
3ec62ec4 469 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
f3074008 470 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
3e47bd25 471 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
f3074008
SC
472}
473
474static void add_buttons(struct gui *ui,
475 struct button_spec *buttonlist,
476 int nbuttons)
477{
478 int i;
479
f3074008
SC
480 for (i = 0; i < nbuttons; i++)
481 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
482}
483
0420ba6a
JA
484static void on_info_bar_response(GtkWidget *widget, gint response,
485 gpointer data)
486{
487 if (response == GTK_RESPONSE_OK) {
488 gtk_widget_destroy(widget);
489 ui.error_info_bar = NULL;
490 }
491}
492
df06f220 493void report_error(GError *error)
0420ba6a
JA
494{
495 if (ui.error_info_bar == NULL) {
496 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
497 GTK_RESPONSE_OK,
498 NULL);
499 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
500 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
501 GTK_MESSAGE_ERROR);
502
503 ui.error_label = gtk_label_new(error->message);
504 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
505 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
506
507 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
508 gtk_widget_show_all(ui.vbox);
509 } else {
510 char buffer[256];
511 snprintf(buffer, sizeof(buffer), "Failed to open file.");
512 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
513 }
514}
515
b9f3c7ed
JA
516static int get_connection_details(char **host, int *port, int *type,
517 int *server_start)
a7a42ce1
JA
518{
519 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
b9f3c7ed 520 GtkWidget *button;
a7a42ce1
JA
521 char *typeentry;
522
523 dialog = gtk_dialog_new_with_buttons("Connection details",
524 GTK_WINDOW(ui.window),
525 GTK_DIALOG_DESTROY_WITH_PARENT,
526 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
527 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
528
529 frame = gtk_frame_new("Hostname / socket name");
530 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
531 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
532
533 box = gtk_vbox_new(FALSE, 6);
534 gtk_container_add(GTK_CONTAINER(frame), box);
535
536 hbox = gtk_hbox_new(TRUE, 10);
537 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
538 hentry = gtk_entry_new();
539 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
540 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
541
542 frame = gtk_frame_new("Port");
543 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
544 box = gtk_vbox_new(FALSE, 10);
545 gtk_container_add(GTK_CONTAINER(frame), box);
546
547 hbox = gtk_hbox_new(TRUE, 4);
548 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
549 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
550
551 frame = gtk_frame_new("Type");
552 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
553 box = gtk_vbox_new(FALSE, 10);
554 gtk_container_add(GTK_CONTAINER(frame), box);
555
556 hbox = gtk_hbox_new(TRUE, 4);
557 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
558
559 combo = gtk_combo_box_text_new();
560 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
561 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
562 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
563 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
564
565 gtk_container_add(GTK_CONTAINER(hbox), combo);
566
b9f3c7ed
JA
567 frame = gtk_frame_new("Options");
568 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
569 box = gtk_vbox_new(FALSE, 10);
570 gtk_container_add(GTK_CONTAINER(frame), box);
571
572 hbox = gtk_hbox_new(TRUE, 4);
573 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
574
575 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
576 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
577 gtk_widget_set_tooltip_text(button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
578 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
579
a7a42ce1
JA
580 gtk_widget_show_all(dialog);
581
582 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
583 gtk_widget_destroy(dialog);
584 return 1;
585 }
586
587 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
588 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
589
590 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
591 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
592 *type = Fio_client_ipv4;
593 else if (!strncmp(typeentry, "IPv6", 4))
594 *type = Fio_client_ipv6;
595 else
596 *type = Fio_client_socket;
597 g_free(typeentry);
598
b9f3c7ed
JA
599 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
600
a7a42ce1
JA
601 gtk_widget_destroy(dialog);
602 return 0;
603}
604
0420ba6a
JA
605static void file_open(GtkWidget *w, gpointer data)
606{
607 GtkWidget *dialog;
608 GSList *filenames, *fn_glist;
609 GtkFileFilter *filter;
a7a42ce1 610 char *host;
b9f3c7ed 611 int port, type, server_start;
0420ba6a
JA
612
613 dialog = gtk_file_chooser_dialog_new("Open File",
614 GTK_WINDOW(ui.window),
615 GTK_FILE_CHOOSER_ACTION_OPEN,
616 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
617 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
618 NULL);
619 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
620
621 filter = gtk_file_filter_new();
622 gtk_file_filter_add_pattern(filter, "*.fio");
623 gtk_file_filter_add_pattern(filter, "*.job");
624 gtk_file_filter_add_mime_type(filter, "text/fio");
625 gtk_file_filter_set_name(filter, "Fio job file");
626 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
627
628 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
629 gtk_widget_destroy(dialog);
630 return;
631 }
632
633 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
634
635 gtk_widget_destroy(dialog);
636
b9f3c7ed 637 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
638 goto err;
639
0420ba6a
JA
640 filenames = fn_glist;
641 while (filenames != NULL) {
0420ba6a
JA
642 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
643 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
644 ui.nr_job_files++;
645
a5276616 646 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
df06f220
JA
647 if (!ui.client) {
648 GError *error;
649
650 error = g_error_new(g_quark_from_string("fio"), 1,
651 "Failed to add client %s", host);
0420ba6a
JA
652 report_error(error);
653 g_error_free(error);
0420ba6a 654 }
df06f220 655 ui.client->client_data = &ui;
0420ba6a
JA
656
657 g_free(filenames->data);
658 filenames = g_slist_next(filenames);
659 }
a7a42ce1
JA
660 free(host);
661err:
0420ba6a 662 g_slist_free(fn_glist);
0420ba6a
JA
663}
664
665static void file_save(GtkWidget *w, gpointer data)
666{
667 GtkWidget *dialog;
668
669 dialog = gtk_file_chooser_dialog_new("Save File",
670 GTK_WINDOW(ui.window),
671 GTK_FILE_CHOOSER_ACTION_SAVE,
672 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
673 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
674 NULL);
675
676 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
677 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
678
679 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
680 char *filename;
681
682 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
683 // save_job_file(filename);
684 g_free(filename);
685 }
686 gtk_widget_destroy(dialog);
687}
688
46974a7d
JA
689static void preferences(GtkWidget *w, gpointer data)
690{
691 GtkWidget *dialog, *frame, *box, **buttons;
692 int i;
693
694 dialog = gtk_dialog_new_with_buttons("Preferences",
695 GTK_WINDOW(ui.window),
696 GTK_DIALOG_DESTROY_WITH_PARENT,
697 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
698 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
699 NULL);
700
0b8d11ed 701 frame = gtk_frame_new("Debug logging");
46974a7d
JA
702 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
703 box = gtk_hbox_new(FALSE, 6);
704 gtk_container_add(GTK_CONTAINER(frame), box);
705
706 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
707
708 for (i = 0; i < FD_DEBUG_MAX; i++) {
709 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
0b8d11ed 710 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
46974a7d
JA
711 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
712 }
713
714 gtk_widget_show_all(dialog);
715
716 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
717 gtk_widget_destroy(dialog);
718 return;
719 }
720
721 for (i = 0; i < FD_DEBUG_MAX; i++) {
722 int set;
723
724 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
725 if (set)
726 fio_debug |= (1UL << i);
727 }
728
729 gtk_widget_destroy(dialog);
730}
731
0420ba6a
JA
732static void about_dialog(GtkWidget *w, gpointer data)
733{
734 gtk_show_about_dialog(NULL,
735 "program-name", "gfio",
736 "comments", "Gtk2 UI for fio",
737 "license", "GPLv2",
738 "version", fio_version_string,
739 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
740 "logo-icon-name", "fio",
741 /* Must be last: */
742 NULL, NULL,
743 NULL);
744}
745
746static GtkActionEntry menu_items[] = {
46974a7d
JA
747 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
748 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
749 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
750 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
751 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
752 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
753 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 754};
3e47bd25 755static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
756
757static const gchar *ui_string = " \
758 <ui> \
759 <menubar name=\"MainMenu\"> \
760 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
761 <menuitem name=\"Open\" action=\"OpenFile\" /> \
762 <menuitem name=\"Save\" action=\"SaveFile\" /> \
763 <separator name=\"Separator\"/> \
46974a7d
JA
764 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
765 <separator name=\"Separator2\"/> \
0420ba6a
JA
766 <menuitem name=\"Quit\" action=\"Quit\" /> \
767 </menu> \
768 <menu name=\"Help\" action=\"HelpMenuAction\"> \
769 <menuitem name=\"About\" action=\"About\" /> \
770 </menu> \
771 </menubar> \
772 </ui> \
773";
774
775static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
776{
777 GtkActionGroup *action_group = gtk_action_group_new("Menu");
778 GError *error = 0;
779
780 action_group = gtk_action_group_new("Menu");
781 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
782
783 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
784 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
785
786 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
787 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
788}
789
790void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
791 GtkWidget *vbox, GtkUIManager *ui_manager)
792{
793 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
794}
795
ff1f3280
SC
796static void init_ui(int *argc, char **argv[], struct gui *ui)
797{
0420ba6a
JA
798 GtkSettings *settings;
799 GtkUIManager *uimanager;
843ad237 800 GtkWidget *menu, *probe, *probe_frame, *probe_box;
0420ba6a
JA
801
802 memset(ui, 0, sizeof(*ui));
45032dd8 803
2839f0c6 804 /* Magical g*thread incantation, you just need this thread stuff.
04cc6b77 805 * Without it, the update that happens in gfio_update_thread_status
2839f0c6
SC
806 * doesn't really happen in a timely fashion, you need expose events
807 */
ed727a46 808 if (!g_thread_supported())
2839f0c6
SC
809 g_thread_init(NULL);
810 gdk_threads_init();
811
ff1f3280 812 gtk_init(argc, argv);
0420ba6a
JA
813 settings = gtk_settings_get_default();
814 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
815 g_type_init();
ff1f3280
SC
816
817 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
818 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
819 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
820
0420ba6a
JA
821 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
822 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
ff1f3280 823
5b7573ab
SC
824 ui->vbox = gtk_vbox_new(FALSE, 0);
825 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
04cc6b77 826
0420ba6a
JA
827 uimanager = gtk_ui_manager_new();
828 menu = get_menubar_menu(ui->window, uimanager);
829 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
830
c36f98d9
SC
831 /*
832 * Set up alignments for widgets at the top of ui,
833 * align top left, expand horizontally but not vertically
834 */
835 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
3ec62ec4 836 ui->topvbox = gtk_vbox_new(FALSE, 3);
c36f98d9 837 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
e164534f 838 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 839
3e47bd25 840 probe = gtk_frame_new("Job");
843ad237
JA
841 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
842 probe_frame = gtk_vbox_new(FALSE, 3);
843 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
844
845 probe_box = gtk_hbox_new(FALSE, 3);
846 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
843ad237
JA
847 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
848 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
849 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
850 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
851
3e47bd25
JA
852 probe_box = gtk_hbox_new(FALSE, 3);
853 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
854
855 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
856 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
857 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
858 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
3e47bd25
JA
859 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
860 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
861
862 probe_box = gtk_hbox_new(FALSE, 3);
863 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
864 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
865 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
807f9971
JA
866 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
867 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
3e47bd25 868
807f9971
JA
869 /*
870 * Only add this if we have a commit rate
871 */
872#if 0
3e47bd25
JA
873 probe_box = gtk_hbox_new(FALSE, 3);
874 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
875
876 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
877 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
878
3e47bd25
JA
879 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
880 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 881#endif
3e47bd25 882
736f2dff
SC
883 /*
884 * Add a text box for text op messages
885 */
886 ui->textview = gtk_text_view_new();
887 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
888 gtk_text_buffer_set_text(ui->text, "", -1);
889 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
890 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
891 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
892 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
893 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
894 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
e164534f
SC
895 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
896 TRUE, TRUE, 0);
736f2dff 897
c36f98d9
SC
898 /*
899 * Set up alignments for widgets at the bottom of ui,
900 * align bottom left, expand horizontally but not vertically
901 */
902 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
903 ui->buttonbox = gtk_hbox_new(FALSE, 0);
904 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
e164534f
SC
905 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
906 FALSE, FALSE, 0);
c36f98d9 907
f3074008 908 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
3ec62ec4
JA
909
910 /*
911 * Set up thread status progress bar
912 */
913 ui->thread_status_pb = gtk_progress_bar_new();
914 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 915 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
916 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
917
918
ff1f3280
SC
919 gtk_widget_show_all(ui->window);
920}
921
8232e285 922int main(int argc, char *argv[], char *envp[])
ff1f3280 923{
8232e285
SC
924 if (initialize_fio(envp))
925 return 1;
0420ba6a
JA
926 if (fio_init_options())
927 return 1;
a1820207 928
ff1f3280 929 init_ui(&argc, &argv, &ui);
5b7573ab 930
2839f0c6 931 gdk_threads_enter();
ff1f3280 932 gtk_main();
2839f0c6 933 gdk_threads_leave();
ff1f3280
SC
934 return 0;
935}