blob: 559c7bd35b37af65bdc2406c1d80451010700086 [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 Axboecd0d2db2012-03-26 19:30:24 +0200318 guint width = gtk_widget_get_allocated_width(w);
319 guint height = gtk_widget_get_allocated_height(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100320 struct gfio_graphs *g = data;
321
Jens Axboecd0d2db2012-03-26 19:30:24 +0200322 graph_set_size(g->iops_graph, width / 2.0, height);
323 graph_set_position(g->iops_graph, width / 2.0, 0.0);
324 graph_set_size(g->bandwidth_graph, width / 2.0, height);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100325 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +0100326 return TRUE;
327}
328
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100329static void draw_graph(struct graph *g, cairo_t *cr)
330{
331 line_graph_draw(g, cr);
332 cairo_stroke(cr);
333}
334
Jens Axboe93e2db22012-03-13 09:45:22 +0100335static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
336 gboolean keyboard_mode, GtkTooltip *tooltip,
337 gpointer data)
338{
339 struct gfio_graphs *g = data;
340 const char *text = NULL;
341
342 if (graph_contains_xy(g->iops_graph, x, y))
343 text = graph_find_tooltip(g->iops_graph, x, y);
344 else if (graph_contains_xy(g->bandwidth_graph, x, y))
345 text = graph_find_tooltip(g->bandwidth_graph, x, y);
346
347 if (text) {
348 gtk_tooltip_set_text(tooltip, text);
349 return TRUE;
350 }
351
352 return FALSE;
353}
354
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100355static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
356{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100357 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100358 cairo_t *cr;
359
Jens Axboecd0d2db2012-03-26 19:30:24 +0200360 cr = gdk_cairo_create(gtk_widget_get_window(w));
Jens Axboe93e2db22012-03-13 09:45:22 +0100361
362 if (graph_has_tooltips(g->iops_graph) ||
363 graph_has_tooltips(g->bandwidth_graph)) {
364 g_object_set(w, "has-tooltip", TRUE, NULL);
365 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
366 }
367
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100368 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +0100369 draw_graph(g->iops_graph, cr);
370 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100371 cairo_destroy(cr);
372
373 return FALSE;
374}
375
Jens Axboe2f99deb2012-03-09 14:37:29 +0100376/*
Jens Axboe0fd18982012-03-14 10:34:48 +0100377 * FIXME: need more handling here
378 */
379static void ge_destroy(struct gui_entry *ge)
380{
381 struct gfio_client *gc = ge->client;
382
Jens Axboe470cdbb2012-03-21 12:02:13 +0100383 if (gc) {
384 if (gc->client) {
385 if (ge->state >= GE_STATE_CONNECTED)
386 fio_client_terminate(gc->client);
Jens Axboe0fd18982012-03-14 10:34:48 +0100387
Jens Axboe470cdbb2012-03-21 12:02:13 +0100388 fio_put_client(gc->client);
389 }
390 free(gc);
Jens Axboe0fd18982012-03-14 10:34:48 +0100391 }
392
Jens Axboeb98ab712012-03-21 12:48:32 +0100393 g_hash_table_remove(ge->ui->ge_hash, &ge->page_num);
394
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100395 free(ge->job_file);
396 free(ge->host);
Jens Axboe0fd18982012-03-14 10:34:48 +0100397 free(ge);
398}
399
400static void ge_widget_destroy(GtkWidget *w, gpointer data)
401{
Jens Axboe2eb98bf2012-03-20 08:20:48 +0100402 struct gui_entry *ge = (struct gui_entry *) data;
403
404 ge_destroy(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +0100405}
406
407static void gfio_quit(struct gui *ui)
408{
Jens Axboe0fd18982012-03-14 10:34:48 +0100409 gtk_main_quit();
410}
411
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100412static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
413 __attribute__((unused)) gpointer data)
414{
Jens Axboe6e02ad62012-03-20 12:25:36 +0100415 struct gui *ui = (struct gui *) data;
416
417 gfio_quit(ui);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100418}
419
Stephen M. Cameron25927252012-02-24 08:17:31 +0100420static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100421{
Jens Axboea9eccde2012-03-09 14:59:42 +0100422 struct gui *ui = arg;
423
424 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100425 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +0100426 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100427 return NULL;
428}
429
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100430static int send_job_file(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100431{
Jens Axboe9988ca72012-03-09 15:14:06 +0100432 struct gfio_client *gc = ge->client;
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100433 int ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100434
Jens Axboe753e9e62012-03-24 20:22:29 +0100435 /*
436 * Prune old options, we are expecting the return options
437 * when the job file is parsed remotely and returned to us.
438 */
439 while (!flist_empty(&gc->o_list)) {
440 struct gfio_client_options *gco;
441
442 gco = flist_entry(gc->o_list.next, struct gfio_client_options, list);
443 flist_del(&gco->list);
444 free(gco);
445 }
446
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100447 ret = fio_client_send_ini(gc->client, ge->job_file);
448 if (!ret)
449 return 0;
Jens Axboec7249262012-03-09 17:11:04 +0100450
Jens Axboe41666582012-03-21 10:25:29 +0100451 gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret));
Jens Axboe0cf3ece2012-03-21 10:15:20 +0100452 return 1;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +0100453}
454
Jens Axboe63a130b2012-03-06 20:08:59 +0100455static void *server_thread(void *arg)
456{
457 is_backend = 1;
458 gfio_server_running = 1;
459 fio_start_server(NULL);
460 gfio_server_running = 0;
461 return NULL;
462}
463
Jens Axboe6e02ad62012-03-20 12:25:36 +0100464static void gfio_start_server(struct gui *ui)
Jens Axboe63a130b2012-03-06 20:08:59 +0100465{
466 if (!gfio_server_running) {
467 gfio_server_running = 1;
468 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +0100469 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +0100470 }
471}
472
Stephen M. Cameron25927252012-02-24 08:17:31 +0100473static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
474 gpointer data)
475{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100476 struct gui_entry *ge = data;
477 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100478
Jens Axboe78cb2fe2012-03-12 23:05:29 +0100479 if (gc)
480 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100481}
482
Jens Axboedf06f222012-03-02 13:32:04 +0100483static void file_open(GtkWidget *w, gpointer data);
484
Jens Axboe62bc9372012-03-07 11:45:07 +0100485struct connection_widgets
486{
487 GtkWidget *hentry;
488 GtkWidget *combo;
489 GtkWidget *button;
490};
491
492static void hostname_cb(GtkEntry *entry, gpointer data)
493{
494 struct connection_widgets *cw = data;
495 int uses_net = 0, is_localhost = 0;
496 const gchar *text;
497 gchar *ctext;
498
499 /*
500 * Check whether to display the 'auto start backend' box
501 * or not. Show it if we are a localhost and using network,
502 * or using a socket.
503 */
Jens Axboef762cef2012-03-26 13:51:11 +0200504 ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo));
Jens Axboe62bc9372012-03-07 11:45:07 +0100505 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
506 uses_net = 1;
507 g_free(ctext);
508
509 if (uses_net) {
510 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
511 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
512 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
513 !strcmp(text, "ip6-loopback"))
514 is_localhost = 1;
515 }
516
517 if (!uses_net || is_localhost) {
518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
519 gtk_widget_set_sensitive(cw->button, 1);
520 } else {
521 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
522 gtk_widget_set_sensitive(cw->button, 0);
523 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100524}
Jens Axboea7a42ce2012-03-02 13:12:04 +0100525
Jens Axboeb0500cc2012-03-21 11:59:18 +0100526static int get_connection_details(struct gui_entry *ge)
527{
528 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
529 struct connection_widgets cw;
530 struct gui *ui = ge->ui;
531 char *typeentry;
Jens Axboea7a42ce2012-03-02 13:12:04 +0100532
Jens Axboeb0500cc2012-03-21 11:59:18 +0100533 if (ge->host)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100534 return 0;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100535
Jens Axboeb0500cc2012-03-21 11:59:18 +0100536 dialog = gtk_dialog_new_with_buttons("Connection details",
537 GTK_WINDOW(ui->window),
538 GTK_DIALOG_DESTROY_WITH_PARENT,
539 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
540 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100541
Jens Axboeb0500cc2012-03-21 11:59:18 +0100542 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef762cef2012-03-26 13:51:11 +0200543 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100544 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100545
Jens Axboeb0500cc2012-03-21 11:59:18 +0100546 box = gtk_vbox_new(FALSE, 6);
547 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100548
Jens Axboeb0500cc2012-03-21 11:59:18 +0100549 hbox = gtk_hbox_new(TRUE, 10);
550 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
551 cw.hentry = gtk_entry_new();
552 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
553 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100554
Jens Axboeb0500cc2012-03-21 11:59:18 +0100555 frame = gtk_frame_new("Port");
556 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
557 box = gtk_vbox_new(FALSE, 10);
558 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100559
Jens Axboeb0500cc2012-03-21 11:59:18 +0100560 hbox = gtk_hbox_new(TRUE, 4);
561 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
562 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100563
Jens Axboeb0500cc2012-03-21 11:59:18 +0100564 frame = gtk_frame_new("Type");
565 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
566 box = gtk_vbox_new(FALSE, 10);
567 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100568
Jens Axboeb0500cc2012-03-21 11:59:18 +0100569 hbox = gtk_hbox_new(TRUE, 4);
570 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100571
Jens Axboef762cef2012-03-26 13:51:11 +0200572 cw.combo = gtk_combo_box_text_new();
573 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4");
574 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6");
575 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket");
Jens Axboeb0500cc2012-03-21 11:59:18 +0100576 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100577
Jens Axboeb0500cc2012-03-21 11:59:18 +0100578 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100579
Jens Axboeb0500cc2012-03-21 11:59:18 +0100580 frame = gtk_frame_new("Options");
581 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
582 box = gtk_vbox_new(FALSE, 10);
583 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboe38634cb2012-03-13 12:26:41 +0100584
Jens Axboeb0500cc2012-03-21 11:59:18 +0100585 hbox = gtk_hbox_new(TRUE, 4);
586 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100587
Jens Axboeb0500cc2012-03-21 11:59:18 +0100588 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
589 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
590 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.");
591 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100592
Jens Axboe1252d8f2012-03-21 11:13:31 +0100593 /*
Jens Axboeb0500cc2012-03-21 11:59:18 +0100594 * Connect edit signal, so we can show/not-show the auto start button
Jens Axboe1252d8f2012-03-21 11:13:31 +0100595 */
Jens Axboecd972fd2012-03-26 12:55:33 +0200596 g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
597 g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboe0420ba62012-02-29 11:16:52 +0100598
Jens Axboeb0500cc2012-03-21 11:59:18 +0100599 gtk_widget_show_all(dialog);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100600
Jens Axboeb0500cc2012-03-21 11:59:18 +0100601 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
602 gtk_widget_destroy(dialog);
Jens Axboea6790902012-03-13 15:16:11 +0100603 return 1;
604 }
605
Jens Axboeb0500cc2012-03-21 11:59:18 +0100606 ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
607 ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
Jens Axboea6790902012-03-13 15:16:11 +0100608
Jens Axboef762cef2012-03-26 13:51:11 +0200609 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100610 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
611 ge->type = Fio_client_ipv4;
612 else if (!strncmp(typeentry, "IPv6", 4))
613 ge->type = Fio_client_ipv6;
614 else
615 ge->type = Fio_client_socket;
616 g_free(typeentry);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100617
Jens Axboeb0500cc2012-03-21 11:59:18 +0100618 ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboe1252d8f2012-03-21 11:13:31 +0100619
Jens Axboeb0500cc2012-03-21 11:59:18 +0100620 gtk_widget_destroy(dialog);
621 return 0;
622}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100623
Jens Axboeb0500cc2012-03-21 11:59:18 +0100624static void gfio_set_client(struct gfio_client *gc, struct fio_client *client)
625{
626 gc->client = fio_get_client(client);
627 client->client_data = gc;
628}
Jens Axboe1252d8f2012-03-21 11:13:31 +0100629
Jens Axboeb0500cc2012-03-21 11:59:18 +0100630static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
631{
Jens Axboecf3d8242012-03-24 08:56:50 +0100632 struct gfio_client_options *gco;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100633 struct gfio_client *gc;
Jens Axboe1252d8f2012-03-21 11:13:31 +0100634
Jens Axboecf3d8242012-03-24 08:56:50 +0100635 gc = calloc(1, sizeof(*gc));
636 INIT_FLIST_HEAD(&gc->o_list);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100637 gc->ge = ge;
638 ge->client = gc;
639 gfio_set_client(gc, client);
Jens Axboecf3d8242012-03-24 08:56:50 +0100640
641 /*
642 * Just add a default set of options, need to consider how best
643 * to handle this
644 */
645 gco = calloc(1, sizeof(*gco));
Jens Axboe753e9e62012-03-24 20:22:29 +0100646 INIT_FLIST_HEAD(&gco->list);
Jens Axboecf3d8242012-03-24 08:56:50 +0100647 options_default_fill(&gco->o);
648 flist_add_tail(&gco->list, &gc->o_list);
Jens Axboe753e9e62012-03-24 20:22:29 +0100649 gc->o_list_nr++;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100650}
Jens Axboea6790902012-03-13 15:16:11 +0100651
Jens Axboeb0500cc2012-03-21 11:59:18 +0100652static void connect_clicked(GtkWidget *widget, gpointer data)
653{
654 struct gui_entry *ge = data;
655 struct gfio_client *gc = ge->client;
Jens Axboea6790902012-03-13 15:16:11 +0100656
Jens Axboeb0500cc2012-03-21 11:59:18 +0100657 if (ge->state == GE_STATE_NEW) {
658 int ret;
Jens Axboea6790902012-03-13 15:16:11 +0100659
Jens Axboeb0500cc2012-03-21 11:59:18 +0100660 if (!ge->job_file)
661 file_open(widget, ge->ui);
662 if (!ge->job_file)
Jens Axboe1252d8f2012-03-21 11:13:31 +0100663 return;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100664
665 gc = ge->client;
666
667 if (!gc->client) {
668 struct fio_client *client;
669
670 if (get_connection_details(ge)) {
671 gfio_report_error(ge, "Failed to get connection details\n");
672 return;
673 }
674
675 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
676 if (!client) {
677 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
678 free(ge->host);
679 ge->host = NULL;
680 return;
681 }
682 gfio_set_client(gc, client);
Jens Axboe1252d8f2012-03-21 11:13:31 +0100683 }
Jens Axboe0420ba62012-02-29 11:16:52 +0100684
Jens Axboeb0500cc2012-03-21 11:59:18 +0100685 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
686 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
687 ret = fio_client_connect(gc->client);
688 if (!ret) {
689 if (!ge->ui->handler_running)
690 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
691 gfio_set_state(ge, GE_STATE_CONNECTED);
692 } else {
693 gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
694 }
695 } else {
696 fio_client_terminate(gc->client);
697 gfio_set_state(ge, GE_STATE_NEW);
698 clear_ge_ui_info(ge);
699 }
700}
701
702static void send_clicked(GtkWidget *widget, gpointer data)
703{
704 struct gui_entry *ge = data;
705
706 if (send_job_file(ge))
707 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
708}
709
710static GtkWidget *new_client_page(struct gui_entry *ge);
711
712static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
713{
714 struct gui_entry *ge;
715
716 ge = malloc(sizeof(*ge));
717 memset(ge, 0, sizeof(*ge));
718 ge->state = GE_STATE_NEW;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100719 ge->ui = ui;
720 return ge;
721}
722
723static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name)
724{
725 struct gui_entry *ge;
726
727 ge = alloc_new_gui_entry(ui);
728
729 ge->vbox = new_client_page(ge);
730 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
731
732 ge->page_label = gtk_label_new(name);
733 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label);
734
Jens Axboeb98ab712012-03-21 12:48:32 +0100735 g_hash_table_insert(ui->ge_hash, &ge->page_num, ge);
736
Jens Axboeb0500cc2012-03-21 11:59:18 +0100737 gtk_widget_show_all(ui->window);
738 return ge;
739}
740
741static void file_new(GtkWidget *w, gpointer data)
742{
743 struct gui *ui = (struct gui *) data;
744 struct gui_entry *ge;
745
746 ge = get_new_ge_with_tab(ui, "Untitled");
747 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
748}
749
750/*
751 * Return the 'ge' corresponding to the tab. If the active tab is the
752 * main tab, open a new tab.
753 */
754static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page,
755 int *created)
756{
Jens Axboeb0500cc2012-03-21 11:59:18 +0100757 if (!cur_page) {
758 if (created)
759 *created = 1;
760 return get_new_ge_with_tab(ui, "Untitled");
761 }
762
763 if (created)
764 *created = 0;
765
Jens Axboeb98ab712012-03-21 12:48:32 +0100766 return g_hash_table_lookup(ui->ge_hash, &cur_page);
Jens Axboeb0500cc2012-03-21 11:59:18 +0100767}
768
769static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
770{
771 gint cur_page;
772
773 /*
774 * Main tab is tab 0, so any current page other than 0 holds
775 * a ge entry.
776 */
777 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
778 if (cur_page)
779 return get_ge_from_page(ui, cur_page, NULL);
780
781 return NULL;
782}
783
784static void file_close(GtkWidget *w, gpointer data)
785{
786 struct gui *ui = (struct gui *) data;
787 struct gui_entry *ge;
788
789 /*
790 * Can't close the main tab
791 */
792 ge = get_ge_from_cur_tab(ui);
793 if (ge) {
794 gtk_widget_destroy(ge->vbox);
795 return;
796 }
797
Jens Axboeb98ab712012-03-21 12:48:32 +0100798 if (g_hash_table_size(ui->ge_hash)) {
Jens Axboeb0500cc2012-03-21 11:59:18 +0100799 gfio_report_info(ui, "Error", "The main page view cannot be closed\n");
800 return;
801 }
802
803 gfio_quit(ui);
804}
805
806static void file_add_recent(struct gui *ui, const gchar *uri)
807{
808 GtkRecentData grd;
809
810 memset(&grd, 0, sizeof(grd));
811 grd.display_name = strdup("gfio");
812 grd.description = strdup("Fio job file");
813 grd.mime_type = strdup(GFIO_MIME);
814 grd.app_name = strdup(g_get_application_name());
815 grd.app_exec = strdup("gfio %f/%u");
816
817 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
818}
819
820static gchar *get_filename_from_uri(const gchar *uri)
821{
822 if (strncmp(uri, "file://", 7))
823 return strdup(uri);
824
825 return strdup(uri + 7);
826}
827
828static int do_file_open(struct gui_entry *ge, const gchar *uri)
829{
830 struct fio_client *client;
831
832 assert(!ge->job_file);
833
834 ge->job_file = get_filename_from_uri(uri);
835
836 client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port);
837 if (client) {
Jens Axboe832fde72012-03-21 12:55:49 +0100838 char *label = strdup(uri);
839
840 basename(label);
841 gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label));
842 free(label);
843
Jens Axboeb0500cc2012-03-21 11:59:18 +0100844 gfio_client_added(ge, client);
845 file_add_recent(ge->ui, uri);
846 return 0;
847 }
848
849 gfio_report_error(ge, "Failed to add client %s\n", ge->host);
850 free(ge->host);
851 ge->host = NULL;
Jens Axboeb98ab712012-03-21 12:48:32 +0100852 free(ge->job_file);
853 ge->job_file = NULL;
Jens Axboeb0500cc2012-03-21 11:59:18 +0100854 return 1;
855}
856
857static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
858{
859 struct gui_entry *ge;
860 gint cur_page;
861 int ret, ge_is_new = 0;
862
863 /*
864 * Creates new tab if current tab is the main window, or the
865 * current tab already has a client.
866 */
867 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
868 ge = get_ge_from_page(ui, cur_page, &ge_is_new);
869 if (ge->client) {
870 ge = get_new_ge_with_tab(ui, "Untitled");
871 ge_is_new = 1;
872 }
873
874 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
875
876 if (get_connection_details(ge)) {
877 if (ge_is_new)
878 gtk_widget_destroy(ge->vbox);
879
880 return 1;
881 }
882
883 ret = do_file_open(ge, uri);
884
885 if (!ret) {
886 if (ge->server_start)
887 gfio_start_server(ui);
888 } else {
889 if (ge_is_new)
890 gtk_widget_destroy(ge->vbox);
891 }
892
893 return ret;
894}
895
896static void recent_open(GtkAction *action, gpointer data)
897{
898 struct gui *ui = (struct gui *) data;
899 GtkRecentInfo *info;
900 const gchar *uri;
901
902 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
903 uri = gtk_recent_info_get_uri(info);
904
905 do_file_open_with_tab(ui, uri);
906}
907
908static void file_open(GtkWidget *w, gpointer data)
909{
910 struct gui *ui = data;
911 GtkWidget *dialog;
912 GtkFileFilter *filter;
913 gchar *filename;
914
915 dialog = gtk_file_chooser_dialog_new("Open File",
916 GTK_WINDOW(ui->window),
917 GTK_FILE_CHOOSER_ACTION_OPEN,
918 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
919 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
920 NULL);
921 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
922
923 filter = gtk_file_filter_new();
924 gtk_file_filter_add_pattern(filter, "*.fio");
925 gtk_file_filter_add_pattern(filter, "*.job");
926 gtk_file_filter_add_pattern(filter, "*.ini");
927 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
928 gtk_file_filter_set_name(filter, "Fio job file");
929 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
930
931 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
932 gtk_widget_destroy(dialog);
933 return;
934 }
935
936 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
937
938 gtk_widget_destroy(dialog);
939
940 do_file_open_with_tab(ui, filename);
941 g_free(filename);
942}
943
944static void file_save(GtkWidget *w, gpointer data)
945{
946 struct gui *ui = data;
947 GtkWidget *dialog;
948
949 dialog = gtk_file_chooser_dialog_new("Save File",
950 GTK_WINDOW(ui->window),
951 GTK_FILE_CHOOSER_ACTION_SAVE,
952 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
953 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
954 NULL);
955
956 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
957 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
958
959 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
960 char *filename;
961
Jens Axboe0420ba62012-02-29 11:16:52 +0100962 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
Jens Axboeb0500cc2012-03-21 11:59:18 +0100963 // save_job_file(filename);
Jens Axboe0420ba62012-02-29 11:16:52 +0100964 g_free(filename);
965 }
Jens Axboeb0500cc2012-03-21 11:59:18 +0100966 gtk_widget_destroy(dialog);
967}
Jens Axboe0420ba62012-02-29 11:16:52 +0100968
Jens Axboe9b260bd2012-03-06 11:02:52 +0100969static void view_log_destroy(GtkWidget *w, gpointer data)
970{
971 struct gui *ui = (struct gui *) data;
972
Jens Axboef762cef2012-03-26 13:51:11 +0200973 g_object_ref(G_OBJECT(ui->log_tree));
Jens Axboe9b260bd2012-03-06 11:02:52 +0100974 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
975 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +0100976 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100977}
978
Jens Axboe1252d8f2012-03-21 11:13:31 +0100979void gfio_view_log(struct gui *ui)
Jens Axboe9b260bd2012-03-06 11:02:52 +0100980{
Jens Axboe4cbe7212012-03-06 13:36:17 +0100981 GtkWidget *win, *scroll, *vbox, *box;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100982
Jens Axboe4cbe7212012-03-06 13:36:17 +0100983 if (ui->log_view)
984 return;
985
986 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100987 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +0100988 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100989
Jens Axboe4cbe7212012-03-06 13:36:17 +0100990 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100991
Jens Axboe4cbe7212012-03-06 13:36:17 +0100992 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
993
994 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
995
996 box = gtk_hbox_new(TRUE, 0);
Jens Axboef762cef2012-03-26 13:51:11 +0200997 gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0);
Jens Axboe4cbe7212012-03-06 13:36:17 +0100998 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
999 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1000
1001 vbox = gtk_vbox_new(TRUE, 5);
Jens Axboef762cef2012-03-26 13:51:11 +02001002 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001003
1004 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001005 gtk_widget_show_all(win);
1006}
1007
Jens Axboe1252d8f2012-03-21 11:13:31 +01001008static void view_log(GtkWidget *w, gpointer data)
1009{
1010 struct gui *ui = (struct gui *) data;
1011
1012 gfio_view_log(ui);
1013}
1014
Jens Axboe85dd01e2012-03-12 14:33:16 +01001015static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001016{
Jens Axboe85dd01e2012-03-12 14:33:16 +01001017 struct gui *ui = (struct gui *) data;
1018 struct gui_entry *ge;
1019
1020 ge = get_ge_from_cur_tab(ui);
1021 if (ge)
1022 connect_clicked(w, ge);
1023}
1024
1025static void send_job_entry(GtkWidget *w, gpointer data)
1026{
1027 struct gui *ui = (struct gui *) data;
1028 struct gui_entry *ge;
1029
1030 ge = get_ge_from_cur_tab(ui);
1031 if (ge)
1032 send_clicked(w, ge);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001033}
1034
1035static void edit_job_entry(GtkWidget *w, gpointer data)
1036{
Jens Axboe9af4a242012-03-16 10:13:49 +01001037 struct gui *ui = (struct gui *) data;
Jens Axboe789f4cc2012-03-16 14:56:44 +01001038 struct gui_entry *ge;
Jens Axboe9af4a242012-03-16 10:13:49 +01001039
Jens Axboe789f4cc2012-03-16 14:56:44 +01001040 ge = get_ge_from_cur_tab(ui);
1041 if (ge && ge->client)
Jens Axboecf3d8242012-03-24 08:56:50 +01001042 gopt_get_options_window(ui->window, ge->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001043}
1044
1045static void start_job_entry(GtkWidget *w, gpointer data)
1046{
1047 struct gui *ui = (struct gui *) data;
1048 struct gui_entry *ge;
1049
1050 ge = get_ge_from_cur_tab(ui);
1051 if (ge)
1052 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001053}
1054
Jens Axboe781ccba2012-03-15 09:44:42 +01001055static void view_results(GtkWidget *w, gpointer data)
1056{
1057 struct gui *ui = (struct gui *) data;
1058 struct gfio_client *gc;
1059 struct gui_entry *ge;
1060
1061 ge = get_ge_from_cur_tab(ui);
1062 if (!ge)
1063 return;
1064
1065 if (ge->results_window)
1066 return;
1067
1068 gc = ge->client;
1069 if (gc && gc->nr_results)
1070 gfio_display_end_results(gc);
1071}
1072
Jens Axboea1e79722012-03-23 10:52:25 +01001073static void __update_graph_settings(struct gfio_graphs *g)
Jens Axboe8577f4f2012-03-09 19:28:27 +01001074{
1075 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001076 graph_set_font(g->iops_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001077 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
Jens Axboea1e79722012-03-23 10:52:25 +01001078 graph_set_font(g->bandwidth_graph, gfio_graph_font);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001079}
1080
Jens Axboea1e79722012-03-23 10:52:25 +01001081static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data)
Jens Axboeb98ab712012-03-21 12:48:32 +01001082{
1083 struct gui_entry *ge = (struct gui_entry *) value;
Jens Axboea1e79722012-03-23 10:52:25 +01001084 GdkEvent *ev;
Jens Axboeb98ab712012-03-21 12:48:32 +01001085
Jens Axboea1e79722012-03-23 10:52:25 +01001086 __update_graph_settings(&ge->graphs);
1087
1088 ev = gdk_event_new(GDK_EXPOSE);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001089 g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs);
Jens Axboea1e79722012-03-23 10:52:25 +01001090 gdk_event_free(ev);
Jens Axboeb98ab712012-03-21 12:48:32 +01001091}
1092
Jens Axboe8577f4f2012-03-09 19:28:27 +01001093static void update_graph_limits(void)
1094{
Jens Axboeb98ab712012-03-21 12:48:32 +01001095 struct gui *ui = &main_ui;
Jens Axboea1e79722012-03-23 10:52:25 +01001096 GdkEvent *ev;
Jens Axboe8577f4f2012-03-09 19:28:27 +01001097
Jens Axboea1e79722012-03-23 10:52:25 +01001098 __update_graph_settings(&ui->graphs);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001099
Jens Axboea1e79722012-03-23 10:52:25 +01001100 ev = gdk_event_new(GDK_EXPOSE);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001101 g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs);
Jens Axboea1e79722012-03-23 10:52:25 +01001102 gdk_event_free(ev);
1103
1104 g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001105}
1106
Jens Axboe46974a72012-03-02 19:34:13 +01001107static void preferences(GtkWidget *w, gpointer data)
1108{
Jens Axboef3e84402012-03-07 13:14:32 +01001109 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001110 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe6e02ad62012-03-20 12:25:36 +01001111 struct gui *ui = (struct gui *) data;
Jens Axboe46974a72012-03-02 19:34:13 +01001112 int i;
1113
1114 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe6e02ad62012-03-20 12:25:36 +01001115 GTK_WINDOW(ui->window),
Jens Axboe46974a72012-03-02 19:34:13 +01001116 GTK_DIALOG_DESTROY_WITH_PARENT,
1117 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1118 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1119 NULL);
1120
Jens Axboe8577f4f2012-03-09 19:28:27 +01001121 frame = gtk_frame_new("Graphing");
Jens Axboef762cef2012-03-26 13:51:11 +02001122 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1123 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001124 vbox = gtk_vbox_new(FALSE, 6);
1125 gtk_container_add(GTK_CONTAINER(frame), vbox);
1126
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001127 hbox = gtk_hbox_new(FALSE, 5);
1128 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1129 entry = gtk_label_new("Font face to use for graph labels");
1130 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
1131
Jens Axboea1e79722012-03-23 10:52:25 +01001132 font = gtk_font_button_new_with_font(gfio_graph_font);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001133 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001134
Jens Axboe8577f4f2012-03-09 19:28:27 +01001135 box = gtk_vbox_new(FALSE, 6);
1136 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1137
1138 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001139 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001140 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
1141 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1142
Jens Axboec05d9052012-03-11 13:05:35 +01001143 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01001144
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001145 box = gtk_vbox_new(FALSE, 6);
1146 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
1147
1148 hbox = gtk_hbox_new(FALSE, 5);
1149 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
1150 entry = gtk_label_new("Client ETA request interval (msec)");
1151 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
1152
1153 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001154 frame = gtk_frame_new("Debug logging");
Jens Axboef762cef2012-03-26 13:51:11 +02001155 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1156 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
Jens Axboea31d9fa2012-03-09 20:23:05 +01001157 vbox = gtk_vbox_new(FALSE, 6);
1158 gtk_container_add(GTK_CONTAINER(frame), vbox);
1159
1160 box = gtk_hbox_new(FALSE, 6);
1161 gtk_container_add(GTK_CONTAINER(vbox), box);
1162
1163 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1164
1165 for (i = 0; i < FD_DEBUG_MAX; i++) {
1166 if (i == 7) {
1167 box = gtk_hbox_new(FALSE, 6);
1168 gtk_container_add(GTK_CONTAINER(vbox), box);
1169 }
1170
1171
1172 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1173 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1174 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1175 }
1176
Jens Axboe46974a72012-03-02 19:34:13 +01001177 gtk_widget_show_all(dialog);
1178
1179 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1180 gtk_widget_destroy(dialog);
1181 return;
1182 }
1183
1184 for (i = 0; i < FD_DEBUG_MAX; i++) {
1185 int set;
1186
1187 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1188 if (set)
1189 fio_debug |= (1UL << i);
1190 }
1191
Jens Axboef3e84402012-03-07 13:14:32 +01001192 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001193 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
1194 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01001195 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01001196
Jens Axboe46974a72012-03-02 19:34:13 +01001197 gtk_widget_destroy(dialog);
1198}
1199
Jens Axboe0420ba62012-02-29 11:16:52 +01001200static void about_dialog(GtkWidget *w, gpointer data)
1201{
Jens Axboe81e4ea62012-03-07 14:18:28 +01001202 const char *authors[] = {
1203 "Jens Axboe <axboe@kernel.dk>",
1204 "Stephen Carmeron <stephenmcameron@gmail.com>",
1205 NULL
1206 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01001207 const char *license[] = {
1208 "Fio is free software; you can redistribute it and/or modify "
1209 "it under the terms of the GNU General Public License as published by "
1210 "the Free Software Foundation; either version 2 of the License, or "
1211 "(at your option) any later version.\n",
1212 "Fio is distributed in the hope that it will be useful, "
1213 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1214 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1215 "GNU General Public License for more details.\n",
1216 "You should have received a copy of the GNU General Public License "
1217 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1218 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1219 };
1220 char *license_trans;
1221
1222 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1223 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01001224
Jens Axboe0420ba62012-02-29 11:16:52 +01001225 gtk_show_about_dialog(NULL,
1226 "program-name", "gfio",
1227 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01001228 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001229 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1230 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01001231 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01001232 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01001233 "logo-icon-name", "fio",
1234 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01001235 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01001236 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01001237
Jens Axboe2f99deb2012-03-09 14:37:29 +01001238 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01001239}
1240
1241static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01001242 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01001243 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001244 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01001245 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01001246 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001247 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01001248 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1249 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1250 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01001251 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01001252 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboebc271d82012-03-15 21:57:40 +01001253 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01001254 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
1255 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
1256 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01001257 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1258 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01001259};
Jens Axboe3e47bd22012-02-29 13:45:02 +01001260static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01001261
1262static const gchar *ui_string = " \
1263 <ui> \
1264 <menubar name=\"MainMenu\"> \
1265 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001266 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01001267 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001268 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001269 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001270 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01001271 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01001272 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1273 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001274 <placeholder name=\"FileRecentFiles\"/> \
1275 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01001276 <menuitem name=\"Quit\" action=\"Quit\" /> \
1277 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001278 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001279 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001280 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001281 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
1282 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01001283 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01001284 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001285 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01001286 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01001287 <menuitem name=\"Results\" action=\"ViewResults\" /> \
1288 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01001289 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1290 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01001291 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1292 <menuitem name=\"About\" action=\"About\" /> \
1293 </menu> \
1294 </menubar> \
1295 </ui> \
1296";
1297
Jens Axboe4cbe7212012-03-06 13:36:17 +01001298static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1299 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01001300{
Jens Axboeca664f42012-03-14 19:49:40 +01001301 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01001302 GError *error = 0;
1303
1304 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001305 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01001306
1307 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1308 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1309
1310 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01001311
Jens Axboe0420ba62012-02-29 11:16:52 +01001312 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1313}
1314
1315void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
Jens Axboe1252d8f2012-03-21 11:13:31 +01001316 GtkWidget *vbox, GtkUIManager *ui_manager)
Jens Axboe0420ba62012-02-29 11:16:52 +01001317{
Jens Axboe1252d8f2012-03-21 11:13:31 +01001318 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
Jens Axboe0420ba62012-02-29 11:16:52 +01001319}
1320
Jens Axboec80b74b2012-03-12 10:23:28 +01001321static void combo_entry_changed(GtkComboBox *box, gpointer data)
1322{
1323 struct gui_entry *ge = (struct gui_entry *) data;
1324 gint index;
1325
1326 index = gtk_combo_box_get_active(box);
1327
1328 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01001329 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01001330 multitext_set_entry(&ge->eta.ioengine, index);
1331 multitext_set_entry(&ge->eta.iodepth, index);
1332}
1333
1334static void combo_entry_destroy(GtkWidget *widget, gpointer data)
1335{
1336 struct gui_entry *ge = (struct gui_entry *) data;
1337
1338 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01001339 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01001340 multitext_free(&ge->eta.ioengine);
1341 multitext_free(&ge->eta.iodepth);
1342}
1343
Jens Axboe2f99deb2012-03-09 14:37:29 +01001344static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001345{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001346 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001347 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01001348
Jens Axboe2f99deb2012-03-09 14:37:29 +01001349 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001350
Jens Axboe65476332012-03-13 10:37:04 +01001351 top_align = gtk_alignment_new(0, 0, 1, 0);
1352 top_vbox = gtk_vbox_new(FALSE, 3);
1353 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1354 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001355
Jens Axboe3e47bd22012-02-29 13:45:02 +01001356 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001357 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01001358 probe_frame = gtk_vbox_new(FALSE, 3);
1359 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1360
1361 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001362 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1363 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1364 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
1365 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1366 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01001367
Jens Axboe3e47bd22012-02-29 13:45:02 +01001368 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001369 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1370
Jens Axboe3863d1a2012-03-09 17:39:05 +01001371 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01001372 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
1373 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
1374 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01001375 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01001376 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
1377 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001378 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1379 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1380
1381 probe_box = gtk_hbox_new(FALSE, 3);
1382 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
1383 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1384 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1385 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1386 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1387
1388 /*
1389 * Only add this if we have a commit rate
1390 */
1391#if 0
1392 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001393 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01001394
Jens Axboe2f99deb2012-03-09 14:37:29 +01001395 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1396 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1397
1398 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1399 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1400#endif
1401
1402 /*
1403 * Set up a drawing area and IOPS and bandwidth graphs
1404 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001405 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001406 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001407 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001408 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001409 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001410 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
1411 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
1412 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001413 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1414 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001415 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001416 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001417 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001418 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001419
1420 setup_graphs(&ge->graphs);
1421
1422 /*
1423 * Set up alignments for widgets at the bottom of ui,
1424 * align bottom left, expand horizontally but not vertically
1425 */
Jens Axboe65476332012-03-13 10:37:04 +01001426 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001427 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001428 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
1429 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001430
Jens Axboe05775432012-03-21 14:07:11 +01001431 add_buttons(ge, buttonspeclist, ARRAY_SIZE(buttonspeclist));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001432
1433 /*
1434 * Set up thread status progress bar
1435 */
1436 ge->thread_status_pb = gtk_progress_bar_new();
1437 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1438 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
1439 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
1440
1441
1442 return main_vbox;
1443}
1444
1445static GtkWidget *new_main_page(struct gui *ui)
1446{
1447 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01001448 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001449
1450 main_vbox = gtk_vbox_new(FALSE, 3);
1451
1452 /*
1453 * Set up alignments for widgets at the top of ui,
1454 * align top left, expand horizontally but not vertically
1455 */
Jens Axboe65476332012-03-13 10:37:04 +01001456 top_align = gtk_alignment_new(0, 0, 1, 0);
1457 top_vbox = gtk_vbox_new(FALSE, 0);
1458 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
1459 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001460
1461 probe = gtk_frame_new("Run statistics");
1462 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
1463 probe_frame = gtk_vbox_new(FALSE, 3);
1464 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001465
1466 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001467 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01001468 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01001469 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1470 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1471 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1472 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001473
1474 /*
1475 * Only add this if we have a commit rate
1476 */
1477#if 0
1478 probe_box = gtk_hbox_new(FALSE, 3);
1479 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1480
Jens Axboe3e47bd22012-02-29 13:45:02 +01001481 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1482 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1483
Jens Axboe3e47bd22012-02-29 13:45:02 +01001484 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1485 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01001486#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01001487
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01001488 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001489 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001490 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01001491 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001492 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001493 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe1252d8f2012-03-21 11:13:31 +01001494 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_white);
Jens Axboeb6ab6a32012-03-26 19:34:15 +02001495 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001496 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
1497 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
1498 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01001499 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1500 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001501 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01001502 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01001503 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01001504 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01001505 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001506
Jens Axboe2f99deb2012-03-09 14:37:29 +01001507 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001508
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001509 /*
1510 * Set up alignments for widgets at the bottom of ui,
1511 * align bottom left, expand horizontally but not vertically
1512 */
Jens Axboe65476332012-03-13 10:37:04 +01001513 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001514 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01001515 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
1516 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01001517
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001518 /*
1519 * Set up thread status progress bar
1520 */
1521 ui->thread_status_pb = gtk_progress_bar_new();
1522 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01001523 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001524 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1525
Jens Axboe2f99deb2012-03-09 14:37:29 +01001526 return main_vbox;
1527}
1528
1529static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
1530 guint page, gpointer data)
1531
1532{
Jens Axboe02421e62012-03-12 12:05:50 +01001533 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01001534 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01001535
Jens Axboe85dd01e2012-03-12 14:33:16 +01001536 if (!page) {
1537 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01001538 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001539 return TRUE;
1540 }
1541
1542 set_job_menu_visible(ui, 1);
Jens Axboe6e02ad62012-03-20 12:25:36 +01001543 ge = get_ge_from_page(ui, page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001544 if (ge)
1545 update_button_states(ui, ge);
1546
Jens Axboe2f99deb2012-03-09 14:37:29 +01001547 return TRUE;
1548}
1549
Jens Axboe38634cb2012-03-13 12:26:41 +01001550static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
1551{
1552 time_t time_a = gtk_recent_info_get_visited(a);
1553 time_t time_b = gtk_recent_info_get_visited(b);
1554
1555 return time_b - time_a;
1556}
1557
1558static void add_recent_file_items(struct gui *ui)
1559{
1560 const gchar *gfio = g_get_application_name();
1561 GList *items, *item;
1562 int i = 0;
1563
1564 if (ui->recent_ui_id) {
1565 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
1566 gtk_ui_manager_ensure_update(ui->uimanager);
1567 }
1568 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
1569
1570 if (ui->actiongroup) {
1571 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
1572 g_object_unref(ui->actiongroup);
1573 }
1574 ui->actiongroup = gtk_action_group_new("RecentFileActions");
1575
1576 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
1577
1578 items = gtk_recent_manager_get_items(ui->recentmanager);
1579 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
1580
1581 for (item = items; item && item->data; item = g_list_next(item)) {
1582 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
1583 gchar *action_name;
1584 const gchar *label;
1585 GtkAction *action;
1586
1587 if (!gtk_recent_info_has_application(info, gfio))
1588 continue;
1589
1590 /*
1591 * We only support local files for now
1592 */
1593 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
1594 continue;
1595
1596 action_name = g_strdup_printf("RecentFile%u", i++);
1597 label = gtk_recent_info_get_display_name(info);
1598
1599 action = g_object_new(GTK_TYPE_ACTION,
1600 "name", action_name,
1601 "label", label, NULL);
1602
1603 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
1604 gtk_recent_info_ref(info),
1605 (GDestroyNotify) gtk_recent_info_unref);
1606
1607
1608 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
1609
1610 gtk_action_group_add_action(ui->actiongroup, action);
1611 g_object_unref(action);
1612
1613 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
1614 "/MainMenu/FileMenu/FileRecentFiles",
1615 label, action_name,
1616 GTK_UI_MANAGER_MENUITEM, FALSE);
1617
1618 g_free(action_name);
1619
1620 if (i == 8)
1621 break;
1622 }
1623
1624 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
1625 g_list_free(items);
1626}
1627
Jens Axboea6790902012-03-13 15:16:11 +01001628static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
Jens Axboe6e02ad62012-03-20 12:25:36 +01001629 gint x, gint y, GtkSelectionData *seldata,
1630 guint info, guint time, gpointer *data)
Jens Axboea6790902012-03-13 15:16:11 +01001631{
Jens Axboe6e02ad62012-03-20 12:25:36 +01001632 struct gui *ui = (struct gui *) data;
Jens Axboea6790902012-03-13 15:16:11 +01001633 gchar **uris;
1634 GtkWidget *source;
Jens Axboea6790902012-03-13 15:16:11 +01001635
1636 source = gtk_drag_get_source_widget(ctx);
1637 if (source && widget == gtk_widget_get_toplevel(source)) {
1638 gtk_drag_finish(ctx, FALSE, FALSE, time);
1639 return;
1640 }
1641
Jens Axboe6e02ad62012-03-20 12:25:36 +01001642 uris = gtk_selection_data_get_uris(seldata);
Jens Axboea6790902012-03-13 15:16:11 +01001643 if (!uris) {
1644 gtk_drag_finish(ctx, FALSE, FALSE, time);
1645 return;
1646 }
1647
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001648 if (uris[0])
1649 do_file_open_with_tab(ui, uris[0]);
Jens Axboea6790902012-03-13 15:16:11 +01001650
1651 gtk_drag_finish(ctx, TRUE, FALSE, time);
1652 g_strfreev(uris);
1653}
1654
Jens Axboe2f99deb2012-03-09 14:37:29 +01001655static void init_ui(int *argc, char **argv[], struct gui *ui)
1656{
1657 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01001658 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001659
1660 /* Magical g*thread incantation, you just need this thread stuff.
1661 * Without it, the update that happens in gfio_update_thread_status
1662 * doesn't really happen in a timely fashion, you need expose events
1663 */
1664 if (!g_thread_supported())
1665 g_thread_init(NULL);
1666 gdk_threads_init();
1667
1668 gtk_init(argc, argv);
1669 settings = gtk_settings_get_default();
1670 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1671 g_type_init();
Jens Axboe1252d8f2012-03-21 11:13:31 +01001672 gdk_color_parse("white", &gfio_color_white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001673
1674 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01001675 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001676 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
1677
Jens Axboe6e02ad62012-03-20 12:25:36 +01001678 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui);
1679 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001680
1681 ui->vbox = gtk_vbox_new(FALSE, 0);
1682 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
1683
Jens Axboe02421e62012-03-12 12:05:50 +01001684 ui->uimanager = gtk_ui_manager_new();
1685 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
1686 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001687
Jens Axboe38634cb2012-03-13 12:26:41 +01001688 ui->recentmanager = gtk_recent_manager_get_default();
1689 add_recent_file_items(ui);
1690
Jens Axboe2f99deb2012-03-09 14:37:29 +01001691 ui->notebook = gtk_notebook_new();
1692 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01001693 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01001694 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001695 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
1696
1697 vbox = new_main_page(ui);
Jens Axboe0cf3ece2012-03-21 10:15:20 +01001698 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY);
Jens Axboea6790902012-03-13 15:16:11 +01001699 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
1700 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001701
1702 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
1703
Jens Axboe9b260bd2012-03-06 11:02:52 +01001704 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001705
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001706 gtk_widget_show_all(ui->window);
1707}
1708
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001709int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001710{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01001711 if (initialize_fio(envp))
1712 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01001713 if (fio_init_options())
1714 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01001715
Jens Axboe753e9e62012-03-24 20:22:29 +01001716 gopt_init();
1717
Jens Axboe2f99deb2012-03-09 14:37:29 +01001718 memset(&main_ui, 0, sizeof(main_ui));
Jens Axboeb98ab712012-03-21 12:48:32 +01001719 main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001720
1721 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001722
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001723 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001724 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01001725 gdk_threads_leave();
Jens Axboeb98ab712012-03-21 12:48:32 +01001726
1727 g_hash_table_destroy(main_ui.ge_hash);
Jens Axboe753e9e62012-03-24 20:22:29 +01001728
1729 gopt_exit();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001730 return 0;
1731}