Commit | Line | Data |
---|---|---|
ff1f3280 SC |
1 | /* |
2 | * gfio - gui front end for fio - the flexible io tester | |
3 | * | |
3c3ed070 | 4 | * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> |
c0187f3b | 5 | * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk> |
ff1f3280 SC |
6 | * |
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 | |
fa07eaa6 | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
ff1f3280 SC |
22 | * |
23 | */ | |
8232e285 | 24 | #include <locale.h> |
cfe1ed39 | 25 | #include <stdlib.h> |
6b79c80c | 26 | #include <string.h> |
cfe1ed39 | 27 | #include <libgen.h> |
8232e285 | 28 | |
5b7573ab | 29 | #include <glib.h> |
2fd3bb0e | 30 | #include <cairo.h> |
ff1f3280 SC |
31 | #include <gtk/gtk.h> |
32 | ||
8232e285 | 33 | #include "fio.h" |
53e0e85d JA |
34 | #include "gfio.h" |
35 | #include "ghelpers.h" | |
9af4a244 | 36 | #include "goptions.h" |
41666588 | 37 | #include "gerror.h" |
1252d8f2 | 38 | #include "gclient.h" |
2fd3bb0e | 39 | #include "graph.h" |
8232e285 | 40 | |
63a4b9cc JA |
41 | struct gui main_ui; |
42 | ||
f1867a7f | 43 | static bool gfio_server_running; |
8577f4fd | 44 | static unsigned int gfio_graph_limit = 100; |
63a130b7 | 45 | |
1252d8f2 | 46 | GdkColor gfio_color_white; |
2a95f716 | 47 | GdkColor gfio_color_lightyellow; |
a1e7972d | 48 | const char *gfio_graph_font = GRAPH_DEFAULT_FONT; |
3e47bd25 | 49 | |
f3074008 SC |
50 | typedef void (*clickfunction)(GtkWidget *widget, gpointer data); |
51 | ||
3e47bd25 | 52 | static void connect_clicked(GtkWidget *widget, gpointer data); |
f3074008 | 53 | static void start_job_clicked(GtkWidget *widget, gpointer data); |
b9d2f30a | 54 | static void send_clicked(GtkWidget *widget, gpointer data); |
f3074008 SC |
55 | |
56 | static struct button_spec { | |
57 | const char *buttontext; | |
58 | clickfunction f; | |
014f4024 JA |
59 | const char *tooltiptext[2]; |
60 | const int start_sensitive; | |
f3074008 | 61 | } buttonspeclist[] = { |
53e0e85d JA |
62 | { |
63 | .buttontext = "Connect", | |
64 | .f = connect_clicked, | |
65 | .tooltiptext = { "Disconnect from host", "Connect to host" }, | |
66 | .start_sensitive = 1, | |
67 | }, | |
68 | { | |
69 | .buttontext = "Send", | |
70 | .f = send_clicked, | |
71 | .tooltiptext = { "Send job description to host", NULL }, | |
72 | .start_sensitive = 0, | |
73 | }, | |
74 | { | |
75 | .buttontext = "Start Job", | |
76 | .f = start_job_clicked, | |
77 | .tooltiptext = { "Start the current job on the server", NULL }, | |
78 | .start_sensitive = 0, | |
79 | }, | |
e0681f3e JA |
80 | }; |
81 | ||
8dfd6071 | 82 | static void setup_iops_graph(struct gfio_graphs *gg) |
2f99deb0 JA |
83 | { |
84 | struct graph *g; | |
85 | ||
86 | g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); | |
d8fbeefb | 87 | graph_title(g, "IOPS (IOs/sec)"); |
2f99deb0 | 88 | graph_x_title(g, "Time (secs)"); |
8dfd6071 JA |
89 | gg->read_iops = graph_add_label(g, "Read IOPS"); |
90 | gg->write_iops = graph_add_label(g, "Write IOPS"); | |
5442cfb0 | 91 | gg->trim_iops = graph_add_label(g, "Trim IOPS"); |
dbc542d4 JA |
92 | graph_set_color(g, gg->read_iops, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B); |
93 | graph_set_color(g, gg->write_iops, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B); | |
b78ca651 | 94 | graph_set_color(g, gg->trim_iops, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B); |
8577f4fd | 95 | line_graph_set_data_count_limit(g, gfio_graph_limit); |
d8fbeefb | 96 | graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0); |
01a947f0 | 97 | graph_set_graph_all_zeroes(g, 0); |
75450781 | 98 | gg->iops_graph = g; |
2f99deb0 JA |
99 | } |
100 | ||
8dfd6071 | 101 | static void setup_bandwidth_graph(struct gfio_graphs *gg) |
2f99deb0 JA |
102 | { |
103 | struct graph *g; | |
104 | ||
105 | g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font); | |
d8fbeefb | 106 | graph_title(g, "Bandwidth (bytes/sec)"); |
2f99deb0 | 107 | graph_x_title(g, "Time (secs)"); |
8dfd6071 JA |
108 | gg->read_bw = graph_add_label(g, "Read Bandwidth"); |
109 | gg->write_bw = graph_add_label(g, "Write Bandwidth"); | |
5442cfb0 | 110 | gg->trim_bw = graph_add_label(g, "Trim Bandwidth"); |
dbc542d4 JA |
111 | graph_set_color(g, gg->read_bw, GFIO_READ_R, GFIO_READ_G, GFIO_READ_B); |
112 | graph_set_color(g, gg->write_bw, GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B); | |
b78ca651 | 113 | graph_set_color(g, gg->trim_bw, GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B); |
d8fbeefb | 114 | graph_set_base_offset(g, 1); |
2f99deb0 | 115 | line_graph_set_data_count_limit(g, 100); |
d8fbeefb | 116 | graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0); |
01a947f0 | 117 | graph_set_graph_all_zeroes(g, 0); |
75450781 | 118 | gg->bandwidth_graph = g; |
2f99deb0 JA |
119 | } |
120 | ||
121 | static void setup_graphs(struct gfio_graphs *g) | |
122 | { | |
8dfd6071 JA |
123 | setup_iops_graph(g); |
124 | setup_bandwidth_graph(g); | |
2f99deb0 JA |
125 | } |
126 | ||
1252d8f2 | 127 | void clear_ge_ui_info(struct gui_entry *ge) |
2f99deb0 JA |
128 | { |
129 | gtk_label_set_text(GTK_LABEL(ge->probe.hostname), ""); | |
130 | gtk_label_set_text(GTK_LABEL(ge->probe.os), ""); | |
131 | gtk_label_set_text(GTK_LABEL(ge->probe.arch), ""); | |
132 | gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), ""); | |
3863d1ad JA |
133 | #if 0 |
134 | /* should we empty it... */ | |
2f99deb0 | 135 | gtk_entry_set_text(GTK_ENTRY(ge->eta.name), ""); |
3863d1ad | 136 | #endif |
c80b74b0 | 137 | multitext_update_entry(&ge->eta.iotype, 0, ""); |
99d633af | 138 | multitext_update_entry(&ge->eta.bs, 0, ""); |
c80b74b0 JA |
139 | multitext_update_entry(&ge->eta.ioengine, 0, ""); |
140 | multitext_update_entry(&ge->eta.iodepth, 0, ""); | |
2f99deb0 JA |
141 | gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), ""); |
142 | gtk_entry_set_text(GTK_ENTRY(ge->eta.files), ""); | |
143 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), ""); | |
144 | gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), ""); | |
145 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), ""); | |
146 | gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), ""); | |
8663ea65 JA |
147 | } |
148 | ||
781ccba6 JA |
149 | static void set_menu_entry_text(struct gui *ui, const char *path, |
150 | const char *text) | |
151 | { | |
152 | GtkWidget *w; | |
153 | ||
154 | w = gtk_ui_manager_get_widget(ui->uimanager, path); | |
155 | if (w) | |
156 | gtk_menu_item_set_label(GTK_MENU_ITEM(w), text); | |
157 | else | |
158 | fprintf(stderr, "gfio: can't find path %s\n", path); | |
159 | } | |
160 | ||
161 | ||
162 | static void set_menu_entry_visible(struct gui *ui, const char *path, int show) | |
163 | { | |
164 | GtkWidget *w; | |
165 | ||
166 | w = gtk_ui_manager_get_widget(ui->uimanager, path); | |
167 | if (w) | |
168 | gtk_widget_set_sensitive(w, show); | |
169 | else | |
170 | fprintf(stderr, "gfio: can't find path %s\n", path); | |
171 | } | |
172 | ||
173 | static void set_job_menu_visible(struct gui *ui, int visible) | |
174 | { | |
175 | set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible); | |
176 | } | |
177 | ||
178 | static void set_view_results_visible(struct gui *ui, int visible) | |
179 | { | |
180 | set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible); | |
181 | } | |
182 | ||
014f4024 JA |
183 | static const char *get_button_tooltip(struct button_spec *s, int sensitive) |
184 | { | |
185 | if (s->tooltiptext[sensitive]) | |
186 | return s->tooltiptext[sensitive]; | |
187 | ||
188 | return s->tooltiptext[0]; | |
189 | } | |
190 | ||
191 | static GtkWidget *add_button(GtkWidget *buttonbox, | |
192 | struct button_spec *buttonspec, gpointer data) | |
193 | { | |
194 | GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext); | |
195 | gboolean sens = buttonspec->start_sensitive; | |
196 | ||
197 | g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data); | |
198 | gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3); | |
199 | ||
200 | sens = buttonspec->start_sensitive; | |
201 | gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens)); | |
202 | gtk_widget_set_sensitive(button, sens); | |
203 | ||
204 | return button; | |
205 | } | |
206 | ||
207 | static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist, | |
208 | int nbuttons) | |
209 | { | |
210 | int i; | |
211 | ||
212 | for (i = 0; i < nbuttons; i++) | |
213 | ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge); | |
214 | } | |
215 | ||
85dd01e7 JA |
216 | /* |
217 | * Update sensitivity of job buttons and job menu items, based on the | |
218 | * state of the client. | |
219 | */ | |
220 | static void update_button_states(struct gui *ui, struct gui_entry *ge) | |
221 | { | |
222 | unsigned int connect_state, send_state, start_state, edit_state; | |
223 | const char *connect_str = NULL; | |
85dd01e7 JA |
224 | |
225 | switch (ge->state) { | |
1252d8f2 JA |
226 | default: |
227 | gfio_report_error(ge, "Bad client state: %u\n", ge->state); | |
63a4b9cc | 228 | /* fall-through */ |
85dd01e7 JA |
229 | case GE_STATE_NEW: |
230 | connect_state = 1; | |
9af4a244 | 231 | edit_state = 1; |
85dd01e7 JA |
232 | connect_str = "Connect"; |
233 | send_state = 0; | |
234 | start_state = 0; | |
235 | break; | |
236 | case GE_STATE_CONNECTED: | |
237 | connect_state = 1; | |
9af4a244 | 238 | edit_state = 1; |
85dd01e7 JA |
239 | connect_str = "Disconnect"; |
240 | send_state = 1; | |
241 | start_state = 0; | |
242 | break; | |
243 | case GE_STATE_JOB_SENT: | |
244 | connect_state = 1; | |
9af4a244 | 245 | edit_state = 1; |
85dd01e7 JA |
246 | connect_str = "Disconnect"; |
247 | send_state = 0; | |
248 | start_state = 1; | |
249 | break; | |
250 | case GE_STATE_JOB_STARTED: | |
251 | connect_state = 1; | |
252 | edit_state = 1; | |
253 | connect_str = "Disconnect"; | |
254 | send_state = 0; | |
255 | start_state = 1; | |
256 | break; | |
257 | case GE_STATE_JOB_RUNNING: | |
258 | connect_state = 1; | |
259 | edit_state = 0; | |
260 | connect_str = "Disconnect"; | |
261 | send_state = 0; | |
262 | start_state = 0; | |
263 | break; | |
264 | case GE_STATE_JOB_DONE: | |
265 | connect_state = 1; | |
266 | edit_state = 0; | |
267 | connect_str = "Connect"; | |
268 | send_state = 0; | |
269 | start_state = 0; | |
270 | break; | |
271 | } | |
272 | ||
53e0e85d JA |
273 | gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state); |
274 | gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state); | |
275 | gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state); | |
276 | gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str); | |
277 | gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state)); | |
85dd01e7 | 278 | |
781ccba6 JA |
279 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state); |
280 | set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str); | |
85dd01e7 | 281 | |
781ccba6 JA |
282 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state); |
283 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state); | |
284 | set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state); | |
85dd01e7 | 285 | |
781ccba6 JA |
286 | if (ge->client && ge->client->nr_results) |
287 | set_view_results_visible(ui, 1); | |
288 | else | |
289 | set_view_results_visible(ui, 0); | |
85dd01e7 JA |
290 | } |
291 | ||
1252d8f2 | 292 | void gfio_set_state(struct gui_entry *ge, unsigned int state) |
85dd01e7 JA |
293 | { |
294 | ge->state = state; | |
295 | update_button_states(ge->ui, ge); | |
296 | } | |
297 | ||
9b260bdf JA |
298 | static void gfio_ui_setup_log(struct gui *ui) |
299 | { | |
300 | GtkTreeSelection *selection; | |
301 | GtkListStore *model; | |
302 | GtkWidget *tree_view; | |
303 | ||
3ad04399 | 304 | model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); |
9b260bdf JA |
305 | |
306 | tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); | |
307 | gtk_widget_set_can_focus(tree_view, FALSE); | |
308 | ||
309 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); | |
310 | gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); | |
661f741a JA |
311 | g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, |
312 | "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); | |
9b260bdf JA |
313 | |
314 | tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE); | |
315 | tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE); | |
316 | tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE); | |
f095d563 | 317 | tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE); |
9b260bdf JA |
318 | |
319 | ui->log_model = model; | |
320 | ui->log_tree = tree_view; | |
321 | } | |
322 | ||
1252d8f2 JA |
323 | static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event, |
324 | gpointer data) | |
c57f254c | 325 | { |
cd0d2db8 JA |
326 | guint width = gtk_widget_get_allocated_width(w); |
327 | guint height = gtk_widget_get_allocated_height(w); | |
1252d8f2 | 328 | struct gfio_graphs *g = data; |
c57f254c | 329 | |
cd0d2db8 JA |
330 | graph_set_size(g->iops_graph, width / 2.0, height); |
331 | graph_set_position(g->iops_graph, width / 2.0, 0.0); | |
332 | graph_set_size(g->bandwidth_graph, width / 2.0, height); | |
1252d8f2 JA |
333 | graph_set_position(g->bandwidth_graph, 0, 0); |
334 | return TRUE; | |
c57f254c SC |
335 | } |
336 | ||
1252d8f2 | 337 | static void draw_graph(struct graph *g, cairo_t *cr) |
a269790c | 338 | { |
1252d8f2 JA |
339 | line_graph_draw(g, cr); |
340 | cairo_stroke(cr); | |
341 | } | |
a269790c | 342 | |
1252d8f2 JA |
343 | static gboolean graph_tooltip(GtkWidget *w, gint x, gint y, |
344 | gboolean keyboard_mode, GtkTooltip *tooltip, | |
345 | gpointer data) | |
346 | { | |
347 | struct gfio_graphs *g = data; | |
348 | const char *text = NULL; | |
a269790c | 349 | |
1252d8f2 JA |
350 | if (graph_contains_xy(g->iops_graph, x, y)) |
351 | text = graph_find_tooltip(g->iops_graph, x, y); | |
352 | else if (graph_contains_xy(g->bandwidth_graph, x, y)) | |
353 | text = graph_find_tooltip(g->bandwidth_graph, x, y); | |
a269790c | 354 | |
1252d8f2 JA |
355 | if (text) { |
356 | gtk_tooltip_set_text(tooltip, text); | |
357 | return TRUE; | |
e0681f3e | 358 | } |
a269790c | 359 | |
1252d8f2 | 360 | return FALSE; |
a269790c JA |
361 | } |
362 | ||
1252d8f2 | 363 | static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p) |
c57f254c | 364 | { |
1252d8f2 | 365 | struct gfio_graphs *g = p; |
c57f254c SC |
366 | cairo_t *cr; |
367 | ||
cd0d2db8 | 368 | cr = gdk_cairo_create(gtk_widget_get_window(w)); |
1252d8f2 JA |
369 | |
370 | if (graph_has_tooltips(g->iops_graph) || | |
371 | graph_has_tooltips(g->bandwidth_graph)) { | |
c57f254c | 372 | g_object_set(w, "has-tooltip", TRUE, NULL); |
1252d8f2 | 373 | g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g); |
c57f254c | 374 | } |
1252d8f2 | 375 | |
c57f254c | 376 | cairo_set_source_rgb(cr, 0, 0, 0); |
1252d8f2 JA |
377 | draw_graph(g->iops_graph, cr); |
378 | draw_graph(g->bandwidth_graph, cr); | |
c57f254c SC |
379 | cairo_destroy(cr); |
380 | ||
381 | return FALSE; | |
382 | } | |
383 | ||
1252d8f2 JA |
384 | /* |
385 | * FIXME: need more handling here | |
386 | */ | |
387 | static void ge_destroy(struct gui_entry *ge) | |
c57f254c | 388 | { |
1252d8f2 | 389 | struct gfio_client *gc = ge->client; |
c57f254c | 390 | |
470cdbb7 JA |
391 | if (gc) { |
392 | if (gc->client) { | |
393 | if (ge->state >= GE_STATE_CONNECTED) | |
394 | fio_client_terminate(gc->client); | |
395 | ||
396 | fio_put_client(gc->client); | |
397 | } | |
398 | free(gc); | |
a269790c JA |
399 | } |
400 | ||
b98ab718 JA |
401 | g_hash_table_remove(ge->ui->ge_hash, &ge->page_num); |
402 | ||
1252d8f2 JA |
403 | free(ge->job_file); |
404 | free(ge->host); | |
1252d8f2 | 405 | free(ge); |
a269790c JA |
406 | } |
407 | ||
1252d8f2 | 408 | static void ge_widget_destroy(GtkWidget *w, gpointer data) |
3650a3ca | 409 | { |
1252d8f2 | 410 | struct gui_entry *ge = (struct gui_entry *) data; |
3650a3ca | 411 | |
1252d8f2 | 412 | ge_destroy(ge); |
3650a3ca JA |
413 | } |
414 | ||
1252d8f2 | 415 | static void gfio_quit(struct gui *ui) |
3650a3ca | 416 | { |
3c3ed070 | 417 | gtk_main_quit(); |
1252d8f2 | 418 | } |
3650a3ca | 419 | |
1252d8f2 | 420 | static void quit_clicked(__attribute__((unused)) GtkWidget *widget, |
3c3ed070 | 421 | gpointer data) |
1252d8f2 JA |
422 | { |
423 | struct gui *ui = (struct gui *) data; | |
3650a3ca | 424 | |
1252d8f2 JA |
425 | gfio_quit(ui); |
426 | } | |
3650a3ca | 427 | |
1252d8f2 JA |
428 | static void *job_thread(void *arg) |
429 | { | |
430 | struct gui *ui = arg; | |
3650a3ca | 431 | |
1252d8f2 JA |
432 | ui->handler_running = 1; |
433 | fio_handle_clients(&gfio_client_ops); | |
434 | ui->handler_running = 0; | |
435 | return NULL; | |
436 | } | |
ca850992 | 437 | |
1252d8f2 JA |
438 | static int send_job_file(struct gui_entry *ge) |
439 | { | |
440 | struct gfio_client *gc = ge->client; | |
441 | int ret = 0; | |
ca850992 | 442 | |
753e9e64 JA |
443 | /* |
444 | * Prune old options, we are expecting the return options | |
445 | * when the job file is parsed remotely and returned to us. | |
446 | */ | |
447 | while (!flist_empty(&gc->o_list)) { | |
448 | struct gfio_client_options *gco; | |
449 | ||
9342d5f8 | 450 | gco = flist_first_entry(&gc->o_list, struct gfio_client_options, list); |
753e9e64 JA |
451 | flist_del(&gco->list); |
452 | free(gco); | |
453 | } | |
454 | ||
bb781643 | 455 | ret = fio_client_send_ini(gc->client, ge->job_file, false); |
1252d8f2 JA |
456 | if (!ret) |
457 | return 0; | |
ca850992 | 458 | |
1252d8f2 JA |
459 | gfio_report_error(ge, "Failed to send file %s: %s\n", ge->job_file, strerror(-ret)); |
460 | return 1; | |
461 | } | |
ca850992 | 462 | |
1252d8f2 JA |
463 | static void *server_thread(void *arg) |
464 | { | |
8267ddba | 465 | fio_server_create_sk_key(); |
f1867a7f JA |
466 | is_backend = true; |
467 | gfio_server_running = true; | |
1252d8f2 | 468 | fio_start_server(NULL); |
f1867a7f | 469 | gfio_server_running = false; |
8267ddba | 470 | fio_server_destroy_sk_key(); |
1252d8f2 JA |
471 | return NULL; |
472 | } | |
ca850992 | 473 | |
1252d8f2 JA |
474 | static void gfio_start_server(struct gui *ui) |
475 | { | |
476 | if (!gfio_server_running) { | |
f1867a7f | 477 | gfio_server_running = true; |
1252d8f2 JA |
478 | pthread_create(&ui->server_t, NULL, server_thread, NULL); |
479 | pthread_detach(ui->server_t); | |
2b089892 | 480 | } |
3650a3ca JA |
481 | } |
482 | ||
1252d8f2 | 483 | static void start_job_clicked(__attribute__((unused)) GtkWidget *widget, |
3c3ed070 | 484 | gpointer data) |
09d574e3 | 485 | { |
1252d8f2 JA |
486 | struct gui_entry *ge = data; |
487 | struct gfio_client *gc = ge->client; | |
09d574e3 | 488 | |
1252d8f2 JA |
489 | if (gc) |
490 | fio_start_client(gc->client); | |
09d574e3 JA |
491 | } |
492 | ||
1252d8f2 | 493 | static void file_open(GtkWidget *w, gpointer data); |
e5bd1347 | 494 | |
1252d8f2 JA |
495 | struct connection_widgets |
496 | { | |
497 | GtkWidget *hentry; | |
498 | GtkWidget *combo; | |
499 | GtkWidget *button; | |
500 | }; | |
e5bd1347 | 501 | |
1252d8f2 JA |
502 | static void hostname_cb(GtkEntry *entry, gpointer data) |
503 | { | |
504 | struct connection_widgets *cw = data; | |
505 | int uses_net = 0, is_localhost = 0; | |
506 | const gchar *text; | |
507 | gchar *ctext; | |
e5bd1347 | 508 | |
1252d8f2 JA |
509 | /* |
510 | * Check whether to display the 'auto start backend' box | |
511 | * or not. Show it if we are a localhost and using network, | |
512 | * or using a socket. | |
513 | */ | |
f762cef9 | 514 | ctext = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw->combo)); |
1252d8f2 JA |
515 | if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4)) |
516 | uses_net = 1; | |
517 | g_free(ctext); | |
e5bd1347 | 518 | |
1252d8f2 JA |
519 | if (uses_net) { |
520 | text = gtk_entry_get_text(GTK_ENTRY(cw->hentry)); | |
521 | if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") || | |
522 | !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") || | |
523 | !strcmp(text, "ip6-loopback")) | |
524 | is_localhost = 1; | |
525 | } | |
e5bd1347 | 526 | |
1252d8f2 JA |
527 | if (!uses_net || is_localhost) { |
528 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1); | |
529 | gtk_widget_set_sensitive(cw->button, 1); | |
530 | } else { | |
531 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0); | |
532 | gtk_widget_set_sensitive(cw->button, 0); | |
533 | } | |
b0500ccb | 534 | } |
661f741a | 535 | |
b0500ccb JA |
536 | static int get_connection_details(struct gui_entry *ge) |
537 | { | |
538 | GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry; | |
539 | struct connection_widgets cw; | |
540 | struct gui *ui = ge->ui; | |
541 | char *typeentry; | |
2e33101f | 542 | |
b0500ccb JA |
543 | if (ge->host) |
544 | return 0; | |
545 | ||
546 | dialog = gtk_dialog_new_with_buttons("Connection details", | |
547 | GTK_WINDOW(ui->window), | |
548 | GTK_DIALOG_DESTROY_WITH_PARENT, | |
549 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | |
550 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); | |
551 | ||
552 | frame = gtk_frame_new("Hostname / socket name"); | |
f762cef9 | 553 | vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); |
b0500ccb JA |
554 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); |
555 | ||
556 | box = gtk_vbox_new(FALSE, 6); | |
557 | gtk_container_add(GTK_CONTAINER(frame), box); | |
558 | ||
559 | hbox = gtk_hbox_new(TRUE, 10); | |
560 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
561 | cw.hentry = gtk_entry_new(); | |
562 | gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost"); | |
563 | gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0); | |
564 | ||
565 | frame = gtk_frame_new("Port"); | |
566 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
567 | box = gtk_vbox_new(FALSE, 10); | |
568 | gtk_container_add(GTK_CONTAINER(frame), box); | |
569 | ||
570 | hbox = gtk_hbox_new(TRUE, 4); | |
571 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
572 | pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT); | |
573 | ||
574 | frame = gtk_frame_new("Type"); | |
575 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
576 | box = gtk_vbox_new(FALSE, 10); | |
577 | gtk_container_add(GTK_CONTAINER(frame), box); | |
578 | ||
579 | hbox = gtk_hbox_new(TRUE, 4); | |
580 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
581 | ||
f762cef9 JA |
582 | cw.combo = gtk_combo_box_text_new(); |
583 | gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv4"); | |
584 | gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "IPv6"); | |
585 | gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cw.combo), "local socket"); | |
b0500ccb JA |
586 | gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0); |
587 | ||
588 | gtk_container_add(GTK_CONTAINER(hbox), cw.combo); | |
589 | ||
590 | frame = gtk_frame_new("Options"); | |
591 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
592 | box = gtk_vbox_new(FALSE, 10); | |
593 | gtk_container_add(GTK_CONTAINER(frame), box); | |
594 | ||
595 | hbox = gtk_hbox_new(TRUE, 4); | |
596 | gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); | |
597 | ||
598 | cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend"); | |
599 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1); | |
600 | 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."); | |
601 | gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6); | |
2e33101f | 602 | |
b0500ccb JA |
603 | /* |
604 | * Connect edit signal, so we can show/not-show the auto start button | |
605 | */ | |
cd972fdd JA |
606 | g_signal_connect(G_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw); |
607 | g_signal_connect(G_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw); | |
2e33101f | 608 | |
b0500ccb | 609 | gtk_widget_show_all(dialog); |
2e33101f | 610 | |
b0500ccb | 611 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { |
1252d8f2 | 612 | gtk_widget_destroy(dialog); |
b0500ccb | 613 | return 1; |
1252d8f2 | 614 | } |
2e33101f | 615 | |
b0500ccb JA |
616 | ge->host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry))); |
617 | ge->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry)); | |
2e33101f | 618 | |
f762cef9 | 619 | typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cw.combo)); |
b0500ccb JA |
620 | if (!typeentry || !strncmp(typeentry, "IPv4", 4)) |
621 | ge->type = Fio_client_ipv4; | |
622 | else if (!strncmp(typeentry, "IPv6", 4)) | |
623 | ge->type = Fio_client_ipv6; | |
624 | else | |
625 | ge->type = Fio_client_socket; | |
626 | g_free(typeentry); | |
2e33101f | 627 | |
b0500ccb | 628 | ge->server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button)); |
2e33101f | 629 | |
b0500ccb JA |
630 | gtk_widget_destroy(dialog); |
631 | return 0; | |
632 | } | |
1252d8f2 | 633 | |
b0500ccb JA |
634 | static void gfio_set_client(struct gfio_client *gc, struct fio_client *client) |
635 | { | |
636 | gc->client = fio_get_client(client); | |
637 | client->client_data = gc; | |
638 | } | |
639 | ||
640 | static void gfio_client_added(struct gui_entry *ge, struct fio_client *client) | |
641 | { | |
cf3d8241 | 642 | struct gfio_client_options *gco; |
b0500ccb JA |
643 | struct gfio_client *gc; |
644 | ||
cf3d8241 JA |
645 | gc = calloc(1, sizeof(*gc)); |
646 | INIT_FLIST_HEAD(&gc->o_list); | |
b0500ccb JA |
647 | gc->ge = ge; |
648 | ge->client = gc; | |
649 | gfio_set_client(gc, client); | |
cf3d8241 JA |
650 | |
651 | /* | |
652 | * Just add a default set of options, need to consider how best | |
653 | * to handle this | |
654 | */ | |
655 | gco = calloc(1, sizeof(*gco)); | |
753e9e64 | 656 | INIT_FLIST_HEAD(&gco->list); |
cf3d8241 JA |
657 | options_default_fill(&gco->o); |
658 | flist_add_tail(&gco->list, &gc->o_list); | |
753e9e64 | 659 | gc->o_list_nr++; |
b0500ccb JA |
660 | } |
661 | ||
5721d270 JA |
662 | static void gfio_clear_graph_data(struct gfio_graphs *g) |
663 | { | |
664 | graph_clear_values(g->iops_graph); | |
665 | graph_clear_values(g->bandwidth_graph); | |
666 | } | |
667 | ||
b0500ccb JA |
668 | static void connect_clicked(GtkWidget *widget, gpointer data) |
669 | { | |
670 | struct gui_entry *ge = data; | |
671 | struct gfio_client *gc = ge->client; | |
672 | ||
673 | if (ge->state == GE_STATE_NEW) { | |
674 | int ret; | |
19998dbc | 675 | |
b0500ccb JA |
676 | if (!ge->job_file) |
677 | file_open(widget, ge->ui); | |
678 | if (!ge->job_file) | |
679 | return; | |
2e33101f | 680 | |
b0500ccb | 681 | gc = ge->client; |
2e33101f | 682 | |
b0500ccb JA |
683 | if (!gc->client) { |
684 | struct fio_client *client; | |
19998dbc | 685 | |
b0500ccb JA |
686 | if (get_connection_details(ge)) { |
687 | gfio_report_error(ge, "Failed to get connection details\n"); | |
688 | return; | |
1252d8f2 | 689 | } |
2e33101f | 690 | |
b0500ccb JA |
691 | client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port); |
692 | if (!client) { | |
693 | gfio_report_error(ge, "Failed to add client %s\n", ge->host); | |
694 | free(ge->host); | |
695 | ge->host = NULL; | |
696 | return; | |
1252d8f2 | 697 | } |
b0500ccb JA |
698 | gfio_set_client(gc, client); |
699 | } | |
700 | ||
701 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running"); | |
702 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); | |
703 | ret = fio_client_connect(gc->client); | |
704 | if (!ret) { | |
705 | if (!ge->ui->handler_running) | |
706 | pthread_create(&ge->ui->t, NULL, job_thread, ge->ui); | |
707 | gfio_set_state(ge, GE_STATE_CONNECTED); | |
5721d270 | 708 | gfio_clear_graph_data(&ge->graphs); |
1252d8f2 | 709 | } else { |
b0500ccb | 710 | gfio_report_error(ge, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret)); |
2e33101f | 711 | } |
b0500ccb JA |
712 | } else { |
713 | fio_client_terminate(gc->client); | |
714 | gfio_set_state(ge, GE_STATE_NEW); | |
715 | clear_ge_ui_info(ge); | |
2e33101f | 716 | } |
b0500ccb | 717 | } |
2e33101f | 718 | |
b0500ccb JA |
719 | static void send_clicked(GtkWidget *widget, gpointer data) |
720 | { | |
721 | struct gui_entry *ge = data; | |
2e33101f | 722 | |
b0500ccb JA |
723 | if (send_job_file(ge)) |
724 | gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1); | |
725 | } | |
62bc937f | 726 | |
b0500ccb | 727 | static GtkWidget *new_client_page(struct gui_entry *ge); |
62bc937f | 728 | |
b0500ccb JA |
729 | static struct gui_entry *alloc_new_gui_entry(struct gui *ui) |
730 | { | |
731 | struct gui_entry *ge; | |
a7a42ce1 | 732 | |
b0500ccb JA |
733 | ge = malloc(sizeof(*ge)); |
734 | memset(ge, 0, sizeof(*ge)); | |
735 | ge->state = GE_STATE_NEW; | |
b0500ccb JA |
736 | ge->ui = ui; |
737 | return ge; | |
738 | } | |
a7a42ce1 | 739 | |
b0500ccb JA |
740 | static struct gui_entry *get_new_ge_with_tab(struct gui *ui, const char *name) |
741 | { | |
742 | struct gui_entry *ge; | |
a7a42ce1 | 743 | |
b0500ccb | 744 | ge = alloc_new_gui_entry(ui); |
a7a42ce1 | 745 | |
b0500ccb JA |
746 | ge->vbox = new_client_page(ge); |
747 | g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge); | |
a7a42ce1 | 748 | |
b0500ccb JA |
749 | ge->page_label = gtk_label_new(name); |
750 | ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), ge->vbox, ge->page_label); | |
b9f3c7ed | 751 | |
b98ab718 JA |
752 | g_hash_table_insert(ui->ge_hash, &ge->page_num, ge); |
753 | ||
b0500ccb JA |
754 | gtk_widget_show_all(ui->window); |
755 | return ge; | |
756 | } | |
b9f3c7ed | 757 | |
b0500ccb JA |
758 | static void file_new(GtkWidget *w, gpointer data) |
759 | { | |
760 | struct gui *ui = (struct gui *) data; | |
761 | struct gui_entry *ge; | |
62bc937f | 762 | |
b0500ccb JA |
763 | ge = get_new_ge_with_tab(ui, "Untitled"); |
764 | gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num); | |
765 | } | |
a7a42ce1 | 766 | |
b0500ccb JA |
767 | /* |
768 | * Return the 'ge' corresponding to the tab. If the active tab is the | |
769 | * main tab, open a new tab. | |
770 | */ | |
771 | static struct gui_entry *get_ge_from_page(struct gui *ui, gint cur_page, | |
772 | int *created) | |
773 | { | |
b0500ccb | 774 | if (!cur_page) { |
1252d8f2 | 775 | if (created) |
b0500ccb JA |
776 | *created = 1; |
777 | return get_new_ge_with_tab(ui, "Untitled"); | |
778 | } | |
b9f3c7ed | 779 | |
b0500ccb JA |
780 | if (created) |
781 | *created = 0; | |
a7a42ce1 | 782 | |
b98ab718 | 783 | return g_hash_table_lookup(ui->ge_hash, &cur_page); |
b0500ccb | 784 | } |
e0681f3e | 785 | |
b0500ccb JA |
786 | static struct gui_entry *get_ge_from_cur_tab(struct gui *ui) |
787 | { | |
788 | gint cur_page; | |
e0681f3e | 789 | |
b0500ccb JA |
790 | /* |
791 | * Main tab is tab 0, so any current page other than 0 holds | |
792 | * a ge entry. | |
793 | */ | |
794 | cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook)); | |
795 | if (cur_page) | |
796 | return get_ge_from_page(ui, cur_page, NULL); | |
0cf3ece0 | 797 | |
b0500ccb JA |
798 | return NULL; |
799 | } | |
0cf3ece0 | 800 | |
b0500ccb JA |
801 | static void file_close(GtkWidget *w, gpointer data) |
802 | { | |
803 | struct gui *ui = (struct gui *) data; | |
804 | struct gui_entry *ge; | |
2f99deb0 | 805 | |
b0500ccb JA |
806 | /* |
807 | * Can't close the main tab | |
808 | */ | |
809 | ge = get_ge_from_cur_tab(ui); | |
810 | if (ge) { | |
811 | gtk_widget_destroy(ge->vbox); | |
812 | return; | |
1252d8f2 | 813 | } |
16ce5adf | 814 | |
b98ab718 | 815 | if (g_hash_table_size(ui->ge_hash)) { |
b0500ccb JA |
816 | gfio_report_info(ui, "Error", "The main page view cannot be closed\n"); |
817 | return; | |
38634cb1 JA |
818 | } |
819 | ||
b0500ccb JA |
820 | gfio_quit(ui); |
821 | } | |
2f99deb0 | 822 | |
b0500ccb JA |
823 | static void file_add_recent(struct gui *ui, const gchar *uri) |
824 | { | |
825 | GtkRecentData grd; | |
2f99deb0 | 826 | |
b0500ccb JA |
827 | memset(&grd, 0, sizeof(grd)); |
828 | grd.display_name = strdup("gfio"); | |
829 | grd.description = strdup("Fio job file"); | |
830 | grd.mime_type = strdup(GFIO_MIME); | |
831 | grd.app_name = strdup(g_get_application_name()); | |
832 | grd.app_exec = strdup("gfio %f/%u"); | |
85dd01e7 | 833 | |
b0500ccb JA |
834 | gtk_recent_manager_add_full(ui->recentmanager, uri, &grd); |
835 | } | |
85dd01e7 | 836 | |
b0500ccb JA |
837 | static gchar *get_filename_from_uri(const gchar *uri) |
838 | { | |
839 | if (strncmp(uri, "file://", 7)) | |
840 | return strdup(uri); | |
16ce5adf | 841 | |
b0500ccb JA |
842 | return strdup(uri + 7); |
843 | } | |
16ce5adf | 844 | |
b0500ccb JA |
845 | static int do_file_open(struct gui_entry *ge, const gchar *uri) |
846 | { | |
847 | struct fio_client *client; | |
f5c6726e | 848 | |
b0500ccb | 849 | assert(!ge->job_file); |
38634cb1 | 850 | |
b0500ccb | 851 | ge->job_file = get_filename_from_uri(uri); |
38634cb1 | 852 | |
b0500ccb JA |
853 | client = fio_client_add_explicit(&gfio_client_ops, ge->host, ge->type, ge->port); |
854 | if (client) { | |
832fde79 JA |
855 | char *label = strdup(uri); |
856 | ||
857 | basename(label); | |
858 | gtk_label_set_text(GTK_LABEL(ge->page_label), basename(label)); | |
859 | free(label); | |
860 | ||
b0500ccb JA |
861 | gfio_client_added(ge, client); |
862 | file_add_recent(ge->ui, uri); | |
863 | return 0; | |
864 | } | |
38634cb1 | 865 | |
b0500ccb JA |
866 | gfio_report_error(ge, "Failed to add client %s\n", ge->host); |
867 | free(ge->host); | |
868 | ge->host = NULL; | |
b98ab718 JA |
869 | free(ge->job_file); |
870 | ge->job_file = NULL; | |
b0500ccb JA |
871 | return 1; |
872 | } | |
38634cb1 | 873 | |
b0500ccb JA |
874 | static int do_file_open_with_tab(struct gui *ui, const gchar *uri) |
875 | { | |
876 | struct gui_entry *ge; | |
877 | gint cur_page; | |
878 | int ret, ge_is_new = 0; | |
2f99deb0 | 879 | |
b0500ccb JA |
880 | /* |
881 | * Creates new tab if current tab is the main window, or the | |
882 | * current tab already has a client. | |
883 | */ | |
884 | cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook)); | |
885 | ge = get_ge_from_page(ui, cur_page, &ge_is_new); | |
886 | if (ge->client) { | |
887 | ge = get_new_ge_with_tab(ui, "Untitled"); | |
888 | ge_is_new = 1; | |
38634cb1 | 889 | } |
2f99deb0 | 890 | |
b0500ccb JA |
891 | gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num); |
892 | ||
893 | if (get_connection_details(ge)) { | |
894 | if (ge_is_new) | |
895 | gtk_widget_destroy(ge->vbox); | |
3c3ed070 | 896 | |
b0500ccb JA |
897 | return 1; |
898 | } | |
a6790906 | 899 | |
b0500ccb | 900 | ret = do_file_open(ge, uri); |
a6790906 | 901 | |
b0500ccb JA |
902 | if (!ret) { |
903 | if (ge->server_start) | |
904 | gfio_start_server(ui); | |
905 | } else { | |
906 | if (ge_is_new) | |
907 | gtk_widget_destroy(ge->vbox); | |
a6790906 JA |
908 | } |
909 | ||
b0500ccb JA |
910 | return ret; |
911 | } | |
a6790906 | 912 | |
b0500ccb JA |
913 | static void recent_open(GtkAction *action, gpointer data) |
914 | { | |
915 | struct gui *ui = (struct gui *) data; | |
916 | GtkRecentInfo *info; | |
917 | const gchar *uri; | |
0420ba6a | 918 | |
b0500ccb JA |
919 | info = g_object_get_data(G_OBJECT(action), "gtk-recent-info"); |
920 | uri = gtk_recent_info_get_uri(info); | |
0420ba6a | 921 | |
b0500ccb JA |
922 | do_file_open_with_tab(ui, uri); |
923 | } | |
a7a42ce1 | 924 | |
b0500ccb JA |
925 | static void file_open(GtkWidget *w, gpointer data) |
926 | { | |
927 | struct gui *ui = data; | |
928 | GtkWidget *dialog; | |
929 | GtkFileFilter *filter; | |
930 | gchar *filename; | |
931 | ||
932 | dialog = gtk_file_chooser_dialog_new("Open File", | |
933 | GTK_WINDOW(ui->window), | |
934 | GTK_FILE_CHOOSER_ACTION_OPEN, | |
935 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
936 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
937 | NULL); | |
938 | gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
939 | ||
940 | filter = gtk_file_filter_new(); | |
941 | gtk_file_filter_add_pattern(filter, "*.fio"); | |
942 | gtk_file_filter_add_pattern(filter, "*.job"); | |
943 | gtk_file_filter_add_pattern(filter, "*.ini"); | |
944 | gtk_file_filter_add_mime_type(filter, GFIO_MIME); | |
945 | gtk_file_filter_set_name(filter, "Fio job file"); | |
946 | gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); | |
947 | ||
948 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
949 | gtk_widget_destroy(dialog); | |
950 | return; | |
1252d8f2 | 951 | } |
0420ba6a | 952 | |
b0500ccb | 953 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); |
0420ba6a | 954 | |
b0500ccb | 955 | gtk_widget_destroy(dialog); |
0420ba6a | 956 | |
b0500ccb JA |
957 | do_file_open_with_tab(ui, filename); |
958 | g_free(filename); | |
959 | } | |
0420ba6a | 960 | |
b0500ccb JA |
961 | static void file_save(GtkWidget *w, gpointer data) |
962 | { | |
963 | struct gui *ui = data; | |
964 | GtkWidget *dialog; | |
0420ba6a | 965 | |
b0500ccb JA |
966 | dialog = gtk_file_chooser_dialog_new("Save File", |
967 | GTK_WINDOW(ui->window), | |
968 | GTK_FILE_CHOOSER_ACTION_SAVE, | |
969 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
970 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
971 | NULL); | |
972 | ||
973 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); | |
974 | gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document"); | |
975 | ||
976 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | |
977 | char *filename; | |
978 | ||
979 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
980 | // save_job_file(filename); | |
981 | g_free(filename); | |
0420ba6a | 982 | } |
b0500ccb JA |
983 | gtk_widget_destroy(dialog); |
984 | } | |
0420ba6a | 985 | |
9b260bdf JA |
986 | static void view_log_destroy(GtkWidget *w, gpointer data) |
987 | { | |
988 | struct gui *ui = (struct gui *) data; | |
989 | ||
f762cef9 | 990 | g_object_ref(G_OBJECT(ui->log_tree)); |
9b260bdf JA |
991 | gtk_container_remove(GTK_CONTAINER(w), ui->log_tree); |
992 | gtk_widget_destroy(w); | |
4cbe7211 | 993 | ui->log_view = NULL; |
9b260bdf JA |
994 | } |
995 | ||
1252d8f2 | 996 | void gfio_view_log(struct gui *ui) |
9b260bdf | 997 | { |
4cbe7211 | 998 | GtkWidget *win, *scroll, *vbox, *box; |
9b260bdf | 999 | |
4cbe7211 JA |
1000 | if (ui->log_view) |
1001 | return; | |
1002 | ||
1003 | ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
9b260bdf | 1004 | gtk_window_set_title(GTK_WINDOW(win), "Log"); |
4cbe7211 | 1005 | gtk_window_set_default_size(GTK_WINDOW(win), 700, 500); |
9b260bdf | 1006 | |
4cbe7211 JA |
1007 | scroll = gtk_scrolled_window_new(NULL, NULL); |
1008 | ||
1009 | gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); | |
1010 | ||
1011 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
1012 | ||
1013 | box = gtk_hbox_new(TRUE, 0); | |
f762cef9 | 1014 | gtk_box_pack_start(GTK_BOX(box), ui->log_tree, TRUE, TRUE, 0); |
4cbe7211 JA |
1015 | g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui); |
1016 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box); | |
1017 | ||
1018 | vbox = gtk_vbox_new(TRUE, 5); | |
f762cef9 | 1019 | gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0); |
9b260bdf | 1020 | |
4cbe7211 | 1021 | gtk_container_add(GTK_CONTAINER(win), vbox); |
9b260bdf JA |
1022 | gtk_widget_show_all(win); |
1023 | } | |
1024 | ||
1252d8f2 JA |
1025 | static void view_log(GtkWidget *w, gpointer data) |
1026 | { | |
1027 | struct gui *ui = (struct gui *) data; | |
1028 | ||
1029 | gfio_view_log(ui); | |
1030 | } | |
1031 | ||
85dd01e7 JA |
1032 | static void connect_job_entry(GtkWidget *w, gpointer data) |
1033 | { | |
1034 | struct gui *ui = (struct gui *) data; | |
1035 | struct gui_entry *ge; | |
3c3ed070 | 1036 | |
85dd01e7 JA |
1037 | ge = get_ge_from_cur_tab(ui); |
1038 | if (ge) | |
1039 | connect_clicked(w, ge); | |
1040 | } | |
1041 | ||
1042 | static void send_job_entry(GtkWidget *w, gpointer data) | |
1043 | { | |
1044 | struct gui *ui = (struct gui *) data; | |
1045 | struct gui_entry *ge; | |
1046 | ||
1047 | ge = get_ge_from_cur_tab(ui); | |
1048 | if (ge) | |
1049 | send_clicked(w, ge); | |
85dd01e7 JA |
1050 | } |
1051 | ||
1052 | static void edit_job_entry(GtkWidget *w, gpointer data) | |
16ce5adf | 1053 | { |
9af4a244 | 1054 | struct gui *ui = (struct gui *) data; |
789f4ccd | 1055 | struct gui_entry *ge; |
9af4a244 | 1056 | |
789f4ccd JA |
1057 | ge = get_ge_from_cur_tab(ui); |
1058 | if (ge && ge->client) | |
cf3d8241 | 1059 | gopt_get_options_window(ui->window, ge->client); |
16ce5adf JA |
1060 | } |
1061 | ||
85dd01e7 JA |
1062 | static void start_job_entry(GtkWidget *w, gpointer data) |
1063 | { | |
1064 | struct gui *ui = (struct gui *) data; | |
1065 | struct gui_entry *ge; | |
1066 | ||
1067 | ge = get_ge_from_cur_tab(ui); | |
1068 | if (ge) | |
1069 | start_job_clicked(w, ge); | |
1070 | } | |
1071 | ||
781ccba6 JA |
1072 | static void view_results(GtkWidget *w, gpointer data) |
1073 | { | |
1074 | struct gui *ui = (struct gui *) data; | |
1075 | struct gfio_client *gc; | |
1076 | struct gui_entry *ge; | |
1077 | ||
1078 | ge = get_ge_from_cur_tab(ui); | |
1079 | if (!ge) | |
1080 | return; | |
1081 | ||
1082 | if (ge->results_window) | |
1083 | return; | |
1084 | ||
1085 | gc = ge->client; | |
1086 | if (gc && gc->nr_results) | |
1087 | gfio_display_end_results(gc); | |
1088 | } | |
1089 | ||
a1e7972d | 1090 | static void __update_graph_settings(struct gfio_graphs *g) |
8577f4fd JA |
1091 | { |
1092 | line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit); | |
a1e7972d | 1093 | graph_set_font(g->iops_graph, gfio_graph_font); |
8577f4fd | 1094 | line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit); |
a1e7972d | 1095 | graph_set_font(g->bandwidth_graph, gfio_graph_font); |
8577f4fd JA |
1096 | } |
1097 | ||
a1e7972d | 1098 | static void ge_update_settings_fn(gpointer key, gpointer value, gpointer data) |
b98ab718 JA |
1099 | { |
1100 | struct gui_entry *ge = (struct gui_entry *) value; | |
a1e7972d | 1101 | GdkEvent *ev; |
b98ab718 | 1102 | |
a1e7972d JA |
1103 | __update_graph_settings(&ge->graphs); |
1104 | ||
1105 | ev = gdk_event_new(GDK_EXPOSE); | |
b6ab6a31 | 1106 | g_signal_emit_by_name(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ge->graphs.drawing_area), ev, &ge->graphs); |
a1e7972d | 1107 | gdk_event_free(ev); |
b98ab718 JA |
1108 | } |
1109 | ||
8577f4fd JA |
1110 | static void update_graph_limits(void) |
1111 | { | |
b98ab718 | 1112 | struct gui *ui = &main_ui; |
a1e7972d JA |
1113 | GdkEvent *ev; |
1114 | ||
1115 | __update_graph_settings(&ui->graphs); | |
8577f4fd | 1116 | |
a1e7972d | 1117 | ev = gdk_event_new(GDK_EXPOSE); |
b6ab6a31 | 1118 | g_signal_emit_by_name(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, GTK_WIDGET(ui->graphs.drawing_area), ev, &ui->graphs); |
a1e7972d | 1119 | gdk_event_free(ev); |
8577f4fd | 1120 | |
a1e7972d | 1121 | g_hash_table_foreach(ui->ge_hash, ge_update_settings_fn, NULL); |
8577f4fd JA |
1122 | } |
1123 | ||
46974a7d JA |
1124 | static void preferences(GtkWidget *w, gpointer data) |
1125 | { | |
f3e8440f | 1126 | GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font; |
1cf6bca5 | 1127 | GtkWidget *hbox, *spin, *entry, *spin_int; |
6e02ad6c | 1128 | struct gui *ui = (struct gui *) data; |
46974a7d JA |
1129 | int i; |
1130 | ||
1131 | dialog = gtk_dialog_new_with_buttons("Preferences", | |
6e02ad6c | 1132 | GTK_WINDOW(ui->window), |
46974a7d JA |
1133 | GTK_DIALOG_DESTROY_WITH_PARENT, |
1134 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | |
1135 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, | |
1136 | NULL); | |
1137 | ||
8577f4fd | 1138 | frame = gtk_frame_new("Graphing"); |
f762cef9 JA |
1139 | vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); |
1140 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
f3e8440f JA |
1141 | vbox = gtk_vbox_new(FALSE, 6); |
1142 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1143 | ||
1cf6bca5 JA |
1144 | hbox = gtk_hbox_new(FALSE, 5); |
1145 | gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); | |
1146 | entry = gtk_label_new("Font face to use for graph labels"); | |
1147 | gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5); | |
1148 | ||
a1e7972d | 1149 | font = gtk_font_button_new_with_font(gfio_graph_font); |
1cf6bca5 | 1150 | gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5); |
f3e8440f | 1151 | |
8577f4fd JA |
1152 | box = gtk_vbox_new(FALSE, 6); |
1153 | gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); | |
1154 | ||
1155 | hbox = gtk_hbox_new(FALSE, 5); | |
1cf6bca5 | 1156 | gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5); |
8577f4fd JA |
1157 | entry = gtk_label_new("Maximum number of data points in graph (seconds)"); |
1158 | gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5); | |
1159 | ||
c05d9056 | 1160 | spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit); |
8577f4fd | 1161 | |
1cf6bca5 JA |
1162 | box = gtk_vbox_new(FALSE, 6); |
1163 | gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); | |
1164 | ||
1165 | hbox = gtk_hbox_new(FALSE, 5); | |
1166 | gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5); | |
1167 | entry = gtk_label_new("Client ETA request interval (msec)"); | |
1168 | gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5); | |
1169 | ||
1170 | spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec); | |
a31d9fa4 | 1171 | frame = gtk_frame_new("Debug logging"); |
f762cef9 JA |
1172 | vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); |
1173 | gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); | |
a31d9fa4 JA |
1174 | vbox = gtk_vbox_new(FALSE, 6); |
1175 | gtk_container_add(GTK_CONTAINER(frame), vbox); | |
1176 | ||
1177 | box = gtk_hbox_new(FALSE, 6); | |
1178 | gtk_container_add(GTK_CONTAINER(vbox), box); | |
1179 | ||
1180 | buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX); | |
1181 | ||
1182 | for (i = 0; i < FD_DEBUG_MAX; i++) { | |
1183 | if (i == 7) { | |
1184 | box = gtk_hbox_new(FALSE, 6); | |
1185 | gtk_container_add(GTK_CONTAINER(vbox), box); | |
1186 | } | |
1187 | ||
1188 | ||
1189 | buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name); | |
1190 | gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help); | |
1191 | gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6); | |
1192 | } | |
1193 | ||
46974a7d JA |
1194 | gtk_widget_show_all(dialog); |
1195 | ||
1196 | if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { | |
1197 | gtk_widget_destroy(dialog); | |
1198 | return; | |
1199 | } | |
1200 | ||
1201 | for (i = 0; i < FD_DEBUG_MAX; i++) { | |
1202 | int set; | |
1203 | ||
1204 | set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i])); | |
1205 | if (set) | |
1206 | fio_debug |= (1UL << i); | |
1207 | } | |
1208 | ||
f3e8440f | 1209 | gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font))); |
8577f4fd JA |
1210 | gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); |
1211 | update_graph_limits(); | |
1cf6bca5 | 1212 | gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int)); |
8577f4fd | 1213 | |
46974a7d JA |
1214 | gtk_widget_destroy(dialog); |
1215 | } | |
1216 | ||
0420ba6a JA |
1217 | static void about_dialog(GtkWidget *w, gpointer data) |
1218 | { | |
81e4ea6e JA |
1219 | const char *authors[] = { |
1220 | "Jens Axboe <axboe@kernel.dk>", | |
5921cea2 | 1221 | "Stephen Cameron <stephenmcameron@gmail.com>", |
81e4ea6e JA |
1222 | NULL |
1223 | }; | |
84a72ed3 JA |
1224 | const char *license[] = { |
1225 | "Fio is free software; you can redistribute it and/or modify " | |
1226 | "it under the terms of the GNU General Public License as published by " | |
1227 | "the Free Software Foundation; either version 2 of the License, or " | |
1228 | "(at your option) any later version.\n", | |
1229 | "Fio is distributed in the hope that it will be useful, " | |
1230 | "but WITHOUT ANY WARRANTY; without even the implied warranty of " | |
1231 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " | |
1232 | "GNU General Public License for more details.\n", | |
1233 | "You should have received a copy of the GNU General Public License " | |
1234 | "along with Fio; if not, write to the Free Software Foundation, Inc., " | |
1235 | "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" | |
1236 | }; | |
1237 | char *license_trans; | |
1238 | ||
1239 | license_trans = g_strconcat(license[0], "\n", license[1], "\n", | |
1240 | license[2], "\n", NULL); | |
81e4ea6e | 1241 | |
0420ba6a JA |
1242 | gtk_show_about_dialog(NULL, |
1243 | "program-name", "gfio", | |
1244 | "comments", "Gtk2 UI for fio", | |
84a72ed3 | 1245 | "license", license_trans, |
ec9a7e69 | 1246 | "website", "http://git.kernel.dk/cgit/fio/", |
81e4ea6e | 1247 | "authors", authors, |
0420ba6a | 1248 | "version", fio_version_string, |
403bb450 | 1249 | "copyright", "© 2012-2017 Jens Axboe <axboe@kernel.dk>", |
0420ba6a JA |
1250 | "logo-icon-name", "fio", |
1251 | /* Must be last: */ | |
81e4ea6e | 1252 | "wrap-license", TRUE, |
0420ba6a | 1253 | NULL); |
84a72ed3 | 1254 | |
2f99deb0 | 1255 | g_free(license_trans); |
0420ba6a JA |
1256 | } |
1257 | ||
1258 | static GtkActionEntry menu_items[] = { | |
46974a7d | 1259 | { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, |
9b260bdf | 1260 | { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL}, |
16ce5adf | 1261 | { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL}, |
46974a7d | 1262 | { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL}, |
2f99deb0 | 1263 | { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) }, |
16ce5adf | 1264 | { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) }, |
46974a7d JA |
1265 | { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) }, |
1266 | { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) }, | |
1267 | { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) }, | |
9b260bdf | 1268 | { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) }, |
781ccba6 | 1269 | { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) }, |
bc271d87 | 1270 | { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) }, |
85dd01e7 JA |
1271 | { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) }, |
1272 | { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) }, | |
1273 | { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) }, | |
46974a7d JA |
1274 | { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) }, |
1275 | { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) }, | |
0420ba6a | 1276 | }; |
59f94d26 | 1277 | static gint nmenu_items = FIO_ARRAY_SIZE(menu_items); |
0420ba6a JA |
1278 | |
1279 | static const gchar *ui_string = " \ | |
1280 | <ui> \ | |
1281 | <menubar name=\"MainMenu\"> \ | |
1282 | <menu name=\"FileMenu\" action=\"FileMenuAction\"> \ | |
2f99deb0 | 1283 | <menuitem name=\"New\" action=\"NewFile\" /> \ |
bf64138b | 1284 | <menuitem name=\"Open\" action=\"OpenFile\" /> \ |
16ce5adf | 1285 | <menuitem name=\"Close\" action=\"CloseFile\" /> \ |
2f99deb0 | 1286 | <separator name=\"Separator1\"/> \ |
0420ba6a | 1287 | <menuitem name=\"Save\" action=\"SaveFile\" /> \ |
46974a7d | 1288 | <separator name=\"Separator2\"/> \ |
2f99deb0 JA |
1289 | <menuitem name=\"Preferences\" action=\"Preferences\" /> \ |
1290 | <separator name=\"Separator3\"/> \ | |
261f21d0 JA |
1291 | <placeholder name=\"FileRecentFiles\"/> \ |
1292 | <separator name=\"Separator4\"/> \ | |
0420ba6a JA |
1293 | <menuitem name=\"Quit\" action=\"Quit\" /> \ |
1294 | </menu> \ | |
16ce5adf | 1295 | <menu name=\"JobMenu\" action=\"JobMenuAction\"> \ |
85dd01e7 | 1296 | <menuitem name=\"Connect\" action=\"ConnectJob\" /> \ |
261f21d0 | 1297 | <separator name=\"Separator5\"/> \ |
85dd01e7 JA |
1298 | <menuitem name=\"Edit job\" action=\"EditJob\" /> \ |
1299 | <menuitem name=\"Send job\" action=\"SendJob\" /> \ | |
261f21d0 | 1300 | <separator name=\"Separator6\"/> \ |
85dd01e7 | 1301 | <menuitem name=\"Start job\" action=\"StartJob\" /> \ |
16ce5adf | 1302 | </menu>\ |
9b260bdf | 1303 | <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \ |
781ccba6 JA |
1304 | <menuitem name=\"Results\" action=\"ViewResults\" /> \ |
1305 | <separator name=\"Separator7\"/> \ | |
9b260bdf JA |
1306 | <menuitem name=\"Log\" action=\"ViewLog\" /> \ |
1307 | </menu>\ | |
0420ba6a JA |
1308 | <menu name=\"Help\" action=\"HelpMenuAction\"> \ |
1309 | <menuitem name=\"About\" action=\"About\" /> \ | |
1310 | </menu> \ | |
1311 | </menubar> \ | |
1312 | </ui> \ | |
1313 | "; | |
1314 | ||
4cbe7211 JA |
1315 | static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager, |
1316 | struct gui *ui) | |
0420ba6a | 1317 | { |
ca664f49 | 1318 | GtkActionGroup *action_group; |
0420ba6a JA |
1319 | GError *error = 0; |
1320 | ||
1321 | action_group = gtk_action_group_new("Menu"); | |
4cbe7211 | 1322 | gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui); |
0420ba6a JA |
1323 | |
1324 | gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); | |
1325 | gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error); | |
1326 | ||
1327 | gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager)); | |
02421e69 | 1328 | |
0420ba6a JA |
1329 | return gtk_ui_manager_get_widget(ui_manager, "/MainMenu"); |
1330 | } | |
1331 | ||
1332 | void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar, | |
1252d8f2 | 1333 | GtkWidget *vbox, GtkUIManager *ui_manager) |
0420ba6a | 1334 | { |
1252d8f2 | 1335 | gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); |
0420ba6a JA |
1336 | } |
1337 | ||
c80b74b0 JA |
1338 | static void combo_entry_changed(GtkComboBox *box, gpointer data) |
1339 | { | |
1340 | struct gui_entry *ge = (struct gui_entry *) data; | |
1341 | gint index; | |
1342 | ||
1343 | index = gtk_combo_box_get_active(box); | |
1344 | ||
1345 | multitext_set_entry(&ge->eta.iotype, index); | |
99d633af | 1346 | multitext_set_entry(&ge->eta.bs, index); |
c80b74b0 JA |
1347 | multitext_set_entry(&ge->eta.ioengine, index); |
1348 | multitext_set_entry(&ge->eta.iodepth, index); | |
1349 | } | |
1350 | ||
1351 | static void combo_entry_destroy(GtkWidget *widget, gpointer data) | |
1352 | { | |
1353 | struct gui_entry *ge = (struct gui_entry *) data; | |
1354 | ||
1355 | multitext_free(&ge->eta.iotype); | |
99d633af | 1356 | multitext_free(&ge->eta.bs); |
c80b74b0 JA |
1357 | multitext_free(&ge->eta.ioengine); |
1358 | multitext_free(&ge->eta.iodepth); | |
1359 | } | |
1360 | ||
2f99deb0 | 1361 | static GtkWidget *new_client_page(struct gui_entry *ge) |
ff1f3280 | 1362 | { |
2f99deb0 | 1363 | GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; |
65476336 | 1364 | GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox; |
0420ba6a | 1365 | |
2f99deb0 | 1366 | main_vbox = gtk_vbox_new(FALSE, 3); |
45032dd8 | 1367 | |
65476336 JA |
1368 | top_align = gtk_alignment_new(0, 0, 1, 0); |
1369 | top_vbox = gtk_vbox_new(FALSE, 3); | |
1370 | gtk_container_add(GTK_CONTAINER(top_align), top_vbox); | |
1371 | gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0); | |
2f99deb0 JA |
1372 | |
1373 | probe = gtk_frame_new("Job"); | |
1374 | gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); | |
1375 | probe_frame = gtk_vbox_new(FALSE, 3); | |
1376 | gtk_container_add(GTK_CONTAINER(probe), probe_frame); | |
1377 | ||
1378 | probe_box = gtk_hbox_new(FALSE, 3); | |
1379 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
1380 | ge->probe.hostname = new_info_label_in_frame(probe_box, "Host"); | |
1381 | ge->probe.os = new_info_label_in_frame(probe_box, "OS"); | |
1382 | ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture"); | |
1383 | ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version"); | |
1384 | ||
1385 | probe_box = gtk_hbox_new(FALSE, 3); | |
1386 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
1387 | ||
3863d1ad | 1388 | ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs"); |
c80b74b0 JA |
1389 | g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge); |
1390 | g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge); | |
1391 | ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO"); | |
5921cea2 | 1392 | ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write/Trim)"); |
c80b74b0 JA |
1393 | ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine"); |
1394 | ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth"); | |
2f99deb0 JA |
1395 | ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs"); |
1396 | ge->eta.files = new_info_entry_in_frame(probe_box, "Open files"); | |
1397 | ||
1398 | probe_box = gtk_hbox_new(FALSE, 3); | |
1399 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); | |
b78ca651 | 1400 | ge->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B); |
5921cea2 | 1401 | ge->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "Read IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B); |
b78ca651 | 1402 | ge->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B); |
5921cea2 | 1403 | ge->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "Write IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B); |
b78ca651 | 1404 | ge->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B); |
5921cea2 | 1405 | ge->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "Trim IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B); |
dbc542d4 | 1406 | |
2f99deb0 JA |
1407 | /* |
1408 | * Only add this if we have a commit rate | |
2839f0c6 | 1409 | */ |
2f99deb0 JA |
1410 | #if 0 |
1411 | probe_box = gtk_hbox_new(FALSE, 3); | |
1412 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); | |
2839f0c6 | 1413 | |
2f99deb0 JA |
1414 | ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
1415 | ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
ff1f3280 | 1416 | |
2f99deb0 JA |
1417 | ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
1418 | ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
1419 | #endif | |
ff1f3280 | 1420 | |
2f99deb0 JA |
1421 | /* |
1422 | * Set up a drawing area and IOPS and bandwidth graphs | |
1423 | */ | |
2f99deb0 | 1424 | ge->graphs.drawing_area = gtk_drawing_area_new(); |
2f99deb0 | 1425 | gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area), |
57f9d28e | 1426 | DRAWING_AREA_XDIM, DRAWING_AREA_YDIM); |
2a95f716 | 1427 | gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow); |
b6ab6a31 | 1428 | g_signal_connect(G_OBJECT(ge->graphs.drawing_area), GFIO_DRAW_EVENT, |
2f99deb0 JA |
1429 | G_CALLBACK(on_expose_drawing_area), &ge->graphs); |
1430 | g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event", | |
1431 | G_CALLBACK(on_config_drawing_area), &ge->graphs); | |
65476336 JA |
1432 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
1433 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | |
2f99deb0 | 1434 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
65476336 | 1435 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), |
2f99deb0 | 1436 | ge->graphs.drawing_area); |
65476336 | 1437 | gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0); |
04cc6b77 | 1438 | |
2f99deb0 JA |
1439 | setup_graphs(&ge->graphs); |
1440 | ||
1441 | /* | |
3c3ed070 | 1442 | * Set up alignments for widgets at the bottom of ui, |
2f99deb0 JA |
1443 | * align bottom left, expand horizontally but not vertically |
1444 | */ | |
65476336 | 1445 | bottom_align = gtk_alignment_new(0, 1, 1, 0); |
2f99deb0 | 1446 | ge->buttonbox = gtk_hbox_new(FALSE, 0); |
65476336 JA |
1447 | gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox); |
1448 | gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0); | |
2f99deb0 | 1449 | |
59f94d26 | 1450 | add_buttons(ge, buttonspeclist, FIO_ARRAY_SIZE(buttonspeclist)); |
0420ba6a | 1451 | |
c36f98d9 | 1452 | /* |
2f99deb0 JA |
1453 | * Set up thread status progress bar |
1454 | */ | |
1455 | ge->thread_status_pb = gtk_progress_bar_new(); | |
1456 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0); | |
1457 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections"); | |
1458 | gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb); | |
1459 | ||
1460 | ||
1461 | return main_vbox; | |
1462 | } | |
1463 | ||
1464 | static GtkWidget *new_main_page(struct gui *ui) | |
1465 | { | |
1466 | GtkWidget *main_vbox, *probe, *probe_frame, *probe_box; | |
65476336 | 1467 | GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox; |
2f99deb0 JA |
1468 | |
1469 | main_vbox = gtk_vbox_new(FALSE, 3); | |
1470 | ||
1471 | /* | |
1472 | * Set up alignments for widgets at the top of ui, | |
c36f98d9 SC |
1473 | * align top left, expand horizontally but not vertically |
1474 | */ | |
65476336 JA |
1475 | top_align = gtk_alignment_new(0, 0, 1, 0); |
1476 | top_vbox = gtk_vbox_new(FALSE, 0); | |
1477 | gtk_container_add(GTK_CONTAINER(top_align), top_vbox); | |
1478 | gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0); | |
c36f98d9 | 1479 | |
2f99deb0 JA |
1480 | probe = gtk_frame_new("Run statistics"); |
1481 | gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3); | |
843ad237 JA |
1482 | probe_frame = gtk_vbox_new(FALSE, 3); |
1483 | gtk_container_add(GTK_CONTAINER(probe), probe_frame); | |
1484 | ||
1485 | probe_box = gtk_hbox_new(FALSE, 3); | |
2f99deb0 | 1486 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3); |
3863d1ad | 1487 | ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running"); |
b78ca651 JA |
1488 | ui->eta.read_bw = new_info_entry_in_frame_rgb(probe_box, "Read BW", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B); |
1489 | ui->eta.read_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_READ_R, GFIO_READ_G, GFIO_READ_B); | |
1490 | ui->eta.write_bw = new_info_entry_in_frame_rgb(probe_box, "Write BW", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B); | |
1491 | ui->eta.write_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_WRITE_R, GFIO_WRITE_G, GFIO_WRITE_B); | |
1492 | ui->eta.trim_bw = new_info_entry_in_frame_rgb(probe_box, "Trim BW", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B); | |
1493 | ui->eta.trim_iops = new_info_entry_in_frame_rgb(probe_box, "IOPS", GFIO_TRIM_R, GFIO_TRIM_G, GFIO_TRIM_B); | |
3e47bd25 | 1494 | |
807f9971 JA |
1495 | /* |
1496 | * Only add this if we have a commit rate | |
1497 | */ | |
1498 | #if 0 | |
3e47bd25 JA |
1499 | probe_box = gtk_hbox_new(FALSE, 3); |
1500 | gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3); | |
807f9971 JA |
1501 | |
1502 | ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW"); | |
1503 | ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
1504 | ||
3e47bd25 JA |
1505 | ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW"); |
1506 | ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS"); | |
807f9971 | 1507 | #endif |
3e47bd25 | 1508 | |
736f2dff | 1509 | /* |
2fd3bb0e | 1510 | * Set up a drawing area and IOPS and bandwidth graphs |
736f2dff | 1511 | */ |
2f99deb0 | 1512 | ui->graphs.drawing_area = gtk_drawing_area_new(); |
2f99deb0 | 1513 | gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area), |
57f9d28e | 1514 | DRAWING_AREA_XDIM, DRAWING_AREA_YDIM); |
2a95f716 | 1515 | gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &gfio_color_lightyellow); |
b6ab6a31 | 1516 | g_signal_connect(G_OBJECT(ui->graphs.drawing_area), GFIO_DRAW_EVENT, |
2f99deb0 JA |
1517 | G_CALLBACK(on_expose_drawing_area), &ui->graphs); |
1518 | g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event", | |
1519 | G_CALLBACK(on_config_drawing_area), &ui->graphs); | |
65476336 JA |
1520 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
1521 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | |
736f2dff | 1522 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
65476336 | 1523 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), |
2f99deb0 | 1524 | ui->graphs.drawing_area); |
65476336 | 1525 | gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, |
e164534f | 1526 | TRUE, TRUE, 0); |
736f2dff | 1527 | |
2f99deb0 | 1528 | setup_graphs(&ui->graphs); |
2fd3bb0e | 1529 | |
c36f98d9 | 1530 | /* |
3c3ed070 | 1531 | * Set up alignments for widgets at the bottom of ui, |
c36f98d9 SC |
1532 | * align bottom left, expand horizontally but not vertically |
1533 | */ | |
65476336 | 1534 | bottom_align = gtk_alignment_new(0, 1, 1, 0); |
c36f98d9 | 1535 | ui->buttonbox = gtk_hbox_new(FALSE, 0); |
65476336 JA |
1536 | gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox); |
1537 | gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0); | |
c36f98d9 | 1538 | |
3ec62ec4 JA |
1539 | /* |
1540 | * Set up thread status progress bar | |
1541 | */ | |
1542 | ui->thread_status_pb = gtk_progress_bar_new(); | |
1543 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0); | |
8663ea65 | 1544 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections"); |
3ec62ec4 JA |
1545 | gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb); |
1546 | ||
2f99deb0 JA |
1547 | return main_vbox; |
1548 | } | |
1549 | ||
1550 | static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget, | |
1551 | guint page, gpointer data) | |
1552 | ||
1553 | { | |
02421e69 | 1554 | struct gui *ui = (struct gui *) data; |
85dd01e7 JA |
1555 | struct gui_entry *ge; |
1556 | ||
1557 | if (!page) { | |
1558 | set_job_menu_visible(ui, 0); | |
781ccba6 | 1559 | set_view_results_visible(ui, 0); |
85dd01e7 JA |
1560 | return TRUE; |
1561 | } | |
1562 | ||
1563 | set_job_menu_visible(ui, 1); | |
6e02ad6c | 1564 | ge = get_ge_from_page(ui, page, NULL); |
85dd01e7 JA |
1565 | if (ge) |
1566 | update_button_states(ui, ge); | |
02421e69 | 1567 | |
2f99deb0 JA |
1568 | return TRUE; |
1569 | } | |
1570 | ||
38634cb1 JA |
1571 | static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b) |
1572 | { | |
1573 | time_t time_a = gtk_recent_info_get_visited(a); | |
1574 | time_t time_b = gtk_recent_info_get_visited(b); | |
1575 | ||
1576 | return time_b - time_a; | |
1577 | } | |
1578 | ||
1579 | static void add_recent_file_items(struct gui *ui) | |
1580 | { | |
1581 | const gchar *gfio = g_get_application_name(); | |
1582 | GList *items, *item; | |
1583 | int i = 0; | |
1584 | ||
1585 | if (ui->recent_ui_id) { | |
1586 | gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id); | |
1587 | gtk_ui_manager_ensure_update(ui->uimanager); | |
1588 | } | |
1589 | ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager); | |
1590 | ||
1591 | if (ui->actiongroup) { | |
1592 | gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup); | |
1593 | g_object_unref(ui->actiongroup); | |
1594 | } | |
1595 | ui->actiongroup = gtk_action_group_new("RecentFileActions"); | |
1596 | ||
1597 | gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1); | |
1598 | ||
1599 | items = gtk_recent_manager_get_items(ui->recentmanager); | |
1600 | items = g_list_sort(items, (GCompareFunc) compare_recent_items); | |
1601 | ||
1602 | for (item = items; item && item->data; item = g_list_next(item)) { | |
1603 | GtkRecentInfo *info = (GtkRecentInfo *) item->data; | |
1604 | gchar *action_name; | |
1605 | const gchar *label; | |
1606 | GtkAction *action; | |
1607 | ||
1608 | if (!gtk_recent_info_has_application(info, gfio)) | |
1609 | continue; | |
1610 | ||
1611 | /* | |
1612 | * We only support local files for now | |
1613 | */ | |
1614 | if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info)) | |
1615 | continue; | |
1616 | ||
1617 | action_name = g_strdup_printf("RecentFile%u", i++); | |
1618 | label = gtk_recent_info_get_display_name(info); | |
1619 | ||
1620 | action = g_object_new(GTK_TYPE_ACTION, | |
1621 | "name", action_name, | |
1622 | "label", label, NULL); | |
1623 | ||
1624 | g_object_set_data_full(G_OBJECT(action), "gtk-recent-info", | |
1625 | gtk_recent_info_ref(info), | |
1626 | (GDestroyNotify) gtk_recent_info_unref); | |
1627 | ||
1628 | ||
1629 | g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui); | |
1630 | ||
1631 | gtk_action_group_add_action(ui->actiongroup, action); | |
1632 | g_object_unref(action); | |
1633 | ||
1634 | gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id, | |
1635 | "/MainMenu/FileMenu/FileRecentFiles", | |
1636 | label, action_name, | |
1637 | GTK_UI_MANAGER_MENUITEM, FALSE); | |
1638 | ||
1639 | g_free(action_name); | |
1640 | ||
1641 | if (i == 8) | |
1642 | break; | |
1643 | } | |
1644 | ||
1645 | g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL); | |
1646 | g_list_free(items); | |
1647 | } | |
1648 | ||
a6790906 | 1649 | static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx, |
6e02ad6c JA |
1650 | gint x, gint y, GtkSelectionData *seldata, |
1651 | guint info, guint time, gpointer *data) | |
a6790906 | 1652 | { |
6e02ad6c | 1653 | struct gui *ui = (struct gui *) data; |
a6790906 JA |
1654 | gchar **uris; |
1655 | GtkWidget *source; | |
a6790906 JA |
1656 | |
1657 | source = gtk_drag_get_source_widget(ctx); | |
1658 | if (source && widget == gtk_widget_get_toplevel(source)) { | |
1659 | gtk_drag_finish(ctx, FALSE, FALSE, time); | |
1660 | return; | |
1661 | } | |
1662 | ||
6e02ad6c | 1663 | uris = gtk_selection_data_get_uris(seldata); |
a6790906 JA |
1664 | if (!uris) { |
1665 | gtk_drag_finish(ctx, FALSE, FALSE, time); | |
1666 | return; | |
1667 | } | |
1668 | ||
0cf3ece0 JA |
1669 | if (uris[0]) |
1670 | do_file_open_with_tab(ui, uris[0]); | |
a6790906 JA |
1671 | |
1672 | gtk_drag_finish(ctx, TRUE, FALSE, time); | |
1673 | g_strfreev(uris); | |
1674 | } | |
1675 | ||
2f99deb0 JA |
1676 | static void init_ui(int *argc, char **argv[], struct gui *ui) |
1677 | { | |
1678 | GtkSettings *settings; | |
02421e69 | 1679 | GtkWidget *vbox; |
2f99deb0 JA |
1680 | |
1681 | /* Magical g*thread incantation, you just need this thread stuff. | |
1682 | * Without it, the update that happens in gfio_update_thread_status | |
1683 | * doesn't really happen in a timely fashion, you need expose events | |
1684 | */ | |
f1c4b372 | 1685 | #if !GLIB_CHECK_VERSION(2, 31, 0) |
2f99deb0 JA |
1686 | if (!g_thread_supported()) |
1687 | g_thread_init(NULL); | |
b11bd8d8 JA |
1688 | #endif |
1689 | ||
2f99deb0 JA |
1690 | gdk_threads_init(); |
1691 | ||
1692 | gtk_init(argc, argv); | |
1693 | settings = gtk_settings_get_default(); | |
1694 | gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting"); | |
c05da82c | 1695 | #if !GLIB_CHECK_VERSION(2, 36, 0) |
2f99deb0 | 1696 | g_type_init(); |
c05da82c | 1697 | #endif |
0950e832 | 1698 | gdk_color_parse("#fffff4", &gfio_color_lightyellow); |
1252d8f2 | 1699 | gdk_color_parse("white", &gfio_color_white); |
3c3ed070 | 1700 | |
2f99deb0 | 1701 | ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
814479d5 | 1702 | gtk_window_set_title(GTK_WINDOW(ui->window), "fio"); |
2f99deb0 JA |
1703 | gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768); |
1704 | ||
6e02ad6c JA |
1705 | g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), ui); |
1706 | g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), ui); | |
2f99deb0 JA |
1707 | |
1708 | ui->vbox = gtk_vbox_new(FALSE, 0); | |
1709 | gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox); | |
1710 | ||
02421e69 JA |
1711 | ui->uimanager = gtk_ui_manager_new(); |
1712 | ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui); | |
1713 | gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager); | |
2f99deb0 | 1714 | |
38634cb1 JA |
1715 | ui->recentmanager = gtk_recent_manager_get_default(); |
1716 | add_recent_file_items(ui); | |
1717 | ||
2f99deb0 JA |
1718 | ui->notebook = gtk_notebook_new(); |
1719 | g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui); | |
b870c31b | 1720 | gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1); |
0aa928c4 | 1721 | gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook)); |
2f99deb0 JA |
1722 | gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook); |
1723 | ||
1724 | vbox = new_main_page(ui); | |
0cf3ece0 | 1725 | gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 1, GDK_ACTION_COPY); |
a6790906 JA |
1726 | gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window)); |
1727 | g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui); | |
2f99deb0 JA |
1728 | |
1729 | gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main")); | |
1730 | ||
9b260bdf | 1731 | gfio_ui_setup_log(ui); |
3ec62ec4 | 1732 | |
ff1f3280 SC |
1733 | gtk_widget_show_all(ui->window); |
1734 | } | |
1735 | ||
8232e285 | 1736 | int main(int argc, char *argv[], char *envp[]) |
ff1f3280 | 1737 | { |
8232e285 SC |
1738 | if (initialize_fio(envp)) |
1739 | return 1; | |
0420ba6a JA |
1740 | if (fio_init_options()) |
1741 | return 1; | |
a1820207 | 1742 | |
753e9e64 JA |
1743 | gopt_init(); |
1744 | ||
2f99deb0 | 1745 | memset(&main_ui, 0, sizeof(main_ui)); |
b98ab718 | 1746 | main_ui.ge_hash = g_hash_table_new(g_int_hash, g_int_equal); |
2f99deb0 JA |
1747 | |
1748 | init_ui(&argc, &argv, &main_ui); | |
5b7573ab | 1749 | |
2839f0c6 | 1750 | gdk_threads_enter(); |
ff1f3280 | 1751 | gtk_main(); |
2839f0c6 | 1752 | gdk_threads_leave(); |
b98ab718 JA |
1753 | |
1754 | g_hash_table_destroy(main_ui.ge_hash); | |
753e9e64 JA |
1755 | |
1756 | gopt_exit(); | |
ff1f3280 SC |
1757 | return 0; |
1758 | } |