blob: 95228326d8cb3d4c584a96302aac05c93f910792 [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
Jens Axboe3c3ed072012-03-27 09:12:39 +02004 * 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 Axboe53e0e852012-03-15 19:38:01 +010033#include "gfio.h"
34#include "ghelpers.h"
Jens Axboe9af4a242012-03-16 10:13:49 +010035#include "goptions.h"
Jens Axboe41666582012-03-21 10:25:29 +010036#include "gerror.h"
Jens Axboe1252d8f2012-03-21 11:13:31 +010037#include "gclient.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010038#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010039
Jens Axboe63a130b2012-03-06 20:08:59 +010040static int gfio_server_running;
Jens Axboe8577f4f2012-03-09 19:28:27 +010041static unsigned int gfio_graph_limit = 100;
Jens Axboe63a130b2012-03-06 20:08:59 +010042
Jens Axboe1252d8f2012-03-21 11:13:31 +010043GdkColor gfio_color_white;
Jens Axboe2a95f712012-04-16 11:48:45 +020044GdkColor gfio_color_lightyellow;
Jens Axboea1e79722012-03-23 10:52:25 +010045const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
Jens Axboe3e47bd22012-02-29 13:45:02 +010046
Stephen M. Cameronf3074002012-02-24 08:17:30 +010047typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
48
Jens Axboe3e47bd22012-02-29 13:45:02 +010049static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010050static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010051static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010052
53static struct button_spec {
54 const char *buttontext;
55 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010056 const char *tooltiptext[2];
57 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010058} buttonspeclist[] = {
Jens Axboe53e0e852012-03-15 19:38:01 +010059 {
60 .buttontext = "Connect",
61 .f = connect_clicked,
62 .tooltiptext = { "Disconnect from host", "Connect to host" },
63 .start_sensitive = 1,
64 },
65 {
66 .buttontext = "Send",
67 .f = send_clicked,
68 .tooltiptext = { "Send job description to host", NULL },
69 .start_sensitive = 0,
70 },
71 {
72 .buttontext = "Start Job",
73 .f = start_job_clicked,
74 .tooltiptext = { "Start the current job on the server", NULL },
75 .start_sensitive = 0,
76 },
Jens Axboee0681f32012-03-06 12:14:42 +010077};
78
Jens Axboe8dfd6072012-03-22 22:10:37 +010079static void setup_iops_graph(struct gfio_graphs *gg)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010080{
Jens Axboe2f99deb2012-03-09 14:37:29 +010081 struct graph *g;
82
83 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +010084 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010085 graph_x_title(g, "Time (secs)");
Jens Axboe8dfd6072012-03-22 22:10:37 +010086 gg->read_iops = graph_add_label(g, "Read IOPS");
87 gg->write_iops = graph_add_label(g, "Write IOPS");
Jens Axboe5442cfb2012-09-24 18:48:46 +020088 gg->trim_iops = graph_add_label(g, "Trim IOPS");
Jens Axboedbc542d2012-09-24 19:54:07 +020089 graph_set_color(g, gg->read_iops, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
90 graph_set_color(g, gg->write_iops, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
Jens Axboeb78ca652012-09-25 14:39:36 +020091 graph_set_color(g, gg->trim_iops, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
Jens Axboe8577f4f2012-03-09 19:28:27 +010092 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +010093 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe01a947f2012-03-22 21:21:00 +010094 graph_set_graph_all_zeroes(g, 0);
Jens Axboe75450782012-03-23 08:15:46 +010095 gg->iops_graph = g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +010096}
97
Jens Axboe8dfd6072012-03-22 22:10:37 +010098static void setup_bandwidth_graph(struct gfio_graphs *gg)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010099{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100100 struct graph *g;
101
102 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100103 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100104 graph_x_title(g, "Time (secs)");
Jens Axboe8dfd6072012-03-22 22:10:37 +0100105 gg->read_bw = graph_add_label(g, "Read Bandwidth");
106 gg->write_bw = graph_add_label(g, "Write Bandwidth");
Jens Axboe5442cfb2012-09-24 18:48:46 +0200107 gg->trim_bw = graph_add_label(g, "Trim Bandwidth");
Jens Axboedbc542d2012-09-24 19:54:07 +0200108 graph_set_color(g, gg->read_bw, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
109 graph_set_color(g, gg->write_bw, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
Jens Axboeb78ca652012-09-25 14:39:36 +0200110 graph_set_color(g, gg->trim_bw, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100111 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100112 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100113 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe01a947f2012-03-22 21:21:00 +0100114 graph_set_graph_all_zeroes(g, 0);
Jens Axboe75450782012-03-23 08:15:46 +0100115 gg->bandwidth_graph = g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100116}
117
Jens Axboe2f99deb2012-03-09 14:37:29 +0100118static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100119{
Jens Axboe8dfd6072012-03-22 22:10:37 +0100120 setup_iops_graph(g);
121 setup_bandwidth_graph(g);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100122}
123
Jens Axboe1252d8f2012-03-21 11:13:31 +0100124void clear_ge_ui_info(struct gui_entry *ge)
Jens Axboe2f99deb2012-03-09 14:37:29 +0100125{
126 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
127 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
128 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
129 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100130#if 0
131 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100132 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100133#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100134 multitext_update_entry(&ge->eta.iotype, 0, "");
Jens Axboe99d633a2012-03-15 15:55:04 +0100135 multitext_update_entry(&ge->eta.bs, 0, "");
Jens Axboec80b74b2012-03-12 10:23:28 +0100136 multitext_update_entry(&ge->eta.ioengine, 0, "");
137 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100138 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
139 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
140 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
141 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
142 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
143 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100144}
145
Jens Axboe781ccba2012-03-15 09:44:42 +0100146static void set_menu_entry_text(struct gui *ui, const char *path,
147 const char *text)
148{
149 GtkWidget *w;
150
151 w = gtk_ui_manager_get_widget(ui->uimanager, path);
152 if (w)
153 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
154 else
155 fprintf(stderr, "gfio: can't find path %s\n", path);
156}
157
158
159static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
160{
161 GtkWidget *w;
162
163 w = gtk_ui_manager_get_widget(ui->uimanager, path);
164 if (w)
165 gtk_widget_set_sensitive(w, show);
166 else
167 fprintf(stderr, "gfio: can't find path %s\n", path);
168}
169
170static void set_job_menu_visible(struct gui *ui, int visible)
171{
172 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
173}
174
175static void set_view_results_visible(struct gui *ui, int visible)
176{
177 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
178}
179
Jens Axboe014f4022012-03-15 14:03:01 +0100180static const char *get_button_tooltip(struct button_spec *s, int sensitive)
181{
182 if (s->tooltiptext[sensitive])
183 return s->tooltiptext[sensitive];
184
185 return s->tooltiptext[0];
186}
187
188static GtkWidget *add_button(GtkWidget *buttonbox,
189 struct button_spec *buttonspec, gpointer data)
190{
191 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
192 gboolean sens = buttonspec->start_sensitive;
193
194 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
195 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
196
197 sens = buttonspec->start_sensitive;
198 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
199 gtk_widget_set_sensitive(button, sens);
200
201 return button;
202}
203
204static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
205 int nbuttons)
206{
207 int i;
208
209 for (i = 0; i < nbuttons; i++)
210 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
211}
212
Jens Axboe85dd01e2012-03-12 14:33:16 +0100213/*
214 * Update sensitivity of job buttons and job menu items, based on the
215 * state of the client.
216 */
217static void update_button_states(struct gui *ui, struct gui_entry *ge)
218{
219 unsigned int connect_state, send_state, start_state, edit_state;
220 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100221
222 switch (ge->state) {
Jens Axboe1252d8f2012-03-21 11:13:31 +0100223 default:
224 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100225 /* fall through to new state */
Jens Axboe85dd01e2012-03-12 14:33:16 +0100226 case GE_STATE_NEW:
227 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100228 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100229 connect_str = "Connect";
230 send_state = 0;
231 start_state = 0;
232 break;
233 case GE_STATE_CONNECTED:
234 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100235 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100236 connect_str = "Disconnect";
237 send_state = 1;
238 start_state = 0;
239 break;
240 case GE_STATE_JOB_SENT:
241 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100242 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100243 connect_str = "Disconnect";
244 send_state = 0;
245 start_state = 1;
246 break;
247 case GE_STATE_JOB_STARTED:
248 connect_state = 1;
249 edit_state = 1;
250 connect_str = "Disconnect";
251 send_state = 0;
252 start_state = 1;
253 break;
254 case GE_STATE_JOB_RUNNING:
255 connect_state = 1;
256 edit_state = 0;
257 connect_str = "Disconnect";
258 send_state = 0;
259 start_state = 0;
260 break;
261 case GE_STATE_JOB_DONE:
262 connect_state = 1;
263 edit_state = 0;
264 connect_str = "Connect";
265 send_state = 0;
266 start_state = 0;
267 break;
268 }
269
Jens Axboe53e0e852012-03-15 19:38:01 +0100270 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
271 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
272 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
273 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
274 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
Jens Axboe85dd01e2012-03-12 14:33:16 +0100275
Jens Axboe781ccba2012-03-15 09:44:42 +0100276 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
277 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100278
Jens Axboe781ccba2012-03-15 09:44:42 +0100279 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
280 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
281 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100282
Jens Axboe781ccba2012-03-15 09:44:42 +0100283 if (ge->client && ge->client->nr_results)
284 set_view_results_visible(ui, 1);
285 else
286 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100287}
288
Jens Axboe1252d8f2012-03-21 11:13:31 +0100289void gfio_set_state(struct gui_entry *ge, unsigned int state)
Jens Axboe85dd01e2012-03-12 14:33:16 +0100290{
291 ge->state = state;
292 update_button_states(ge->ui, ge);
293}
294
Jens Axboe9b260bd2012-03-06 11:02:52 +0100295static void gfio_ui_setup_log(struct gui *ui)
296{
297 GtkTreeSelection *selection;
298 GtkListStore *model;
299 GtkWidget *tree_view;
300
Jens Axboe3ad04392012-03-26 21:08:46 +0200301 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100302
303 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
304 gtk_widget_set_can_focus(tree_view, FALSE);
305
306 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
307 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100308 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
309 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100310
311 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
312 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
313 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100314 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100315
316 ui->log_model = model;
317 ui->log_tree = tree_view;
318}
319
Jens Axboe2f99deb2012-03-09 14:37:29 +0100320static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
321 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +0100322{
Jens Axboecd0d2db2012-03-26 19:30:24 +0200323 guint width = gtk_widget_get_allocated_width(w);
324 guint height = gtk_widget_get_allocated_height(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100325 struct gfio_graphs *g = data;
326
Jens Axboecd0d2db2012-03-26 19:30:24 +0200327 graph_set_size(g->iops_graph, width / 2.0, height);
328 graph_set_position(g->iops_graph, width / 2.0, 0.0);
329 graph_set_size(g->bandwidth_graph, width / 2.0, height);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100330 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +0100331 return TRUE;
332}
333
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100334static void draw_graph(struct graph *g, cairo_t *cr)
335{
336 line_graph_draw(g, cr);
337 cairo_stroke(cr);
338}
339
Jens Axboe93e2db22012-03-13 09:45:22 +0100340static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
341 gboolean keyboard_mode, GtkTooltip *tooltip,
342 gpointer data)
343{
344 struct gfio_graphs *g = data;
345 const char *text = NULL;
346
347 if (graph_contains_xy(g->iops_graph, x, y))
348 text = graph_find_tooltip(g->iops_graph, x, y);
349 else if (graph_contains_xy(g->bandwidth_graph, x, y))
350 text = graph_find_tooltip(g->bandwidth_graph, x, y);
351
352 if (text) {
353 gtk_tooltip_set_text(tooltip, text);
354 return TRUE;
355 }
356
357 return FALSE;
358}
359
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100360static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
361{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100362 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100363 cairo_t *cr;
364
Jens Axboecd0d2db2012-03-26 19:30:24 +0200365 cr = gdk_cairo_create(gtk_widget_get_window(w));
Jens Axboe93e2db22012-03-13 09:45:22 +0100366
367 if (graph_has_tooltips(g->iops_graph) ||
368 graph_has_tooltips(g->bandwidth_graph)) {
369 g_object_set(w, "has-tooltip", TRUE, NULL);
370 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
371 }
372
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100373 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100374 draw_graph(g->iops_graph, cr);
375 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100376 cairo_destroy(cr);
377
378 return FALSE;
379}
380
Jens Axboe2f99deb2012-03-09 14:37:29 +0100381/*
Jens Axboe0fd18982012-03-14 10:34:48 +0100382 * FIXME: need more handling here
383 */
384static void ge_destroy(struct gui_entry *ge)
385{
386 struct gfio_client *gc = ge->client;
387
Jens Axboe470cdbb2012-03-21 12:02:13 +0100388 if (gc) {
389 if (gc->client) {
390 if (ge->state >= GE_STATE_CONNECTED)
391 fio_client_terminate(gc->client);
Jens Axboe0fd18982012-03-14 10:34:48 +0100392
Jens Axboe470cdbb2012-03-21 12:02:13 +0100393 fio_put_client(gc->client);
394 }
395 free(gc);
Jens Axboe0fd18982012-03-14 10:34:48 +0100396 }
397
Jens Axboeb98ab712012-03-21 12:48:32 +0100398 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
399
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100400 free(ge->job_file);
401 free(ge->host);
Jens Axboe0fd18982012-03-14 10:34:48 +0100402 free(ge);
403}
404
405static void ge_widget_destroy(GtkWidget *w, gpointer data)
406{
Jens Axboe2eb98bf2012-03-20 08:20:48 +0100407 struct gui_entry *ge = (struct gui_entry *) data;
408
409 ge_destroy(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +0100410}
411
412static void gfio_quit(struct gui *ui)
413{
Jens Axboe3c3ed072012-03-27 09:12:39 +0200414 gtk_main_quit();
Jens Axboe0fd18982012-03-14 10:34:48 +0100415}
416
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100417static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
Jens Axboe3c3ed072012-03-27 09:12:39 +0200418 gpointer data)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100419{
Jens Axboe6e02ad62012-03-20 12:25:36 +0100420 struct gui *ui = (struct gui *) data;
421
422 gfio_quit(ui);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100423}
424
Stephen M. Cameron25927252012-02-24 08:17:31 +0100425static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100426{
Jens Axboea9eccde2012-03-09 14:59:42 +0100427 struct gui *ui = arg;
428
429 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100430 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +0100431 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100432 return NULL;
433}
434
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100435static int send_job_file(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100436{
Jens Axboe9988ca72012-03-09 15:14:06 +0100437 struct gfio_client *gc = ge->client;
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100438 int ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100439
Jens Axboe753e9e62012-03-24 20:22:29 +0100440 /*
441 * Prune old options, we are expecting the return options
442 * when the job file is parsed remotely and returned to us.
443 */
444 while (!flist_empty(&gc->o_list)) {
445 struct gfio_client_options *gco;
446
447 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list);
448 flist_del(&gco->list);
449 free(gco);
450 }
451
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100452 ret = fio_client_send_ini(gc->client, ge->job_file);
453 if (!ret)
454 return 0;
Jens Axboec7249262012-03-09 17:11:04 +0100455
Jens Axboe41666582012-03-21 10:25:29 +0100456 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100457 return 1;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100458}
459
Jens Axboe63a130b2012-03-06 20:08:59 +0100460static void *server_thread(void *arg)
461{
462 is_backend = 1;
463 gfio_server_running = 1;
464 fio_start_server(NULL);
465 gfio_server_running = 0;
466 return NULL;
467}
468
Jens Axboe6e02ad62012-03-20 12:25:36 +0100469static void gfio_start_server(struct gui *ui)
Jens Axboe63a130b2012-03-06 20:08:59 +0100470{
471 if (!gfio_server_running) {
472 gfio_server_running = 1;
473 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +0100474 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +0100475 }
476}
477
Stephen M. Cameron25927252012-02-24 08:17:31 +0100478static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
Jens Axboe3c3ed072012-03-27 09:12:39 +0200479 gpointer data)
Stephen M. Cameron25927252012-02-24 08:17:31 +0100480{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100481 struct gui_entry *ge = data;
482 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100483
Jens Axboe78cb2fe2012-03-12 23:05:29 +0100484 if (gc)
485 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100486}
487
Jens Axboedf06f222012-03-02 13:32:04 +0100488static void file_open(GtkWidget *w, gpointer data);
489
Jens Axboe62bc9372012-03-07 11:45:07 +0100490struct connection_widgets
491{
492 GtkWidget *hentry;
493 GtkWidget *combo;
494 GtkWidget *button;
495};
496
497static void hostname_cb(GtkEntry *entry, gpointer data)
498{
499 struct connection_widgets *cw = data;
500 int uses_net = 0, is_localhost = 0;
501 const gchar *text;
502 gchar *ctext;
503
504 /*
505 * Check whether to display the 'auto start backend' box
506 * or not. Show it if we are a localhost and using network,
507 * or using a socket.
508 */
Jens Axboef762cef2012-03-26 13:51:11 +0200509 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
Jens Axboe62bc9372012-03-07 11:45:07 +0100510 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
511 uses_net = 1;
512 g_free(ctext);
513
514 if (uses_net) {
515 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
516 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
517 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
518 !strcmp(text, "ip6-loopback"))
519 is_localhost = 1;
520 }
521
522 if (!uses_net || is_localhost) {
523 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
524 gtk_widget_set_sensitive(cw->button, 1);
525 } else {
526 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
527 gtk_widget_set_sensitive(cw->button, 0);
528 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100529}
Jens Axboea7a42ce2012-03-02 13:12:04 +0100530
Jens Axboeb0500cc2012-03-21 11:59:18 +0100531static int get_connection_details(struct gui_entry *ge)
532{
533 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
534 struct connection_widgets cw;
535 struct gui *ui = ge->ui;
536 char *typeentry;
Jens Axboea7a42ce2012-03-02 13:12:04 +0100537
Jens Axboeb0500cc2012-03-21 11:59:18 +0100538 if (ge->host)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100539 return 0;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100540
Jens Axboeb0500cc2012-03-21 11:59:18 +0100541 dialog = gtk_dialog_new_with_buttons("Connection details",
542 GTK_WINDOW(ui->window),
543 GTK_DIALOG_DESTROY_WITH_PARENT,
544 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
545 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100546
Jens Axboeb0500cc2012-03-21 11:59:18 +0100547 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef762cef2012-03-26 13:51:11 +0200548 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100549 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100550
Jens Axboeb0500cc2012-03-21 11:59:18 +0100551 box = gtk_vbox_new(FALSE, 6);
552 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100553
Jens Axboeb0500cc2012-03-21 11:59:18 +0100554 hbox = gtk_hbox_new(TRUE, 10);
555 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
556 cw.hentry = gtk_entry_new();
557 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
558 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100559
Jens Axboeb0500cc2012-03-21 11:59:18 +0100560 frame = gtk_frame_new("Port");
561 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
562 box = gtk_vbox_new(FALSE, 10);
563 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100564
Jens Axboeb0500cc2012-03-21 11:59:18 +0100565 hbox = gtk_hbox_new(TRUE, 4);
566 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
567 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100568
Jens Axboeb0500cc2012-03-21 11:59:18 +0100569 frame = gtk_frame_new("Type");
570 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
571 box = gtk_vbox_new(FALSE, 10);
572 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100573
Jens Axboeb0500cc2012-03-21 11:59:18 +0100574 hbox = gtk_hbox_new(TRUE, 4);
575 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100576
Jens Axboef762cef2012-03-26 13:51:11 +0200577 cw.combo = gtk_combo_box_text_new();
578 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
579 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
580 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
Jens Axboeb0500cc2012-03-21 11:59:18 +0100581 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100582
Jens Axboeb0500cc2012-03-21 11:59:18 +0100583 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100584
Jens Axboeb0500cc2012-03-21 11:59:18 +0100585 frame = gtk_frame_new("Options");
586 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
587 box = gtk_vbox_new(FALSE, 10);
588 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe38634cb2012-03-13 12:26:41 +0100589
Jens Axboeb0500cc2012-03-21 11:59:18 +0100590 hbox = gtk_hbox_new(TRUE, 4);
591 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100592
Jens Axboeb0500cc2012-03-21 11:59:18 +0100593 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
594 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
595 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.");
596 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100597
Jens Axboe1252d8f2012-03-21 11:13:31 +0100598 /*
Jens Axboeb0500cc2012-03-21 11:59:18 +0100599 * Connect edit signal, so we can show/not-show the auto start button
Jens Axboe1252d8f2012-03-21 11:13:31 +0100600 */
Jens Axboecd972fd2012-03-26 12:55:33 +0200601 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
602 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboe0420ba62012-02-29 11:16:52 +0100603
Jens Axboeb0500cc2012-03-21 11:59:18 +0100604 gtk_widget_show_all(dialog);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100605
Jens Axboeb0500cc2012-03-21 11:59:18 +0100606 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
607 gtk_widget_destroy(dialog);
Jens Axboea6790902012-03-13 15:16:11 +0100608 return 1;
609 }
610
Jens Axboeb0500cc2012-03-21 11:59:18 +0100611 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
612 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
Jens Axboea6790902012-03-13 15:16:11 +0100613
Jens Axboef762cef2012-03-26 13:51:11 +0200614 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100615 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
616 ge->type = Fio_client_ipv4;
617 else if (!strncmp(typeentry, "IPv6", 4))
618 ge->type = Fio_client_ipv6;
619 else
620 ge->type = Fio_client_socket;
621 g_free(typeentry);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100622
Jens Axboeb0500cc2012-03-21 11:59:18 +0100623 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboe1252d8f2012-03-21 11:13:31 +0100624
Jens Axboeb0500cc2012-03-21 11:59:18 +0100625 gtk_widget_destroy(dialog);
626 return 0;
627}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100628
Jens Axboeb0500cc2012-03-21 11:59:18 +0100629static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
630{
631 gc->client = fio_get_client(client);
632 client->client_data = gc;
633}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100634
Jens Axboeb0500cc2012-03-21 11:59:18 +0100635static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
636{
Jens Axboecf3d8242012-03-24 08:56:50 +0100637 struct gfio_client_options *gco;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100638 struct gfio_client *gc;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100639
Jens Axboecf3d8242012-03-24 08:56:50 +0100640 gc = calloc(1, sizeof(*gc));
641 INIT_FLIST_HEAD(&gc->o_list);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100642 gc->ge = ge;
643 ge->client = gc;
644 gfio_set_client(gc, client);
Jens Axboecf3d8242012-03-24 08:56:50 +0100645
646 /*
647 * Just add a default set of options, need to consider how best
648 * to handle this
649 */
650 gco = calloc(1, sizeof(*gco));
Jens Axboe753e9e62012-03-24 20:22:29 +0100651 INIT_FLIST_HEAD(&gco->list);
Jens Axboecf3d8242012-03-24 08:56:50 +0100652 options_default_fill(&gco->o);
653 flist_add_tail(&gco->list, &gc->o_list);
Jens Axboe753e9e62012-03-24 20:22:29 +0100654 gc->o_list_nr++;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100655}
Jens Axboea6790902012-03-13 15:16:11 +0100656
Jens Axboeb0500cc2012-03-21 11:59:18 +0100657static void connect_clicked(GtkWidget *widget, gpointer data)
658{
659 struct gui_entry *ge = data;
660 struct gfio_client *gc = ge->client;
Jens Axboea6790902012-03-13 15:16:11 +0100661
Jens Axboeb0500cc2012-03-21 11:59:18 +0100662 if (ge->state == GE_STATE_NEW) {
663 int ret;
Jens Axboea6790902012-03-13 15:16:11 +0100664
Jens Axboeb0500cc2012-03-21 11:59:18 +0100665 if (!ge->job_file)
666 file_open(widget, ge->ui);
667 if (!ge->job_file)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100668 return;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100669
670 gc = ge->client;
671
672 if (!gc->client) {
673 struct fio_client *client;
674
675 if (get_connection_details(ge)) {
676 gfio_report_error(ge, "Failed to get connection details\n");
677 return;
678 }
679
680 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
681 if (!client) {
682 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
683 free(ge->host);
684 ge->host = NULL;
685 return;
686 }
687 gfio_set_client(gc, client);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100688 }
Jens Axboe0420ba62012-02-29 11:16:52 +0100689
Jens Axboeb0500cc2012-03-21 11:59:18 +0100690 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
691 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
692 ret = fio_client_connect(gc->client);
693 if (!ret) {
694 if (!ge->ui->handler_running)
695 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
696 gfio_set_state(ge, GE_STATE_CONNECTED);
697 } else {
698 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
699 }
700 } else {
701 fio_client_terminate(gc->client);
702 gfio_set_state(ge, GE_STATE_NEW);
703 clear_ge_ui_info(ge);
704 }
705}
706
707static void send_clicked(GtkWidget *widget, gpointer data)
708{
709 struct gui_entry *ge = data;
710
711 if (send_job_file(ge))
712 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
713}
714
715static GtkWidget *new_client_page(struct gui_entry *ge);
716
717static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
718{
719 struct gui_entry *ge;
720
721 ge = malloc(sizeof(*ge));
722 memset(ge, 0, sizeof(*ge));
723 ge->state = GE_STATE_NEW;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100724 ge->ui = ui;
725 return ge;
726}
727
728static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
729{
730 struct gui_entry *ge;
731
732 ge = alloc_new_gui_entry(ui);
733
734 ge->vbox = new_client_page(ge);
735 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
736
737 ge->page_label = gtk_label_new(name);
738 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
739
Jens Axboeb98ab712012-03-21 12:48:32 +0100740 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
741
Jens Axboeb0500cc2012-03-21 11:59:18 +0100742 gtk_widget_show_all(ui->window);
743 return ge;
744}
745
746static void file_new(GtkWidget *w, gpointer data)
747{
748 struct gui *ui = (struct gui *) data;
749 struct gui_entry *ge;
750
751 ge = get_new_ge_with_tab(ui, "Untitled");
752 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
753}
754
755/*
756 * Return the 'ge' corresponding to the tab. If the active tab is the
757 * main tab, open a new tab.
758 */
759static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
760 int *created)
761{
Jens Axboeb0500cc2012-03-21 11:59:18 +0100762 if (!cur_page) {
763 if (created)
764 *created = 1;
765 return get_new_ge_with_tab(ui, "Untitled");
766 }
767
768 if (created)
769 *created = 0;
770
Jens Axboeb98ab712012-03-21 12:48:32 +0100771 return g_hash_table_lookup(ui->ge_hash, &cur_page);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100772}
773
774static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
775{
776 gint cur_page;
777
778 /*
779 * Main tab is tab 0, so any current page other than 0 holds
780 * a ge entry.
781 */
782 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
783 if (cur_page)
784 return get_ge_from_page(ui, cur_page, NULL);
785
786 return NULL;
787}
788
789static void file_close(GtkWidget *w, gpointer data)
790{
791 struct gui *ui = (struct gui *) data;
792 struct gui_entry *ge;
793
794 /*
795 * Can't close the main tab
796 */
797 ge = get_ge_from_cur_tab(ui);
798 if (ge) {
799 gtk_widget_destroy(ge->vbox);
800 return;
801 }
802
Jens Axboeb98ab712012-03-21 12:48:32 +0100803 if (g_hash_table_size(ui->ge_hash)) {
Jens Axboeb0500cc2012-03-21 11:59:18 +0100804 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
805 return;
806 }
807
808 gfio_quit(ui);
809}
810
811static void file_add_recent(struct gui *ui, const gchar *uri)
812{
813 GtkRecentData grd;
814
815 memset(&grd, 0, sizeof(grd));
816 grd.display_name = strdup("gfio");
817 grd.description = strdup("Fio job file");
818 grd.mime_type = strdup(GFIO_MIME);
819 grd.app_name = strdup(g_get_application_name());
820 grd.app_exec = strdup("gfio %f/%u");
821
822 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
823}
824
825static gchar *get_filename_from_uri(const gchar *uri)
826{
827 if (strncmp(uri, "file://", 7))
828 return strdup(uri);
829
830 return strdup(uri + 7);
831}
832
833static int do_file_open(struct gui_entry *ge, const gchar *uri)
834{
835 struct fio_client *client;
836
837 assert(!ge->job_file);
838
839 ge->job_file = get_filename_from_uri(uri);
840
841 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
842 if (client) {
Jens Axboe832fde72012-03-21 12:55:49 +0100843 char *label = strdup(uri);
844
845 basename(label);
846 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
847 free(label);
848
Jens Axboeb0500cc2012-03-21 11:59:18 +0100849 gfio_client_added(ge, client);
850 file_add_recent(ge->ui, uri);
851 return 0;
852 }
853
854 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
855 free(ge->host);
856 ge->host = NULL;
Jens Axboeb98ab712012-03-21 12:48:32 +0100857 free(ge->job_file);
858 ge->job_file = NULL;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100859 return 1;
860}
861
862static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
863{
864 struct gui_entry *ge;
865 gint cur_page;
866 int ret, ge_is_new = 0;
867
868 /*
869 * Creates new tab if current tab is the main window, or the
870 * current tab already has a client.
871 */
872 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
873 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
874 if (ge->client) {
875 ge = get_new_ge_with_tab(ui, "Untitled");
876 ge_is_new = 1;
877 }
878
879 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
880
881 if (get_connection_details(ge)) {
882 if (ge_is_new)
883 gtk_widget_destroy(ge->vbox);
Jens Axboe3c3ed072012-03-27 09:12:39 +0200884
Jens Axboeb0500cc2012-03-21 11:59:18 +0100885 return 1;
886 }
887
888 ret = do_file_open(ge, uri);
889
890 if (!ret) {
891 if (ge->server_start)
892 gfio_start_server(ui);
893 } else {
894 if (ge_is_new)
895 gtk_widget_destroy(ge->vbox);
896 }
897
898 return ret;
899}
900
901static void recent_open(GtkAction *action, gpointer data)
902{
903 struct gui *ui = (struct gui *) data;
904 GtkRecentInfo *info;
905 const gchar *uri;
906
907 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
908 uri = gtk_recent_info_get_uri(info);
909
910 do_file_open_with_tab(ui, uri);
911}
912
913static void file_open(GtkWidget *w, gpointer data)
914{
915 struct gui *ui = data;
916 GtkWidget *dialog;
917 GtkFileFilter *filter;
918 gchar *filename;
919
920 dialog = gtk_file_chooser_dialog_new("Open File",
921 GTK_WINDOW(ui->window),
922 GTK_FILE_CHOOSER_ACTION_OPEN,
923 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
924 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
925 NULL);
926 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
927
928 filter = gtk_file_filter_new();
929 gtk_file_filter_add_pattern(filter, "*.fio");
930 gtk_file_filter_add_pattern(filter, "*.job");
931 gtk_file_filter_add_pattern(filter, "*.ini");
932 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
933 gtk_file_filter_set_name(filter, "Fio job file");
934 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
935
936 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
937 gtk_widget_destroy(dialog);
938 return;
939 }
940
941 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
942
943 gtk_widget_destroy(dialog);
944
945 do_file_open_with_tab(ui, filename);
946 g_free(filename);
947}
948
949static void file_save(GtkWidget *w, gpointer data)
950{
951 struct gui *ui = data;
952 GtkWidget *dialog;
953
954 dialog = gtk_file_chooser_dialog_new("Save File",
955 GTK_WINDOW(ui->window),
956 GTK_FILE_CHOOSER_ACTION_SAVE,
957 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
958 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
959 NULL);
960
961 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
962 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
963
964 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
965 char *filename;
966
Jens Axboe0420ba62012-02-29 11:16:52 +0100967 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100968 // save_job_file(filename);
Jens Axboe0420ba62012-02-29 11:16:52 +0100969 g_free(filename);
970 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100971 gtk_widget_destroy(dialog);
972}
Jens Axboe0420ba62012-02-29 11:16:52 +0100973
Jens Axboe9b260bd2012-03-06 11:02:52 +0100974static void view_log_destroy(GtkWidget *w, gpointer data)
975{
976 struct gui *ui = (struct gui *) data;
977
Jens Axboef762cef2012-03-26 13:51:11 +0200978 g_object_ref(G_OBJECT(ui->log_tree));
Jens Axboe9b260bd2012-03-06 11:02:52 +0100979 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
980 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +0100981 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100982}
983
Jens Axboe1252d8f2012-03-21 11:13:31 +0100984void gfio_view_log(struct gui *ui)
Jens Axboe9b260bd2012-03-06 11:02:52 +0100985{
Jens Axboe4cbe7212012-03-06 13:36:17 +0100986 GtkWidget *win, *scroll, *vbox, *box;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100987
Jens Axboe4cbe7212012-03-06 13:36:17 +0100988 if (ui->log_view)
989 return;
990
991 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100992 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +0100993 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100994
Jens Axboe4cbe7212012-03-06 13:36:17 +0100995 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100996
Jens Axboe4cbe7212012-03-06 13:36:17 +0100997 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
998
999 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1000
1001 box = gtk_hbox_new(TRUE, 0);
Jens Axboef762cef2012-03-26 13:51:11 +02001002 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001003 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1004 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1005
1006 vbox = gtk_vbox_new(TRUE, 5);
Jens Axboef762cef2012-03-26 13:51:11 +02001007 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001008
1009 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001010 gtk_widget_show_all(win);
1011}
1012
Jens Axboe1252d8f2012-03-21 11:13:31 +01001013static void view_log(GtkWidget *w, gpointer data)
1014{
1015 struct gui *ui = (struct gui *) data;
1016
1017 gfio_view_log(ui);
1018}
1019
Jens Axboe85dd01e2012-03-12 14:33:16 +01001020static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001021{
Jens Axboe85dd01e2012-03-12 14:33:16 +01001022 struct gui *ui = (struct gui *) data;
1023 struct gui_entry *ge;
Jens Axboe3c3ed072012-03-27 09:12:39 +02001024
Jens Axboe85dd01e2012-03-12 14:33:16 +01001025 ge = get_ge_from_cur_tab(ui);
1026 if (ge)
1027 connect_clicked(w, ge);
1028}
1029
1030static void send_job_entry(GtkWidget *w, gpointer data)
1031{
1032 struct gui *ui = (struct gui *) data;
1033 struct gui_entry *ge;
1034
1035 ge = get_ge_from_cur_tab(ui);
1036 if (ge)
1037 send_clicked(w, ge);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001038}
1039
1040static void edit_job_entry(GtkWidget *w, gpointer data)
1041{
Jens Axboe9af4a242012-03-16 10:13:49 +01001042 struct gui *ui = (struct gui *) data;
Jens Axboe789f4cc2012-03-16 14:56:44 +01001043 struct gui_entry *ge;
Jens Axboe9af4a242012-03-16 10:13:49 +01001044
Jens Axboe789f4cc2012-03-16 14:56:44 +01001045 ge = get_ge_from_cur_tab(ui);
1046 if (ge && ge->client)
Jens Axboecf3d8242012-03-24 08:56:50 +01001047 gopt_get_options_window(ui->window, ge->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001048}
1049
1050static void start_job_entry(GtkWidget *w, gpointer data)
1051{
1052 struct gui *ui = (struct gui *) data;
1053 struct gui_entry *ge;
1054
1055 ge = get_ge_from_cur_tab(ui);
1056 if (ge)
1057 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001058}
1059
Jens Axboe781ccba2012-03-15 09:44:42 +01001060static void view_results(GtkWidget *w, gpointer data)
1061{
1062 struct gui *ui = (struct gui *) data;
1063 struct gfio_client *gc;
1064 struct gui_entry *ge;
1065
1066 ge = get_ge_from_cur_tab(ui);
1067 if (!ge)
1068 return;
1069
1070 if (ge->results_window)
1071 return;
1072
1073 gc = ge->client;
1074 if (gc && gc->nr_results)
1075 gfio_display_end_results(gc);
1076}
1077
Jens Axboea1e79722012-03-23 10:52:25 +01001078static void __update_graph_settings(struct gfio_graphs *g)
Jens Axboe8577f4f2012-03-09 19:28:27 +01001079{
1080 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001081 graph_set_font(g->iops_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001082 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001083 graph_set_font(g->bandwidth_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001084}
1085
Jens Axboea1e79722012-03-23 10:52:25 +01001086static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
Jens Axboeb98ab712012-03-21 12:48:32 +01001087{
1088 struct gui_entry *ge = (struct gui_entry *) value;
Jens Axboea1e79722012-03-23 10:52:25 +01001089 GdkEvent *ev;
Jens Axboeb98ab712012-03-21 12:48:32 +01001090
Jens Axboea1e79722012-03-23 10:52:25 +01001091 __update_graph_settings(&ge->graphs);
1092
1093 ev = gdk_event_new(GDK_EXPOSE);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001094 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
Jens Axboea1e79722012-03-23 10:52:25 +01001095 gdk_event_free(ev);
Jens Axboeb98ab712012-03-21 12:48:32 +01001096}
1097
Jens Axboe8577f4f2012-03-09 19:28:27 +01001098static void update_graph_limits(void)
1099{
Jens Axboeb98ab712012-03-21 12:48:32 +01001100 struct gui *ui = &main_ui;
Jens Axboea1e79722012-03-23 10:52:25 +01001101 GdkEvent *ev;
Jens Axboe8577f4f2012-03-09 19:28:27 +01001102
Jens Axboea1e79722012-03-23 10:52:25 +01001103 __update_graph_settings(&ui->graphs);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001104
Jens Axboea1e79722012-03-23 10:52:25 +01001105 ev = gdk_event_new(GDK_EXPOSE);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001106 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
Jens Axboea1e79722012-03-23 10:52:25 +01001107 gdk_event_free(ev);
1108
1109 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001110}
1111
Jens Axboe46974a72012-03-02 19:34:13 +01001112static void preferences(GtkWidget *w, gpointer data)
1113{
Jens Axboef3e84402012-03-07 13:14:32 +01001114 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001115 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe6e02ad62012-03-20 12:25:36 +01001116 struct gui *ui = (struct gui *) data;
Jens Axboe46974a72012-03-02 19:34:13 +01001117 int i;
1118
1119 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe6e02ad62012-03-20 12:25:36 +01001120 GTK_WINDOW(ui->window),
Jens Axboe46974a72012-03-02 19:34:13 +01001121 GTK_DIALOG_DESTROY_WITH_PARENT,
1122 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1123 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1124 NULL);
1125
Jens Axboe8577f4f2012-03-09 19:28:27 +01001126 frame = gtk_frame_new("Graphing");
Jens Axboef762cef2012-03-26 13:51:11 +02001127 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1128 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001129 vbox = gtk_vbox_new(FALSE, 6);
1130 gtk_container_add(GTK_CONTAINER(frame), vbox);
1131
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001132 hbox = gtk_hbox_new(FALSE, 5);
1133 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1134 entry = gtk_label_new("Font face to use for graph labels");
1135 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1136
Jens Axboea1e79722012-03-23 10:52:25 +01001137 font = gtk_font_button_new_with_font(gfio_graph_font);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001138 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001139
Jens Axboe8577f4f2012-03-09 19:28:27 +01001140 box = gtk_vbox_new(FALSE, 6);
1141 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1142
1143 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001144 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001145 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1146 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1147
Jens Axboec05d9052012-03-11 13:05:35 +01001148 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001149
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001150 box = gtk_vbox_new(FALSE, 6);
1151 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1152
1153 hbox = gtk_hbox_new(FALSE, 5);
1154 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1155 entry = gtk_label_new("Client ETA request interval (msec)");
1156 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1157
1158 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001159 frame = gtk_frame_new("Debug logging");
Jens Axboef762cef2012-03-26 13:51:11 +02001160 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1161 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001162 vbox = gtk_vbox_new(FALSE, 6);
1163 gtk_container_add(GTK_CONTAINER(frame), vbox);
1164
1165 box = gtk_hbox_new(FALSE, 6);
1166 gtk_container_add(GTK_CONTAINER(vbox), box);
1167
1168 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1169
1170 for (i = 0; i < FD_DEBUG_MAX; i++) {
1171 if (i == 7) {
1172 box = gtk_hbox_new(FALSE, 6);
1173 gtk_container_add(GTK_CONTAINER(vbox), box);
1174 }
1175
1176
1177 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1178 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1179 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1180 }
1181
Jens Axboe46974a72012-03-02 19:34:13 +01001182 gtk_widget_show_all(dialog);
1183
1184 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1185 gtk_widget_destroy(dialog);
1186 return;
1187 }
1188
1189 for (i = 0; i < FD_DEBUG_MAX; i++) {
1190 int set;
1191
1192 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1193 if (set)
1194 fio_debug |= (1UL << i);
1195 }
1196
Jens Axboef3e84402012-03-07 13:14:32 +01001197 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001198 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1199 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001200 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001201
Jens Axboe46974a72012-03-02 19:34:13 +01001202 gtk_widget_destroy(dialog);
1203}
1204
Jens Axboe0420ba62012-02-29 11:16:52 +01001205static void about_dialog(GtkWidget *w, gpointer data)
1206{
Jens Axboe81e4ea62012-03-07 14:18:28 +01001207 const char *authors[] = {
1208 "Jens Axboe <axboe@kernel.dk>",
1209 "Stephen Carmeron <stephenmcameron@gmail.com>",
1210 NULL
1211 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01001212 const char *license[] = {
1213 "Fio is free software; you can redistribute it and/or modify "
1214 "it under the terms of the GNU General Public License as published by "
1215 "the Free Software Foundation; either version 2 of the License, or "
1216 "(at your option) any later version.\n",
1217 "Fio is distributed in the hope that it will be useful, "
1218 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1219 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1220 "GNU General Public License for more details.\n",
1221 "You should have received a copy of the GNU General Public License "
1222 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1223 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1224 };
1225 char *license_trans;
1226
1227 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1228 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01001229
Jens Axboe0420ba62012-02-29 11:16:52 +01001230 gtk_show_about_dialog(NULL,
1231 "program-name", "gfio",
1232 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01001233 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001234 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1235 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01001236 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001237 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01001238 "logo-icon-name", "fio",
1239 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01001240 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01001241 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01001242
Jens Axboe2f99deb2012-03-09 14:37:29 +01001243 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01001244}
1245
1246static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001247 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01001248 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001249 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01001250 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01001251 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001252 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01001253 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1254 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1255 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01001256 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01001257 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboebc271d82012-03-15 21:57:40 +01001258 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01001259 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1260 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1261 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01001262 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1263 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001264};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001265static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001266
1267static const gchar *ui_string = " \
1268 <ui> \
1269 <menubar name=\"MainMenu\"> \
1270 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001271 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01001272 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001273 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001274 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001275 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01001276 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001277 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1278 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001279 <placeholder name=\"FileRecentFiles\"/> \
1280 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001281 <menuitem name=\"Quit\" action=\"Quit\" /> \
1282 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001283 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001284 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001285 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001286 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1287 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001288 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001289 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001290 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01001291 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01001292 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1293 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01001294 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1295 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01001296 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1297 <menuitem name=\"About\" action=\"About\" /> \
1298 </menu> \
1299 </menubar> \
1300 </ui> \
1301";
1302
Jens Axboe4cbe7212012-03-06 13:36:17 +01001303static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1304 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01001305{
Jens Axboeca664f42012-03-14 19:49:40 +01001306 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01001307 GError *error = 0;
1308
1309 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001310 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01001311
1312 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1313 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1314
1315 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01001316
Jens Axboe0420ba62012-02-29 11:16:52 +01001317 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1318}
1319
1320void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
Jens Axboe1252d8f2012-03-21 11:13:31 +01001321 GtkWidget *vbox, GtkUIManager *ui_manager)
Jens Axboe0420ba62012-02-29 11:16:52 +01001322{
Jens Axboe1252d8f2012-03-21 11:13:31 +01001323 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
Jens Axboe0420ba62012-02-29 11:16:52 +01001324}
1325
Jens Axboec80b74b2012-03-12 10:23:28 +01001326static void combo_entry_changed(GtkComboBox *box, gpointer data)
1327{
1328 struct gui_entry *ge = (struct gui_entry *) data;
1329 gint index;
1330
1331 index = gtk_combo_box_get_active(box);
1332
1333 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01001334 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01001335 multitext_set_entry(&ge->eta.ioengine, index);
1336 multitext_set_entry(&ge->eta.iodepth, index);
1337}
1338
1339static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1340{
1341 struct gui_entry *ge = (struct gui_entry *) data;
1342
1343 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01001344 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01001345 multitext_free(&ge->eta.ioengine);
1346 multitext_free(&ge->eta.iodepth);
1347}
1348
Jens Axboe2f99deb2012-03-09 14:37:29 +01001349static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001350{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001351 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001352 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01001353
Jens Axboe2f99deb2012-03-09 14:37:29 +01001354 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001355
Jens Axboe65476332012-03-13 10:37:04 +01001356 top_align = gtk_alignment_new(0, 0, 1, 0);
1357 top_vbox = gtk_vbox_new(FALSE, 3);
1358 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1359 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001360
Jens Axboe3e47bd22012-02-29 13:45:02 +01001361 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001362 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001363 probe_frame = gtk_vbox_new(FALSE, 3);
1364 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1365
1366 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001367 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1368 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1369 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1370 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1371 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01001372
Jens Axboe3e47bd22012-02-29 13:45:02 +01001373 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001374 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1375
Jens Axboe3863d1a2012-03-09 17:39:05 +01001376 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01001377 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1378 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1379 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01001380 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01001381 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1382 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001383 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1384 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1385
1386 probe_box = gtk_hbox_new(FALSE, 3);
1387 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboeb78ca652012-09-25 14:39:36 +02001388 ge->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1389 ge->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1390 ge->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1391 ge->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1392 ge->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1393 ge->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
Jens Axboedbc542d2012-09-24 19:54:07 +02001394
Jens Axboe2f99deb2012-03-09 14:37:29 +01001395 /*
1396 * Only add this if we have a commit rate
1397 */
1398#if 0
1399 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001400 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001401
Jens Axboe2f99deb2012-03-09 14:37:29 +01001402 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1403 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1404
1405 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1406 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1407#endif
1408
1409 /*
1410 * Set up a drawing area and IOPS and bandwidth graphs
1411 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001412 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001413 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001414 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2a95f712012-04-16 11:48:45 +02001415 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001416 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001417 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1418 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1419 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001420 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1421 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001422 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001423 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001424 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001425 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001426
1427 setup_graphs(&ge->graphs);
1428
1429 /*
Jens Axboe3c3ed072012-03-27 09:12:39 +02001430 * Set up alignments for widgets at the bottom of ui,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001431 * align bottom left, expand horizontally but not vertically
1432 */
Jens Axboe65476332012-03-13 10:37:04 +01001433 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001434 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001435 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1436 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001437
Jens Axboe05775432012-03-21 14:07:11 +01001438 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001439
1440 /*
1441 * Set up thread status progress bar
1442 */
1443 ge->thread_status_pb = gtk_progress_bar_new();
1444 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1445 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1446 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1447
1448
1449 return main_vbox;
1450}
1451
1452static GtkWidget *new_main_page(struct gui *ui)
1453{
1454 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001455 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001456
1457 main_vbox = gtk_vbox_new(FALSE, 3);
1458
1459 /*
1460 * Set up alignments for widgets at the top of ui,
1461 * align top left, expand horizontally but not vertically
1462 */
Jens Axboe65476332012-03-13 10:37:04 +01001463 top_align = gtk_alignment_new(0, 0, 1, 0);
1464 top_vbox = gtk_vbox_new(FALSE, 0);
1465 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1466 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001467
1468 probe = gtk_frame_new("Run statistics");
1469 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1470 probe_frame = gtk_vbox_new(FALSE, 3);
1471 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001472
1473 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001474 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01001475 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeb78ca652012-09-25 14:39:36 +02001476 ui->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1477 ui->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B);
1478 ui->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1479 ui->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B);
1480 ui->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
1481 ui->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B);
Jens Axboe807f9972012-03-02 10:25:24 +01001482
1483 /*
1484 * Only add this if we have a commit rate
1485 */
1486#if 0
1487 probe_box = gtk_hbox_new(FALSE, 3);
1488 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1489
Jens Axboe3e47bd22012-02-29 13:45:02 +01001490 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1491 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1492
Jens Axboe3e47bd22012-02-29 13:45:02 +01001493 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1494 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001495#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001496
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001497 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001498 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001499 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001500 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001501 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001502 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2a95f712012-04-16 11:48:45 +02001503 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001504 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001505 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1506 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1507 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001508 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1509 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001510 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001511 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001512 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001513 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01001514 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001515
Jens Axboe2f99deb2012-03-09 14:37:29 +01001516 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001517
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001518 /*
Jens Axboe3c3ed072012-03-27 09:12:39 +02001519 * Set up alignments for widgets at the bottom of ui,
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001520 * align bottom left, expand horizontally but not vertically
1521 */
Jens Axboe65476332012-03-13 10:37:04 +01001522 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001523 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001524 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1525 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001526
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001527 /*
1528 * Set up thread status progress bar
1529 */
1530 ui->thread_status_pb = gtk_progress_bar_new();
1531 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001532 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001533 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1534
Jens Axboe2f99deb2012-03-09 14:37:29 +01001535 return main_vbox;
1536}
1537
1538static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1539 guint page, gpointer data)
1540
1541{
Jens Axboe02421e62012-03-12 12:05:50 +01001542 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01001543 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01001544
Jens Axboe85dd01e2012-03-12 14:33:16 +01001545 if (!page) {
1546 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01001547 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001548 return TRUE;
1549 }
1550
1551 set_job_menu_visible(ui, 1);
Jens Axboe6e02ad62012-03-20 12:25:36 +01001552 ge = get_ge_from_page(ui, page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001553 if (ge)
1554 update_button_states(ui, ge);
1555
Jens Axboe2f99deb2012-03-09 14:37:29 +01001556 return TRUE;
1557}
1558
Jens Axboe38634cb2012-03-13 12:26:41 +01001559static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1560{
1561 time_t time_a = gtk_recent_info_get_visited(a);
1562 time_t time_b = gtk_recent_info_get_visited(b);
1563
1564 return time_b - time_a;
1565}
1566
1567static void add_recent_file_items(struct gui *ui)
1568{
1569 const gchar *gfio = g_get_application_name();
1570 GList *items, *item;
1571 int i = 0;
1572
1573 if (ui->recent_ui_id) {
1574 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1575 gtk_ui_manager_ensure_update(ui->uimanager);
1576 }
1577 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1578
1579 if (ui->actiongroup) {
1580 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1581 g_object_unref(ui->actiongroup);
1582 }
1583 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1584
1585 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1586
1587 items = gtk_recent_manager_get_items(ui->recentmanager);
1588 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1589
1590 for (item = items; item && item->data; item = g_list_next(item)) {
1591 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1592 gchar *action_name;
1593 const gchar *label;
1594 GtkAction *action;
1595
1596 if (!gtk_recent_info_has_application(info, gfio))
1597 continue;
1598
1599 /*
1600 * We only support local files for now
1601 */
1602 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1603 continue;
1604
1605 action_name = g_strdup_printf("RecentFile%u", i++);
1606 label = gtk_recent_info_get_display_name(info);
1607
1608 action = g_object_new(GTK_TYPE_ACTION,
1609 "name", action_name,
1610 "label", label, NULL);
1611
1612 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1613 gtk_recent_info_ref(info),
1614 (GDestroyNotify) gtk_recent_info_unref);
1615
1616
1617 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1618
1619 gtk_action_group_add_action(ui->actiongroup, action);
1620 g_object_unref(action);
1621
1622 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1623 "/MainMenu/FileMenu/FileRecentFiles",
1624 label, action_name,
1625 GTK_UI_MANAGER_MENUITEM, FALSE);
1626
1627 g_free(action_name);
1628
1629 if (i == 8)
1630 break;
1631 }
1632
1633 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1634 g_list_free(items);
1635}
1636
Jens Axboea6790902012-03-13 15:16:11 +01001637static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
Jens Axboe6e02ad62012-03-20 12:25:36 +01001638 gint x, gint y, GtkSelectionData *seldata,
1639 guint info, guint time, gpointer *data)
Jens Axboea6790902012-03-13 15:16:11 +01001640{
Jens Axboe6e02ad62012-03-20 12:25:36 +01001641 struct gui *ui = (struct gui *) data;
Jens Axboea6790902012-03-13 15:16:11 +01001642 gchar **uris;
1643 GtkWidget *source;
Jens Axboea6790902012-03-13 15:16:11 +01001644
1645 source = gtk_drag_get_source_widget(ctx);
1646 if (source && widget == gtk_widget_get_toplevel(source)) {
1647 gtk_drag_finish(ctx, FALSE, FALSE, time);
1648 return;
1649 }
1650
Jens Axboe6e02ad62012-03-20 12:25:36 +01001651 uris = gtk_selection_data_get_uris(seldata);
Jens Axboea6790902012-03-13 15:16:11 +01001652 if (!uris) {
1653 gtk_drag_finish(ctx, FALSE, FALSE, time);
1654 return;
1655 }
1656
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001657 if (uris[0])
1658 do_file_open_with_tab(ui, uris[0]);
Jens Axboea6790902012-03-13 15:16:11 +01001659
1660 gtk_drag_finish(ctx, TRUE, FALSE, time);
1661 g_strfreev(uris);
1662}
1663
Jens Axboe2f99deb2012-03-09 14:37:29 +01001664static void init_ui(int *argc, char **argv[], struct gui *ui)
1665{
1666 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01001667 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001668
1669 /* Magical g*thread incantation, you just need this thread stuff.
1670 * Without it, the update that happens in gfio_update_thread_status
1671 * doesn't really happen in a timely fashion, you need expose events
1672 */
1673 if (!g_thread_supported())
1674 g_thread_init(NULL);
1675 gdk_threads_init();
1676
1677 gtk_init(argc, argv);
1678 settings = gtk_settings_get_default();
1679 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1680 g_type_init();
Jens Axboe2a95f712012-04-16 11:48:45 +02001681 gdk_color_parse("#ffffee", &gfio_color_lightyellow);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001682 gdk_color_parse("white", &gfio_color_white);
Jens Axboe3c3ed072012-03-27 09:12:39 +02001683
Jens Axboe2f99deb2012-03-09 14:37:29 +01001684 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01001685 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001686 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1687
Jens Axboe6e02ad62012-03-20 12:25:36 +01001688 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1689 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001690
1691 ui->vbox = gtk_vbox_new(FALSE, 0);
1692 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1693
Jens Axboe02421e62012-03-12 12:05:50 +01001694 ui->uimanager = gtk_ui_manager_new();
1695 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1696 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001697
Jens Axboe38634cb2012-03-13 12:26:41 +01001698 ui->recentmanager = gtk_recent_manager_get_default();
1699 add_recent_file_items(ui);
1700
Jens Axboe2f99deb2012-03-09 14:37:29 +01001701 ui->notebook = gtk_notebook_new();
1702 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01001703 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01001704 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001705 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1706
1707 vbox = new_main_page(ui);
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001708 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
Jens Axboea6790902012-03-13 15:16:11 +01001709 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1710 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001711
1712 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1713
Jens Axboe9b260bd2012-03-06 11:02:52 +01001714 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001715
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001716 gtk_widget_show_all(ui->window);
1717}
1718
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001719int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001720{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001721 if (initialize_fio(envp))
1722 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001723 if (fio_init_options())
1724 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001725
Jens Axboe753e9e62012-03-24 20:22:29 +01001726 gopt_init();
1727
Jens Axboe2f99deb2012-03-09 14:37:29 +01001728 memset(&main_ui, 0, sizeof(main_ui));
Jens Axboeb98ab712012-03-21 12:48:32 +01001729 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001730
1731 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001732
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001733 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001734 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001735 gdk_threads_leave();
Jens Axboeb98ab712012-03-21 12:48:32 +01001736
1737 g_hash_table_destroy(main_ui.ge_hash);
Jens Axboe753e9e62012-03-24 20:22:29 +01001738
1739 gopt_exit();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001740 return 0;
1741}