Parent Directory | Revision Log
Geotoad gui started
1 | /* |
2 | * Copyright (C) 2009 Till Harbaum <till@harbaum.org>. |
3 | * |
4 | * This file is part of GPXView. |
5 | * |
6 | * GPXView is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * GPXView is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with GPXView. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "gpxview.h" |
21 | |
22 | #include <fcntl.h> |
23 | #include <sys/types.h> |
24 | #include <sys/wait.h> |
25 | #include <errno.h> |
26 | |
27 | #define GEOTOAD "/usr/bin/geotoad" |
28 | |
29 | #define COLOR_ERR "red" |
30 | #define COLOR_OK "darkgreen" |
31 | #define COLOR_SYSTEM "darkblue" |
32 | |
33 | #define BUFFER_SIZE 256 |
34 | |
35 | typedef struct { |
36 | appdata_t *appdata; |
37 | |
38 | char buf[BUFFER_SIZE]; |
39 | int bused; |
40 | |
41 | /** gdk input tag for async IO */ |
42 | gint stdout_tag, stderr_tag; |
43 | gint stdin_fd, stdout_fd, stderr_fd; |
44 | |
45 | struct log_s { |
46 | GtkTextBuffer *buffer; |
47 | GtkWidget *view; |
48 | } log; |
49 | |
50 | GtkWidget *username, *password; |
51 | |
52 | } gt_context_t; |
53 | |
54 | static void appendf(struct log_s *log, char *colname, |
55 | const char *fmt, ...) { |
56 | va_list args; |
57 | va_start( args, fmt ); |
58 | char *buf = g_strdup_vprintf(fmt, args); |
59 | va_end( args ); |
60 | |
61 | printf("append: %s", buf); |
62 | |
63 | GtkTextTag *tag = NULL; |
64 | if(colname) |
65 | tag = gtk_text_buffer_create_tag(log->buffer, NULL, |
66 | "foreground", colname, |
67 | NULL); |
68 | |
69 | GtkTextIter end; |
70 | gtk_text_buffer_get_end_iter(log->buffer, &end); |
71 | if(tag) |
72 | gtk_text_buffer_insert_with_tags(log->buffer, &end, buf, -1, tag, NULL); |
73 | else |
74 | gtk_text_buffer_insert(log->buffer, &end, buf, -1); |
75 | |
76 | g_free(buf); |
77 | |
78 | gtk_text_buffer_get_end_iter(log->buffer, &end); |
79 | gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view), |
80 | &end, 0.0, FALSE, 0, 0); |
81 | } |
82 | |
83 | /* watch child process and receive events */ |
84 | static void child_state_cb(GPid pid, gint status, gpointer data) { |
85 | gt_context_t *context = (gt_context_t*)data; |
86 | |
87 | puts("child state"); |
88 | |
89 | if(WIFEXITED(status)) { |
90 | printf("child exited with return code %d\n", WEXITSTATUS(status)); |
91 | } else |
92 | printf("child failed\n"); |
93 | |
94 | puts("gt exited"); |
95 | |
96 | appendf(&context->log, COLOR_SYSTEM, "GeoToad finished\n"); |
97 | |
98 | appendf(&context->log, COLOR_SYSTEM, "TODO: free context!!!\n"); |
99 | // printf("freeing context\n"); |
100 | // g_free(context); |
101 | |
102 | /* Reap child if needed. */ |
103 | waitpid (pid, NULL, WNOHANG); |
104 | } |
105 | |
106 | static gboolean child_input_cb(GIOChannel *source, |
107 | GIOCondition condition, |
108 | gpointer data) { |
109 | gt_context_t *context = (gt_context_t*)data; |
110 | int fd = g_io_channel_unix_get_fd(source); |
111 | ssize_t bytes; |
112 | |
113 | g_assert(condition == G_IO_IN); |
114 | |
115 | /* append to current buffer content */ |
116 | while( (bytes = read(fd, context->buf+context->bused, |
117 | sizeof(context->buf) - context->bused - 1)) > 0) { |
118 | context->bused += bytes; |
119 | context->buf[context->bused]='\0'; |
120 | |
121 | /* consume line by line */ |
122 | gchar *ptr = context->buf; |
123 | |
124 | /* parse as long as the buffer contains a newline */ |
125 | while( strchr(ptr, '\n') != NULL) { |
126 | char *p = strchr(ptr, '\n'); |
127 | *p = '\0'; |
128 | |
129 | char *color = NULL; |
130 | if(strstr(ptr, "Saved to ") != NULL) |
131 | color = COLOR_OK; |
132 | |
133 | appendf(&context->log, color, "%s\n", ptr); |
134 | |
135 | ptr = p+1; |
136 | } |
137 | |
138 | /* move remaining bytes down the buffer */ |
139 | memmove(context->buf, ptr, sizeof(context->buf)-(ptr-context->buf)); |
140 | context->bused -= ptr - context->buf; |
141 | |
142 | /* check if buffer is full but doesn't contain any newline */ |
143 | if(context->bused >= sizeof(context->buf)-1) { |
144 | printf("ERROR: reply buffer overflow\n"); |
145 | context->bused = 0; |
146 | } |
147 | } |
148 | |
149 | return TRUE; |
150 | } |
151 | |
152 | static void run(gt_context_t *context) { |
153 | /* setup context */ |
154 | context->bused = 0; |
155 | context->stdout_tag = -1; |
156 | context->stderr_tag = -1; |
157 | context->stdin_fd = -1; |
158 | context->stderr_fd = -1; |
159 | context->stderr_fd = -1; |
160 | |
161 | /* build list of arguments to call geotoad */ |
162 | GPtrArray *gt_argv = g_ptr_array_new(); |
163 | g_ptr_array_add (gt_argv, GEOTOAD); |
164 | g_ptr_array_add (gt_argv, "--distanceMax=1.0"); |
165 | g_ptr_array_add (gt_argv, "--output=gtoad.gpx"); |
166 | g_ptr_array_add (gt_argv, "--password=winterblume"); |
167 | g_ptr_array_add (gt_argv, "--queryType=coord"); |
168 | g_ptr_array_add (gt_argv, "--user=Tantil"); |
169 | |
170 | /* check if we need to add proxy config */ |
171 | char *proxy = NULL; |
172 | if(context->appdata->proxy && context->appdata->proxy->host) { |
173 | if(context->appdata->proxy->use_authentication && |
174 | context->appdata->proxy->authentication_user && |
175 | context->appdata->proxy->authentication_password) |
176 | proxy = g_strdup_printf("--proxy=http://%s:%s@%s:%d", |
177 | context->appdata->proxy->authentication_user, |
178 | context->appdata->proxy->authentication_password, |
179 | context->appdata->proxy->host, |
180 | context->appdata->proxy->port); |
181 | else |
182 | proxy = g_strdup_printf("--proxy=http://%s:%d", |
183 | context->appdata->proxy->host, |
184 | context->appdata->proxy->port); |
185 | |
186 | appendf(&context->log, COLOR_SYSTEM, "Using proxy: %s\n", proxy); |
187 | g_ptr_array_add (gt_argv, proxy); |
188 | } |
189 | |
190 | g_ptr_array_add (gt_argv, "N49 00.000 E008 23.000"); |
191 | g_ptr_array_add (gt_argv, NULL); |
192 | |
193 | GError *error=NULL; |
194 | GPid pid; |
195 | GSource *gt_watch; |
196 | |
197 | if (!g_spawn_async_with_pipes (NULL, /* CWD */ |
198 | (char **) gt_argv->pdata, /* argv */ |
199 | NULL, /* envp */ |
200 | G_SPAWN_DO_NOT_REAP_CHILD, /* flags */ |
201 | NULL, /* child setup */ |
202 | NULL, /* user data */ |
203 | &pid, |
204 | &context->stdin_fd, |
205 | &context->stdout_fd, |
206 | &context->stderr_fd, |
207 | &error)) { |
208 | g_ptr_array_free(gt_argv, TRUE); |
209 | if(proxy) g_free(proxy); |
210 | appendf(&context->log, COLOR_ERR, |
211 | _("GeoToad failed to start!\n%s\n"), error->message); |
212 | g_error_free(error); |
213 | return; |
214 | } |
215 | |
216 | g_ptr_array_free (gt_argv, TRUE); |
217 | if(proxy) g_free(proxy); |
218 | |
219 | gt_watch = g_child_watch_source_new(pid); |
220 | g_source_set_callback(gt_watch, (GSourceFunc) child_state_cb, context, NULL); |
221 | |
222 | g_source_attach(gt_watch, NULL); |
223 | g_source_unref(gt_watch); |
224 | |
225 | /* make nonblocking */ |
226 | if(fcntl(context->stdout_fd, F_SETFL, O_NONBLOCK) == -1) |
227 | perror("fcntl failed"); |
228 | |
229 | if(fcntl(context->stderr_fd, F_SETFL, O_NONBLOCK) == -1) |
230 | perror("fcntl failed"); |
231 | |
232 | GIOChannel *ioc = g_io_channel_unix_new(context->stdout_fd); |
233 | g_io_channel_set_close_on_unref (ioc, TRUE); |
234 | g_io_channel_set_encoding (ioc, NULL, NULL); |
235 | g_io_add_watch(ioc, G_IO_IN, child_input_cb, context); |
236 | g_io_channel_unref(ioc); |
237 | |
238 | // ioc = g_io_channel_unix_new(context->stderr_fd); |
239 | // g_io_add_watch(ioc, G_IO_IN, child_input_cb, context); |
240 | // g_io_channel_unref(ioc); |
241 | } |
242 | |
243 | /* show text window and display output of running geotoad */ |
244 | static void gui_run(gt_context_t *context) { |
245 | GtkWidget *dialog = gtk_dialog_new_with_buttons(_("GeoToad - Run"), |
246 | GTK_WINDOW(context->appdata->window), |
247 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, |
248 | GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
249 | NULL); |
250 | |
251 | gtk_window_set_default_size(GTK_WINDOW(dialog), 640, 480); |
252 | |
253 | #ifndef USE_PANNABLE_AREA |
254 | GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
255 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), |
256 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
257 | #else |
258 | GtkWidget *pannable_area = hildon_pannable_area_new(); |
259 | #endif |
260 | |
261 | context->log.buffer = gtk_text_buffer_new(NULL); |
262 | |
263 | #ifndef USE_HILDON_TEXT_VIEW |
264 | context->log.view = gtk_text_view_new_with_buffer(context->log.buffer); |
265 | #else |
266 | context->log.view = hildon_text_view_new(); |
267 | hildon_text_view_set_buffer(HILDON_TEXT_VIEW(context->log.view), |
268 | context->log.buffer); |
269 | #endif |
270 | |
271 | #ifndef USE_PANNABLE_AREA |
272 | gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view); |
273 | gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(scrolled_window), |
274 | GTK_SHADOW_IN); |
275 | |
276 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), |
277 | scrolled_window, TRUE, TRUE, 0); |
278 | #else |
279 | gtk_container_add(GTK_CONTAINER(pannable_area), context->log.view); |
280 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), |
281 | pannable_area, TRUE, TRUE, 0); |
282 | #endif |
283 | |
284 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); |
285 | |
286 | gtk_widget_show_all(dialog); |
287 | |
288 | appendf(&context->log, COLOR_SYSTEM, "Running GeoToad\n"); |
289 | run(context); |
290 | |
291 | gtk_dialog_run(GTK_DIALOG(dialog)); |
292 | |
293 | gtk_widget_destroy(dialog); |
294 | } |
295 | |
296 | static gboolean gui_setup(gt_context_t *context) { |
297 | gboolean ok = FALSE; |
298 | |
299 | GtkWidget *dialog = gtk_dialog_new_with_buttons(_("GeoToad - Setup"), |
300 | GTK_WINDOW(context->appdata->window), |
301 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, |
302 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
303 | GTK_STOCK_OK, GTK_RESPONSE_OK, |
304 | NULL); |
305 | |
306 | GtkWidget *hbox = gtk_hbox_new(FALSE, 0); |
307 | GtkWidget *vbox = gtk_vbox_new(FALSE, 0); |
308 | gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Username:"))); |
309 | gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_label_new(_("Password:"))); |
310 | gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox); |
311 | |
312 | vbox = gtk_vbox_new(FALSE, 0); |
313 | #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5) |
314 | context->username = gtk_entry_new(); |
315 | context->password = gtk_entry_new(); |
316 | #else |
317 | context->username = hildon_entry_new(HILDON_SIZE_AUTO); |
318 | hildon_gtk_entry_set_input_mode(GTK_ENTRY(context->username), |
319 | HILDON_GTK_INPUT_MODE_FULL); |
320 | context->password = hildon_entry_new(HILDON_SIZE_AUTO); |
321 | hildon_gtk_entry_set_input_mode(GTK_ENTRY(context->password), |
322 | HILDON_GTK_INPUT_MODE_FULL); |
323 | #endif |
324 | gtk_entry_set_visibility(GTK_ENTRY(context->password), FALSE); |
325 | |
326 | /* set saved defaults */ |
327 | if(context->appdata->gt.username) |
328 | gtk_entry_set_text(GTK_ENTRY(context->username), |
329 | context->appdata->gt.username); |
330 | |
331 | if(context->appdata->gt.password) |
332 | gtk_entry_set_text(GTK_ENTRY(context->password), |
333 | context->appdata->gt.password); |
334 | |
335 | gtk_box_pack_start_defaults(GTK_BOX(vbox), context->username); |
336 | gtk_box_pack_start_defaults(GTK_BOX(vbox), context->password); |
337 | gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox); |
338 | |
339 | gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox); |
340 | |
341 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); |
342 | |
343 | gtk_widget_show_all(dialog); |
344 | |
345 | if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { |
346 | |
347 | /* save values */ |
348 | if(context->appdata->gt.username) g_free(context->appdata->gt.username); |
349 | context->appdata->gt.username = |
350 | g_strdup(gtk_entry_get_text(GTK_ENTRY(context->username))); |
351 | |
352 | if(context->appdata->gt.password) g_free(context->appdata->gt.password); |
353 | context->appdata->gt.password = |
354 | g_strdup(gtk_entry_get_text(GTK_ENTRY(context->password))); |
355 | |
356 | ok = TRUE; |
357 | } |
358 | |
359 | gtk_widget_destroy(dialog); |
360 | |
361 | return ok; |
362 | } |
363 | |
364 | void geotoad(appdata_t *appdata) { |
365 | if(!geotoad_available()) { |
366 | errorf(_("GeoToad is not installed on this device.\n" |
367 | "You need to install it in order to be able to use it.")); |
368 | return; |
369 | } |
370 | |
371 | gt_context_t *context = g_new0(gt_context_t, 1); |
372 | context->appdata = appdata; |
373 | |
374 | printf("geoToad\n"); |
375 | |
376 | if(gui_setup(context)) |
377 | gui_run(context); |
378 | } |
379 | |
380 | gboolean geotoad_available(void) { |
381 | /* before doing anything make sure geotoad is installed */ |
382 | return g_file_test(GEOTOAD, G_FILE_TEST_IS_EXECUTABLE); |
383 | } |
384 | |
385 |