blob: 2d28ca8f1099058602a6a03680e79e9b6315e163 [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
Jens Axboec0187f32012-03-06 15:39:15 +01005 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01006 *
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
Stephen M. Cameron8232e282012-02-24 08:17:31 +010024#include <locale.h>
Stephen M. Cameron60f6b332012-02-24 08:17:32 +010025#include <malloc.h>
Jens Axboe6b79c802012-03-08 10:51:36 +010026#include <string.h>
Stephen M. Cameron8232e282012-02-24 08:17:31 +010027
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010028#include <glib.h>
Jens Axboe2fd3bb02012-03-07 08:07:39 +010029#include <cairo.h>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010030#include <gtk/gtk.h>
31
Stephen M. Cameron8232e282012-02-24 08:17:31 +010032#include "fio.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010033#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010034
Jens Axboe38634cb2012-03-13 12:26:41 +010035#define GFIO_MIME "text/fio"
36
Jens Axboe63a130b2012-03-06 20:08:59 +010037static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010038static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010039static unsigned int gfio_graph_limit = 100;
Jens Axboe63a130b2012-03-06 20:08:59 +010040
Jens Axboe6b79c802012-03-08 10:51:36 +010041static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010042
Stephen M. Cameronf3074002012-02-24 08:17:30 +010043#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
44
45typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
46
Jens Axboe3e47bd22012-02-29 13:45:02 +010047static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010048static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010049static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010050
51static struct button_spec {
52 const char *buttontext;
53 clickfunction f;
54 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010055 const int start_insensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010056} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010057#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010058#define SEND_BUTTON 1
59#define START_JOB_BUTTON 2
Jens Axboe3e47bd22012-02-29 13:45:02 +010060 { "Connect", connect_clicked, "Connect to host", 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010061 { "Send", send_clicked, "Send job description to host", 1 },
62 { "Start Job", start_job_clicked,
Jens Axboe2f99deb2012-03-09 14:37:29 +010063 "Start the current job on the server", 1 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010064};
65
Jens Axboe843ad232012-02-29 11:44:53 +010066struct probe_widget {
67 GtkWidget *hostname;
68 GtkWidget *os;
69 GtkWidget *arch;
70 GtkWidget *fio_ver;
71};
72
Jens Axboec80b74b2012-03-12 10:23:28 +010073struct multitext_widget {
74 GtkWidget *entry;
75 char **text;
76 unsigned int cur_text;
77 unsigned int max_text;
78};
79
Jens Axboe3e47bd22012-02-29 13:45:02 +010080struct eta_widget {
Jens Axboe3863d1a2012-03-09 17:39:05 +010081 GtkWidget *names;
Jens Axboec80b74b2012-03-12 10:23:28 +010082 struct multitext_widget iotype;
83 struct multitext_widget ioengine;
84 struct multitext_widget iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010085 GtkWidget *jobs;
86 GtkWidget *files;
87 GtkWidget *read_bw;
88 GtkWidget *read_iops;
89 GtkWidget *cr_bw;
90 GtkWidget *cr_iops;
91 GtkWidget *write_bw;
92 GtkWidget *write_iops;
93 GtkWidget *cw_bw;
94 GtkWidget *cw_iops;
95};
96
Jens Axboe2f99deb2012-03-09 14:37:29 +010097struct gfio_graphs {
98#define DRAWING_AREA_XDIM 1000
99#define DRAWING_AREA_YDIM 400
100 GtkWidget *drawing_area;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100101 struct graph *iops_graph;
102 struct graph *bandwidth_graph;
103};
104
105/*
106 * Main window widgets and data
107 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100108struct gui {
Jens Axboe02421e62012-03-12 12:05:50 +0100109 GtkUIManager *uimanager;
Jens Axboe38634cb2012-03-13 12:26:41 +0100110 GtkRecentManager *recentmanager;
111 GtkActionGroup *actiongroup;
112 guint recent_ui_id;
Jens Axboe02421e62012-03-12 12:05:50 +0100113 GtkWidget *menu;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100114 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100115 GtkWidget *vbox;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100116 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100117 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100118 GtkWidget *notebook;
119 GtkWidget *error_info_bar;
120 GtkWidget *error_label;
121 GtkListStore *log_model;
122 GtkWidget *log_tree;
123 GtkWidget *log_view;
124 struct gfio_graphs graphs;
125 struct probe_widget probe;
126 struct eta_widget eta;
127 pthread_t server_t;
128
Jens Axboea9eccde2012-03-09 14:59:42 +0100129 pthread_t t;
130 int handler_running;
131
Jens Axboe2f99deb2012-03-09 14:37:29 +0100132 struct flist_head list;
133} main_ui;
134
Jens Axboe85dd01e2012-03-12 14:33:16 +0100135enum {
136 GE_STATE_NEW = 1,
137 GE_STATE_CONNECTED,
138 GE_STATE_JOB_SENT,
139 GE_STATE_JOB_STARTED,
140 GE_STATE_JOB_RUNNING,
141 GE_STATE_JOB_DONE,
142};
143
Jens Axboe2f99deb2012-03-09 14:37:29 +0100144/*
145 * Notebook entry
146 */
147struct gui_entry {
148 struct flist_head list;
149 struct gui *ui;
150
151 GtkWidget *vbox;
Jens Axboec80b74b2012-03-12 10:23:28 +0100152 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100153 GtkWidget *thread_status_pb;
154 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100155 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Jens Axboe2f99deb2012-03-09 14:37:29 +0100156 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100157 GtkWidget *error_info_bar;
158 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100159 GtkWidget *results_notebook;
160 GtkWidget *results_window;
Jens Axboe17b97212012-03-14 20:17:57 +0100161 GtkUIManager *results_uimanager;
162 GtkWidget *results_vbox;
163 GtkWidget *results_menu;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100164 GtkListStore *log_model;
165 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100166 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100167 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100168 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100169 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100170 GtkWidget *page_label;
171 gint page_num;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100172 unsigned int state;
Jens Axboe0420ba62012-02-29 11:16:52 +0100173
Jens Axboeb9d2f302012-03-08 20:36:28 +0100174 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100175 int nr_job_files;
176 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100177};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100178
Jens Axboee0681f32012-03-06 12:14:42 +0100179struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100180 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100181 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100182 GtkWidget *results_widget;
183 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100184 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100185 unsigned int job_added;
186 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100187};
188
Jens Axboe9988ca72012-03-09 15:14:06 +0100189static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
190static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100191void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100192
Jens Axboe2f99deb2012-03-09 14:37:29 +0100193static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100194{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100195 struct graph *g;
196
197 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100198 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100199 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100200 graph_add_label(g, "Read IOPS");
201 graph_add_label(g, "Write IOPS");
202 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
203 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +0100204 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100205 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100206 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100207}
208
Jens Axboe2f99deb2012-03-09 14:37:29 +0100209static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100210{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100211 struct graph *g;
212
213 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100214 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100215 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100216 graph_add_label(g, "Read Bandwidth");
217 graph_add_label(g, "Write Bandwidth");
218 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
219 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100220 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100221 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100222 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100223 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100224}
225
Jens Axboe2f99deb2012-03-09 14:37:29 +0100226static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100227{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100228 g->iops_graph = setup_iops_graph();
229 g->bandwidth_graph = setup_bandwidth_graph();
230}
231
Jens Axboec80b74b2012-03-12 10:23:28 +0100232static void multitext_add_entry(struct multitext_widget *mt, const char *text)
233{
234 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
235 mt->text[mt->max_text] = strdup(text);
236 mt->max_text++;
237}
238
239static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
240{
241 if (index >= mt->max_text)
242 return;
Jens Axboeda185432012-03-12 11:05:46 +0100243 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100244 return;
245
246 mt->cur_text = index;
247 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
248}
249
250static void multitext_update_entry(struct multitext_widget *mt,
251 unsigned int index, const char *text)
252{
Jens Axboeda185432012-03-12 11:05:46 +0100253 if (!mt->text)
254 return;
255
Jens Axboec80b74b2012-03-12 10:23:28 +0100256 if (mt->text[index])
257 free(mt->text[index]);
258
259 mt->text[index] = strdup(text);
260 if (mt->cur_text == index)
261 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
262}
263
264static void multitext_free(struct multitext_widget *mt)
265{
266 int i;
267
268 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
269
270 for (i = 0; i < mt->max_text; i++) {
271 if (mt->text[i])
272 free(mt->text[i]);
273 }
274
275 free(mt->text);
276 mt->cur_text = -1;
277 mt->max_text = 0;
278}
279
Jens Axboe2f99deb2012-03-09 14:37:29 +0100280static void clear_ge_ui_info(struct gui_entry *ge)
281{
282 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
283 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
284 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
285 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100286#if 0
287 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100288 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100289#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100290 multitext_update_entry(&ge->eta.iotype, 0, "");
291 multitext_update_entry(&ge->eta.ioengine, 0, "");
292 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100293 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
294 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
295 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
296 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
297 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
298 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100299}
300
Jens Axboe3863d1a2012-03-09 17:39:05 +0100301static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
302{
303 GtkWidget *entry, *frame;
304
305 frame = gtk_frame_new(label);
306 entry = gtk_combo_box_new_text();
307 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
308 gtk_container_add(GTK_CONTAINER(frame), entry);
309
310 return entry;
311}
312
Jens Axboe3650a3c2012-03-05 14:09:03 +0100313static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
314{
315 GtkWidget *entry, *frame;
316
317 frame = gtk_frame_new(label);
318 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100319 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100320 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
321 gtk_container_add(GTK_CONTAINER(frame), entry);
322
323 return entry;
324}
325
326static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
327{
328 GtkWidget *label_widget;
329 GtkWidget *frame;
330
331 frame = gtk_frame_new(label);
332 label_widget = gtk_label_new(NULL);
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), label_widget);
335
336 return label_widget;
337}
338
339static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
340{
341 GtkWidget *button, *box;
342
343 box = gtk_hbox_new(FALSE, 3);
344 gtk_container_add(GTK_CONTAINER(hbox), box);
345
346 button = gtk_spin_button_new_with_range(min, max, 1.0);
347 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
348
349 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
350 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
351
352 return button;
353}
354
Jens Axboe3650a3c2012-03-05 14:09:03 +0100355static void label_set_int_value(GtkWidget *entry, unsigned int val)
356{
357 char tmp[80];
358
359 sprintf(tmp, "%u", val);
360 gtk_label_set_text(GTK_LABEL(entry), tmp);
361}
362
363static void entry_set_int_value(GtkWidget *entry, unsigned int val)
364{
365 char tmp[80];
366
367 sprintf(tmp, "%u", val);
368 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
369}
370
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100371static void show_info_dialog(struct gui *ui, const char *title,
372 const char *message)
373{
374 GtkWidget *dialog, *content, *label;
375
376 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
377 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
378 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
379
380 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
381 label = gtk_label_new(message);
382 gtk_container_add(GTK_CONTAINER(content), label);
383 gtk_widget_show_all(dialog);
384 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
385 gtk_dialog_run(GTK_DIALOG(dialog));
386 gtk_widget_destroy(dialog);
387}
388
Jens Axboe85dd01e2012-03-12 14:33:16 +0100389/*
390 * Update sensitivity of job buttons and job menu items, based on the
391 * state of the client.
392 */
393static void update_button_states(struct gui *ui, struct gui_entry *ge)
394{
395 unsigned int connect_state, send_state, start_state, edit_state;
396 const char *connect_str = NULL;
397 GtkWidget *w;
398
399 switch (ge->state) {
400 default: {
401 char tmp[80];
402
403 sprintf(tmp, "Bad client state: %u\n", ge->state);
404 show_info_dialog(ui, "Error", tmp);
405 /* fall through to new state */
406 }
407
408 case GE_STATE_NEW:
409 connect_state = 1;
410 edit_state = 0;
411 connect_str = "Connect";
412 send_state = 0;
413 start_state = 0;
414 break;
415 case GE_STATE_CONNECTED:
416 connect_state = 1;
417 edit_state = 0;
418 connect_str = "Disconnect";
419 send_state = 1;
420 start_state = 0;
421 break;
422 case GE_STATE_JOB_SENT:
423 connect_state = 1;
424 edit_state = 0;
425 connect_str = "Disconnect";
426 send_state = 0;
427 start_state = 1;
428 break;
429 case GE_STATE_JOB_STARTED:
430 connect_state = 1;
431 edit_state = 1;
432 connect_str = "Disconnect";
433 send_state = 0;
434 start_state = 1;
435 break;
436 case GE_STATE_JOB_RUNNING:
437 connect_state = 1;
438 edit_state = 0;
439 connect_str = "Disconnect";
440 send_state = 0;
441 start_state = 0;
442 break;
443 case GE_STATE_JOB_DONE:
444 connect_state = 1;
445 edit_state = 0;
446 connect_str = "Connect";
447 send_state = 0;
448 start_state = 0;
449 break;
450 }
451
452 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
453 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
454 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
455 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
456
457 /*
458 * So the below doesn't work at all, how to set those menu items
459 * invisibible...
460 */
461 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
Jens Axboe21ba9042012-03-13 10:58:59 +0100462 gtk_widget_set_sensitive(w, connect_state);
463 gtk_menu_item_set_label(GTK_MENU_ITEM(w), connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100464
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100465 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
Jens Axboe21ba9042012-03-13 10:58:59 +0100466 gtk_widget_set_sensitive(w, edit_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100467
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100468 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
Jens Axboe21ba9042012-03-13 10:58:59 +0100469 gtk_widget_set_sensitive(w, send_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100470
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100471 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
Jens Axboe21ba9042012-03-13 10:58:59 +0100472 gtk_widget_set_sensitive(w, start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100473}
474
475static void gfio_set_state(struct gui_entry *ge, unsigned int state)
476{
477 ge->state = state;
478 update_button_states(ge->ui, ge);
479}
480
Jens Axboea2697902012-03-05 16:43:49 +0100481#define ALIGN_LEFT 1
482#define ALIGN_RIGHT 2
483#define INVISIBLE 4
484#define UNSORTABLE 8
485
486GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
487{
488 GtkCellRenderer *renderer;
489 GtkTreeViewColumn *col;
490 double xalign = 0.0; /* left as default */
491 PangoAlignment align;
492 gboolean visible;
493
494 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
495 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
496 PANGO_ALIGN_CENTER;
497 visible = !(flags & INVISIBLE);
498
499 renderer = gtk_cell_renderer_text_new();
500 col = gtk_tree_view_column_new();
501
502 gtk_tree_view_column_set_title(col, title);
503 if (!(flags & UNSORTABLE))
504 gtk_tree_view_column_set_sort_column_id(col, index);
505 gtk_tree_view_column_set_resizable(col, TRUE);
506 gtk_tree_view_column_pack_start(col, renderer, TRUE);
507 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
508 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
509 switch (align) {
510 case PANGO_ALIGN_LEFT:
511 xalign = 0.0;
512 break;
513 case PANGO_ALIGN_CENTER:
514 xalign = 0.5;
515 break;
516 case PANGO_ALIGN_RIGHT:
517 xalign = 1.0;
518 break;
519 }
520 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
521 gtk_tree_view_column_set_visible(col, visible);
522 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
523 return col;
524}
525
Jens Axboe9b260bd2012-03-06 11:02:52 +0100526static void gfio_ui_setup_log(struct gui *ui)
527{
528 GtkTreeSelection *selection;
529 GtkListStore *model;
530 GtkWidget *tree_view;
531
532 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
533
534 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
535 gtk_widget_set_can_focus(tree_view, FALSE);
536
537 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
538 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100539 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
540 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100541
542 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
543 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
544 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100545 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100546
547 ui->log_model = model;
548 ui->log_tree = tree_view;
549}
550
Jens Axboea2697902012-03-05 16:43:49 +0100551static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
552 fio_fp64_t *plist,
553 unsigned int len,
554 const char *base,
555 unsigned int scale)
556{
557 GType types[FIO_IO_U_LIST_MAX_LEN];
558 GtkWidget *tree_view;
559 GtkTreeSelection *selection;
560 GtkListStore *model;
561 GtkTreeIter iter;
562 int i;
563
564 for (i = 0; i < len; i++)
565 types[i] = G_TYPE_INT;
566
567 model = gtk_list_store_newv(len, types);
568
569 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
570 gtk_widget_set_can_focus(tree_view, FALSE);
571
Jens Axboe661f7412012-03-06 13:55:45 +0100572 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
573 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
574
Jens Axboea2697902012-03-05 16:43:49 +0100575 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
576 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
577
578 for (i = 0; i < len; i++) {
579 char fbuf[8];
580
581 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
582 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
583 }
584
585 gtk_list_store_append(model, &iter);
586
Jens Axboee0681f32012-03-06 12:14:42 +0100587 for (i = 0; i < len; i++) {
588 if (scale)
589 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100590 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100591 }
Jens Axboea2697902012-03-05 16:43:49 +0100592
593 return tree_view;
594}
595
596static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
597 int ddir)
598{
599 unsigned int *io_u_plat = ts->io_u_plat[ddir];
600 unsigned long nr = ts->clat_stat[ddir].samples;
601 fio_fp64_t *plist = ts->percentile_list;
602 unsigned int *ovals, len, minv, maxv, scale_down;
603 const char *base;
604 GtkWidget *tree_view, *frame, *hbox;
605 char tmp[64];
606
607 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
608 if (!len)
609 goto out;
610
611 /*
612 * We default to usecs, but if the value range is such that we
613 * should scale down to msecs, do that.
614 */
615 if (minv > 2000 && maxv > 99999) {
616 scale_down = 1;
617 base = "msec";
618 } else {
619 scale_down = 0;
620 base = "usec";
621 }
622
623 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
624
625 sprintf(tmp, "Completion percentiles (%s)", base);
626 frame = gtk_frame_new(tmp);
627 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
628
629 hbox = gtk_hbox_new(FALSE, 3);
630 gtk_container_add(GTK_CONTAINER(frame), hbox);
631
632 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
633out:
634 if (ovals)
635 free(ovals);
636}
637
Jens Axboe3650a3c2012-03-05 14:09:03 +0100638static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
639 unsigned long max, double mean, double dev)
640{
641 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100642 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100643 char *minp, *maxp;
644 char tmp[64];
645
646 if (!usec_to_msec(&min, &max, &mean, &dev))
647 base = "(msec)";
648
649 minp = num2str(min, 6, 1, 0);
650 maxp = num2str(max, 6, 1, 0);
651
Jens Axboe3650a3c2012-03-05 14:09:03 +0100652 sprintf(tmp, "%s %s", name, base);
653 frame = gtk_frame_new(tmp);
654 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
655
Jens Axboe3650a3c2012-03-05 14:09:03 +0100656 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100657 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100658
659 label = new_info_label_in_frame(hbox, "Minimum");
660 gtk_label_set_text(GTK_LABEL(label), minp);
661 label = new_info_label_in_frame(hbox, "Maximum");
662 gtk_label_set_text(GTK_LABEL(label), maxp);
663 label = new_info_label_in_frame(hbox, "Average");
664 sprintf(tmp, "%5.02f", mean);
665 gtk_label_set_text(GTK_LABEL(label), tmp);
666 label = new_info_label_in_frame(hbox, "Standard deviation");
667 sprintf(tmp, "%5.02f", dev);
668 gtk_label_set_text(GTK_LABEL(label), tmp);
669
670 free(minp);
671 free(maxp);
672
673}
674
Jens Axboeca850992012-03-05 20:04:43 +0100675#define GFIO_CLAT 1
676#define GFIO_SLAT 2
677#define GFIO_LAT 4
678
Jens Axboe3650a3c2012-03-05 14:09:03 +0100679static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
680 struct thread_stat *ts, int ddir)
681{
682 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100683 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100684 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100685 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100686 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100687 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100688 char *io_p, *bw_p, *iops_p;
689 int i2p;
690
691 if (!ts->runtime[ddir])
692 return;
693
694 i2p = is_power_of_2(rs->kb_base);
695 runt = ts->runtime[ddir];
696
697 bw = (1000 * ts->io_bytes[ddir]) / runt;
698 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
699 bw_p = num2str(bw, 6, 1, i2p);
700
701 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
702 iops_p = num2str(iops, 6, 1, 0);
703
704 box = gtk_hbox_new(FALSE, 3);
705 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
706
707 frame = gtk_frame_new(ddir_label[ddir]);
708 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
709
Jens Axboe0b761302012-03-05 20:44:11 +0100710 main_vbox = gtk_vbox_new(FALSE, 3);
711 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100712
713 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100714 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100715
716 label = new_info_label_in_frame(box, "IO");
717 gtk_label_set_text(GTK_LABEL(label), io_p);
718 label = new_info_label_in_frame(box, "Bandwidth");
719 gtk_label_set_text(GTK_LABEL(label), bw_p);
720 label = new_info_label_in_frame(box, "IOPS");
721 gtk_label_set_text(GTK_LABEL(label), iops_p);
722 label = new_info_label_in_frame(box, "Runtime (msec)");
723 label_set_int_value(label, ts->runtime[ddir]);
724
Jens Axboee0681f32012-03-06 12:14:42 +0100725 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100726 double p_of_agg = 100.0;
727 const char *bw_str = "KB";
728 char tmp[32];
729
730 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100731 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100732 if (p_of_agg > 100.0)
733 p_of_agg = 100.0;
734 }
735
Jens Axboee0681f32012-03-06 12:14:42 +0100736 if (mean[0] > 999999.9) {
737 min[0] /= 1000.0;
738 max[0] /= 1000.0;
739 mean[0] /= 1000.0;
740 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100741 bw_str = "MB";
742 }
743
Jens Axboe0b761302012-03-05 20:44:11 +0100744 sprintf(tmp, "Bandwidth (%s)", bw_str);
745 frame = gtk_frame_new(tmp);
746 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100747
Jens Axboe0b761302012-03-05 20:44:11 +0100748 box = gtk_hbox_new(FALSE, 3);
749 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100750
Jens Axboe0b761302012-03-05 20:44:11 +0100751 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100752 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100753 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100754 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100755 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100756 sprintf(tmp, "%3.2f%%", p_of_agg);
757 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100758 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100759 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100760 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100761 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100762 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100763 gtk_label_set_text(GTK_LABEL(label), tmp);
764 }
765
Jens Axboee0681f32012-03-06 12:14:42 +0100766 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100767 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100768 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100769 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100770 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100771 flags |= GFIO_LAT;
772
773 if (flags) {
774 frame = gtk_frame_new("Latency");
775 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
776
777 vbox = gtk_vbox_new(FALSE, 3);
778 gtk_container_add(GTK_CONTAINER(frame), vbox);
779
780 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100781 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100782 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100783 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100784 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100785 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100786 }
787
788 if (ts->clat_percentiles)
789 gfio_show_clat_percentiles(main_vbox, ts, ddir);
790
791
Jens Axboe3650a3c2012-03-05 14:09:03 +0100792 free(io_p);
793 free(bw_p);
794 free(iops_p);
795}
796
Jens Axboee5bd1342012-03-05 21:38:12 +0100797static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
798 const char **labels)
799{
800 GtkWidget *tree_view;
801 GtkTreeSelection *selection;
802 GtkListStore *model;
803 GtkTreeIter iter;
804 GType *types;
805 int i, skipped;
806
807 /*
808 * Check if all are empty, in which case don't bother
809 */
810 for (i = 0, skipped = 0; i < num; i++)
811 if (lat[i] <= 0.0)
812 skipped++;
813
814 if (skipped == num)
815 return NULL;
816
817 types = malloc(num * sizeof(GType));
818
819 for (i = 0; i < num; i++)
820 types[i] = G_TYPE_STRING;
821
822 model = gtk_list_store_newv(num, types);
823 free(types);
824 types = NULL;
825
826 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
827 gtk_widget_set_can_focus(tree_view, FALSE);
828
Jens Axboe661f7412012-03-06 13:55:45 +0100829 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
830 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
831
Jens Axboee5bd1342012-03-05 21:38:12 +0100832 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
833 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
834
835 for (i = 0; i < num; i++)
836 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
837
838 gtk_list_store_append(model, &iter);
839
840 for (i = 0; i < num; i++) {
841 char fbuf[32];
842
843 if (lat[i] <= 0.0)
844 sprintf(fbuf, "0.00");
845 else
846 sprintf(fbuf, "%3.2f%%", lat[i]);
847
848 gtk_list_store_set(model, &iter, i, fbuf, -1);
849 }
850
851 return tree_view;
852}
853
854static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
855{
856 GtkWidget *box, *frame, *tree_view;
857 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
858 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
859 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
860 "250", "500", "750", "1000", };
861 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
862 "250", "500", "750", "1000", "2000",
863 ">= 2000", };
864
865 stat_calc_lat_u(ts, io_u_lat_u);
866 stat_calc_lat_m(ts, io_u_lat_m);
867
868 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
869 if (tree_view) {
870 frame = gtk_frame_new("Latency buckets (usec)");
871 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
872
873 box = gtk_hbox_new(FALSE, 3);
874 gtk_container_add(GTK_CONTAINER(frame), box);
875 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
876 }
877
878 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
879 if (tree_view) {
880 frame = gtk_frame_new("Latency buckets (msec)");
881 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
882
883 box = gtk_hbox_new(FALSE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), box);
885 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
886 }
887}
888
Jens Axboe2e331012012-03-05 22:07:54 +0100889static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
890{
891 GtkWidget *box, *frame, *entry;
892 double usr_cpu, sys_cpu;
893 unsigned long runtime;
894 char tmp[32];
895
896 runtime = ts->total_run_time;
897 if (runtime) {
898 double runt = (double) runtime;
899
900 usr_cpu = (double) ts->usr_time * 100 / runt;
901 sys_cpu = (double) ts->sys_time * 100 / runt;
902 } else {
903 usr_cpu = 0;
904 sys_cpu = 0;
905 }
906
907 frame = gtk_frame_new("OS resources");
908 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
909
910 box = gtk_hbox_new(FALSE, 3);
911 gtk_container_add(GTK_CONTAINER(frame), box);
912
913 entry = new_info_entry_in_frame(box, "User CPU");
914 sprintf(tmp, "%3.2f%%", usr_cpu);
915 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
916 entry = new_info_entry_in_frame(box, "System CPU");
917 sprintf(tmp, "%3.2f%%", sys_cpu);
918 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
919 entry = new_info_entry_in_frame(box, "Context switches");
920 entry_set_int_value(entry, ts->ctx);
921 entry = new_info_entry_in_frame(box, "Major faults");
922 entry_set_int_value(entry, ts->majf);
923 entry = new_info_entry_in_frame(box, "Minor faults");
924 entry_set_int_value(entry, ts->minf);
925}
Jens Axboe19998db2012-03-06 09:17:59 +0100926static void gfio_add_sc_depths_tree(GtkListStore *model,
927 struct thread_stat *ts, unsigned int len,
928 int submit)
929{
930 double io_u_dist[FIO_IO_U_MAP_NR];
931 GtkTreeIter iter;
932 /* Bits 0, and 3-8 */
933 const int add_mask = 0x1f9;
934 int i, j;
935
936 if (submit)
937 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
938 else
939 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
940
941 gtk_list_store_append(model, &iter);
942
943 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
944
945 for (i = 1, j = 0; i < len; i++) {
946 char fbuf[32];
947
948 if (!(add_mask & (1UL << (i - 1))))
949 sprintf(fbuf, "0.0%%");
950 else {
951 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
952 j++;
953 }
954
955 gtk_list_store_set(model, &iter, i, fbuf, -1);
956 }
957
958}
959
960static void gfio_add_total_depths_tree(GtkListStore *model,
961 struct thread_stat *ts, unsigned int len)
962{
963 double io_u_dist[FIO_IO_U_MAP_NR];
964 GtkTreeIter iter;
965 /* Bits 1-6, and 8 */
966 const int add_mask = 0x17e;
967 int i, j;
968
969 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
970
971 gtk_list_store_append(model, &iter);
972
973 gtk_list_store_set(model, &iter, 0, "Total", -1);
974
975 for (i = 1, j = 0; i < len; i++) {
976 char fbuf[32];
977
978 if (!(add_mask & (1UL << (i - 1))))
979 sprintf(fbuf, "0.0%%");
980 else {
981 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
982 j++;
983 }
984
985 gtk_list_store_set(model, &iter, i, fbuf, -1);
986 }
987
988}
Jens Axboe2e331012012-03-05 22:07:54 +0100989
990static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
991{
Jens Axboe2e331012012-03-05 22:07:54 +0100992 GtkWidget *frame, *box, *tree_view;
993 GtkTreeSelection *selection;
994 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100995 GType types[FIO_IO_U_MAP_NR + 1];
996 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100997#define NR_LABELS 10
998 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100999
1000 frame = gtk_frame_new("IO depths");
1001 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1002
1003 box = gtk_hbox_new(FALSE, 3);
1004 gtk_container_add(GTK_CONTAINER(frame), box);
1005
Jens Axboe19998db2012-03-06 09:17:59 +01001006 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001007 types[i] = G_TYPE_STRING;
1008
Jens Axboe19998db2012-03-06 09:17:59 +01001009 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001010
1011 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1012 gtk_widget_set_can_focus(tree_view, FALSE);
1013
Jens Axboe661f7412012-03-06 13:55:45 +01001014 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1015 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1016
Jens Axboe2e331012012-03-05 22:07:54 +01001017 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1018 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1019
Jens Axboe19998db2012-03-06 09:17:59 +01001020 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001021 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1022
Jens Axboe19998db2012-03-06 09:17:59 +01001023 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1024 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1025 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001026
1027 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1028}
1029
Jens Axboef9d40b42012-03-06 09:52:49 +01001030static gboolean results_window_delete(GtkWidget *w, gpointer data)
1031{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001032 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001033
1034 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001035 ge->results_window = NULL;
1036 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001037 return TRUE;
1038}
1039
Jens Axboe17b97212012-03-14 20:17:57 +01001040static void results_close(GtkWidget *w, gpointer *data)
1041{
1042 struct gui_entry *ge = (struct gui_entry *) data;
1043
1044 gtk_widget_destroy(ge->results_window);
1045}
1046
1047static GtkActionEntry results_menu_items[] = {
1048 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1049 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1050 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1051};
1052static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1053
1054static const gchar *results_ui_string = " \
1055 <ui> \
1056 <menubar name=\"MainMenu\"> \
1057 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1058 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1059 </menu> \
1060 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1061 </menu>\
1062 </menubar> \
1063 </ui> \
1064";
1065
1066static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1067{
1068 GtkActionGroup *action_group;
1069 GtkWidget *widget;
1070 GError *error = 0;
1071
1072 ge->results_uimanager = gtk_ui_manager_new();
1073
1074 action_group = gtk_action_group_new("ResultsMenu");
1075 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1076
1077 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1078 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1079
1080 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1081
1082 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1083 return widget;
1084}
1085
Jens Axboe2f99deb2012-03-09 14:37:29 +01001086static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001087{
Jens Axboe17b97212012-03-14 20:17:57 +01001088 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001089
Jens Axboe2f99deb2012-03-09 14:37:29 +01001090 if (ge->results_window)
1091 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001092
1093 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1094 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001095 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001096 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1097 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001098
Jens Axboe17b97212012-03-14 20:17:57 +01001099 vbox = gtk_vbox_new(FALSE, 0);
1100 gtk_container_add(GTK_CONTAINER(win), vbox);
1101
1102 ge->results_menu = get_results_menubar(win, ge);
1103 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1104
Jens Axboef9d40b42012-03-06 09:52:49 +01001105 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001106 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1107 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001108 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001109
Jens Axboe2f99deb2012-03-09 14:37:29 +01001110 ge->results_window = win;
1111 ge->results_notebook = notebook;
1112 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001113}
1114
Jens Axboe3650a3c2012-03-05 14:09:03 +01001115static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1116 struct group_run_stats *rs)
1117{
Jens Axboeb01329d2012-03-07 20:31:28 +01001118 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +01001119 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001120
1121 gdk_threads_enter();
1122
Jens Axboe2f99deb2012-03-09 14:37:29 +01001123 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001124
Jens Axboeb01329d2012-03-07 20:31:28 +01001125 scroll = gtk_scrolled_window_new(NULL, NULL);
1126 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1127 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1128
Jens Axboe3650a3c2012-03-05 14:09:03 +01001129 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001130
Jens Axboeb01329d2012-03-07 20:31:28 +01001131 box = gtk_hbox_new(FALSE, 0);
1132 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001133
Jens Axboeb01329d2012-03-07 20:31:28 +01001134 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1135
1136 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +01001137
Jens Axboee0681f32012-03-06 12:14:42 +01001138 gc->results_widget = vbox;
1139
Jens Axboe3650a3c2012-03-05 14:09:03 +01001140 entry = new_info_entry_in_frame(box, "Name");
1141 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1142 if (strlen(ts->description)) {
1143 entry = new_info_entry_in_frame(box, "Description");
1144 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1145 }
1146 entry = new_info_entry_in_frame(box, "Group ID");
1147 entry_set_int_value(entry, ts->groupid);
1148 entry = new_info_entry_in_frame(box, "Jobs");
1149 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +01001150 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +01001151 entry_set_int_value(entry, ts->error);
1152 entry = new_info_entry_in_frame(box, "PID");
1153 entry_set_int_value(entry, ts->pid);
1154
1155 if (ts->io_bytes[DDIR_READ])
1156 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1157 if (ts->io_bytes[DDIR_WRITE])
1158 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1159
Jens Axboee5bd1342012-03-05 21:38:12 +01001160 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +01001161 gfio_show_cpu_usage(vbox, ts);
1162 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +01001163
Jens Axboe2f99deb2012-03-09 14:37:29 +01001164 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001165 gdk_threads_leave();
1166}
1167
Jens Axboe084d1c62012-03-03 20:28:07 +01001168static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001169{
Jens Axboe9b260bd2012-03-06 11:02:52 +01001170 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001171 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001172 GtkTreeIter iter;
1173 struct tm *tm;
1174 time_t sec;
1175 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001176
Jens Axboe9b260bd2012-03-06 11:02:52 +01001177 sec = p->log_sec;
1178 tm = localtime(&sec);
1179 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1180 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1181
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001182 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +01001183
Jens Axboe2f99deb2012-03-09 14:37:29 +01001184 gtk_list_store_append(ui->log_model, &iter);
1185 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1186 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1187 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1188 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001189
Jens Axboe6b79c802012-03-08 10:51:36 +01001190 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001191 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +01001192
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001193 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001194}
1195
1196static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1197{
Jens Axboee0681f32012-03-06 12:14:42 +01001198 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1199 struct gfio_client *gc = client->client_data;
1200 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +01001201 double util;
1202 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001203
Jens Axboe0050e5f2012-03-06 09:23:27 +01001204 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +01001205
Jens Axboe45dcb2e2012-03-07 16:16:50 +01001206 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +01001207 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +01001208
1209 if (!gc->disk_util_frame) {
1210 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1211 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1212 }
1213
1214 vbox = gtk_vbox_new(FALSE, 3);
1215 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1216
1217 frame = gtk_frame_new((char *) p->dus.name);
1218 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1219
1220 box = gtk_vbox_new(FALSE, 3);
1221 gtk_container_add(GTK_CONTAINER(frame), box);
1222
1223 frame = gtk_frame_new("Read");
1224 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1225 vbox = gtk_hbox_new(TRUE, 3);
1226 gtk_container_add(GTK_CONTAINER(frame), vbox);
1227 entry = new_info_entry_in_frame(vbox, "IOs");
1228 entry_set_int_value(entry, p->dus.ios[0]);
1229 entry = new_info_entry_in_frame(vbox, "Merges");
1230 entry_set_int_value(entry, p->dus.merges[0]);
1231 entry = new_info_entry_in_frame(vbox, "Sectors");
1232 entry_set_int_value(entry, p->dus.sectors[0]);
1233 entry = new_info_entry_in_frame(vbox, "Ticks");
1234 entry_set_int_value(entry, p->dus.ticks[0]);
1235
1236 frame = gtk_frame_new("Write");
1237 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1238 vbox = gtk_hbox_new(TRUE, 3);
1239 gtk_container_add(GTK_CONTAINER(frame), vbox);
1240 entry = new_info_entry_in_frame(vbox, "IOs");
1241 entry_set_int_value(entry, p->dus.ios[1]);
1242 entry = new_info_entry_in_frame(vbox, "Merges");
1243 entry_set_int_value(entry, p->dus.merges[1]);
1244 entry = new_info_entry_in_frame(vbox, "Sectors");
1245 entry_set_int_value(entry, p->dus.sectors[1]);
1246 entry = new_info_entry_in_frame(vbox, "Ticks");
1247 entry_set_int_value(entry, p->dus.ticks[1]);
1248
1249 frame = gtk_frame_new("Shared");
1250 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1251 vbox = gtk_hbox_new(TRUE, 3);
1252 gtk_container_add(GTK_CONTAINER(frame), vbox);
1253 entry = new_info_entry_in_frame(vbox, "IO ticks");
1254 entry_set_int_value(entry, p->dus.io_ticks);
1255 entry = new_info_entry_in_frame(vbox, "Time in queue");
1256 entry_set_int_value(entry, p->dus.time_in_queue);
1257
Jens Axboe604cfe32012-03-07 19:51:36 +01001258 util = 0.0;
1259 if (p->dus.msec)
1260 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1261 if (util > 100.0)
1262 util = 100.0;
1263
1264 sprintf(tmp, "%3.2f%%", util);
1265 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1266 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1267
Jens Axboee0681f32012-03-06 12:14:42 +01001268 gtk_widget_show_all(gc->results_widget);
1269out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001270 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001271}
1272
Jens Axboe3650a3c2012-03-05 14:09:03 +01001273extern int sum_stat_clients;
1274extern struct thread_stat client_ts;
1275extern struct group_run_stats client_gs;
1276
1277static int sum_stat_nr;
1278
Jens Axboe89e5fad2012-03-05 09:21:12 +01001279static void gfio_thread_status_op(struct fio_client *client,
1280 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001281{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001282 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1283
1284 gfio_display_ts(client, &p->ts, &p->rs);
1285
1286 if (sum_stat_clients == 1)
1287 return;
1288
1289 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1290 sum_group_stats(&client_gs, &p->rs);
1291
1292 client_ts.members++;
1293 client_ts.groupid = p->ts.groupid;
1294
1295 if (++sum_stat_nr == sum_stat_clients) {
1296 strcpy(client_ts.name, "All clients");
1297 gfio_display_ts(client, &client_ts, &client_gs);
1298 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001299}
1300
Jens Axboe89e5fad2012-03-05 09:21:12 +01001301static void gfio_group_stats_op(struct fio_client *client,
1302 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001303{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001304 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001305}
1306
Jens Axboe2f99deb2012-03-09 14:37:29 +01001307static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1308 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001309{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001310 struct gfio_graphs *g = data;
1311
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001312 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1313 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1314 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1315 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001316 return TRUE;
1317}
1318
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001319static void draw_graph(struct graph *g, cairo_t *cr)
1320{
1321 line_graph_draw(g, cr);
1322 cairo_stroke(cr);
1323}
1324
Jens Axboe93e2db22012-03-13 09:45:22 +01001325static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1326 gboolean keyboard_mode, GtkTooltip *tooltip,
1327 gpointer data)
1328{
1329 struct gfio_graphs *g = data;
1330 const char *text = NULL;
1331
1332 if (graph_contains_xy(g->iops_graph, x, y))
1333 text = graph_find_tooltip(g->iops_graph, x, y);
1334 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1335 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1336
1337 if (text) {
1338 gtk_tooltip_set_text(tooltip, text);
1339 return TRUE;
1340 }
1341
1342 return FALSE;
1343}
1344
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001345static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1346{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001347 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001348 cairo_t *cr;
1349
1350 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001351
1352 if (graph_has_tooltips(g->iops_graph) ||
1353 graph_has_tooltips(g->bandwidth_graph)) {
1354 g_object_set(w, "has-tooltip", TRUE, NULL);
1355 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1356 }
1357
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001358 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001359 draw_graph(g->iops_graph, cr);
1360 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001361 cairo_destroy(cr);
1362
1363 return FALSE;
1364}
1365
Jens Axboe2f99deb2012-03-09 14:37:29 +01001366/*
1367 * Client specific ETA
1368 */
1369static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001370{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001371 struct gfio_client *gc = client->client_data;
1372 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001373 static int eta_good;
1374 char eta_str[128];
1375 char output[256];
1376 char tmp[32];
1377 double perc = 0.0;
1378 int i2p = 0;
1379
Jens Axboe0050e5f2012-03-06 09:23:27 +01001380 gdk_threads_enter();
1381
Jens Axboe3e47bd22012-02-29 13:45:02 +01001382 eta_str[0] = '\0';
1383 output[0] = '\0';
1384
1385 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1386 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1387 eta_to_str(eta_str, je->eta_sec);
1388 }
1389
1390 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001391 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001392 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001393 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001394
1395#if 0
1396 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1397 if (je->m_rate || je->t_rate) {
1398 char *tr, *mr;
1399
1400 mr = num2str(je->m_rate, 4, 0, i2p);
1401 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001402 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001403 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1404 free(tr);
1405 free(mr);
1406 } else if (je->m_iops || je->t_iops)
1407 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001408
Jens Axboe2f99deb2012-03-09 14:37:29 +01001409 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1410 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1411 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1412 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001413#endif
1414
1415 if (je->eta_sec != INT_MAX && je->nr_running) {
1416 char *iops_str[2];
1417 char *rate_str[2];
1418
1419 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1420 strcpy(output, "-.-% done");
1421 else {
1422 eta_good = 1;
1423 perc *= 100.0;
1424 sprintf(output, "%3.1f%% done", perc);
1425 }
1426
1427 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1428 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1429
1430 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1431 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1432
Jens Axboe2f99deb2012-03-09 14:37:29 +01001433 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1434 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1435 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1436 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001437
Jens Axboe93e2db22012-03-13 09:45:22 +01001438 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1439 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1440 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1441 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001442
1443 free(rate_str[0]);
1444 free(rate_str[1]);
1445 free(iops_str[0]);
1446 free(iops_str[1]);
1447 }
1448
1449 if (eta_str[0]) {
1450 char *dst = output + strlen(output);
1451
1452 sprintf(dst, " - %s", eta_str);
1453 }
1454
Jens Axboe9988ca72012-03-09 15:14:06 +01001455 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001456 gdk_threads_leave();
1457}
1458
1459/*
1460 * Update ETA in main window for all clients
1461 */
1462static void gfio_update_all_eta(struct jobs_eta *je)
1463{
1464 struct gui *ui = &main_ui;
1465 static int eta_good;
1466 char eta_str[128];
1467 char output[256];
1468 double perc = 0.0;
1469 int i2p = 0;
1470
1471 gdk_threads_enter();
1472
1473 eta_str[0] = '\0';
1474 output[0] = '\0';
1475
1476 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1477 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1478 eta_to_str(eta_str, je->eta_sec);
1479 }
1480
1481#if 0
1482 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1483 if (je->m_rate || je->t_rate) {
1484 char *tr, *mr;
1485
1486 mr = num2str(je->m_rate, 4, 0, i2p);
1487 tr = num2str(je->t_rate, 4, 0, i2p);
1488 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1489 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1490 free(tr);
1491 free(mr);
1492 } else if (je->m_iops || je->t_iops)
1493 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1494
1495 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1496 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1497 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1498 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1499#endif
1500
Jens Axboe3863d1a2012-03-09 17:39:05 +01001501 entry_set_int_value(ui->eta.jobs, je->nr_running);
1502
Jens Axboe2f99deb2012-03-09 14:37:29 +01001503 if (je->eta_sec != INT_MAX && je->nr_running) {
1504 char *iops_str[2];
1505 char *rate_str[2];
1506
1507 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1508 strcpy(output, "-.-% done");
1509 else {
1510 eta_good = 1;
1511 perc *= 100.0;
1512 sprintf(output, "%3.1f%% done", perc);
1513 }
1514
1515 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1516 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1517
1518 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1519 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1520
1521 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1522 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1523 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1524 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1525
Jens Axboe93e2db22012-03-13 09:45:22 +01001526 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1527 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1528 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1529 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001530
Jens Axboe3e47bd22012-02-29 13:45:02 +01001531 free(rate_str[0]);
1532 free(rate_str[1]);
1533 free(iops_str[0]);
1534 free(iops_str[1]);
1535 }
1536
1537 if (eta_str[0]) {
1538 char *dst = output + strlen(output);
1539
1540 sprintf(dst, " - %s", eta_str);
1541 }
1542
Jens Axboe9988ca72012-03-09 15:14:06 +01001543 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001544 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001545}
1546
Stephen M. Camerona1820202012-02-24 08:17:31 +01001547static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1548{
Jens Axboe843ad232012-02-29 11:44:53 +01001549 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001550 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001551 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001552 const char *os, *arch;
1553 char buf[64];
1554
1555 os = fio_get_os_string(probe->os);
1556 if (!os)
1557 os = "unknown";
1558
1559 arch = fio_get_arch_string(probe->arch);
1560 if (!arch)
1561 os = "unknown";
1562
1563 if (!client->name)
1564 client->name = strdup((char *) probe->hostname);
1565
Jens Axboe0050e5f2012-03-06 09:23:27 +01001566 gdk_threads_enter();
1567
Jens Axboe2f99deb2012-03-09 14:37:29 +01001568 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1569 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1570 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001571 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001572 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001573
Jens Axboe85dd01e2012-03-12 14:33:16 +01001574 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001575
1576 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001577}
1578
Jens Axboe9988ca72012-03-09 15:14:06 +01001579static void gfio_update_thread_status(struct gui_entry *ge,
1580 char *status_message, double perc)
1581{
1582 static char message[100];
1583 const char *m = message;
1584
1585 strncpy(message, status_message, sizeof(message) - 1);
1586 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1587 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1588 gtk_widget_queue_draw(main_ui.window);
1589}
1590
1591static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001592{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001593 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001594 static char message[100];
1595 const char *m = message;
1596
1597 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001598 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1599 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1600 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001601}
1602
Jens Axboe35c0ba72012-03-14 10:56:40 +01001603static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001604{
Jens Axboee0681f32012-03-06 12:14:42 +01001605 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001606
Jens Axboe0050e5f2012-03-06 09:23:27 +01001607 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001608 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001609 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001610}
1611
Jens Axboe807f9972012-03-02 10:25:24 +01001612static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1613{
1614 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001615 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001616 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001617 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001618 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001619
Jens Axboedcaeb602012-03-08 19:45:37 +01001620 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001621
Jens Axboe0050e5f2012-03-06 09:23:27 +01001622 gdk_threads_enter();
1623
Jens Axboe2f99deb2012-03-09 14:37:29 +01001624 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1625
Jens Axboe3863d1a2012-03-09 17:39:05 +01001626 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1627 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1628
Jens Axboec80b74b2012-03-12 10:23:28 +01001629 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1630 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001631
Jens Axboedcaeb602012-03-08 19:45:37 +01001632 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001633 multitext_add_entry(&ge->eta.iodepth, tmp);
1634
1635 multitext_set_entry(&ge->eta.iotype, 0);
1636 multitext_set_entry(&ge->eta.ioengine, 0);
1637 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001638
Jens Axboedcaeb602012-03-08 19:45:37 +01001639 gc->job_added++;
1640
Jens Axboe85dd01e2012-03-12 14:33:16 +01001641 gfio_set_state(ge, GE_STATE_JOB_SENT);
1642
Jens Axboe0050e5f2012-03-06 09:23:27 +01001643 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001644}
1645
Jens Axboeed727a42012-03-02 12:14:40 +01001646static void gfio_client_timed_out(struct fio_client *client)
1647{
Jens Axboee0681f32012-03-06 12:14:42 +01001648 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001649 char buf[256];
1650
1651 gdk_threads_enter();
1652
Jens Axboe85dd01e2012-03-12 14:33:16 +01001653 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001654 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001655
1656 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001657 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001658
1659 gdk_threads_leave();
1660}
1661
Jens Axboe6b79c802012-03-08 10:51:36 +01001662static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1663{
1664 struct gfio_client *gc = client->client_data;
1665
1666 gdk_threads_enter();
1667
Jens Axboe85dd01e2012-03-12 14:33:16 +01001668 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001669
1670 if (gc->err_entry)
1671 entry_set_int_value(gc->err_entry, client->error);
1672
1673 gdk_threads_leave();
1674}
1675
Jens Axboe85dd01e2012-03-12 14:33:16 +01001676static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1677{
1678 struct gfio_client *gc = client->client_data;
1679
1680 gdk_threads_enter();
1681 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1682 gdk_threads_leave();
1683}
1684
1685static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1686{
1687 struct gfio_client *gc = client->client_data;
1688
1689 gdk_threads_enter();
1690 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1691 gdk_threads_leave();
1692}
1693
Jens Axboe1b427252012-03-14 15:03:03 +01001694static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1695{
1696 free(pdu);
1697}
1698
Stephen M. Camerona1820202012-02-24 08:17:31 +01001699struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001700 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001701 .disk_util = gfio_disk_util_op,
1702 .thread_status = gfio_thread_status_op,
1703 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001704 .jobs_eta = gfio_update_client_eta,
1705 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001706 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001707 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001708 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001709 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001710 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001711 .start = gfio_client_start,
1712 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001713 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001714 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001715 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001716 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001717};
1718
Jens Axboe0fd18982012-03-14 10:34:48 +01001719/*
1720 * FIXME: need more handling here
1721 */
1722static void ge_destroy(struct gui_entry *ge)
1723{
1724 struct gfio_client *gc = ge->client;
1725
1726 if (gc && gc->client) {
1727 if (ge->state >= GE_STATE_CONNECTED)
1728 fio_client_terminate(gc->client);
1729
1730 fio_put_client(gc->client);
1731 }
1732
1733 flist_del(&ge->list);
1734 free(ge);
1735}
1736
1737static void ge_widget_destroy(GtkWidget *w, gpointer data)
1738{
Jens Axboe0fd18982012-03-14 10:34:48 +01001739}
1740
1741static void gfio_quit(struct gui *ui)
1742{
1743 struct gui_entry *ge;
1744
1745 while (!flist_empty(&ui->list)) {
1746 ge = flist_entry(ui->list.next, struct gui_entry, list);
1747 ge_destroy(ge);
1748 }
1749
1750 gtk_main_quit();
1751}
1752
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001753static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1754 __attribute__((unused)) gpointer data)
1755{
Jens Axboe0fd18982012-03-14 10:34:48 +01001756 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001757}
1758
Stephen M. Cameron25927252012-02-24 08:17:31 +01001759static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001760{
Jens Axboea9eccde2012-03-09 14:59:42 +01001761 struct gui *ui = arg;
1762
1763 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001764 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001765 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001766 return NULL;
1767}
1768
Jens Axboe2f99deb2012-03-09 14:37:29 +01001769static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001770{
Jens Axboe9988ca72012-03-09 15:14:06 +01001771 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001772 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001773
Jens Axboe2f99deb2012-03-09 14:37:29 +01001774 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001775 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001776 if (ret < 0) {
1777 GError *error;
1778
1779 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1780 report_error(error);
1781 g_error_free(error);
1782 break;
1783 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001784 break;
1785
Jens Axboe2f99deb2012-03-09 14:37:29 +01001786 free(ge->job_files[i]);
1787 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001788 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001789 while (i < ge->nr_job_files) {
1790 free(ge->job_files[i]);
1791 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001792 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001793 }
1794
Jens Axboe2c77d832012-03-13 19:02:04 +01001795 free(ge->job_files);
1796 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01001797 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001798 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001799}
1800
Jens Axboe63a130b2012-03-06 20:08:59 +01001801static void *server_thread(void *arg)
1802{
1803 is_backend = 1;
1804 gfio_server_running = 1;
1805 fio_start_server(NULL);
1806 gfio_server_running = 0;
1807 return NULL;
1808}
1809
Jens Axboe2f99deb2012-03-09 14:37:29 +01001810static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001811{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001812 struct gui *ui = &main_ui;
1813
Jens Axboe63a130b2012-03-06 20:08:59 +01001814 if (!gfio_server_running) {
1815 gfio_server_running = 1;
1816 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001817 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001818 }
1819}
1820
Stephen M. Cameron25927252012-02-24 08:17:31 +01001821static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1822 gpointer data)
1823{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001824 struct gui_entry *ge = data;
1825 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001826
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001827 if (gc)
1828 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001829}
1830
Jens Axboedf06f222012-03-02 13:32:04 +01001831static void file_open(GtkWidget *w, gpointer data);
1832
1833static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001834{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001835 struct gui_entry *ge = data;
1836 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001837
Jens Axboe85dd01e2012-03-12 14:33:16 +01001838 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001839 int ret;
1840
Jens Axboe2f99deb2012-03-09 14:37:29 +01001841 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001842 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001843 if (!ge->nr_job_files)
1844 return;
1845
1846 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1847 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001848 ret = fio_client_connect(gc->client);
1849 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001850 if (!ge->ui->handler_running)
1851 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001852 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001853 } else {
1854 GError *error;
1855
1856 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1857 report_error(error);
1858 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001859 }
Jens Axboedf06f222012-03-02 13:32:04 +01001860 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001861 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001862 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001863 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001864 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001865}
1866
Jens Axboeb9d2f302012-03-08 20:36:28 +01001867static void send_clicked(GtkWidget *widget, gpointer data)
1868{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001869 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001870
Jens Axboe2f99deb2012-03-09 14:37:29 +01001871 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001872 GError *error;
1873
1874 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1875 report_error(error);
1876 g_error_free(error);
1877
Jens Axboe2f99deb2012-03-09 14:37:29 +01001878 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001879 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01001880}
1881
Jens Axboe2f99deb2012-03-09 14:37:29 +01001882static GtkWidget *add_button(GtkWidget *buttonbox,
1883 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001884{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001885 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1886
1887 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1888 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1889 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1890 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1891
1892 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001893}
1894
Jens Axboe2f99deb2012-03-09 14:37:29 +01001895static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1896 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001897{
1898 int i;
1899
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001900 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001901 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001902}
1903
Jens Axboe0420ba62012-02-29 11:16:52 +01001904static void on_info_bar_response(GtkWidget *widget, gint response,
1905 gpointer data)
1906{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001907 struct gui *ui = &main_ui;
1908
Jens Axboe0420ba62012-02-29 11:16:52 +01001909 if (response == GTK_RESPONSE_OK) {
1910 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001911 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001912 }
1913}
1914
Jens Axboedf06f222012-03-02 13:32:04 +01001915void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001916{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001917 struct gui *ui = &main_ui;
1918
1919 if (ui->error_info_bar == NULL) {
1920 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001921 GTK_RESPONSE_OK,
1922 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001923 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1924 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001925 GTK_MESSAGE_ERROR);
1926
Jens Axboe2f99deb2012-03-09 14:37:29 +01001927 ui->error_label = gtk_label_new(error->message);
1928 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1929 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001930
Jens Axboe2f99deb2012-03-09 14:37:29 +01001931 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1932 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001933 } else {
1934 char buffer[256];
1935 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001936 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001937 }
1938}
1939
Jens Axboe62bc9372012-03-07 11:45:07 +01001940struct connection_widgets
1941{
1942 GtkWidget *hentry;
1943 GtkWidget *combo;
1944 GtkWidget *button;
1945};
1946
1947static void hostname_cb(GtkEntry *entry, gpointer data)
1948{
1949 struct connection_widgets *cw = data;
1950 int uses_net = 0, is_localhost = 0;
1951 const gchar *text;
1952 gchar *ctext;
1953
1954 /*
1955 * Check whether to display the 'auto start backend' box
1956 * or not. Show it if we are a localhost and using network,
1957 * or using a socket.
1958 */
1959 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1960 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1961 uses_net = 1;
1962 g_free(ctext);
1963
1964 if (uses_net) {
1965 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1966 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1967 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1968 !strcmp(text, "ip6-loopback"))
1969 is_localhost = 1;
1970 }
1971
1972 if (!uses_net || is_localhost) {
1973 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1974 gtk_widget_set_sensitive(cw->button, 1);
1975 } else {
1976 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1977 gtk_widget_set_sensitive(cw->button, 0);
1978 }
1979}
1980
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001981static int get_connection_details(char **host, int *port, int *type,
1982 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001983{
Jens Axboe62bc9372012-03-07 11:45:07 +01001984 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1985 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001986 char *typeentry;
1987
1988 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001989 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001990 GTK_DIALOG_DESTROY_WITH_PARENT,
1991 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1992 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1993
1994 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001995 /* gtk_dialog_get_content_area() is 2.14 and newer */
1996 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001997 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1998
1999 box = gtk_vbox_new(FALSE, 6);
2000 gtk_container_add(GTK_CONTAINER(frame), box);
2001
2002 hbox = gtk_hbox_new(TRUE, 10);
2003 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002004 cw.hentry = gtk_entry_new();
2005 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2006 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002007
2008 frame = gtk_frame_new("Port");
2009 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2010 box = gtk_vbox_new(FALSE, 10);
2011 gtk_container_add(GTK_CONTAINER(frame), box);
2012
2013 hbox = gtk_hbox_new(TRUE, 4);
2014 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2015 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2016
2017 frame = gtk_frame_new("Type");
2018 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2019 box = gtk_vbox_new(FALSE, 10);
2020 gtk_container_add(GTK_CONTAINER(frame), box);
2021
2022 hbox = gtk_hbox_new(TRUE, 4);
2023 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2024
Jens Axboe62bc9372012-03-07 11:45:07 +01002025 cw.combo = gtk_combo_box_new_text();
2026 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2027 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2028 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2029 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002030
Jens Axboe62bc9372012-03-07 11:45:07 +01002031 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002032
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002033 frame = gtk_frame_new("Options");
2034 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2035 box = gtk_vbox_new(FALSE, 10);
2036 gtk_container_add(GTK_CONTAINER(frame), box);
2037
2038 hbox = gtk_hbox_new(TRUE, 4);
2039 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2040
Jens Axboe62bc9372012-03-07 11:45:07 +01002041 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2042 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2043 gtk_widget_set_tooltip_text(cw.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.");
2044 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2045
2046 /*
2047 * Connect edit signal, so we can show/not-show the auto start button
2048 */
2049 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2050 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002051
Jens Axboea7a42ce2012-03-02 13:12:04 +01002052 gtk_widget_show_all(dialog);
2053
2054 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2055 gtk_widget_destroy(dialog);
2056 return 1;
2057 }
2058
Jens Axboe62bc9372012-03-07 11:45:07 +01002059 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002060 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2061
Jens Axboe62bc9372012-03-07 11:45:07 +01002062 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002063 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2064 *type = Fio_client_ipv4;
2065 else if (!strncmp(typeentry, "IPv6", 4))
2066 *type = Fio_client_ipv6;
2067 else
2068 *type = Fio_client_socket;
2069 g_free(typeentry);
2070
Jens Axboe62bc9372012-03-07 11:45:07 +01002071 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002072
Jens Axboea7a42ce2012-03-02 13:12:04 +01002073 gtk_widget_destroy(dialog);
2074 return 0;
2075}
2076
Jens Axboe2f99deb2012-03-09 14:37:29 +01002077static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002078{
2079 struct gfio_client *gc;
2080
2081 gc = malloc(sizeof(*gc));
2082 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002083 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002084 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002085
Jens Axboe2f99deb2012-03-09 14:37:29 +01002086 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002087
2088 client->client_data = gc;
2089}
2090
Jens Axboe2f99deb2012-03-09 14:37:29 +01002091static GtkWidget *new_client_page(struct gui_entry *ge);
2092
2093static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2094{
2095 struct gui_entry *ge;
2096
2097 ge = malloc(sizeof(*ge));
2098 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002099 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002100 INIT_FLIST_HEAD(&ge->list);
2101 flist_add_tail(&ge->list, &ui->list);
2102 ge->ui = ui;
2103 return ge;
2104}
2105
Jens Axboe2f99deb2012-03-09 14:37:29 +01002106static struct gui_entry *get_new_ge_with_tab(const char *name)
2107{
2108 struct gui_entry *ge;
2109
2110 ge = alloc_new_gui_entry(&main_ui);
2111
2112 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002113 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002114
2115 ge->page_label = gtk_label_new(name);
2116 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2117
2118 gtk_widget_show_all(main_ui.window);
2119 return ge;
2120}
2121
2122static void file_new(GtkWidget *w, gpointer data)
2123{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002124 struct gui *ui = (struct gui *) data;
2125 struct gui_entry *ge;
2126
2127 ge = get_new_ge_with_tab("Untitled");
2128 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002129}
2130
2131/*
2132 * Return the 'ge' corresponding to the tab. If the active tab is the
2133 * main tab, open a new tab.
2134 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002135static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002136{
2137 struct flist_head *entry;
2138 struct gui_entry *ge;
2139
Jens Axboe38634cb2012-03-13 12:26:41 +01002140 if (!cur_page) {
2141 if (created)
2142 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002143 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002144 }
2145
2146 if (created)
2147 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002148
2149 flist_for_each(entry, &main_ui.list) {
2150 ge = flist_entry(entry, struct gui_entry, list);
2151 if (ge->page_num == cur_page)
2152 return ge;
2153 }
2154
2155 return NULL;
2156}
2157
Jens Axboe85dd01e2012-03-12 14:33:16 +01002158static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2159{
2160 gint cur_page;
2161
2162 /*
2163 * Main tab is tab 0, so any current page other than 0 holds
2164 * a ge entry.
2165 */
2166 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2167 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002168 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002169
2170 return NULL;
2171}
2172
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002173static void file_close(GtkWidget *w, gpointer data)
2174{
2175 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002176 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002177
2178 /*
2179 * Can't close the main tab
2180 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002181 ge = get_ge_from_cur_tab(ui);
2182 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002183 gtk_widget_destroy(ge->vbox);
2184 return;
2185 }
2186
Jens Axboef5c67262012-03-13 08:20:41 +01002187 if (!flist_empty(&ui->list)) {
2188 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2189 return;
2190 }
2191
Jens Axboe0fd18982012-03-14 10:34:48 +01002192 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002193}
2194
Jens Axboe38634cb2012-03-13 12:26:41 +01002195static void file_add_recent(struct gui *ui, const gchar *uri)
2196{
Jens Axboea217ba72012-03-13 20:29:39 +01002197 GtkRecentData grd;
2198
2199 memset(&grd, 0, sizeof(grd));
2200 grd.display_name = strdup("gfio");
2201 grd.description = strdup("Fio job file");
2202 grd.mime_type = strdup(GFIO_MIME);
2203 grd.app_name = strdup(g_get_application_name());
2204 grd.app_exec = strdup("gfio %f/%u");
2205
2206 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002207}
2208
2209static gchar *get_filename_from_uri(const gchar *uri)
2210{
2211 if (strncmp(uri, "file://", 7))
2212 return strdup(uri);
2213
2214 return strdup(uri + 7);
2215}
2216
2217static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2218 int type, int port)
2219{
2220 struct fio_client *client;
2221 gchar *filename;
2222
2223 filename = get_filename_from_uri(uri);
2224
2225 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2226 ge->job_files[ge->nr_job_files] = strdup(filename);
2227 ge->nr_job_files++;
2228
2229 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2230 if (!client) {
2231 GError *error;
2232
2233 error = g_error_new(g_quark_from_string("fio"), 1,
2234 "Failed to add client %s", host);
2235 report_error(error);
2236 g_error_free(error);
2237 return 1;
2238 }
2239
2240 gfio_client_added(ge, client);
2241 file_add_recent(ge->ui, uri);
2242 return 0;
2243}
2244
Jens Axboea6790902012-03-13 15:16:11 +01002245static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002246{
Jens Axboea6790902012-03-13 15:16:11 +01002247 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002248 struct gui_entry *ge;
2249 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002250 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002251 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002252
2253 /*
2254 * Creates new tab if current tab is the main window, or the
2255 * current tab already has a client.
2256 */
2257 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002258 ge = get_ge_from_page(cur_page, &ge_is_new);
2259 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002260 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002261 ge_is_new = 1;
2262 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002263
2264 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002265
Jens Axboea6790902012-03-13 15:16:11 +01002266 if (get_connection_details(&host, &port, &type, &server_start)) {
2267 if (ge_is_new)
2268 gtk_widget_destroy(ge->vbox);
2269
2270 return 1;
2271 }
2272
2273 ret = do_file_open(ge, uri, host, type, port);
2274
2275 free(host);
2276
2277 if (!ret) {
2278 if (server_start)
2279 gfio_start_server();
2280 } else {
2281 if (ge_is_new)
2282 gtk_widget_destroy(ge->vbox);
2283 }
2284
2285 return ret;
2286}
2287
2288static void recent_open(GtkAction *action, gpointer data)
2289{
2290 struct gui *ui = (struct gui *) data;
2291 GtkRecentInfo *info;
2292 const gchar *uri;
2293
2294 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2295 uri = gtk_recent_info_get_uri(info);
2296
2297 do_file_open_with_tab(ui, uri);
2298}
2299
2300static void file_open(GtkWidget *w, gpointer data)
2301{
2302 struct gui *ui = data;
2303 GtkWidget *dialog;
2304 GSList *filenames, *fn_glist;
2305 GtkFileFilter *filter;
2306
Jens Axboe0420ba62012-02-29 11:16:52 +01002307 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002308 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002309 GTK_FILE_CHOOSER_ACTION_OPEN,
2310 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2311 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2312 NULL);
2313 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2314
2315 filter = gtk_file_filter_new();
2316 gtk_file_filter_add_pattern(filter, "*.fio");
2317 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002318 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002319 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002320 gtk_file_filter_set_name(filter, "Fio job file");
2321 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2322
2323 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2324 gtk_widget_destroy(dialog);
2325 return;
2326 }
2327
2328 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002329
2330 gtk_widget_destroy(dialog);
2331
Jens Axboe0420ba62012-02-29 11:16:52 +01002332 filenames = fn_glist;
2333 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002334 if (do_file_open_with_tab(ui, filenames->data))
2335 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002336 filenames = g_slist_next(filenames);
2337 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002338
Jens Axboe0420ba62012-02-29 11:16:52 +01002339 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002340}
2341
2342static void file_save(GtkWidget *w, gpointer data)
2343{
Jens Axboe63a130b2012-03-06 20:08:59 +01002344 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002345 GtkWidget *dialog;
2346
2347 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002348 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002349 GTK_FILE_CHOOSER_ACTION_SAVE,
2350 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2351 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2352 NULL);
2353
2354 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2355 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2356
2357 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2358 char *filename;
2359
2360 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2361 // save_job_file(filename);
2362 g_free(filename);
2363 }
2364 gtk_widget_destroy(dialog);
2365}
2366
Jens Axboe9b260bd2012-03-06 11:02:52 +01002367static void view_log_destroy(GtkWidget *w, gpointer data)
2368{
2369 struct gui *ui = (struct gui *) data;
2370
2371 gtk_widget_ref(ui->log_tree);
2372 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2373 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002374 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002375}
2376
2377static void view_log(GtkWidget *w, gpointer data)
2378{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002379 GtkWidget *win, *scroll, *vbox, *box;
2380 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002381
Jens Axboe4cbe7212012-03-06 13:36:17 +01002382 if (ui->log_view)
2383 return;
2384
2385 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002386 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002387 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002388
Jens Axboe4cbe7212012-03-06 13:36:17 +01002389 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002390
Jens Axboe4cbe7212012-03-06 13:36:17 +01002391 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2392
2393 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2394
2395 box = gtk_hbox_new(TRUE, 0);
2396 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2397 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2398 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2399
2400 vbox = gtk_vbox_new(TRUE, 5);
2401 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2402
2403 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002404 gtk_widget_show_all(win);
2405}
2406
Jens Axboe85dd01e2012-03-12 14:33:16 +01002407static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002408{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002409 struct gui *ui = (struct gui *) data;
2410 struct gui_entry *ge;
2411
2412 ge = get_ge_from_cur_tab(ui);
2413 if (ge)
2414 connect_clicked(w, ge);
2415}
2416
2417static void send_job_entry(GtkWidget *w, gpointer data)
2418{
2419 struct gui *ui = (struct gui *) data;
2420 struct gui_entry *ge;
2421
2422 ge = get_ge_from_cur_tab(ui);
2423 if (ge)
2424 send_clicked(w, ge);
2425
2426}
2427
2428static void edit_job_entry(GtkWidget *w, gpointer data)
2429{
2430}
2431
2432static void start_job_entry(GtkWidget *w, gpointer data)
2433{
2434 struct gui *ui = (struct gui *) data;
2435 struct gui_entry *ge;
2436
2437 ge = get_ge_from_cur_tab(ui);
2438 if (ge)
2439 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002440}
2441
Jens Axboe8577f4f2012-03-09 19:28:27 +01002442static void __update_graph_limits(struct gfio_graphs *g)
2443{
2444 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2445 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2446}
2447
2448static void update_graph_limits(void)
2449{
2450 struct flist_head *entry;
2451 struct gui_entry *ge;
2452
2453 __update_graph_limits(&main_ui.graphs);
2454
2455 flist_for_each(entry, &main_ui.list) {
2456 ge = flist_entry(entry, struct gui_entry, list);
2457 __update_graph_limits(&ge->graphs);
2458 }
2459}
2460
Jens Axboe46974a72012-03-02 19:34:13 +01002461static void preferences(GtkWidget *w, gpointer data)
2462{
Jens Axboef3e84402012-03-07 13:14:32 +01002463 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002464 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002465 int i;
2466
2467 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002468 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002469 GTK_DIALOG_DESTROY_WITH_PARENT,
2470 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2471 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2472 NULL);
2473
Jens Axboe8577f4f2012-03-09 19:28:27 +01002474 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002475 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2476 vbox = gtk_vbox_new(FALSE, 6);
2477 gtk_container_add(GTK_CONTAINER(frame), vbox);
2478
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002479 hbox = gtk_hbox_new(FALSE, 5);
2480 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2481 entry = gtk_label_new("Font face to use for graph labels");
2482 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2483
Jens Axboef3e84402012-03-07 13:14:32 +01002484 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002485 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002486
Jens Axboe8577f4f2012-03-09 19:28:27 +01002487 box = gtk_vbox_new(FALSE, 6);
2488 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2489
2490 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002491 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002492 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2493 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2494
Jens Axboec05d9052012-03-11 13:05:35 +01002495 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002496
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002497 box = gtk_vbox_new(FALSE, 6);
2498 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2499
2500 hbox = gtk_hbox_new(FALSE, 5);
2501 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2502 entry = gtk_label_new("Client ETA request interval (msec)");
2503 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2504
2505 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002506 frame = gtk_frame_new("Debug logging");
2507 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2508 vbox = gtk_vbox_new(FALSE, 6);
2509 gtk_container_add(GTK_CONTAINER(frame), vbox);
2510
2511 box = gtk_hbox_new(FALSE, 6);
2512 gtk_container_add(GTK_CONTAINER(vbox), box);
2513
2514 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2515
2516 for (i = 0; i < FD_DEBUG_MAX; i++) {
2517 if (i == 7) {
2518 box = gtk_hbox_new(FALSE, 6);
2519 gtk_container_add(GTK_CONTAINER(vbox), box);
2520 }
2521
2522
2523 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2524 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2525 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2526 }
2527
Jens Axboe46974a72012-03-02 19:34:13 +01002528 gtk_widget_show_all(dialog);
2529
2530 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2531 gtk_widget_destroy(dialog);
2532 return;
2533 }
2534
2535 for (i = 0; i < FD_DEBUG_MAX; i++) {
2536 int set;
2537
2538 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2539 if (set)
2540 fio_debug |= (1UL << i);
2541 }
2542
Jens Axboef3e84402012-03-07 13:14:32 +01002543 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002544 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2545 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002546 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002547
Jens Axboe46974a72012-03-02 19:34:13 +01002548 gtk_widget_destroy(dialog);
2549}
2550
Jens Axboe0420ba62012-02-29 11:16:52 +01002551static void about_dialog(GtkWidget *w, gpointer data)
2552{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002553 const char *authors[] = {
2554 "Jens Axboe <axboe@kernel.dk>",
2555 "Stephen Carmeron <stephenmcameron@gmail.com>",
2556 NULL
2557 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002558 const char *license[] = {
2559 "Fio is free software; you can redistribute it and/or modify "
2560 "it under the terms of the GNU General Public License as published by "
2561 "the Free Software Foundation; either version 2 of the License, or "
2562 "(at your option) any later version.\n",
2563 "Fio is distributed in the hope that it will be useful, "
2564 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2565 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2566 "GNU General Public License for more details.\n",
2567 "You should have received a copy of the GNU General Public License "
2568 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2569 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2570 };
2571 char *license_trans;
2572
2573 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2574 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002575
Jens Axboe0420ba62012-02-29 11:16:52 +01002576 gtk_show_about_dialog(NULL,
2577 "program-name", "gfio",
2578 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002579 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002580 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2581 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002582 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002583 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002584 "logo-icon-name", "fio",
2585 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002586 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002587 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002588
Jens Axboe2f99deb2012-03-09 14:37:29 +01002589 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002590}
2591
2592static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002593 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002594 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002595 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002596 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002597 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002598 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002599 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2600 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2601 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002602 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002603 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2604 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2605 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2606 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002607 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2608 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002609};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002610static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002611
2612static const gchar *ui_string = " \
2613 <ui> \
2614 <menubar name=\"MainMenu\"> \
2615 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002616 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002617 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002618 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002619 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2620 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002621 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002622 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2623 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002624 <placeholder name=\"FileRecentFiles\"/> \
2625 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002626 <menuitem name=\"Quit\" action=\"Quit\" /> \
2627 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002628 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002629 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002630 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002631 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2632 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002633 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002634 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002635 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002636 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2637 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2638 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002639 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2640 <menuitem name=\"About\" action=\"About\" /> \
2641 </menu> \
2642 </menubar> \
2643 </ui> \
2644";
2645
Jens Axboe02421e62012-03-12 12:05:50 +01002646static void set_job_menu_visible(struct gui *ui, int visible)
2647{
2648 GtkWidget *job;
2649
2650 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2651 gtk_widget_set_sensitive(job, visible);
2652}
2653
Jens Axboe4cbe7212012-03-06 13:36:17 +01002654static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2655 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002656{
Jens Axboeca664f42012-03-14 19:49:40 +01002657 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002658 GError *error = 0;
2659
2660 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002661 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002662
2663 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2664 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2665
2666 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002667
Jens Axboe0420ba62012-02-29 11:16:52 +01002668 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2669}
2670
2671void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2672 GtkWidget *vbox, GtkUIManager *ui_manager)
2673{
2674 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2675}
2676
Jens Axboec80b74b2012-03-12 10:23:28 +01002677static void combo_entry_changed(GtkComboBox *box, gpointer data)
2678{
2679 struct gui_entry *ge = (struct gui_entry *) data;
2680 gint index;
2681
2682 index = gtk_combo_box_get_active(box);
2683
2684 multitext_set_entry(&ge->eta.iotype, index);
2685 multitext_set_entry(&ge->eta.ioengine, index);
2686 multitext_set_entry(&ge->eta.iodepth, index);
2687}
2688
2689static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2690{
2691 struct gui_entry *ge = (struct gui_entry *) data;
2692
2693 multitext_free(&ge->eta.iotype);
2694 multitext_free(&ge->eta.ioengine);
2695 multitext_free(&ge->eta.iodepth);
2696}
2697
Jens Axboe2f99deb2012-03-09 14:37:29 +01002698static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002699{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002700 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002701 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002702 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002703
Jens Axboe2f99deb2012-03-09 14:37:29 +01002704 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002705
Jens Axboe65476332012-03-13 10:37:04 +01002706 top_align = gtk_alignment_new(0, 0, 1, 0);
2707 top_vbox = gtk_vbox_new(FALSE, 3);
2708 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2709 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002710
Jens Axboe3e47bd22012-02-29 13:45:02 +01002711 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002712 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002713 probe_frame = gtk_vbox_new(FALSE, 3);
2714 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2715
2716 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002717 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2718 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2719 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2720 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2721 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002722
Jens Axboe3e47bd22012-02-29 13:45:02 +01002723 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002724 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2725
Jens Axboe3863d1a2012-03-09 17:39:05 +01002726 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002727 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2728 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2729 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2730 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2731 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002732 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2733 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2734
2735 probe_box = gtk_hbox_new(FALSE, 3);
2736 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2737 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2738 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2739 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2740 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2741
2742 /*
2743 * Only add this if we have a commit rate
2744 */
2745#if 0
2746 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002747 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002748
Jens Axboe2f99deb2012-03-09 14:37:29 +01002749 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2750 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2751
2752 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2753 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2754#endif
2755
2756 /*
2757 * Set up a drawing area and IOPS and bandwidth graphs
2758 */
2759 gdk_color_parse("white", &white);
2760 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002761 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002762 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002763 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2764 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2765 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2766 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2767 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002768 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2769 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002770 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002771 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002772 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002773 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002774
2775 setup_graphs(&ge->graphs);
2776
2777 /*
2778 * Set up alignments for widgets at the bottom of ui,
2779 * align bottom left, expand horizontally but not vertically
2780 */
Jens Axboe65476332012-03-13 10:37:04 +01002781 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002782 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002783 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2784 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002785
2786 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2787
2788 /*
2789 * Set up thread status progress bar
2790 */
2791 ge->thread_status_pb = gtk_progress_bar_new();
2792 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2793 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2794 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2795
2796
2797 return main_vbox;
2798}
2799
2800static GtkWidget *new_main_page(struct gui *ui)
2801{
2802 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002803 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002804 GdkColor white;
2805
2806 main_vbox = gtk_vbox_new(FALSE, 3);
2807
2808 /*
2809 * Set up alignments for widgets at the top of ui,
2810 * align top left, expand horizontally but not vertically
2811 */
Jens Axboe65476332012-03-13 10:37:04 +01002812 top_align = gtk_alignment_new(0, 0, 1, 0);
2813 top_vbox = gtk_vbox_new(FALSE, 0);
2814 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2815 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002816
2817 probe = gtk_frame_new("Run statistics");
2818 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2819 probe_frame = gtk_vbox_new(FALSE, 3);
2820 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002821
2822 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002823 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002824 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002825 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2826 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2827 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2828 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002829
2830 /*
2831 * Only add this if we have a commit rate
2832 */
2833#if 0
2834 probe_box = gtk_hbox_new(FALSE, 3);
2835 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2836
Jens Axboe3e47bd22012-02-29 13:45:02 +01002837 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2838 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2839
Jens Axboe3e47bd22012-02-29 13:45:02 +01002840 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2841 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002842#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002843
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002844 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002845 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002846 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002847 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002848 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002849 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002850 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002851 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2852 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2853 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2854 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2855 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002856 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2857 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002858 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002859 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002860 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002861 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002862 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002863
Jens Axboe2f99deb2012-03-09 14:37:29 +01002864 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002865
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002866 /*
2867 * Set up alignments for widgets at the bottom of ui,
2868 * align bottom left, expand horizontally but not vertically
2869 */
Jens Axboe65476332012-03-13 10:37:04 +01002870 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002871 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002872 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2873 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002874
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002875 /*
2876 * Set up thread status progress bar
2877 */
2878 ui->thread_status_pb = gtk_progress_bar_new();
2879 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002880 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002881 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2882
Jens Axboe2f99deb2012-03-09 14:37:29 +01002883 return main_vbox;
2884}
2885
2886static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2887 guint page, gpointer data)
2888
2889{
Jens Axboe02421e62012-03-12 12:05:50 +01002890 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002891 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01002892
Jens Axboe85dd01e2012-03-12 14:33:16 +01002893 if (!page) {
2894 set_job_menu_visible(ui, 0);
2895 return TRUE;
2896 }
2897
2898 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01002899 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002900 if (ge)
2901 update_button_states(ui, ge);
2902
Jens Axboe2f99deb2012-03-09 14:37:29 +01002903 return TRUE;
2904}
2905
Jens Axboe38634cb2012-03-13 12:26:41 +01002906static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
2907{
2908 time_t time_a = gtk_recent_info_get_visited(a);
2909 time_t time_b = gtk_recent_info_get_visited(b);
2910
2911 return time_b - time_a;
2912}
2913
2914static void add_recent_file_items(struct gui *ui)
2915{
2916 const gchar *gfio = g_get_application_name();
2917 GList *items, *item;
2918 int i = 0;
2919
2920 if (ui->recent_ui_id) {
2921 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
2922 gtk_ui_manager_ensure_update(ui->uimanager);
2923 }
2924 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
2925
2926 if (ui->actiongroup) {
2927 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
2928 g_object_unref(ui->actiongroup);
2929 }
2930 ui->actiongroup = gtk_action_group_new("RecentFileActions");
2931
2932 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
2933
2934 items = gtk_recent_manager_get_items(ui->recentmanager);
2935 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
2936
2937 for (item = items; item && item->data; item = g_list_next(item)) {
2938 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
2939 gchar *action_name;
2940 const gchar *label;
2941 GtkAction *action;
2942
2943 if (!gtk_recent_info_has_application(info, gfio))
2944 continue;
2945
2946 /*
2947 * We only support local files for now
2948 */
2949 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
2950 continue;
2951
2952 action_name = g_strdup_printf("RecentFile%u", i++);
2953 label = gtk_recent_info_get_display_name(info);
2954
2955 action = g_object_new(GTK_TYPE_ACTION,
2956 "name", action_name,
2957 "label", label, NULL);
2958
2959 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
2960 gtk_recent_info_ref(info),
2961 (GDestroyNotify) gtk_recent_info_unref);
2962
2963
2964 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
2965
2966 gtk_action_group_add_action(ui->actiongroup, action);
2967 g_object_unref(action);
2968
2969 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
2970 "/MainMenu/FileMenu/FileRecentFiles",
2971 label, action_name,
2972 GTK_UI_MANAGER_MENUITEM, FALSE);
2973
2974 g_free(action_name);
2975
2976 if (i == 8)
2977 break;
2978 }
2979
2980 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
2981 g_list_free(items);
2982}
2983
Jens Axboea6790902012-03-13 15:16:11 +01002984static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
2985 gint x, gint y, GtkSelectionData *data,
2986 guint info, guint time)
2987{
2988 struct gui *ui = &main_ui;
2989 gchar **uris;
2990 GtkWidget *source;
2991 int i;
2992
2993 source = gtk_drag_get_source_widget(ctx);
2994 if (source && widget == gtk_widget_get_toplevel(source)) {
2995 gtk_drag_finish(ctx, FALSE, FALSE, time);
2996 return;
2997 }
2998
2999 uris = gtk_selection_data_get_uris(data);
3000 if (!uris) {
3001 gtk_drag_finish(ctx, FALSE, FALSE, time);
3002 return;
3003 }
3004
3005 i = 0;
3006 while (uris[i]) {
3007 if (do_file_open_with_tab(ui, uris[i]))
3008 break;
3009 i++;
3010 }
3011
3012 gtk_drag_finish(ctx, TRUE, FALSE, time);
3013 g_strfreev(uris);
3014}
3015
Jens Axboe2f99deb2012-03-09 14:37:29 +01003016static void init_ui(int *argc, char **argv[], struct gui *ui)
3017{
3018 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003019 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003020
3021 /* Magical g*thread incantation, you just need this thread stuff.
3022 * Without it, the update that happens in gfio_update_thread_status
3023 * doesn't really happen in a timely fashion, you need expose events
3024 */
3025 if (!g_thread_supported())
3026 g_thread_init(NULL);
3027 gdk_threads_init();
3028
3029 gtk_init(argc, argv);
3030 settings = gtk_settings_get_default();
3031 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3032 g_type_init();
3033
3034 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3035 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3036 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3037
3038 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3039 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3040
3041 ui->vbox = gtk_vbox_new(FALSE, 0);
3042 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3043
Jens Axboe02421e62012-03-12 12:05:50 +01003044 ui->uimanager = gtk_ui_manager_new();
3045 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3046 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003047
Jens Axboe38634cb2012-03-13 12:26:41 +01003048 ui->recentmanager = gtk_recent_manager_get_default();
3049 add_recent_file_items(ui);
3050
Jens Axboe2f99deb2012-03-09 14:37:29 +01003051 ui->notebook = gtk_notebook_new();
3052 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003053 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003054 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003055 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3056
3057 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003058 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3059 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3060 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003061
3062 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3063
Jens Axboe9b260bd2012-03-06 11:02:52 +01003064 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003065
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003066 gtk_widget_show_all(ui->window);
3067}
3068
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003069int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003070{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003071 if (initialize_fio(envp))
3072 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003073 if (fio_init_options())
3074 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003075
Jens Axboe2f99deb2012-03-09 14:37:29 +01003076 memset(&main_ui, 0, sizeof(main_ui));
3077 INIT_FLIST_HEAD(&main_ui.list);
3078
3079 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003080
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003081 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003082 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003083 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003084 return 0;
3085}