blob: d200ade05168c6ae83d50f92757b1945ee07e660 [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 Axboe0cf3ece2012-03-21 10:15:20 +0100433 ret = fio_client_send_ini(gc->client, ge->job_file);
434 if (!ret)
435 return 0;
Jens Axboec7249262012-03-09 17:11:04 +0100436
Jens Axboe41666582012-03-21 10:25:29 +0100437 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100438 return 1;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100439}
440
Jens Axboe63a130b2012-03-06 20:08:59 +0100441static void *server_thread(void *arg)
442{
443 is_backend = 1;
444 gfio_server_running = 1;
445 fio_start_server(NULL);
446 gfio_server_running = 0;
447 return NULL;
448}
449
Jens Axboe6e02ad62012-03-20 12:25:36 +0100450static void gfio_start_server(struct gui *ui)
Jens Axboe63a130b2012-03-06 20:08:59 +0100451{
452 if (!gfio_server_running) {
453 gfio_server_running = 1;
454 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +0100455 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +0100456 }
457}
458
Stephen M. Cameron25927252012-02-24 08:17:31 +0100459static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
460 gpointer data)
461{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100462 struct gui_entry *ge = data;
463 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100464
Jens Axboe78cb2fe2012-03-12 23:05:29 +0100465 if (gc)
466 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100467}
468
Jens Axboedf06f222012-03-02 13:32:04 +0100469static void file_open(GtkWidget *w, gpointer data);
470
Jens Axboe62bc9372012-03-07 11:45:07 +0100471struct connection_widgets
472{
473 GtkWidget *hentry;
474 GtkWidget *combo;
475 GtkWidget *button;
476};
477
478static void hostname_cb(GtkEntry *entry, gpointer data)
479{
480 struct connection_widgets *cw = data;
481 int uses_net = 0, is_localhost = 0;
482 const gchar *text;
483 gchar *ctext;
484
485 /*
486 * Check whether to display the 'auto start backend' box
487 * or not. Show it if we are a localhost and using network,
488 * or using a socket.
489 */
490 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
491 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
492 uses_net = 1;
493 g_free(ctext);
494
495 if (uses_net) {
496 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
497 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
498 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
499 !strcmp(text, "ip6-loopback"))
500 is_localhost = 1;
501 }
502
503 if (!uses_net || is_localhost) {
504 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
505 gtk_widget_set_sensitive(cw->button, 1);
506 } else {
507 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
508 gtk_widget_set_sensitive(cw->button, 0);
509 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100510}
Jens Axboea7a42ce2012-03-02 13:12:04 +0100511
Jens Axboeb0500cc2012-03-21 11:59:18 +0100512static int get_connection_details(struct gui_entry *ge)
513{
514 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
515 struct connection_widgets cw;
516 struct gui *ui = ge->ui;
517 char *typeentry;
Jens Axboea7a42ce2012-03-02 13:12:04 +0100518
Jens Axboeb0500cc2012-03-21 11:59:18 +0100519 if (ge->host)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100520 return 0;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100521
Jens Axboeb0500cc2012-03-21 11:59:18 +0100522 dialog = gtk_dialog_new_with_buttons("Connection details",
523 GTK_WINDOW(ui->window),
524 GTK_DIALOG_DESTROY_WITH_PARENT,
525 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
526 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100527
Jens Axboeb0500cc2012-03-21 11:59:18 +0100528 frame = gtk_frame_new("Hostname / socket name");
529 /* gtk_dialog_get_content_area() is 2.14 and newer */
530 vbox = GTK_DIALOG(dialog)->vbox;
531 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100532
Jens Axboeb0500cc2012-03-21 11:59:18 +0100533 box = gtk_vbox_new(FALSE, 6);
534 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100535
Jens Axboeb0500cc2012-03-21 11:59:18 +0100536 hbox = gtk_hbox_new(TRUE, 10);
537 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
538 cw.hentry = gtk_entry_new();
539 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
540 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100541
Jens Axboeb0500cc2012-03-21 11:59:18 +0100542 frame = gtk_frame_new("Port");
543 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
544 box = gtk_vbox_new(FALSE, 10);
545 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100546
Jens Axboeb0500cc2012-03-21 11:59:18 +0100547 hbox = gtk_hbox_new(TRUE, 4);
548 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
549 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100550
Jens Axboeb0500cc2012-03-21 11:59:18 +0100551 frame = gtk_frame_new("Type");
552 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
553 box = gtk_vbox_new(FALSE, 10);
554 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100555
Jens Axboeb0500cc2012-03-21 11:59:18 +0100556 hbox = gtk_hbox_new(TRUE, 4);
557 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100558
Jens Axboeb0500cc2012-03-21 11:59:18 +0100559 cw.combo = gtk_combo_box_new_text();
560 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
561 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
562 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
563 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100564
Jens Axboeb0500cc2012-03-21 11:59:18 +0100565 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100566
Jens Axboeb0500cc2012-03-21 11:59:18 +0100567 frame = gtk_frame_new("Options");
568 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
569 box = gtk_vbox_new(FALSE, 10);
570 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe38634cb2012-03-13 12:26:41 +0100571
Jens Axboeb0500cc2012-03-21 11:59:18 +0100572 hbox = gtk_hbox_new(TRUE, 4);
573 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100574
Jens Axboeb0500cc2012-03-21 11:59:18 +0100575 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
576 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
577 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.");
578 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100579
Jens Axboe1252d8f2012-03-21 11:13:31 +0100580 /*
Jens Axboeb0500cc2012-03-21 11:59:18 +0100581 * Connect edit signal, so we can show/not-show the auto start button
Jens Axboe1252d8f2012-03-21 11:13:31 +0100582 */
Jens Axboeb0500cc2012-03-21 11:59:18 +0100583 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
584 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboe0420ba62012-02-29 11:16:52 +0100585
Jens Axboeb0500cc2012-03-21 11:59:18 +0100586 gtk_widget_show_all(dialog);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100587
Jens Axboeb0500cc2012-03-21 11:59:18 +0100588 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
589 gtk_widget_destroy(dialog);
Jens Axboea6790902012-03-13 15:16:11 +0100590 return 1;
591 }
592
Jens Axboeb0500cc2012-03-21 11:59:18 +0100593 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
594 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
Jens Axboea6790902012-03-13 15:16:11 +0100595
Jens Axboeb0500cc2012-03-21 11:59:18 +0100596 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
597 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
598 ge->type = Fio_client_ipv4;
599 else if (!strncmp(typeentry, "IPv6", 4))
600 ge->type = Fio_client_ipv6;
601 else
602 ge->type = Fio_client_socket;
603 g_free(typeentry);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100604
Jens Axboeb0500cc2012-03-21 11:59:18 +0100605 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboe1252d8f2012-03-21 11:13:31 +0100606
Jens Axboeb0500cc2012-03-21 11:59:18 +0100607 gtk_widget_destroy(dialog);
608 return 0;
609}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100610
Jens Axboeb0500cc2012-03-21 11:59:18 +0100611static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
612{
613 gc->client = fio_get_client(client);
614 client->client_data = gc;
615}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100616
Jens Axboeb0500cc2012-03-21 11:59:18 +0100617static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
618{
Jens Axboecf3d8242012-03-24 08:56:50 +0100619 struct gfio_client_options *gco;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100620 struct gfio_client *gc;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100621
Jens Axboecf3d8242012-03-24 08:56:50 +0100622 gc = calloc(1, sizeof(*gc));
623 INIT_FLIST_HEAD(&gc->o_list);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100624 gc->ge = ge;
625 ge->client = gc;
626 gfio_set_client(gc, client);
Jens Axboecf3d8242012-03-24 08:56:50 +0100627
628 /*
629 * Just add a default set of options, need to consider how best
630 * to handle this
631 */
632 gco = calloc(1, sizeof(*gco));
633 options_default_fill(&gco->o);
634 flist_add_tail(&gco->list, &gc->o_list);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100635}
Jens Axboea6790902012-03-13 15:16:11 +0100636
Jens Axboeb0500cc2012-03-21 11:59:18 +0100637static void connect_clicked(GtkWidget *widget, gpointer data)
638{
639 struct gui_entry *ge = data;
640 struct gfio_client *gc = ge->client;
Jens Axboea6790902012-03-13 15:16:11 +0100641
Jens Axboeb0500cc2012-03-21 11:59:18 +0100642 if (ge->state == GE_STATE_NEW) {
643 int ret;
Jens Axboea6790902012-03-13 15:16:11 +0100644
Jens Axboeb0500cc2012-03-21 11:59:18 +0100645 if (!ge->job_file)
646 file_open(widget, ge->ui);
647 if (!ge->job_file)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100648 return;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100649
650 gc = ge->client;
651
652 if (!gc->client) {
653 struct fio_client *client;
654
655 if (get_connection_details(ge)) {
656 gfio_report_error(ge, "Failed to get connection details\n");
657 return;
658 }
659
660 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
661 if (!client) {
662 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
663 free(ge->host);
664 ge->host = NULL;
665 return;
666 }
667 gfio_set_client(gc, client);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100668 }
Jens Axboe0420ba62012-02-29 11:16:52 +0100669
Jens Axboeb0500cc2012-03-21 11:59:18 +0100670 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
671 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
672 ret = fio_client_connect(gc->client);
673 if (!ret) {
674 if (!ge->ui->handler_running)
675 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
676 gfio_set_state(ge, GE_STATE_CONNECTED);
677 } else {
678 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
679 }
680 } else {
681 fio_client_terminate(gc->client);
682 gfio_set_state(ge, GE_STATE_NEW);
683 clear_ge_ui_info(ge);
684 }
685}
686
687static void send_clicked(GtkWidget *widget, gpointer data)
688{
689 struct gui_entry *ge = data;
690
691 if (send_job_file(ge))
692 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
693}
694
695static GtkWidget *new_client_page(struct gui_entry *ge);
696
697static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
698{
699 struct gui_entry *ge;
700
701 ge = malloc(sizeof(*ge));
702 memset(ge, 0, sizeof(*ge));
703 ge->state = GE_STATE_NEW;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100704 ge->ui = ui;
705 return ge;
706}
707
708static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
709{
710 struct gui_entry *ge;
711
712 ge = alloc_new_gui_entry(ui);
713
714 ge->vbox = new_client_page(ge);
715 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
716
717 ge->page_label = gtk_label_new(name);
718 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
719
Jens Axboeb98ab712012-03-21 12:48:32 +0100720 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
721
Jens Axboeb0500cc2012-03-21 11:59:18 +0100722 gtk_widget_show_all(ui->window);
723 return ge;
724}
725
726static void file_new(GtkWidget *w, gpointer data)
727{
728 struct gui *ui = (struct gui *) data;
729 struct gui_entry *ge;
730
731 ge = get_new_ge_with_tab(ui, "Untitled");
732 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
733}
734
735/*
736 * Return the 'ge' corresponding to the tab. If the active tab is the
737 * main tab, open a new tab.
738 */
739static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
740 int *created)
741{
Jens Axboeb0500cc2012-03-21 11:59:18 +0100742 if (!cur_page) {
743 if (created)
744 *created = 1;
745 return get_new_ge_with_tab(ui, "Untitled");
746 }
747
748 if (created)
749 *created = 0;
750
Jens Axboeb98ab712012-03-21 12:48:32 +0100751 return g_hash_table_lookup(ui->ge_hash, &cur_page);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100752}
753
754static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
755{
756 gint cur_page;
757
758 /*
759 * Main tab is tab 0, so any current page other than 0 holds
760 * a ge entry.
761 */
762 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
763 if (cur_page)
764 return get_ge_from_page(ui, cur_page, NULL);
765
766 return NULL;
767}
768
769static void file_close(GtkWidget *w, gpointer data)
770{
771 struct gui *ui = (struct gui *) data;
772 struct gui_entry *ge;
773
774 /*
775 * Can't close the main tab
776 */
777 ge = get_ge_from_cur_tab(ui);
778 if (ge) {
779 gtk_widget_destroy(ge->vbox);
780 return;
781 }
782
Jens Axboeb98ab712012-03-21 12:48:32 +0100783 if (g_hash_table_size(ui->ge_hash)) {
Jens Axboeb0500cc2012-03-21 11:59:18 +0100784 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
785 return;
786 }
787
788 gfio_quit(ui);
789}
790
791static void file_add_recent(struct gui *ui, const gchar *uri)
792{
793 GtkRecentData grd;
794
795 memset(&grd, 0, sizeof(grd));
796 grd.display_name = strdup("gfio");
797 grd.description = strdup("Fio job file");
798 grd.mime_type = strdup(GFIO_MIME);
799 grd.app_name = strdup(g_get_application_name());
800 grd.app_exec = strdup("gfio %f/%u");
801
802 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
803}
804
805static gchar *get_filename_from_uri(const gchar *uri)
806{
807 if (strncmp(uri, "file://", 7))
808 return strdup(uri);
809
810 return strdup(uri + 7);
811}
812
813static int do_file_open(struct gui_entry *ge, const gchar *uri)
814{
815 struct fio_client *client;
816
817 assert(!ge->job_file);
818
819 ge->job_file = get_filename_from_uri(uri);
820
821 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
822 if (client) {
Jens Axboe832fde72012-03-21 12:55:49 +0100823 char *label = strdup(uri);
824
825 basename(label);
826 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
827 free(label);
828
Jens Axboeb0500cc2012-03-21 11:59:18 +0100829 gfio_client_added(ge, client);
830 file_add_recent(ge->ui, uri);
831 return 0;
832 }
833
834 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
835 free(ge->host);
836 ge->host = NULL;
Jens Axboeb98ab712012-03-21 12:48:32 +0100837 free(ge->job_file);
838 ge->job_file = NULL;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100839 return 1;
840}
841
842static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
843{
844 struct gui_entry *ge;
845 gint cur_page;
846 int ret, ge_is_new = 0;
847
848 /*
849 * Creates new tab if current tab is the main window, or the
850 * current tab already has a client.
851 */
852 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
853 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
854 if (ge->client) {
855 ge = get_new_ge_with_tab(ui, "Untitled");
856 ge_is_new = 1;
857 }
858
859 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
860
861 if (get_connection_details(ge)) {
862 if (ge_is_new)
863 gtk_widget_destroy(ge->vbox);
864
865 return 1;
866 }
867
868 ret = do_file_open(ge, uri);
869
870 if (!ret) {
871 if (ge->server_start)
872 gfio_start_server(ui);
873 } else {
874 if (ge_is_new)
875 gtk_widget_destroy(ge->vbox);
876 }
877
878 return ret;
879}
880
881static void recent_open(GtkAction *action, gpointer data)
882{
883 struct gui *ui = (struct gui *) data;
884 GtkRecentInfo *info;
885 const gchar *uri;
886
887 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
888 uri = gtk_recent_info_get_uri(info);
889
890 do_file_open_with_tab(ui, uri);
891}
892
893static void file_open(GtkWidget *w, gpointer data)
894{
895 struct gui *ui = data;
896 GtkWidget *dialog;
897 GtkFileFilter *filter;
898 gchar *filename;
899
900 dialog = gtk_file_chooser_dialog_new("Open File",
901 GTK_WINDOW(ui->window),
902 GTK_FILE_CHOOSER_ACTION_OPEN,
903 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
904 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
905 NULL);
906 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
907
908 filter = gtk_file_filter_new();
909 gtk_file_filter_add_pattern(filter, "*.fio");
910 gtk_file_filter_add_pattern(filter, "*.job");
911 gtk_file_filter_add_pattern(filter, "*.ini");
912 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
913 gtk_file_filter_set_name(filter, "Fio job file");
914 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
915
916 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
917 gtk_widget_destroy(dialog);
918 return;
919 }
920
921 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
922
923 gtk_widget_destroy(dialog);
924
925 do_file_open_with_tab(ui, filename);
926 g_free(filename);
927}
928
929static void file_save(GtkWidget *w, gpointer data)
930{
931 struct gui *ui = data;
932 GtkWidget *dialog;
933
934 dialog = gtk_file_chooser_dialog_new("Save File",
935 GTK_WINDOW(ui->window),
936 GTK_FILE_CHOOSER_ACTION_SAVE,
937 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
938 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
939 NULL);
940
941 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
942 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
943
944 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
945 char *filename;
946
Jens Axboe0420ba62012-02-29 11:16:52 +0100947 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100948 // save_job_file(filename);
Jens Axboe0420ba62012-02-29 11:16:52 +0100949 g_free(filename);
950 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100951 gtk_widget_destroy(dialog);
952}
Jens Axboe0420ba62012-02-29 11:16:52 +0100953
Jens Axboe9b260bd2012-03-06 11:02:52 +0100954static void view_log_destroy(GtkWidget *w, gpointer data)
955{
956 struct gui *ui = (struct gui *) data;
957
958 gtk_widget_ref(ui->log_tree);
959 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
960 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +0100961 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100962}
963
Jens Axboe1252d8f2012-03-21 11:13:31 +0100964void gfio_view_log(struct gui *ui)
Jens Axboe9b260bd2012-03-06 11:02:52 +0100965{
Jens Axboe4cbe7212012-03-06 13:36:17 +0100966 GtkWidget *win, *scroll, *vbox, *box;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100967
Jens Axboe4cbe7212012-03-06 13:36:17 +0100968 if (ui->log_view)
969 return;
970
971 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100972 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +0100973 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100974
Jens Axboe4cbe7212012-03-06 13:36:17 +0100975 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100976
Jens Axboe4cbe7212012-03-06 13:36:17 +0100977 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
978
979 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
980
981 box = gtk_hbox_new(TRUE, 0);
982 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
983 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
984 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
985
986 vbox = gtk_vbox_new(TRUE, 5);
987 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
988
989 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100990 gtk_widget_show_all(win);
991}
992
Jens Axboe1252d8f2012-03-21 11:13:31 +0100993static void view_log(GtkWidget *w, gpointer data)
994{
995 struct gui *ui = (struct gui *) data;
996
997 gfio_view_log(ui);
998}
999
Jens Axboe85dd01e2012-03-12 14:33:16 +01001000static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001001{
Jens Axboe85dd01e2012-03-12 14:33:16 +01001002 struct gui *ui = (struct gui *) data;
1003 struct gui_entry *ge;
1004
1005 ge = get_ge_from_cur_tab(ui);
1006 if (ge)
1007 connect_clicked(w, ge);
1008}
1009
1010static void send_job_entry(GtkWidget *w, gpointer data)
1011{
1012 struct gui *ui = (struct gui *) data;
1013 struct gui_entry *ge;
1014
1015 ge = get_ge_from_cur_tab(ui);
1016 if (ge)
1017 send_clicked(w, ge);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001018}
1019
1020static void edit_job_entry(GtkWidget *w, gpointer data)
1021{
Jens Axboe9af4a242012-03-16 10:13:49 +01001022 struct gui *ui = (struct gui *) data;
Jens Axboe789f4cc2012-03-16 14:56:44 +01001023 struct gui_entry *ge;
Jens Axboe9af4a242012-03-16 10:13:49 +01001024
Jens Axboe789f4cc2012-03-16 14:56:44 +01001025 ge = get_ge_from_cur_tab(ui);
1026 if (ge && ge->client)
Jens Axboecf3d8242012-03-24 08:56:50 +01001027 gopt_get_options_window(ui->window, ge->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001028}
1029
1030static void start_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 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001038}
1039
Jens Axboe781ccba2012-03-15 09:44:42 +01001040static void view_results(GtkWidget *w, gpointer data)
1041{
1042 struct gui *ui = (struct gui *) data;
1043 struct gfio_client *gc;
1044 struct gui_entry *ge;
1045
1046 ge = get_ge_from_cur_tab(ui);
1047 if (!ge)
1048 return;
1049
1050 if (ge->results_window)
1051 return;
1052
1053 gc = ge->client;
1054 if (gc && gc->nr_results)
1055 gfio_display_end_results(gc);
1056}
1057
Jens Axboea1e79722012-03-23 10:52:25 +01001058static void __update_graph_settings(struct gfio_graphs *g)
Jens Axboe8577f4f2012-03-09 19:28:27 +01001059{
1060 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001061 graph_set_font(g->iops_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001062 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001063 graph_set_font(g->bandwidth_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001064}
1065
Jens Axboea1e79722012-03-23 10:52:25 +01001066static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
Jens Axboeb98ab712012-03-21 12:48:32 +01001067{
1068 struct gui_entry *ge = (struct gui_entry *) value;
Jens Axboea1e79722012-03-23 10:52:25 +01001069 GdkEvent *ev;
Jens Axboeb98ab712012-03-21 12:48:32 +01001070
Jens Axboea1e79722012-03-23 10:52:25 +01001071 __update_graph_settings(&ge->graphs);
1072
1073 ev = gdk_event_new(GDK_EXPOSE);
1074 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), "expose_event", GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
1075 gdk_event_free(ev);
Jens Axboeb98ab712012-03-21 12:48:32 +01001076}
1077
Jens Axboe8577f4f2012-03-09 19:28:27 +01001078static void update_graph_limits(void)
1079{
Jens Axboeb98ab712012-03-21 12:48:32 +01001080 struct gui *ui = &main_ui;
Jens Axboea1e79722012-03-23 10:52:25 +01001081 GdkEvent *ev;
Jens Axboe8577f4f2012-03-09 19:28:27 +01001082
Jens Axboea1e79722012-03-23 10:52:25 +01001083 __update_graph_settings(&ui->graphs);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001084
Jens Axboea1e79722012-03-23 10:52:25 +01001085 ev = gdk_event_new(GDK_EXPOSE);
1086 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), "expose_event", GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
1087 gdk_event_free(ev);
1088
1089 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001090}
1091
Jens Axboe46974a72012-03-02 19:34:13 +01001092static void preferences(GtkWidget *w, gpointer data)
1093{
Jens Axboef3e84402012-03-07 13:14:32 +01001094 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001095 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe6e02ad62012-03-20 12:25:36 +01001096 struct gui *ui = (struct gui *) data;
Jens Axboe46974a72012-03-02 19:34:13 +01001097 int i;
1098
1099 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe6e02ad62012-03-20 12:25:36 +01001100 GTK_WINDOW(ui->window),
Jens Axboe46974a72012-03-02 19:34:13 +01001101 GTK_DIALOG_DESTROY_WITH_PARENT,
1102 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1103 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1104 NULL);
1105
Jens Axboe8577f4f2012-03-09 19:28:27 +01001106 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01001107 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1108 vbox = gtk_vbox_new(FALSE, 6);
1109 gtk_container_add(GTK_CONTAINER(frame), vbox);
1110
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001111 hbox = gtk_hbox_new(FALSE, 5);
1112 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1113 entry = gtk_label_new("Font face to use for graph labels");
1114 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1115
Jens Axboea1e79722012-03-23 10:52:25 +01001116 font = gtk_font_button_new_with_font(gfio_graph_font);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001117 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001118
Jens Axboe8577f4f2012-03-09 19:28:27 +01001119 box = gtk_vbox_new(FALSE, 6);
1120 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1121
1122 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001123 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001124 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1125 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1126
Jens Axboec05d9052012-03-11 13:05:35 +01001127 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001128
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001129 box = gtk_vbox_new(FALSE, 6);
1130 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1131
1132 hbox = gtk_hbox_new(FALSE, 5);
1133 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1134 entry = gtk_label_new("Client ETA request interval (msec)");
1135 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1136
1137 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001138 frame = gtk_frame_new("Debug logging");
1139 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1140 vbox = gtk_vbox_new(FALSE, 6);
1141 gtk_container_add(GTK_CONTAINER(frame), vbox);
1142
1143 box = gtk_hbox_new(FALSE, 6);
1144 gtk_container_add(GTK_CONTAINER(vbox), box);
1145
1146 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1147
1148 for (i = 0; i < FD_DEBUG_MAX; i++) {
1149 if (i == 7) {
1150 box = gtk_hbox_new(FALSE, 6);
1151 gtk_container_add(GTK_CONTAINER(vbox), box);
1152 }
1153
1154
1155 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1156 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1157 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1158 }
1159
Jens Axboe46974a72012-03-02 19:34:13 +01001160 gtk_widget_show_all(dialog);
1161
1162 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1163 gtk_widget_destroy(dialog);
1164 return;
1165 }
1166
1167 for (i = 0; i < FD_DEBUG_MAX; i++) {
1168 int set;
1169
1170 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1171 if (set)
1172 fio_debug |= (1UL << i);
1173 }
1174
Jens Axboef3e84402012-03-07 13:14:32 +01001175 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001176 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1177 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001178 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001179
Jens Axboe46974a72012-03-02 19:34:13 +01001180 gtk_widget_destroy(dialog);
1181}
1182
Jens Axboe0420ba62012-02-29 11:16:52 +01001183static void about_dialog(GtkWidget *w, gpointer data)
1184{
Jens Axboe81e4ea62012-03-07 14:18:28 +01001185 const char *authors[] = {
1186 "Jens Axboe <axboe@kernel.dk>",
1187 "Stephen Carmeron <stephenmcameron@gmail.com>",
1188 NULL
1189 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01001190 const char *license[] = {
1191 "Fio is free software; you can redistribute it and/or modify "
1192 "it under the terms of the GNU General Public License as published by "
1193 "the Free Software Foundation; either version 2 of the License, or "
1194 "(at your option) any later version.\n",
1195 "Fio is distributed in the hope that it will be useful, "
1196 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1197 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1198 "GNU General Public License for more details.\n",
1199 "You should have received a copy of the GNU General Public License "
1200 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1201 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1202 };
1203 char *license_trans;
1204
1205 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1206 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01001207
Jens Axboe0420ba62012-02-29 11:16:52 +01001208 gtk_show_about_dialog(NULL,
1209 "program-name", "gfio",
1210 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01001211 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001212 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1213 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01001214 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001215 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01001216 "logo-icon-name", "fio",
1217 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01001218 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01001219 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01001220
Jens Axboe2f99deb2012-03-09 14:37:29 +01001221 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01001222}
1223
1224static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001225 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01001226 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001227 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01001228 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01001229 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001230 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01001231 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1232 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1233 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01001234 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01001235 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboebc271d82012-03-15 21:57:40 +01001236 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01001237 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1238 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1239 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01001240 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1241 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001242};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001243static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001244
1245static const gchar *ui_string = " \
1246 <ui> \
1247 <menubar name=\"MainMenu\"> \
1248 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001249 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01001250 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001251 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001252 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001253 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01001254 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001255 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1256 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001257 <placeholder name=\"FileRecentFiles\"/> \
1258 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001259 <menuitem name=\"Quit\" action=\"Quit\" /> \
1260 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001261 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001262 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001263 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001264 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1265 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001266 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001267 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001268 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01001269 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01001270 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1271 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01001272 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1273 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01001274 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1275 <menuitem name=\"About\" action=\"About\" /> \
1276 </menu> \
1277 </menubar> \
1278 </ui> \
1279";
1280
Jens Axboe4cbe7212012-03-06 13:36:17 +01001281static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1282 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01001283{
Jens Axboeca664f42012-03-14 19:49:40 +01001284 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01001285 GError *error = 0;
1286
1287 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001288 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01001289
1290 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1291 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1292
1293 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01001294
Jens Axboe0420ba62012-02-29 11:16:52 +01001295 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1296}
1297
1298void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
Jens Axboe1252d8f2012-03-21 11:13:31 +01001299 GtkWidget *vbox, GtkUIManager *ui_manager)
Jens Axboe0420ba62012-02-29 11:16:52 +01001300{
Jens Axboe1252d8f2012-03-21 11:13:31 +01001301 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
Jens Axboe0420ba62012-02-29 11:16:52 +01001302}
1303
Jens Axboec80b74b2012-03-12 10:23:28 +01001304static void combo_entry_changed(GtkComboBox *box, gpointer data)
1305{
1306 struct gui_entry *ge = (struct gui_entry *) data;
1307 gint index;
1308
1309 index = gtk_combo_box_get_active(box);
1310
1311 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01001312 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01001313 multitext_set_entry(&ge->eta.ioengine, index);
1314 multitext_set_entry(&ge->eta.iodepth, index);
1315}
1316
1317static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1318{
1319 struct gui_entry *ge = (struct gui_entry *) data;
1320
1321 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01001322 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01001323 multitext_free(&ge->eta.ioengine);
1324 multitext_free(&ge->eta.iodepth);
1325}
1326
Jens Axboe2f99deb2012-03-09 14:37:29 +01001327static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001328{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001329 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001330 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01001331
Jens Axboe2f99deb2012-03-09 14:37:29 +01001332 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001333
Jens Axboe65476332012-03-13 10:37:04 +01001334 top_align = gtk_alignment_new(0, 0, 1, 0);
1335 top_vbox = gtk_vbox_new(FALSE, 3);
1336 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1337 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001338
Jens Axboe3e47bd22012-02-29 13:45:02 +01001339 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001340 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001341 probe_frame = gtk_vbox_new(FALSE, 3);
1342 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1343
1344 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001345 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1346 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1347 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1348 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1349 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01001350
Jens Axboe3e47bd22012-02-29 13:45:02 +01001351 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001352 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1353
Jens Axboe3863d1a2012-03-09 17:39:05 +01001354 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01001355 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1356 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1357 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01001358 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01001359 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1360 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001361 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1362 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1363
1364 probe_box = gtk_hbox_new(FALSE, 3);
1365 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1366 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1367 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1368 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1369 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1370
1371 /*
1372 * Only add this if we have a commit rate
1373 */
1374#if 0
1375 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001376 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001377
Jens Axboe2f99deb2012-03-09 14:37:29 +01001378 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1379 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1380
1381 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1382 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1383#endif
1384
1385 /*
1386 * Set up a drawing area and IOPS and bandwidth graphs
1387 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001388 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001389 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001390 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001391 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001392 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
1393 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1394 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1395 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001396 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1397 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001398 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001399 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001400 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001401 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001402
1403 setup_graphs(&ge->graphs);
1404
1405 /*
1406 * Set up alignments for widgets at the bottom of ui,
1407 * align bottom left, expand horizontally but not vertically
1408 */
Jens Axboe65476332012-03-13 10:37:04 +01001409 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001410 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001411 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1412 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001413
Jens Axboe05775432012-03-21 14:07:11 +01001414 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001415
1416 /*
1417 * Set up thread status progress bar
1418 */
1419 ge->thread_status_pb = gtk_progress_bar_new();
1420 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1421 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1422 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1423
1424
1425 return main_vbox;
1426}
1427
1428static GtkWidget *new_main_page(struct gui *ui)
1429{
1430 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001431 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001432
1433 main_vbox = gtk_vbox_new(FALSE, 3);
1434
1435 /*
1436 * Set up alignments for widgets at the top of ui,
1437 * align top left, expand horizontally but not vertically
1438 */
Jens Axboe65476332012-03-13 10:37:04 +01001439 top_align = gtk_alignment_new(0, 0, 1, 0);
1440 top_vbox = gtk_vbox_new(FALSE, 0);
1441 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1442 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001443
1444 probe = gtk_frame_new("Run statistics");
1445 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1446 probe_frame = gtk_vbox_new(FALSE, 3);
1447 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001448
1449 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001450 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01001451 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01001452 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1453 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1454 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1455 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001456
1457 /*
1458 * Only add this if we have a commit rate
1459 */
1460#if 0
1461 probe_box = gtk_hbox_new(FALSE, 3);
1462 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1463
Jens Axboe3e47bd22012-02-29 13:45:02 +01001464 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1465 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1466
Jens Axboe3e47bd22012-02-29 13:45:02 +01001467 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1468 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001469#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001470
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001471 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001472 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001473 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001474 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001475 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001476 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001477 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001478 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
1479 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1480 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1481 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001482 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1483 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001484 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001485 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001486 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001487 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01001488 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001489
Jens Axboe2f99deb2012-03-09 14:37:29 +01001490 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001491
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001492 /*
1493 * Set up alignments for widgets at the bottom of ui,
1494 * align bottom left, expand horizontally but not vertically
1495 */
Jens Axboe65476332012-03-13 10:37:04 +01001496 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001497 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001498 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1499 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001500
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001501 /*
1502 * Set up thread status progress bar
1503 */
1504 ui->thread_status_pb = gtk_progress_bar_new();
1505 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001506 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001507 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1508
Jens Axboe2f99deb2012-03-09 14:37:29 +01001509 return main_vbox;
1510}
1511
1512static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1513 guint page, gpointer data)
1514
1515{
Jens Axboe02421e62012-03-12 12:05:50 +01001516 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01001517 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01001518
Jens Axboe85dd01e2012-03-12 14:33:16 +01001519 if (!page) {
1520 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01001521 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001522 return TRUE;
1523 }
1524
1525 set_job_menu_visible(ui, 1);
Jens Axboe6e02ad62012-03-20 12:25:36 +01001526 ge = get_ge_from_page(ui, page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001527 if (ge)
1528 update_button_states(ui, ge);
1529
Jens Axboe2f99deb2012-03-09 14:37:29 +01001530 return TRUE;
1531}
1532
Jens Axboe38634cb2012-03-13 12:26:41 +01001533static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1534{
1535 time_t time_a = gtk_recent_info_get_visited(a);
1536 time_t time_b = gtk_recent_info_get_visited(b);
1537
1538 return time_b - time_a;
1539}
1540
1541static void add_recent_file_items(struct gui *ui)
1542{
1543 const gchar *gfio = g_get_application_name();
1544 GList *items, *item;
1545 int i = 0;
1546
1547 if (ui->recent_ui_id) {
1548 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1549 gtk_ui_manager_ensure_update(ui->uimanager);
1550 }
1551 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1552
1553 if (ui->actiongroup) {
1554 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1555 g_object_unref(ui->actiongroup);
1556 }
1557 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1558
1559 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1560
1561 items = gtk_recent_manager_get_items(ui->recentmanager);
1562 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1563
1564 for (item = items; item && item->data; item = g_list_next(item)) {
1565 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1566 gchar *action_name;
1567 const gchar *label;
1568 GtkAction *action;
1569
1570 if (!gtk_recent_info_has_application(info, gfio))
1571 continue;
1572
1573 /*
1574 * We only support local files for now
1575 */
1576 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1577 continue;
1578
1579 action_name = g_strdup_printf("RecentFile%u", i++);
1580 label = gtk_recent_info_get_display_name(info);
1581
1582 action = g_object_new(GTK_TYPE_ACTION,
1583 "name", action_name,
1584 "label", label, NULL);
1585
1586 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1587 gtk_recent_info_ref(info),
1588 (GDestroyNotify) gtk_recent_info_unref);
1589
1590
1591 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1592
1593 gtk_action_group_add_action(ui->actiongroup, action);
1594 g_object_unref(action);
1595
1596 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1597 "/MainMenu/FileMenu/FileRecentFiles",
1598 label, action_name,
1599 GTK_UI_MANAGER_MENUITEM, FALSE);
1600
1601 g_free(action_name);
1602
1603 if (i == 8)
1604 break;
1605 }
1606
1607 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1608 g_list_free(items);
1609}
1610
Jens Axboea6790902012-03-13 15:16:11 +01001611static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
Jens Axboe6e02ad62012-03-20 12:25:36 +01001612 gint x, gint y, GtkSelectionData *seldata,
1613 guint info, guint time, gpointer *data)
Jens Axboea6790902012-03-13 15:16:11 +01001614{
Jens Axboe6e02ad62012-03-20 12:25:36 +01001615 struct gui *ui = (struct gui *) data;
Jens Axboea6790902012-03-13 15:16:11 +01001616 gchar **uris;
1617 GtkWidget *source;
Jens Axboea6790902012-03-13 15:16:11 +01001618
1619 source = gtk_drag_get_source_widget(ctx);
1620 if (source && widget == gtk_widget_get_toplevel(source)) {
1621 gtk_drag_finish(ctx, FALSE, FALSE, time);
1622 return;
1623 }
1624
Jens Axboe6e02ad62012-03-20 12:25:36 +01001625 uris = gtk_selection_data_get_uris(seldata);
Jens Axboea6790902012-03-13 15:16:11 +01001626 if (!uris) {
1627 gtk_drag_finish(ctx, FALSE, FALSE, time);
1628 return;
1629 }
1630
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001631 if (uris[0])
1632 do_file_open_with_tab(ui, uris[0]);
Jens Axboea6790902012-03-13 15:16:11 +01001633
1634 gtk_drag_finish(ctx, TRUE, FALSE, time);
1635 g_strfreev(uris);
1636}
1637
Jens Axboe2f99deb2012-03-09 14:37:29 +01001638static void init_ui(int *argc, char **argv[], struct gui *ui)
1639{
1640 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01001641 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001642
1643 /* Magical g*thread incantation, you just need this thread stuff.
1644 * Without it, the update that happens in gfio_update_thread_status
1645 * doesn't really happen in a timely fashion, you need expose events
1646 */
1647 if (!g_thread_supported())
1648 g_thread_init(NULL);
1649 gdk_threads_init();
1650
1651 gtk_init(argc, argv);
1652 settings = gtk_settings_get_default();
1653 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1654 g_type_init();
Jens Axboe1252d8f2012-03-21 11:13:31 +01001655 gdk_color_parse("white", &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001656
1657 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01001658 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001659 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1660
Jens Axboe6e02ad62012-03-20 12:25:36 +01001661 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1662 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001663
1664 ui->vbox = gtk_vbox_new(FALSE, 0);
1665 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1666
Jens Axboe02421e62012-03-12 12:05:50 +01001667 ui->uimanager = gtk_ui_manager_new();
1668 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1669 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001670
Jens Axboe38634cb2012-03-13 12:26:41 +01001671 ui->recentmanager = gtk_recent_manager_get_default();
1672 add_recent_file_items(ui);
1673
Jens Axboe2f99deb2012-03-09 14:37:29 +01001674 ui->notebook = gtk_notebook_new();
1675 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01001676 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01001677 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001678 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1679
1680 vbox = new_main_page(ui);
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001681 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
Jens Axboea6790902012-03-13 15:16:11 +01001682 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1683 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001684
1685 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1686
Jens Axboe9b260bd2012-03-06 11:02:52 +01001687 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001688
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001689 gtk_widget_show_all(ui->window);
1690}
1691
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001692int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001693{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001694 if (initialize_fio(envp))
1695 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001696 if (fio_init_options())
1697 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001698
Jens Axboe2f99deb2012-03-09 14:37:29 +01001699 memset(&main_ui, 0, sizeof(main_ui));
Jens Axboeb98ab712012-03-21 12:48:32 +01001700 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001701
1702 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001703
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001704 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001705 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001706 gdk_threads_leave();
Jens Axboeb98ab712012-03-21 12:48:32 +01001707
1708 g_hash_table_destroy(main_ui.ge_hash);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001709 return 0;
1710}