Add "full screen" parameter to MiniBrowser
[platform/framework/web/webkit-efl.git] / Tools / MiniBrowser / efl / main.c
1 /*
2  * Copyright (C) 2012 Samsung Electronics
3  * Copyright (C) 2012 Intel Corporation. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "EWebKit2.h"
21 #include "url_utils.h"
22 #include <Ecore.h>
23 #include <Ecore_Evas.h>
24 #include <Eina.h>
25 #include <Elementary.h>
26 #include <Evas.h>
27
28 static const int DEFAULT_WIDTH = 800;
29 static const int DEFAULT_HEIGHT = 600;
30 static const char DEFAULT_URL[] = "http://www.google.com/";
31 static const char APP_NAME[] = "EFL MiniBrowser";
32 static const int TOOL_BAR_ICON_SIZE = 24;
33 static const int TOOL_BAR_BUTTON_SIZE = 32;
34
35 #define info(format, args...)       \
36     do {                            \
37         if (verbose)                \
38             printf(format, ##args); \
39     } while (0)
40
41 static int verbose = 1;
42 static Eina_List *windows = NULL;
43 static char *evas_engine_name = NULL;
44 static Eina_Bool frame_flattening_enabled = EINA_FALSE;
45 static Eina_Bool fullscreen_enabled = EINA_FALSE;
46
47 typedef struct _Browser_Window {
48     Evas_Object *window;
49     Evas_Object *webview;
50     Evas_Object *url_bar;
51     Evas_Object *back_button;
52     Evas_Object *forward_button;
53 } Browser_Window;
54
55 static const Ecore_Getopt options = {
56     "MiniBrowser",
57     "%prog [options] [url]",
58     "0.0.1",
59     "(C)2012 Samsung Electronics\n",
60     "",
61     "Test Web Browser using the Enlightenment Foundation Libraries of WebKit2",
62     EINA_TRUE, {
63         ECORE_GETOPT_STORE_STR
64             ('e', "engine", "ecore-evas engine to use."),
65         ECORE_GETOPT_CALLBACK_NOARGS
66             ('E', "list-engines", "list ecore-evas engines.",
67              ecore_getopt_callback_ecore_evas_list_engines, NULL),
68         ECORE_GETOPT_STORE_DEF_BOOL
69             ('f', "flattening", "frame flattening.", EINA_FALSE),
70         ECORE_GETOPT_STORE_DEF_BOOL
71             ('F', "full-screen", "start in full-screen.", EINA_FALSE),
72         ECORE_GETOPT_VERSION
73             ('V', "version"),
74         ECORE_GETOPT_COPYRIGHT
75             ('R', "copyright"),
76         ECORE_GETOPT_HELP
77             ('h', "help"),
78         ECORE_GETOPT_SENTINEL
79     }
80 };
81
82 static Browser_Window *window_create(const char *url);
83
84 static Browser_Window *browser_window_find(Evas_Object *window)
85 {
86     Eina_List *l;
87     void *data;
88
89     if (!window)
90         return NULL;
91
92     EINA_LIST_FOREACH(windows, l, data) {
93         Browser_Window *browser_window = (Browser_Window *)data;
94         if (browser_window->window == window)
95             return browser_window;
96     }
97     return NULL;
98 }
99
100 static void window_free(Browser_Window *window)
101 {
102     evas_object_del(window->webview);
103     /* The elm_win will take care of freeing its children */
104     evas_object_del(window->window);
105     free(window);
106 }
107
108 static void window_close(Browser_Window *window)
109 {
110     windows = eina_list_remove(windows, window);
111     window_free(window);
112
113     if (!windows)
114         elm_exit();
115 }
116
117 static void
118 on_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
119 {
120     Evas_Event_Key_Down *ev = (Evas_Event_Key_Down*) event_info;
121     static const char *encodings[] = {
122         "ISO-8859-1",
123         "UTF-8",
124         NULL
125     };
126     static int currentEncoding = -1;
127     Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(e), "Control");
128
129     if (!strcmp(ev->key, "F1")) {
130         info("Back (F1) was pressed\n");
131         if (!ewk_view_back(obj))
132             info("Back ignored: No back history\n");
133     } else if (!strcmp(ev->key, "F2")) {
134         info("Forward (F2) was pressed\n");
135         if (!ewk_view_forward(obj))
136             info("Forward ignored: No forward history\n");
137     } else if (!strcmp(ev->key, "F3")) {
138         currentEncoding = (currentEncoding + 1) % (sizeof(encodings) / sizeof(encodings[0]));
139         info("Set encoding (F3) pressed. New encoding to %s", encodings[currentEncoding]);
140         ewk_view_custom_encoding_set(obj, encodings[currentEncoding]);
141     } else if (!strcmp(ev->key, "F5")) {
142         info("Reload (F5) was pressed, reloading.\n");
143         ewk_view_reload(obj);
144     } else if (!strcmp(ev->key, "F6")) {
145         info("Stop (F6) was pressed, stop loading.\n");
146         ewk_view_stop(obj);
147     } else if (!strcmp(ev->key, "n") && ctrlPressed) {
148         info("Create new window (Ctrl+n) was pressed.\n");
149         Browser_Window *window = window_create(DEFAULT_URL);
150         windows = eina_list_append(windows, window);
151     }
152 }
153
154 static void
155 view_focus_set(Browser_Window *window, Eina_Bool focus)
156 {
157     /* We steal focus away from elm's focus model and start to do things
158      * manually here, so elm now has no clue what's up. Tell elm that its
159      * toplevel widget is to be unfocused so elm gives up the focus */
160     elm_object_focus_set(elm_object_top_widget_get(window->window), EINA_FALSE);
161     evas_object_focus_set(window->webview, focus);
162 }
163
164 static void
165 on_mouse_down(void *user_data, Evas *e, Evas_Object *webview, void *event_info)
166 {
167     Browser_Window *window = (Browser_Window *)user_data;
168     Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down *)event_info;
169
170     /* Clear selection from the URL bar */
171     elm_entry_select_none(window->url_bar);
172
173     if (ev->button == 1)
174         view_focus_set(window, EINA_TRUE);
175     else if (ev->button == 2)
176         view_focus_set(window, !evas_object_focus_get(webview));
177 }
178
179 static void
180 title_set(Evas_Object *window, const char *title, int progress)
181 {
182     Eina_Strbuf *buffer;
183
184     if (!title || !*title) {
185         elm_win_title_set(window, APP_NAME);
186         return;
187     }
188
189     buffer = eina_strbuf_new();
190     if (progress < 100)
191         eina_strbuf_append_printf(buffer, "%s (%d%%) - %s", title, progress, APP_NAME);
192     else
193         eina_strbuf_append_printf(buffer, "%s - %s", title, APP_NAME);
194
195     elm_win_title_set(window, eina_strbuf_string_get(buffer));
196     eina_strbuf_free(buffer);
197 }
198
199 static void
200 on_title_changed(void *user_data, Evas_Object *webview, void *event_info)
201 {
202     Browser_Window *window = (Browser_Window *)user_data;
203     const char *title = (const char *)event_info;
204
205     title_set(window->window, title, 100);
206 }
207
208 static void
209 on_url_changed(void *user_data, Evas_Object *webview, void *event_info)
210 {
211     Browser_Window *window = (Browser_Window *)user_data;
212     elm_entry_entry_set(window->url_bar, ewk_view_url_get(window->webview));
213 }
214
215 static void
216 on_back_forward_list_changed(void *user_data, Evas_Object *webview, void *event_info)
217 {
218     Browser_Window *window = (Browser_Window *)user_data;
219
220     /* Update navigation buttons state */
221     elm_object_disabled_set(window->back_button, !ewk_view_back_possible(webview));
222     elm_object_disabled_set(window->forward_button, !ewk_view_forward_possible(webview));
223 }
224
225 static void
226 on_new_window(void *user_data, Evas_Object *webview, void *event_info)
227 {
228     Evas_Object **new_view = (Evas_Object **)event_info;
229     Browser_Window *window = window_create(NULL);
230     *new_view = window->webview;
231     windows = eina_list_append(windows, window);
232 }
233
234 static void
235 on_close_window(void *user_data, Evas_Object *webview, void *event_info)
236 {
237     window_close((Browser_Window *)user_data);
238 }
239
240 static void
241 on_progress(void *user_data, Evas_Object *webview, void *event_info)
242 {
243     Browser_Window *window = (Browser_Window *)user_data;
244     double progress = *(double *)event_info;
245
246     title_set(window->window, ewk_view_title_get(window->webview), progress * 100);
247 }
248
249 static void
250 on_error(void *user_data, Evas_Object *webview, void *event_info)
251 {
252     Eina_Strbuf* buffer;
253     const Ewk_Error *error = (const Ewk_Error *)event_info;
254
255     /* This is a cancellation, do not display the error page */
256     if (ewk_error_cancellation_get(error))
257         return;
258
259     buffer = eina_strbuf_new();
260     eina_strbuf_append_printf(buffer, "<html><body><div style=\"color:#ff0000\">ERROR!</div><br><div>Code: %d<br>Description: %s<br>URL: %s</div></body</html>",
261         ewk_error_code_get(error), ewk_error_description_get(error), ewk_error_url_get(error));
262
263     ewk_view_html_string_load(webview, eina_strbuf_string_get(buffer), 0, ewk_error_url_get(error));
264     eina_strbuf_free(buffer);
265 }
266
267 static void
268 on_download_request(void *user_data, Evas_Object *webview, void *event_info)
269 {
270     Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
271
272     // FIXME: The destination folder should be selected by the user but we set it to '/tmp' for now.
273     Eina_Strbuf *destination_path = eina_strbuf_new();
274
275     const char *suggested_name = ewk_download_job_suggested_filename_get(download);
276     if (suggested_name && *suggested_name)
277         eina_strbuf_append_printf(destination_path, "/tmp/%s", suggested_name);
278     else {
279         // Generate a unique file name since no name was suggested.
280         char unique_path[] = "/tmp/downloaded-file.XXXXXX";
281         // change from org distribution
282         eina_strbuf_append(destination_path, mktemp(unique_path));
283     }
284
285     ewk_download_job_destination_set(download, eina_strbuf_string_get(destination_path));
286     info("Downloading: %s\n", eina_strbuf_string_get(destination_path));
287     eina_strbuf_free(destination_path);
288 }
289
290 static void
291 on_download_finished(void *user_data, Evas_Object *webview, void *event_info)
292 {
293     Ewk_Download_Job *download = (Ewk_Download_Job *)event_info;
294     info("Download finished: %s\n",  ewk_download_job_destination_get(download));
295 }
296
297 static void
298 on_download_failed(void *user_data, Evas_Object *webview, void *event_info)
299 {
300     info("Download failed!\n");
301 }
302
303 static int
304 quit(Eina_Bool success, const char *msg)
305 {
306     ewk_shutdown();
307     elm_shutdown();
308
309     if (msg)
310         fputs(msg, (success) ? stdout : stderr);
311
312     if (!success)
313         return EXIT_FAILURE;
314
315     return EXIT_SUCCESS;
316 }
317
318 static void
319 on_url_bar_activated(void *user_data, Evas_Object *url_bar, void *event_info)
320 {
321     Browser_Window *app_data = (Browser_Window *)user_data;
322
323     const char *user_url = elm_entry_entry_get(url_bar);
324     char *url = url_from_user_input(user_url);
325     ewk_view_url_set(app_data->webview, url);
326     free(url);
327
328     /* Give focus back to the view */
329     view_focus_set(app_data, EINA_TRUE);
330 }
331
332 static void
333 on_url_bar_clicked(void *user_data, Evas_Object *url_bar, void *event_info)
334 {
335     Browser_Window *app_data = (Browser_Window *)user_data;
336
337     /* Grab focus from the view */
338     evas_object_focus_set(app_data->webview, EINA_FALSE);
339     elm_object_focus_set(url_bar, EINA_TRUE);
340 }
341
342 static void
343 on_back_button_clicked(void *user_data, Evas_Object *back_button, void *event_info)
344 {
345     Browser_Window *app_data = (Browser_Window *)user_data;
346
347     ewk_view_back(app_data->webview);
348     /* Update back button state */
349     elm_object_disabled_set(back_button, !ewk_view_back_possible(app_data->webview));
350 }
351
352 static void
353 on_forward_button_clicked(void *user_data, Evas_Object *forward_button, void *event_info)
354 {
355     Browser_Window *app_data = (Browser_Window *)user_data;
356
357     ewk_view_forward(app_data->webview);
358     /* Update forward button state */
359     elm_object_disabled_set(forward_button, !ewk_view_forward_possible(app_data->webview));
360 }
361
362 static void
363 on_refresh_button_clicked(void *user_data, Evas_Object *refresh_button, void *event_info)
364 {
365     Browser_Window *app_data = (Browser_Window *)user_data;
366
367     Evas *evas = evas_object_evas_get(refresh_button);
368     Eina_Bool ctrlPressed = evas_key_modifier_is_set(evas_key_modifier_get(evas), "Control");
369     if (ctrlPressed) {
370         info("Reloading and bypassing cache...\n");
371         ewk_view_reload_bypass_cache(app_data->webview);
372     } else {
373         info("Reloading...\n");
374         ewk_view_reload(app_data->webview);
375     }
376 }
377
378 static void
379 on_home_button_clicked(void *user_data, Evas_Object *home_button, void *event_info)
380 {
381     Browser_Window *app_data = (Browser_Window *)user_data;
382
383     ewk_view_url_set(app_data->webview, DEFAULT_URL);
384 }
385
386 static void
387 on_window_deletion(void *user_data, Evas_Object *window, void *event_info)
388 {
389     window_close(browser_window_find(window));
390 }
391
392 static Evas_Object *
393 create_toolbar_button(Evas_Object *window, const char *icon_name)
394 {
395     Evas_Object *button = elm_button_add(window);
396
397     Evas_Object *icon = elm_icon_add(window);
398     elm_icon_standard_set(icon, icon_name);
399     evas_object_size_hint_max_set(icon, TOOL_BAR_ICON_SIZE, TOOL_BAR_ICON_SIZE);
400     evas_object_color_set(icon, 44, 44, 102, 128);
401     evas_object_show(icon);
402     elm_object_part_content_set(button, "icon", icon);
403     evas_object_size_hint_min_set(button, TOOL_BAR_BUTTON_SIZE, TOOL_BAR_BUTTON_SIZE);
404
405     return button;
406 }
407
408 static Browser_Window *window_create(const char *url)
409 {
410     Browser_Window *app_data = malloc(sizeof(Browser_Window));
411     if (!app_data) {
412         info("ERROR: could not create browser window.\n");
413         return NULL;
414     }
415
416     /* Create window */
417     app_data->window = elm_win_add(NULL, "minibrowser-window", ELM_WIN_BASIC);
418     elm_win_title_set(app_data->window, APP_NAME);
419     evas_object_smart_callback_add(app_data->window, "delete,request", on_window_deletion, &app_data);
420
421     /* Create window background */
422     Evas_Object *bg = elm_bg_add(app_data->window);
423     elm_bg_color_set(bg, 193, 192, 191);
424     evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
425     elm_win_resize_object_add(app_data->window, bg);
426     evas_object_show(bg);
427
428     /* Create vertical layout */
429     Evas_Object *vertical_layout = elm_box_add(app_data->window);
430     elm_box_padding_set(vertical_layout, 0, 2);
431     evas_object_size_hint_weight_set(vertical_layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
432     elm_win_resize_object_add(app_data->window, vertical_layout);
433     evas_object_show(vertical_layout);
434
435     /* Create horizontal layout for top bar */
436     Evas_Object *horizontal_layout = elm_box_add(app_data->window);
437     elm_box_horizontal_set(horizontal_layout, EINA_TRUE);
438     evas_object_size_hint_weight_set(horizontal_layout, EVAS_HINT_EXPAND, 0.0);
439     evas_object_size_hint_align_set(horizontal_layout, EVAS_HINT_FILL, 0.0);
440     elm_box_pack_end(vertical_layout, horizontal_layout);
441     evas_object_show(horizontal_layout);
442
443     /* Create Back button */
444     app_data->back_button = create_toolbar_button(app_data->window, "arrow_left");
445     evas_object_smart_callback_add(app_data->back_button, "clicked", on_back_button_clicked, app_data);
446     elm_object_disabled_set(app_data->back_button, EINA_TRUE);
447     evas_object_size_hint_weight_set(app_data->back_button, 0.0, EVAS_HINT_EXPAND);
448     evas_object_size_hint_align_set(app_data->back_button, 0.0, 0.5);
449     elm_box_pack_end(horizontal_layout, app_data->back_button);
450     evas_object_show(app_data->back_button);
451
452     /* Create Forward button */
453     app_data->forward_button = create_toolbar_button(app_data->window, "arrow_right");
454     evas_object_smart_callback_add(app_data->forward_button, "clicked", on_forward_button_clicked, app_data);
455     elm_object_disabled_set(app_data->forward_button, EINA_TRUE);
456     evas_object_size_hint_weight_set(app_data->forward_button, 0.0, EVAS_HINT_EXPAND);
457     evas_object_size_hint_align_set(app_data->forward_button, 0.0, 0.5);
458     elm_box_pack_end(horizontal_layout, app_data->forward_button);
459     evas_object_show(app_data->forward_button);
460
461     /* Create URL bar */
462     app_data->url_bar = elm_entry_add(app_data->window);
463     elm_entry_scrollable_set(app_data->url_bar, EINA_TRUE);
464     elm_entry_single_line_set(app_data->url_bar, EINA_TRUE);
465     elm_entry_cnp_mode_set(app_data->url_bar, ELM_CNP_MODE_PLAINTEXT);
466     elm_entry_text_style_user_push(app_data->url_bar, "DEFAULT='font_size=18'");
467     evas_object_smart_callback_add(app_data->url_bar, "activated", on_url_bar_activated, app_data);
468     evas_object_smart_callback_add(app_data->url_bar, "clicked", on_url_bar_clicked, app_data);
469     evas_object_size_hint_weight_set(app_data->url_bar, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
470     evas_object_size_hint_align_set(app_data->url_bar, EVAS_HINT_FILL, EVAS_HINT_FILL);
471     elm_box_pack_end(horizontal_layout, app_data->url_bar);
472     evas_object_show(app_data->url_bar);
473
474     /* Create Refresh button */
475     Evas_Object *refresh_button = create_toolbar_button(app_data->window, "refresh");
476     evas_object_smart_callback_add(refresh_button, "clicked", on_refresh_button_clicked, app_data);
477     evas_object_size_hint_weight_set(refresh_button, 0.0, EVAS_HINT_EXPAND);
478     evas_object_size_hint_align_set(refresh_button, 1.0, 0.5);
479     elm_box_pack_end(horizontal_layout, refresh_button);
480     evas_object_show(refresh_button);
481
482     /* Create Home button */
483     Evas_Object *home_button = create_toolbar_button(app_data->window, "home");
484     evas_object_smart_callback_add(home_button, "clicked", on_home_button_clicked, app_data);
485     evas_object_size_hint_weight_set(home_button, 0.0, EVAS_HINT_EXPAND);
486     evas_object_size_hint_align_set(home_button, 1.0, 0.5);
487     elm_box_pack_end(horizontal_layout, home_button);
488     evas_object_show(home_button);
489
490     /* Create webview */
491     Evas *evas = evas_object_evas_get(app_data->window);
492     app_data->webview = ewk_view_add(evas);
493     ewk_view_theme_set(app_data->webview, THEME_DIR "/default.edj");
494
495     /* set a file containing certificates for recognized SSL Certificate Authorities */
496     ewk_context_certificate_file_set(ewk_context_default_get(), "/var/lib/ca-certificates/ca-bundle.pem");
497
498 //    Ewk_Settings *settings = ewk_view_settings_get(app_data->webview);
499 //    ewk_settings_file_access_from_file_urls_allowed_set(settings, EINA_TRUE);
500 //    ewk_settings_enable_frame_flattening_set(settings, frame_flattening_enabled);
501     elm_win_fullscreen_set(app_data->window, fullscreen_enabled);
502
503     evas_object_smart_callback_add(app_data->webview, "close,window", on_close_window, app_data);
504     evas_object_smart_callback_add(app_data->webview, "create,window", on_new_window, app_data);
505     evas_object_smart_callback_add(app_data->webview, "download,failed", on_download_failed, app_data);
506     evas_object_smart_callback_add(app_data->webview, "download,finished", on_download_finished, app_data);
507     evas_object_smart_callback_add(app_data->webview, "download,request", on_download_request, app_data);
508     evas_object_smart_callback_add(app_data->webview, "load,error", on_error, app_data);
509     evas_object_smart_callback_add(app_data->webview, "load,progress", on_progress, app_data);
510     evas_object_smart_callback_add(app_data->webview, "title,changed", on_title_changed, app_data);
511     evas_object_smart_callback_add(app_data->webview, "url,changed", on_url_changed, app_data);
512     evas_object_smart_callback_add(app_data->webview, "back,forward,list,changed", on_back_forward_list_changed, app_data);
513
514     evas_object_event_callback_add(app_data->webview, EVAS_CALLBACK_KEY_DOWN, on_key_down, app_data);
515     evas_object_event_callback_add(app_data->webview, EVAS_CALLBACK_MOUSE_DOWN, on_mouse_down, app_data);
516
517     evas_object_size_hint_weight_set(app_data->webview, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
518     evas_object_size_hint_align_set(app_data->webview, EVAS_HINT_FILL, EVAS_HINT_FILL);
519     elm_box_pack_end(vertical_layout, app_data->webview);
520     evas_object_show(app_data->webview);
521
522     if (url)
523         ewk_view_url_set(app_data->webview, url);
524
525     evas_object_resize(app_data->window, DEFAULT_WIDTH, DEFAULT_HEIGHT);
526     elm_win_center(app_data->window, EINA_TRUE, EINA_TRUE);
527     evas_object_show(app_data->window);
528
529     view_focus_set(app_data, EINA_TRUE);
530
531     return app_data;
532 }
533
534 EAPI_MAIN int
535 elm_main(int argc, char *argv[])
536 {
537     int args = 1;
538     unsigned char quitOption = 0;
539     Browser_Window *window;
540
541     Ecore_Getopt_Value values[] = {
542         ECORE_GETOPT_VALUE_STR(evas_engine_name),
543         ECORE_GETOPT_VALUE_BOOL(quitOption),
544         ECORE_GETOPT_VALUE_BOOL(frame_flattening_enabled),
545         ECORE_GETOPT_VALUE_BOOL(fullscreen_enabled),
546         ECORE_GETOPT_VALUE_BOOL(quitOption),
547         ECORE_GETOPT_VALUE_BOOL(quitOption),
548         ECORE_GETOPT_VALUE_BOOL(quitOption),
549         ECORE_GETOPT_VALUE_NONE
550     };
551
552     if (!ewk_init())
553         return EXIT_FAILURE;
554
555     ecore_app_args_set(argc, (const char **) argv);
556     args = ecore_getopt_parse(&options, values, argc, argv);
557
558     if (args < 0)
559         return quit(EINA_FALSE, "ERROR: could not parse options.\n");
560
561     if (quitOption)
562         return quit(EINA_TRUE, NULL);
563
564     if (args < argc) {
565         char *url = url_from_user_input(argv[args]);
566         window = window_create(url);
567         free(url);
568     } else
569         window = window_create(DEFAULT_URL);
570
571     if (!window)
572         return quit(EINA_FALSE, "ERROR: could not create browser window.\n");
573
574     windows = eina_list_append(windows, window);
575
576     elm_run();
577
578     return quit(EINA_TRUE, NULL);
579 }
580 ELM_MAIN()