blob: 1ee2745c1f980ac62789f255ae1d6cb43c130ac6 [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 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 Axboea1e79722012-03-23 10:52:25 +010044const char *gfio_graph_font = GRAPH_DEFAULT_FONT;
Jens Axboe3e47bd22012-02-29 13:45:02 +010045
Stephen M. Cameronf3074002012-02-24 08:17:30 +010046typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47
Jens Axboe3e47bd22012-02-29 13:45:02 +010048static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010049static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010050static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010051
52static struct button_spec {
53 const char *buttontext;
54 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010055 const char *tooltiptext[2];
56 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010057} buttonspeclist[] = {
Jens Axboe53e0e852012-03-15 19:38:01 +010058 {
59 .buttontext = "Connect",
60 .f = connect_clicked,
61 .tooltiptext = { "Disconnect from host", "Connect to host" },
62 .start_sensitive = 1,
63 },
64 {
65 .buttontext = "Send",
66 .f = send_clicked,
67 .tooltiptext = { "Send job description to host", NULL },
68 .start_sensitive = 0,
69 },
70 {
71 .buttontext = "Start Job",
72 .f = start_job_clicked,
73 .tooltiptext = { "Start the current job on the server", NULL },
74 .start_sensitive = 0,
75 },
Jens Axboee0681f32012-03-06 12:14:42 +010076};
77
Jens Axboe8dfd6072012-03-22 22:10:37 +010078static void setup_iops_graph(struct gfio_graphs *gg)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010079{
Jens Axboe2f99deb2012-03-09 14:37:29 +010080 struct graph *g;
81
82 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +010083 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010084 graph_x_title(g, "Time (secs)");
Jens Axboe8dfd6072012-03-22 22:10:37 +010085 gg->read_iops = graph_add_label(g, "Read IOPS");
86 gg->write_iops = graph_add_label(g, "Write IOPS");
87 graph_set_color(g, gg->read_iops, 0.13, 0.54, 0.13);
88 graph_set_color(g, gg->write_iops, 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +010089 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +010090 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe01a947f2012-03-22 21:21:00 +010091 graph_set_graph_all_zeroes(g, 0);
Jens Axboe75450782012-03-23 08:15:46 +010092 gg->iops_graph = g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +010093}
94
Jens Axboe8dfd6072012-03-22 22:10:37 +010095static void setup_bandwidth_graph(struct gfio_graphs *gg)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010096{
Jens Axboe2f99deb2012-03-09 14:37:29 +010097 struct graph *g;
98
99 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100100 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100101 graph_x_title(g, "Time (secs)");
Jens Axboe8dfd6072012-03-22 22:10:37 +0100102 gg->read_bw = graph_add_label(g, "Read Bandwidth");
103 gg->write_bw = graph_add_label(g, "Write Bandwidth");
104 graph_set_color(g, gg->read_bw, 0.13, 0.54, 0.13);
105 graph_set_color(g, gg->write_bw, 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100106 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100107 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100108 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe01a947f2012-03-22 21:21:00 +0100109 graph_set_graph_all_zeroes(g, 0);
Jens Axboe75450782012-03-23 08:15:46 +0100110 gg->bandwidth_graph = g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100111}
112
Jens Axboe2f99deb2012-03-09 14:37:29 +0100113static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100114{
Jens Axboe8dfd6072012-03-22 22:10:37 +0100115 setup_iops_graph(g);
116 setup_bandwidth_graph(g);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100117}
118
Jens Axboe1252d8f2012-03-21 11:13:31 +0100119void clear_ge_ui_info(struct gui_entry *ge)
Jens Axboe2f99deb2012-03-09 14:37:29 +0100120{
121 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
122 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
123 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
124 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100125#if 0
126 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100127 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100128#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100129 multitext_update_entry(&ge->eta.iotype, 0, "");
Jens Axboe99d633a2012-03-15 15:55:04 +0100130 multitext_update_entry(&ge->eta.bs, 0, "");
Jens Axboec80b74b2012-03-12 10:23:28 +0100131 multitext_update_entry(&ge->eta.ioengine, 0, "");
132 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100133 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
134 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
137 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
138 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100139}
140
Jens Axboe781ccba2012-03-15 09:44:42 +0100141static void set_menu_entry_text(struct gui *ui, const char *path,
142 const char *text)
143{
144 GtkWidget *w;
145
146 w = gtk_ui_manager_get_widget(ui->uimanager, path);
147 if (w)
148 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
149 else
150 fprintf(stderr, "gfio: can't find path %s\n", path);
151}
152
153
154static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
155{
156 GtkWidget *w;
157
158 w = gtk_ui_manager_get_widget(ui->uimanager, path);
159 if (w)
160 gtk_widget_set_sensitive(w, show);
161 else
162 fprintf(stderr, "gfio: can't find path %s\n", path);
163}
164
165static void set_job_menu_visible(struct gui *ui, int visible)
166{
167 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
168}
169
170static void set_view_results_visible(struct gui *ui, int visible)
171{
172 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
173}
174
Jens Axboe014f4022012-03-15 14:03:01 +0100175static const char *get_button_tooltip(struct button_spec *s, int sensitive)
176{
177 if (s->tooltiptext[sensitive])
178 return s->tooltiptext[sensitive];
179
180 return s->tooltiptext[0];
181}
182
183static GtkWidget *add_button(GtkWidget *buttonbox,
184 struct button_spec *buttonspec, gpointer data)
185{
186 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
187 gboolean sens = buttonspec->start_sensitive;
188
189 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
190 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
191
192 sens = buttonspec->start_sensitive;
193 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
194 gtk_widget_set_sensitive(button, sens);
195
196 return button;
197}
198
199static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
200 int nbuttons)
201{
202 int i;
203
204 for (i = 0; i < nbuttons; i++)
205 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
206}
207
Jens Axboe85dd01e2012-03-12 14:33:16 +0100208/*
209 * Update sensitivity of job buttons and job menu items, based on the
210 * state of the client.
211 */
212static void update_button_states(struct gui *ui, struct gui_entry *ge)
213{
214 unsigned int connect_state, send_state, start_state, edit_state;
215 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100216
217 switch (ge->state) {
Jens Axboe1252d8f2012-03-21 11:13:31 +0100218 default:
219 gfio_report_error(ge, "Bad client state: %u\n", ge->state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100220 /* fall through to new state */
Jens Axboe85dd01e2012-03-12 14:33:16 +0100221 case GE_STATE_NEW:
222 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100223 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100224 connect_str = "Connect";
225 send_state = 0;
226 start_state = 0;
227 break;
228 case GE_STATE_CONNECTED:
229 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100230 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100231 connect_str = "Disconnect";
232 send_state = 1;
233 start_state = 0;
234 break;
235 case GE_STATE_JOB_SENT:
236 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100237 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100238 connect_str = "Disconnect";
239 send_state = 0;
240 start_state = 1;
241 break;
242 case GE_STATE_JOB_STARTED:
243 connect_state = 1;
244 edit_state = 1;
245 connect_str = "Disconnect";
246 send_state = 0;
247 start_state = 1;
248 break;
249 case GE_STATE_JOB_RUNNING:
250 connect_state = 1;
251 edit_state = 0;
252 connect_str = "Disconnect";
253 send_state = 0;
254 start_state = 0;
255 break;
256 case GE_STATE_JOB_DONE:
257 connect_state = 1;
258 edit_state = 0;
259 connect_str = "Connect";
260 send_state = 0;
261 start_state = 0;
262 break;
263 }
264
Jens Axboe53e0e852012-03-15 19:38:01 +0100265 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
266 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
267 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
268 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
269 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 +0100270
Jens Axboe781ccba2012-03-15 09:44:42 +0100271 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
272 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100273
Jens Axboe781ccba2012-03-15 09:44:42 +0100274 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
275 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
276 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100277
Jens Axboe781ccba2012-03-15 09:44:42 +0100278 if (ge->client && ge->client->nr_results)
279 set_view_results_visible(ui, 1);
280 else
281 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100282}
283
Jens Axboe1252d8f2012-03-21 11:13:31 +0100284void gfio_set_state(struct gui_entry *ge, unsigned int state)
Jens Axboe85dd01e2012-03-12 14:33:16 +0100285{
286 ge->state = state;
287 update_button_states(ge->ui, ge);
288}
289
Jens Axboe9b260bd2012-03-06 11:02:52 +0100290static void gfio_ui_setup_log(struct gui *ui)
291{
292 GtkTreeSelection *selection;
293 GtkListStore *model;
294 GtkWidget *tree_view;
295
296 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
297
298 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
299 gtk_widget_set_can_focus(tree_view, FALSE);
300
301 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
302 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100303 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
304 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100305
306 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
307 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
308 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100309 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100310
311 ui->log_model = model;
312 ui->log_tree = tree_view;
313}
314
Jens Axboe2f99deb2012-03-09 14:37:29 +0100315static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
316 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +0100317{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100318 struct gfio_graphs *g = data;
319
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100320 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
321 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
322 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
323 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +0100324 return TRUE;
325}
326
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100327static void draw_graph(struct graph *g, cairo_t *cr)
328{
329 line_graph_draw(g, cr);
330 cairo_stroke(cr);
331}
332
Jens Axboe93e2db22012-03-13 09:45:22 +0100333static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
334 gboolean keyboard_mode, GtkTooltip *tooltip,
335 gpointer data)
336{
337 struct gfio_graphs *g = data;
338 const char *text = NULL;
339
340 if (graph_contains_xy(g->iops_graph, x, y))
341 text = graph_find_tooltip(g->iops_graph, x, y);
342 else if (graph_contains_xy(g->bandwidth_graph, x, y))
343 text = graph_find_tooltip(g->bandwidth_graph, x, y);
344
345 if (text) {
346 gtk_tooltip_set_text(tooltip, text);
347 return TRUE;
348 }
349
350 return FALSE;
351}
352
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100353static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
354{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100355 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100356 cairo_t *cr;
357
358 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +0100359
360 if (graph_has_tooltips(g->iops_graph) ||
361 graph_has_tooltips(g->bandwidth_graph)) {
362 g_object_set(w, "has-tooltip", TRUE, NULL);
363 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
364 }
365
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100366 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100367 draw_graph(g->iops_graph, cr);
368 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100369 cairo_destroy(cr);
370
371 return FALSE;
372}
373
Jens Axboe2f99deb2012-03-09 14:37:29 +0100374/*
Jens Axboe0fd18982012-03-14 10:34:48 +0100375 * FIXME: need more handling here
376 */
377static void ge_destroy(struct gui_entry *ge)
378{
379 struct gfio_client *gc = ge->client;
380
Jens Axboe470cdbb2012-03-21 12:02:13 +0100381 if (gc) {
382 if (gc->client) {
383 if (ge->state >= GE_STATE_CONNECTED)
384 fio_client_terminate(gc->client);
Jens Axboe0fd18982012-03-14 10:34:48 +0100385
Jens Axboe470cdbb2012-03-21 12:02:13 +0100386 fio_put_client(gc->client);
387 }
388 free(gc);
Jens Axboe0fd18982012-03-14 10:34:48 +0100389 }
390
Jens Axboeb98ab712012-03-21 12:48:32 +0100391 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
392
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100393 free(ge->job_file);
394 free(ge->host);
Jens Axboe0fd18982012-03-14 10:34:48 +0100395 free(ge);
396}
397
398static void ge_widget_destroy(GtkWidget *w, gpointer data)
399{
Jens Axboe2eb98bf2012-03-20 08:20:48 +0100400 struct gui_entry *ge = (struct gui_entry *) data;
401
402 ge_destroy(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +0100403}
404
405static void gfio_quit(struct gui *ui)
406{
Jens Axboe0fd18982012-03-14 10:34:48 +0100407 gtk_main_quit();
408}
409
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100410static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
411 __attribute__((unused)) gpointer data)
412{
Jens Axboe6e02ad62012-03-20 12:25:36 +0100413 struct gui *ui = (struct gui *) data;
414
415 gfio_quit(ui);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100416}
417
Stephen M. Cameron25927252012-02-24 08:17:31 +0100418static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100419{
Jens Axboea9eccde2012-03-09 14:59:42 +0100420 struct gui *ui = arg;
421
422 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100423 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +0100424 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100425 return NULL;
426}
427
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100428static int send_job_file(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100429{
Jens Axboe9988ca72012-03-09 15:14:06 +0100430 struct gfio_client *gc = ge->client;
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100431 int ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100432
Jens Axboe753e9e62012-03-24 20:22:29 +0100433 /*
434 * Prune old options, we are expecting the return options
435 * when the job file is parsed remotely and returned to us.
436 */
437 while (!flist_empty(&gc->o_list)) {
438 struct gfio_client_options *gco;
439
440 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list);
441 flist_del(&gco->list);
442 free(gco);
443 }
444
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100445 ret = fio_client_send_ini(gc->client, ge->job_file);
446 if (!ret)
447 return 0;
Jens Axboec7249262012-03-09 17:11:04 +0100448
Jens Axboe41666582012-03-21 10:25:29 +0100449 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100450 return 1;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100451}
452
Jens Axboe63a130b2012-03-06 20:08:59 +0100453static void *server_thread(void *arg)
454{
455 is_backend = 1;
456 gfio_server_running = 1;
457 fio_start_server(NULL);
458 gfio_server_running = 0;
459 return NULL;
460}
461
Jens Axboe6e02ad62012-03-20 12:25:36 +0100462static void gfio_start_server(struct gui *ui)
Jens Axboe63a130b2012-03-06 20:08:59 +0100463{
464 if (!gfio_server_running) {
465 gfio_server_running = 1;
466 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +0100467 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +0100468 }
469}
470
Stephen M. Cameron25927252012-02-24 08:17:31 +0100471static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
472 gpointer data)
473{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100474 struct gui_entry *ge = data;
475 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100476
Jens Axboe78cb2fe2012-03-12 23:05:29 +0100477 if (gc)
478 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100479}
480
Jens Axboedf06f222012-03-02 13:32:04 +0100481static void file_open(GtkWidget *w, gpointer data);
482
Jens Axboe62bc9372012-03-07 11:45:07 +0100483struct connection_widgets
484{
485 GtkWidget *hentry;
486 GtkWidget *combo;
487 GtkWidget *button;
488};
489
490static void hostname_cb(GtkEntry *entry, gpointer data)
491{
492 struct connection_widgets *cw = data;
493 int uses_net = 0, is_localhost = 0;
494 const gchar *text;
495 gchar *ctext;
496
497 /*
498 * Check whether to display the 'auto start backend' box
499 * or not. Show it if we are a localhost and using network,
500 * or using a socket.
501 */
Jens Axboef762cef2012-03-26 13:51:11 +0200502 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
Jens Axboe62bc9372012-03-07 11:45:07 +0100503 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
504 uses_net = 1;
505 g_free(ctext);
506
507 if (uses_net) {
508 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
509 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
510 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
511 !strcmp(text, "ip6-loopback"))
512 is_localhost = 1;
513 }
514
515 if (!uses_net || is_localhost) {
516 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
517 gtk_widget_set_sensitive(cw->button, 1);
518 } else {
519 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
520 gtk_widget_set_sensitive(cw->button, 0);
521 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100522}
Jens Axboea7a42ce2012-03-02 13:12:04 +0100523
Jens Axboeb0500cc2012-03-21 11:59:18 +0100524static int get_connection_details(struct gui_entry *ge)
525{
526 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
527 struct connection_widgets cw;
528 struct gui *ui = ge->ui;
529 char *typeentry;
Jens Axboea7a42ce2012-03-02 13:12:04 +0100530
Jens Axboeb0500cc2012-03-21 11:59:18 +0100531 if (ge->host)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100532 return 0;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100533
Jens Axboeb0500cc2012-03-21 11:59:18 +0100534 dialog = gtk_dialog_new_with_buttons("Connection details",
535 GTK_WINDOW(ui->window),
536 GTK_DIALOG_DESTROY_WITH_PARENT,
537 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
538 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100539
Jens Axboeb0500cc2012-03-21 11:59:18 +0100540 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef762cef2012-03-26 13:51:11 +0200541 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100542 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100543
Jens Axboeb0500cc2012-03-21 11:59:18 +0100544 box = gtk_vbox_new(FALSE, 6);
545 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100546
Jens Axboeb0500cc2012-03-21 11:59:18 +0100547 hbox = gtk_hbox_new(TRUE, 10);
548 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
549 cw.hentry = gtk_entry_new();
550 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
551 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100552
Jens Axboeb0500cc2012-03-21 11:59:18 +0100553 frame = gtk_frame_new("Port");
554 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
555 box = gtk_vbox_new(FALSE, 10);
556 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100557
Jens Axboeb0500cc2012-03-21 11:59:18 +0100558 hbox = gtk_hbox_new(TRUE, 4);
559 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
560 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100561
Jens Axboeb0500cc2012-03-21 11:59:18 +0100562 frame = gtk_frame_new("Type");
563 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
564 box = gtk_vbox_new(FALSE, 10);
565 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100566
Jens Axboeb0500cc2012-03-21 11:59:18 +0100567 hbox = gtk_hbox_new(TRUE, 4);
568 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100569
Jens Axboef762cef2012-03-26 13:51:11 +0200570 cw.combo = gtk_combo_box_text_new();
571 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
572 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
573 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
Jens Axboeb0500cc2012-03-21 11:59:18 +0100574 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100575
Jens Axboeb0500cc2012-03-21 11:59:18 +0100576 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100577
Jens Axboeb0500cc2012-03-21 11:59:18 +0100578 frame = gtk_frame_new("Options");
579 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
580 box = gtk_vbox_new(FALSE, 10);
581 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe38634cb2012-03-13 12:26:41 +0100582
Jens Axboeb0500cc2012-03-21 11:59:18 +0100583 hbox = gtk_hbox_new(TRUE, 4);
584 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100585
Jens Axboeb0500cc2012-03-21 11:59:18 +0100586 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
587 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
588 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.");
589 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100590
Jens Axboe1252d8f2012-03-21 11:13:31 +0100591 /*
Jens Axboeb0500cc2012-03-21 11:59:18 +0100592 * Connect edit signal, so we can show/not-show the auto start button
Jens Axboe1252d8f2012-03-21 11:13:31 +0100593 */
Jens Axboecd972fd2012-03-26 12:55:33 +0200594 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
595 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboe0420ba62012-02-29 11:16:52 +0100596
Jens Axboeb0500cc2012-03-21 11:59:18 +0100597 gtk_widget_show_all(dialog);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100598
Jens Axboeb0500cc2012-03-21 11:59:18 +0100599 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
600 gtk_widget_destroy(dialog);
Jens Axboea6790902012-03-13 15:16:11 +0100601 return 1;
602 }
603
Jens Axboeb0500cc2012-03-21 11:59:18 +0100604 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
605 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
Jens Axboea6790902012-03-13 15:16:11 +0100606
Jens Axboef762cef2012-03-26 13:51:11 +0200607 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100608 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
609 ge->type = Fio_client_ipv4;
610 else if (!strncmp(typeentry, "IPv6", 4))
611 ge->type = Fio_client_ipv6;
612 else
613 ge->type = Fio_client_socket;
614 g_free(typeentry);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100615
Jens Axboeb0500cc2012-03-21 11:59:18 +0100616 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboe1252d8f2012-03-21 11:13:31 +0100617
Jens Axboeb0500cc2012-03-21 11:59:18 +0100618 gtk_widget_destroy(dialog);
619 return 0;
620}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100621
Jens Axboeb0500cc2012-03-21 11:59:18 +0100622static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
623{
624 gc->client = fio_get_client(client);
625 client->client_data = gc;
626}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100627
Jens Axboeb0500cc2012-03-21 11:59:18 +0100628static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
629{
Jens Axboecf3d8242012-03-24 08:56:50 +0100630 struct gfio_client_options *gco;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100631 struct gfio_client *gc;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100632
Jens Axboecf3d8242012-03-24 08:56:50 +0100633 gc = calloc(1, sizeof(*gc));
634 INIT_FLIST_HEAD(&gc->o_list);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100635 gc->ge = ge;
636 ge->client = gc;
637 gfio_set_client(gc, client);
Jens Axboecf3d8242012-03-24 08:56:50 +0100638
639 /*
640 * Just add a default set of options, need to consider how best
641 * to handle this
642 */
643 gco = calloc(1, sizeof(*gco));
Jens Axboe753e9e62012-03-24 20:22:29 +0100644 INIT_FLIST_HEAD(&gco->list);
Jens Axboecf3d8242012-03-24 08:56:50 +0100645 options_default_fill(&gco->o);
646 flist_add_tail(&gco->list, &gc->o_list);
Jens Axboe753e9e62012-03-24 20:22:29 +0100647 gc->o_list_nr++;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100648}
Jens Axboea6790902012-03-13 15:16:11 +0100649
Jens Axboeb0500cc2012-03-21 11:59:18 +0100650static void connect_clicked(GtkWidget *widget, gpointer data)
651{
652 struct gui_entry *ge = data;
653 struct gfio_client *gc = ge->client;
Jens Axboea6790902012-03-13 15:16:11 +0100654
Jens Axboeb0500cc2012-03-21 11:59:18 +0100655 if (ge->state == GE_STATE_NEW) {
656 int ret;
Jens Axboea6790902012-03-13 15:16:11 +0100657
Jens Axboeb0500cc2012-03-21 11:59:18 +0100658 if (!ge->job_file)
659 file_open(widget, ge->ui);
660 if (!ge->job_file)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100661 return;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100662
663 gc = ge->client;
664
665 if (!gc->client) {
666 struct fio_client *client;
667
668 if (get_connection_details(ge)) {
669 gfio_report_error(ge, "Failed to get connection details\n");
670 return;
671 }
672
673 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
674 if (!client) {
675 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
676 free(ge->host);
677 ge->host = NULL;
678 return;
679 }
680 gfio_set_client(gc, client);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100681 }
Jens Axboe0420ba62012-02-29 11:16:52 +0100682
Jens Axboeb0500cc2012-03-21 11:59:18 +0100683 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
684 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
685 ret = fio_client_connect(gc->client);
686 if (!ret) {
687 if (!ge->ui->handler_running)
688 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
689 gfio_set_state(ge, GE_STATE_CONNECTED);
690 } else {
691 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
692 }
693 } else {
694 fio_client_terminate(gc->client);
695 gfio_set_state(ge, GE_STATE_NEW);
696 clear_ge_ui_info(ge);
697 }
698}
699
700static void send_clicked(GtkWidget *widget, gpointer data)
701{
702 struct gui_entry *ge = data;
703
704 if (send_job_file(ge))
705 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
706}
707
708static GtkWidget *new_client_page(struct gui_entry *ge);
709
710static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
711{
712 struct gui_entry *ge;
713
714 ge = malloc(sizeof(*ge));
715 memset(ge, 0, sizeof(*ge));
716 ge->state = GE_STATE_NEW;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100717 ge->ui = ui;
718 return ge;
719}
720
721static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
722{
723 struct gui_entry *ge;
724
725 ge = alloc_new_gui_entry(ui);
726
727 ge->vbox = new_client_page(ge);
728 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
729
730 ge->page_label = gtk_label_new(name);
731 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
732
Jens Axboeb98ab712012-03-21 12:48:32 +0100733 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
734
Jens Axboeb0500cc2012-03-21 11:59:18 +0100735 gtk_widget_show_all(ui->window);
736 return ge;
737}
738
739static void file_new(GtkWidget *w, gpointer data)
740{
741 struct gui *ui = (struct gui *) data;
742 struct gui_entry *ge;
743
744 ge = get_new_ge_with_tab(ui, "Untitled");
745 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
746}
747
748/*
749 * Return the 'ge' corresponding to the tab. If the active tab is the
750 * main tab, open a new tab.
751 */
752static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
753 int *created)
754{
Jens Axboeb0500cc2012-03-21 11:59:18 +0100755 if (!cur_page) {
756 if (created)
757 *created = 1;
758 return get_new_ge_with_tab(ui, "Untitled");
759 }
760
761 if (created)
762 *created = 0;
763
Jens Axboeb98ab712012-03-21 12:48:32 +0100764 return g_hash_table_lookup(ui->ge_hash, &cur_page);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100765}
766
767static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
768{
769 gint cur_page;
770
771 /*
772 * Main tab is tab 0, so any current page other than 0 holds
773 * a ge entry.
774 */
775 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
776 if (cur_page)
777 return get_ge_from_page(ui, cur_page, NULL);
778
779 return NULL;
780}
781
782static void file_close(GtkWidget *w, gpointer data)
783{
784 struct gui *ui = (struct gui *) data;
785 struct gui_entry *ge;
786
787 /*
788 * Can't close the main tab
789 */
790 ge = get_ge_from_cur_tab(ui);
791 if (ge) {
792 gtk_widget_destroy(ge->vbox);
793 return;
794 }
795
Jens Axboeb98ab712012-03-21 12:48:32 +0100796 if (g_hash_table_size(ui->ge_hash)) {
Jens Axboeb0500cc2012-03-21 11:59:18 +0100797 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
798 return;
799 }
800
801 gfio_quit(ui);
802}
803
804static void file_add_recent(struct gui *ui, const gchar *uri)
805{
806 GtkRecentData grd;
807
808 memset(&grd, 0, sizeof(grd));
809 grd.display_name = strdup("gfio");
810 grd.description = strdup("Fio job file");
811 grd.mime_type = strdup(GFIO_MIME);
812 grd.app_name = strdup(g_get_application_name());
813 grd.app_exec = strdup("gfio %f/%u");
814
815 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
816}
817
818static gchar *get_filename_from_uri(const gchar *uri)
819{
820 if (strncmp(uri, "file://", 7))
821 return strdup(uri);
822
823 return strdup(uri + 7);
824}
825
826static int do_file_open(struct gui_entry *ge, const gchar *uri)
827{
828 struct fio_client *client;
829
830 assert(!ge->job_file);
831
832 ge->job_file = get_filename_from_uri(uri);
833
834 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
835 if (client) {
Jens Axboe832fde72012-03-21 12:55:49 +0100836 char *label = strdup(uri);
837
838 basename(label);
839 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
840 free(label);
841
Jens Axboeb0500cc2012-03-21 11:59:18 +0100842 gfio_client_added(ge, client);
843 file_add_recent(ge->ui, uri);
844 return 0;
845 }
846
847 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
848 free(ge->host);
849 ge->host = NULL;
Jens Axboeb98ab712012-03-21 12:48:32 +0100850 free(ge->job_file);
851 ge->job_file = NULL;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100852 return 1;
853}
854
855static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
856{
857 struct gui_entry *ge;
858 gint cur_page;
859 int ret, ge_is_new = 0;
860
861 /*
862 * Creates new tab if current tab is the main window, or the
863 * current tab already has a client.
864 */
865 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
866 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
867 if (ge->client) {
868 ge = get_new_ge_with_tab(ui, "Untitled");
869 ge_is_new = 1;
870 }
871
872 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
873
874 if (get_connection_details(ge)) {
875 if (ge_is_new)
876 gtk_widget_destroy(ge->vbox);
877
878 return 1;
879 }
880
881 ret = do_file_open(ge, uri);
882
883 if (!ret) {
884 if (ge->server_start)
885 gfio_start_server(ui);
886 } else {
887 if (ge_is_new)
888 gtk_widget_destroy(ge->vbox);
889 }
890
891 return ret;
892}
893
894static void recent_open(GtkAction *action, gpointer data)
895{
896 struct gui *ui = (struct gui *) data;
897 GtkRecentInfo *info;
898 const gchar *uri;
899
900 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
901 uri = gtk_recent_info_get_uri(info);
902
903 do_file_open_with_tab(ui, uri);
904}
905
906static void file_open(GtkWidget *w, gpointer data)
907{
908 struct gui *ui = data;
909 GtkWidget *dialog;
910 GtkFileFilter *filter;
911 gchar *filename;
912
913 dialog = gtk_file_chooser_dialog_new("Open File",
914 GTK_WINDOW(ui->window),
915 GTK_FILE_CHOOSER_ACTION_OPEN,
916 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
917 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
918 NULL);
919 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
920
921 filter = gtk_file_filter_new();
922 gtk_file_filter_add_pattern(filter, "*.fio");
923 gtk_file_filter_add_pattern(filter, "*.job");
924 gtk_file_filter_add_pattern(filter, "*.ini");
925 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
926 gtk_file_filter_set_name(filter, "Fio job file");
927 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
928
929 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
930 gtk_widget_destroy(dialog);
931 return;
932 }
933
934 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
935
936 gtk_widget_destroy(dialog);
937
938 do_file_open_with_tab(ui, filename);
939 g_free(filename);
940}
941
942static void file_save(GtkWidget *w, gpointer data)
943{
944 struct gui *ui = data;
945 GtkWidget *dialog;
946
947 dialog = gtk_file_chooser_dialog_new("Save File",
948 GTK_WINDOW(ui->window),
949 GTK_FILE_CHOOSER_ACTION_SAVE,
950 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
951 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
952 NULL);
953
954 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
955 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
956
957 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
958 char *filename;
959
Jens Axboe0420ba62012-02-29 11:16:52 +0100960 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100961 // save_job_file(filename);
Jens Axboe0420ba62012-02-29 11:16:52 +0100962 g_free(filename);
963 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100964 gtk_widget_destroy(dialog);
965}
Jens Axboe0420ba62012-02-29 11:16:52 +0100966
Jens Axboe9b260bd2012-03-06 11:02:52 +0100967static void view_log_destroy(GtkWidget *w, gpointer data)
968{
969 struct gui *ui = (struct gui *) data;
970
Jens Axboef762cef2012-03-26 13:51:11 +0200971 g_object_ref(G_OBJECT(ui->log_tree));
Jens Axboe9b260bd2012-03-06 11:02:52 +0100972 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
973 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +0100974 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100975}
976
Jens Axboe1252d8f2012-03-21 11:13:31 +0100977void gfio_view_log(struct gui *ui)
Jens Axboe9b260bd2012-03-06 11:02:52 +0100978{
Jens Axboe4cbe7212012-03-06 13:36:17 +0100979 GtkWidget *win, *scroll, *vbox, *box;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100980
Jens Axboe4cbe7212012-03-06 13:36:17 +0100981 if (ui->log_view)
982 return;
983
984 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100985 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +0100986 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100987
Jens Axboe4cbe7212012-03-06 13:36:17 +0100988 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100989
Jens Axboe4cbe7212012-03-06 13:36:17 +0100990 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
991
992 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
993
994 box = gtk_hbox_new(TRUE, 0);
Jens Axboef762cef2012-03-26 13:51:11 +0200995 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
Jens Axboe4cbe7212012-03-06 13:36:17 +0100996 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
997 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
998
999 vbox = gtk_vbox_new(TRUE, 5);
Jens Axboef762cef2012-03-26 13:51:11 +02001000 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001001
1002 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001003 gtk_widget_show_all(win);
1004}
1005
Jens Axboe1252d8f2012-03-21 11:13:31 +01001006static void view_log(GtkWidget *w, gpointer data)
1007{
1008 struct gui *ui = (struct gui *) data;
1009
1010 gfio_view_log(ui);
1011}
1012
Jens Axboe85dd01e2012-03-12 14:33:16 +01001013static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001014{
Jens Axboe85dd01e2012-03-12 14:33:16 +01001015 struct gui *ui = (struct gui *) data;
1016 struct gui_entry *ge;
1017
1018 ge = get_ge_from_cur_tab(ui);
1019 if (ge)
1020 connect_clicked(w, ge);
1021}
1022
1023static void send_job_entry(GtkWidget *w, gpointer data)
1024{
1025 struct gui *ui = (struct gui *) data;
1026 struct gui_entry *ge;
1027
1028 ge = get_ge_from_cur_tab(ui);
1029 if (ge)
1030 send_clicked(w, ge);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001031}
1032
1033static void edit_job_entry(GtkWidget *w, gpointer data)
1034{
Jens Axboe9af4a242012-03-16 10:13:49 +01001035 struct gui *ui = (struct gui *) data;
Jens Axboe789f4cc2012-03-16 14:56:44 +01001036 struct gui_entry *ge;
Jens Axboe9af4a242012-03-16 10:13:49 +01001037
Jens Axboe789f4cc2012-03-16 14:56:44 +01001038 ge = get_ge_from_cur_tab(ui);
1039 if (ge && ge->client)
Jens Axboecf3d8242012-03-24 08:56:50 +01001040 gopt_get_options_window(ui->window, ge->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001041}
1042
1043static void start_job_entry(GtkWidget *w, gpointer data)
1044{
1045 struct gui *ui = (struct gui *) data;
1046 struct gui_entry *ge;
1047
1048 ge = get_ge_from_cur_tab(ui);
1049 if (ge)
1050 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001051}
1052
Jens Axboe781ccba2012-03-15 09:44:42 +01001053static void view_results(GtkWidget *w, gpointer data)
1054{
1055 struct gui *ui = (struct gui *) data;
1056 struct gfio_client *gc;
1057 struct gui_entry *ge;
1058
1059 ge = get_ge_from_cur_tab(ui);
1060 if (!ge)
1061 return;
1062
1063 if (ge->results_window)
1064 return;
1065
1066 gc = ge->client;
1067 if (gc && gc->nr_results)
1068 gfio_display_end_results(gc);
1069}
1070
Jens Axboea1e79722012-03-23 10:52:25 +01001071static void __update_graph_settings(struct gfio_graphs *g)
Jens Axboe8577f4f2012-03-09 19:28:27 +01001072{
1073 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001074 graph_set_font(g->iops_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001075 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001076 graph_set_font(g->bandwidth_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001077}
1078
Jens Axboea1e79722012-03-23 10:52:25 +01001079static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
Jens Axboeb98ab712012-03-21 12:48:32 +01001080{
1081 struct gui_entry *ge = (struct gui_entry *) value;
Jens Axboea1e79722012-03-23 10:52:25 +01001082 GdkEvent *ev;
Jens Axboeb98ab712012-03-21 12:48:32 +01001083
Jens Axboea1e79722012-03-23 10:52:25 +01001084 __update_graph_settings(&ge->graphs);
1085
1086 ev = gdk_event_new(GDK_EXPOSE);
1087 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), "expose_event", GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1088 gdk_event_free(ev);
Jens Axboeb98ab712012-03-21 12:48:32 +01001089}
1090
Jens Axboe8577f4f2012-03-09 19:28:27 +01001091static void update_graph_limits(void)
1092{
Jens Axboeb98ab712012-03-21 12:48:32 +01001093 struct gui *ui = &main_ui;
Jens Axboea1e79722012-03-23 10:52:25 +01001094 GdkEvent *ev;
Jens Axboe8577f4f2012-03-09 19:28:27 +01001095
Jens Axboea1e79722012-03-23 10:52:25 +01001096 __update_graph_settings(&ui->graphs);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001097
Jens Axboea1e79722012-03-23 10:52:25 +01001098 ev = gdk_event_new(GDK_EXPOSE);
1099 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), "expose_event", GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1100 gdk_event_free(ev);
1101
1102 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001103}
1104
Jens Axboe46974a72012-03-02 19:34:13 +01001105static void preferences(GtkWidget *w, gpointer data)
1106{
Jens Axboef3e84402012-03-07 13:14:32 +01001107 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001108 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe6e02ad62012-03-20 12:25:36 +01001109 struct gui *ui = (struct gui *) data;
Jens Axboe46974a72012-03-02 19:34:13 +01001110 int i;
1111
1112 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe6e02ad62012-03-20 12:25:36 +01001113 GTK_WINDOW(ui->window),
Jens Axboe46974a72012-03-02 19:34:13 +01001114 GTK_DIALOG_DESTROY_WITH_PARENT,
1115 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1116 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1117 NULL);
1118
Jens Axboe8577f4f2012-03-09 19:28:27 +01001119 frame = gtk_frame_new("Graphing");
Jens Axboef762cef2012-03-26 13:51:11 +02001120 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1121 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001122 vbox = gtk_vbox_new(FALSE, 6);
1123 gtk_container_add(GTK_CONTAINER(frame), vbox);
1124
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001125 hbox = gtk_hbox_new(FALSE, 5);
1126 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1127 entry = gtk_label_new("Font face to use for graph labels");
1128 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1129
Jens Axboea1e79722012-03-23 10:52:25 +01001130 font = gtk_font_button_new_with_font(gfio_graph_font);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001131 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001132
Jens Axboe8577f4f2012-03-09 19:28:27 +01001133 box = gtk_vbox_new(FALSE, 6);
1134 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1135
1136 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001137 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001138 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1139 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1140
Jens Axboec05d9052012-03-11 13:05:35 +01001141 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001142
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001143 box = gtk_vbox_new(FALSE, 6);
1144 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1145
1146 hbox = gtk_hbox_new(FALSE, 5);
1147 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1148 entry = gtk_label_new("Client ETA request interval (msec)");
1149 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1150
1151 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001152 frame = gtk_frame_new("Debug logging");
Jens Axboef762cef2012-03-26 13:51:11 +02001153 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1154 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001155 vbox = gtk_vbox_new(FALSE, 6);
1156 gtk_container_add(GTK_CONTAINER(frame), vbox);
1157
1158 box = gtk_hbox_new(FALSE, 6);
1159 gtk_container_add(GTK_CONTAINER(vbox), box);
1160
1161 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1162
1163 for (i = 0; i < FD_DEBUG_MAX; i++) {
1164 if (i == 7) {
1165 box = gtk_hbox_new(FALSE, 6);
1166 gtk_container_add(GTK_CONTAINER(vbox), box);
1167 }
1168
1169
1170 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1171 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1172 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1173 }
1174
Jens Axboe46974a72012-03-02 19:34:13 +01001175 gtk_widget_show_all(dialog);
1176
1177 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1178 gtk_widget_destroy(dialog);
1179 return;
1180 }
1181
1182 for (i = 0; i < FD_DEBUG_MAX; i++) {
1183 int set;
1184
1185 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1186 if (set)
1187 fio_debug |= (1UL << i);
1188 }
1189
Jens Axboef3e84402012-03-07 13:14:32 +01001190 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001191 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1192 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001193 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001194
Jens Axboe46974a72012-03-02 19:34:13 +01001195 gtk_widget_destroy(dialog);
1196}
1197
Jens Axboe0420ba62012-02-29 11:16:52 +01001198static void about_dialog(GtkWidget *w, gpointer data)
1199{
Jens Axboe81e4ea62012-03-07 14:18:28 +01001200 const char *authors[] = {
1201 "Jens Axboe <axboe@kernel.dk>",
1202 "Stephen Carmeron <stephenmcameron@gmail.com>",
1203 NULL
1204 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01001205 const char *license[] = {
1206 "Fio is free software; you can redistribute it and/or modify "
1207 "it under the terms of the GNU General Public License as published by "
1208 "the Free Software Foundation; either version 2 of the License, or "
1209 "(at your option) any later version.\n",
1210 "Fio is distributed in the hope that it will be useful, "
1211 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1212 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1213 "GNU General Public License for more details.\n",
1214 "You should have received a copy of the GNU General Public License "
1215 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1216 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1217 };
1218 char *license_trans;
1219
1220 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1221 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01001222
Jens Axboe0420ba62012-02-29 11:16:52 +01001223 gtk_show_about_dialog(NULL,
1224 "program-name", "gfio",
1225 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01001226 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001227 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1228 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01001229 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001230 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01001231 "logo-icon-name", "fio",
1232 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01001233 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01001234 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01001235
Jens Axboe2f99deb2012-03-09 14:37:29 +01001236 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01001237}
1238
1239static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001240 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01001241 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001242 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01001243 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01001244 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001245 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01001246 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1247 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1248 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01001249 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01001250 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboebc271d82012-03-15 21:57:40 +01001251 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01001252 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1253 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1254 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01001255 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1256 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001257};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001258static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001259
1260static const gchar *ui_string = " \
1261 <ui> \
1262 <menubar name=\"MainMenu\"> \
1263 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001264 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01001265 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001266 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001267 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001268 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01001269 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001270 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1271 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001272 <placeholder name=\"FileRecentFiles\"/> \
1273 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001274 <menuitem name=\"Quit\" action=\"Quit\" /> \
1275 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001276 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001277 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001278 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001279 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1280 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001281 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001282 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001283 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01001284 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01001285 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1286 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01001287 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1288 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01001289 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1290 <menuitem name=\"About\" action=\"About\" /> \
1291 </menu> \
1292 </menubar> \
1293 </ui> \
1294";
1295
Jens Axboe4cbe7212012-03-06 13:36:17 +01001296static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1297 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01001298{
Jens Axboeca664f42012-03-14 19:49:40 +01001299 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01001300 GError *error = 0;
1301
1302 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001303 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01001304
1305 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1306 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1307
1308 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01001309
Jens Axboe0420ba62012-02-29 11:16:52 +01001310 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1311}
1312
1313void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
Jens Axboe1252d8f2012-03-21 11:13:31 +01001314 GtkWidget *vbox, GtkUIManager *ui_manager)
Jens Axboe0420ba62012-02-29 11:16:52 +01001315{
Jens Axboe1252d8f2012-03-21 11:13:31 +01001316 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
Jens Axboe0420ba62012-02-29 11:16:52 +01001317}
1318
Jens Axboec80b74b2012-03-12 10:23:28 +01001319static void combo_entry_changed(GtkComboBox *box, gpointer data)
1320{
1321 struct gui_entry *ge = (struct gui_entry *) data;
1322 gint index;
1323
1324 index = gtk_combo_box_get_active(box);
1325
1326 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01001327 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01001328 multitext_set_entry(&ge->eta.ioengine, index);
1329 multitext_set_entry(&ge->eta.iodepth, index);
1330}
1331
1332static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1333{
1334 struct gui_entry *ge = (struct gui_entry *) data;
1335
1336 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01001337 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01001338 multitext_free(&ge->eta.ioengine);
1339 multitext_free(&ge->eta.iodepth);
1340}
1341
Jens Axboe2f99deb2012-03-09 14:37:29 +01001342static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001343{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001344 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001345 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01001346
Jens Axboe2f99deb2012-03-09 14:37:29 +01001347 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001348
Jens Axboe65476332012-03-13 10:37:04 +01001349 top_align = gtk_alignment_new(0, 0, 1, 0);
1350 top_vbox = gtk_vbox_new(FALSE, 3);
1351 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1352 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001353
Jens Axboe3e47bd22012-02-29 13:45:02 +01001354 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001355 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001356 probe_frame = gtk_vbox_new(FALSE, 3);
1357 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1358
1359 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001360 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1361 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1362 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1363 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1364 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01001365
Jens Axboe3e47bd22012-02-29 13:45:02 +01001366 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
Jens Axboe3863d1a2012-03-09 17:39:05 +01001369 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01001370 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1371 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1372 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01001373 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01001374 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1375 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001376 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1377 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1378
1379 probe_box = gtk_hbox_new(FALSE, 3);
1380 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1381 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1382 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1383 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1384 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1385
1386 /*
1387 * Only add this if we have a commit rate
1388 */
1389#if 0
1390 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001391 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001392
Jens Axboe2f99deb2012-03-09 14:37:29 +01001393 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1394 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1395
1396 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1397 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1398#endif
1399
1400 /*
1401 * Set up a drawing area and IOPS and bandwidth graphs
1402 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001403 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001404 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001405 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001406 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001407 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1408 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1409 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1410 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001411 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1412 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001413 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001414 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001415 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001416 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001417
1418 setup_graphs(&ge->graphs);
1419
1420 /*
1421 * Set up alignments for widgets at the bottom of ui,
1422 * align bottom left, expand horizontally but not vertically
1423 */
Jens Axboe65476332012-03-13 10:37:04 +01001424 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001425 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001426 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1427 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001428
Jens Axboe05775432012-03-21 14:07:11 +01001429 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001430
1431 /*
1432 * Set up thread status progress bar
1433 */
1434 ge->thread_status_pb = gtk_progress_bar_new();
1435 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1436 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1437 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1438
1439
1440 return main_vbox;
1441}
1442
1443static GtkWidget *new_main_page(struct gui *ui)
1444{
1445 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001446 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001447
1448 main_vbox = gtk_vbox_new(FALSE, 3);
1449
1450 /*
1451 * Set up alignments for widgets at the top of ui,
1452 * align top left, expand horizontally but not vertically
1453 */
Jens Axboe65476332012-03-13 10:37:04 +01001454 top_align = gtk_alignment_new(0, 0, 1, 0);
1455 top_vbox = gtk_vbox_new(FALSE, 0);
1456 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1457 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001458
1459 probe = gtk_frame_new("Run statistics");
1460 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1461 probe_frame = gtk_vbox_new(FALSE, 3);
1462 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001463
1464 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001465 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01001466 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01001467 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1468 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1469 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1470 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001471
1472 /*
1473 * Only add this if we have a commit rate
1474 */
1475#if 0
1476 probe_box = gtk_hbox_new(FALSE, 3);
1477 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1478
Jens Axboe3e47bd22012-02-29 13:45:02 +01001479 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1480 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1481
Jens Axboe3e47bd22012-02-29 13:45:02 +01001482 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1483 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001484#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001485
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001486 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001487 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001488 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001489 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001490 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001491 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001492 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001493 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1494 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1495 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1496 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001497 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1498 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001499 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001500 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001501 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001502 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01001503 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001504
Jens Axboe2f99deb2012-03-09 14:37:29 +01001505 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001506
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001507 /*
1508 * Set up alignments for widgets at the bottom of ui,
1509 * align bottom left, expand horizontally but not vertically
1510 */
Jens Axboe65476332012-03-13 10:37:04 +01001511 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001512 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001513 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1514 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001515
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001516 /*
1517 * Set up thread status progress bar
1518 */
1519 ui->thread_status_pb = gtk_progress_bar_new();
1520 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001521 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001522 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1523
Jens Axboe2f99deb2012-03-09 14:37:29 +01001524 return main_vbox;
1525}
1526
1527static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1528 guint page, gpointer data)
1529
1530{
Jens Axboe02421e62012-03-12 12:05:50 +01001531 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01001532 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01001533
Jens Axboe85dd01e2012-03-12 14:33:16 +01001534 if (!page) {
1535 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01001536 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001537 return TRUE;
1538 }
1539
1540 set_job_menu_visible(ui, 1);
Jens Axboe6e02ad62012-03-20 12:25:36 +01001541 ge = get_ge_from_page(ui, page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001542 if (ge)
1543 update_button_states(ui, ge);
1544
Jens Axboe2f99deb2012-03-09 14:37:29 +01001545 return TRUE;
1546}
1547
Jens Axboe38634cb2012-03-13 12:26:41 +01001548static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1549{
1550 time_t time_a = gtk_recent_info_get_visited(a);
1551 time_t time_b = gtk_recent_info_get_visited(b);
1552
1553 return time_b - time_a;
1554}
1555
1556static void add_recent_file_items(struct gui *ui)
1557{
1558 const gchar *gfio = g_get_application_name();
1559 GList *items, *item;
1560 int i = 0;
1561
1562 if (ui->recent_ui_id) {
1563 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1564 gtk_ui_manager_ensure_update(ui->uimanager);
1565 }
1566 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1567
1568 if (ui->actiongroup) {
1569 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1570 g_object_unref(ui->actiongroup);
1571 }
1572 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1573
1574 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1575
1576 items = gtk_recent_manager_get_items(ui->recentmanager);
1577 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1578
1579 for (item = items; item && item->data; item = g_list_next(item)) {
1580 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1581 gchar *action_name;
1582 const gchar *label;
1583 GtkAction *action;
1584
1585 if (!gtk_recent_info_has_application(info, gfio))
1586 continue;
1587
1588 /*
1589 * We only support local files for now
1590 */
1591 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1592 continue;
1593
1594 action_name = g_strdup_printf("RecentFile%u", i++);
1595 label = gtk_recent_info_get_display_name(info);
1596
1597 action = g_object_new(GTK_TYPE_ACTION,
1598 "name", action_name,
1599 "label", label, NULL);
1600
1601 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1602 gtk_recent_info_ref(info),
1603 (GDestroyNotify) gtk_recent_info_unref);
1604
1605
1606 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1607
1608 gtk_action_group_add_action(ui->actiongroup, action);
1609 g_object_unref(action);
1610
1611 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1612 "/MainMenu/FileMenu/FileRecentFiles",
1613 label, action_name,
1614 GTK_UI_MANAGER_MENUITEM, FALSE);
1615
1616 g_free(action_name);
1617
1618 if (i == 8)
1619 break;
1620 }
1621
1622 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1623 g_list_free(items);
1624}
1625
Jens Axboea6790902012-03-13 15:16:11 +01001626static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
Jens Axboe6e02ad62012-03-20 12:25:36 +01001627 gint x, gint y, GtkSelectionData *seldata,
1628 guint info, guint time, gpointer *data)
Jens Axboea6790902012-03-13 15:16:11 +01001629{
Jens Axboe6e02ad62012-03-20 12:25:36 +01001630 struct gui *ui = (struct gui *) data;
Jens Axboea6790902012-03-13 15:16:11 +01001631 gchar **uris;
1632 GtkWidget *source;
Jens Axboea6790902012-03-13 15:16:11 +01001633
1634 source = gtk_drag_get_source_widget(ctx);
1635 if (source && widget == gtk_widget_get_toplevel(source)) {
1636 gtk_drag_finish(ctx, FALSE, FALSE, time);
1637 return;
1638 }
1639
Jens Axboe6e02ad62012-03-20 12:25:36 +01001640 uris = gtk_selection_data_get_uris(seldata);
Jens Axboea6790902012-03-13 15:16:11 +01001641 if (!uris) {
1642 gtk_drag_finish(ctx, FALSE, FALSE, time);
1643 return;
1644 }
1645
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001646 if (uris[0])
1647 do_file_open_with_tab(ui, uris[0]);
Jens Axboea6790902012-03-13 15:16:11 +01001648
1649 gtk_drag_finish(ctx, TRUE, FALSE, time);
1650 g_strfreev(uris);
1651}
1652
Jens Axboe2f99deb2012-03-09 14:37:29 +01001653static void init_ui(int *argc, char **argv[], struct gui *ui)
1654{
1655 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01001656 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001657
1658 /* Magical g*thread incantation, you just need this thread stuff.
1659 * Without it, the update that happens in gfio_update_thread_status
1660 * doesn't really happen in a timely fashion, you need expose events
1661 */
1662 if (!g_thread_supported())
1663 g_thread_init(NULL);
1664 gdk_threads_init();
1665
1666 gtk_init(argc, argv);
1667 settings = gtk_settings_get_default();
1668 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1669 g_type_init();
Jens Axboe1252d8f2012-03-21 11:13:31 +01001670 gdk_color_parse("white", &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001671
1672 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01001673 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001674 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1675
Jens Axboe6e02ad62012-03-20 12:25:36 +01001676 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1677 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001678
1679 ui->vbox = gtk_vbox_new(FALSE, 0);
1680 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1681
Jens Axboe02421e62012-03-12 12:05:50 +01001682 ui->uimanager = gtk_ui_manager_new();
1683 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1684 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001685
Jens Axboe38634cb2012-03-13 12:26:41 +01001686 ui->recentmanager = gtk_recent_manager_get_default();
1687 add_recent_file_items(ui);
1688
Jens Axboe2f99deb2012-03-09 14:37:29 +01001689 ui->notebook = gtk_notebook_new();
1690 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01001691 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01001692 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001693 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1694
1695 vbox = new_main_page(ui);
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001696 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
Jens Axboea6790902012-03-13 15:16:11 +01001697 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1698 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001699
1700 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1701
Jens Axboe9b260bd2012-03-06 11:02:52 +01001702 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001703
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001704 gtk_widget_show_all(ui->window);
1705}
1706
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001707int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001708{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001709 if (initialize_fio(envp))
1710 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001711 if (fio_init_options())
1712 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001713
Jens Axboe753e9e62012-03-24 20:22:29 +01001714 gopt_init();
1715
Jens Axboe2f99deb2012-03-09 14:37:29 +01001716 memset(&main_ui, 0, sizeof(main_ui));
Jens Axboeb98ab712012-03-21 12:48:32 +01001717 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001718
1719 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001720
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001721 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001722 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001723 gdk_threads_leave();
Jens Axboeb98ab712012-03-21 12:48:32 +01001724
1725 g_hash_table_destroy(main_ui.ge_hash);
Jens Axboe753e9e62012-03-24 20:22:29 +01001726
1727 gopt_exit();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001728 return 0;
1729}