Imported Upstream version 1.7.1
[platform/upstream/edje.git] / src / bin / edje_player.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <ctype.h>
12
13 #ifdef HAVE_EVIL
14 # include <Evil.h>
15 #endif
16
17 #include <Evas.h>
18 #include <Ecore.h>
19 #include <Ecore_Getopt.h>
20 #include <Ecore_Evas.h>
21 #include <Edje.h>
22
23 struct opts {
24    char *file;
25    char *group;
26    Eina_Bool list_groups;
27    char *engine;
28    Eina_Rectangle size;
29    unsigned char color[3];
30    Eina_Bool borderless;
31    Eina_Bool sticky;
32    Eina_Bool shaped;
33    Eina_Bool alpha;
34    Eina_Bool print;
35    Eina_Bool slave_mode;
36    double scale;
37    char *title;
38 };
39
40 static Eina_Bool _edje_load_or_show_error(Evas_Object *edje, const char *file, const char *group);
41
42 static Ecore_Evas *win;
43
44 static void
45 _win_title_set(const char *group, const char *file)
46 {
47    char buf[1024];
48    snprintf(buf, sizeof(buf), "Edje_Player - %s of %s", group, file);
49    ecore_evas_title_set(win, buf);
50 }
51
52 static char *
53 _slave_mode_tok(char **p_arg)
54 {
55    char *s, *e;
56    Eina_Bool is_quoted;
57
58    if (!*p_arg) return NULL;
59
60    s = *p_arg;
61    while (isspace(*s))
62      s++;
63
64    if (*s == '\0')
65      {
66         *p_arg = NULL;
67         return NULL;
68      }
69    else if (*s == '"')
70      {
71         is_quoted = EINA_TRUE;
72         s++;
73         *p_arg = s;
74      }
75    else
76      {
77         is_quoted = EINA_FALSE;
78         *p_arg = s;
79      }
80
81    for (e = s; *e != '\0'; e++)
82      {
83         if ((!is_quoted) && (isspace(*e)))
84           break;
85         else if ((is_quoted) && (*e == '"'))
86           break;
87      }
88
89    if (*e == '\0') return NULL;
90
91    *e = '\0';
92    return e + 1;
93 }
94
95 static void
96 _slave_mode_signal(Evas_Object *edje, char *args)
97 {
98    char *emission, *source;
99
100    emission = args;
101    source = _slave_mode_tok(&emission);
102    _slave_mode_tok(&source);
103
104    if ((!emission) || (!source))
105      {
106         fputs("ERROR: Invalid command arguments.\n", stderr);
107         return;
108      }
109
110    edje_object_signal_emit(edje, emission, source);
111 }
112
113 static void
114 _slave_mode_info(Evas_Object *edje, char *args)
115 {
116    _slave_mode_tok(&args);
117
118    if (!args)
119      {
120         fputs("ERROR: Invalid command arguments.\n", stderr);
121         return;
122      }
123
124    if (!edje_object_part_exists(edje, args))
125      {
126         printf("INFO: \"%s\" does not exist.\n", args);
127      }
128    else
129      {
130         Evas_Coord x, y, w, h;
131         edje_object_part_geometry_get(edje, args, &x, &y, &w, &h);
132         printf("INFO: \"%s\" %d,%d,%d,%d\n", args, x, y, w, h);
133      }
134 }
135
136 static void
137 _slave_mode_quit(Evas_Object *edje __UNUSED__, char *args __UNUSED__)
138 {
139    puts("Bye!");
140    ecore_main_loop_quit();
141 }
142
143 static void
144 _slave_mode_help(Evas_Object *edje __UNUSED__, char *args __UNUSED__)
145 {
146    puts("Help:\n"
147         "One command per line, arguments separated by space. Strings may have "
148         "spaces if enclosed in quotes (\").\n"
149         "\n"
150         "\t<command> [arguments]\n"
151         "\n"
152         "Available commands:\n"
153         "\tsignal <emission> <source>\n"
154         "\t   sends a signal to edje\n"
155         "\tinfo <part>\n"
156         "\t   Print part geometry: <x>,<y>,<w>,<h>\n"
157         "\tquit\n"
158         "\t   exit edje player.\n"
159         "\thelp\n"
160         "\t   shows this message.\n");
161    /*
162     * Extension ideas (are they useful?):
163     *  - message: send a message
164     *  - data: show data value
165     *  - color_class: set color class values (maybe also list?)
166     *  - text_class: set text class values (maybe also list?)
167     *  - play_set: change play state
168     *  - animation_set: change animation state
169     */
170 }
171
172 struct slave_cmd
173 {
174    const char *cmd;
175    void (*func)(Evas_Object *edje, char *args);
176 } _slave_mode_commands[] = {
177   {"signal", _slave_mode_signal},
178   {"info", _slave_mode_info},
179   {"quit", _slave_mode_quit},
180   {"help", _slave_mode_help},
181   {NULL, NULL}
182 };
183
184 static Eina_Bool
185 _slave_mode(void *data, Ecore_Fd_Handler *fd_handler)
186 {
187    Evas_Object *edje = data;
188    char buf[1024], *p;
189    const struct slave_cmd *itr;
190    size_t len;
191
192    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR))
193      {
194         fputs("ERROR: error on stdin! Exit.\n", stderr);
195         ecore_main_loop_quit();
196         return ECORE_CALLBACK_CANCEL;
197      }
198    if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
199      return ECORE_CALLBACK_RENEW;
200
201    if (!fgets(buf, sizeof(buf), stdin))
202      {
203         fputs("ERROR: end of stdin! Exit.\n", stderr);
204         ecore_main_loop_quit();
205         return ECORE_CALLBACK_CANCEL;
206      }
207
208    len = strlen(buf);
209    if (len < 1)
210      {
211         fputs("ERROR: no input! Try: help\n", stderr);
212         return ECORE_CALLBACK_RENEW;
213      }
214    if (buf[len - 1] == '\n')
215      {
216         len--;
217         buf[len] = '\0';
218      }
219
220    p = strchr(buf, ' ');
221    if (p)
222      {
223         *p = '\0';
224         p++;
225
226         while (isspace(*p))
227           p++;
228         if (*p == '\0')
229           p = NULL;
230
231         if (p)
232           {
233              char *q = p + strlen(p) - 1;
234              while (isspace(*q))
235                {
236                   *q = '\0';
237                   q--;
238                }
239           }
240      }
241
242    for (itr = _slave_mode_commands; itr->cmd; itr++)
243      {
244         if (strcmp(itr->cmd, buf) == 0)
245           {
246              itr->func(edje, p);
247              break;
248           }
249      }
250
251    return ECORE_CALLBACK_RENEW;
252 }
253
254 static void
255 _print_signal(void *data __UNUSED__, Evas_Object *o __UNUSED__, const char *emission, const char *source)
256 {
257    printf("SIGNAL: \"%s\" \"%s\"\n", emission, source);
258 }
259
260 static void
261 _print_message(void *data __UNUSED__, Evas_Object *edje __UNUSED__, Edje_Message_Type type, int id, void *msg)
262 {
263    const char *typestr;
264    char buf[64];
265
266    switch (type)
267      {
268       case EDJE_MESSAGE_NONE:
269          typestr = "NONE";
270          break;
271       case EDJE_MESSAGE_SIGNAL:
272          typestr = "SIGNAL";
273          break;
274       case EDJE_MESSAGE_STRING:
275          typestr = "STRING";
276          break;
277       case EDJE_MESSAGE_INT:
278          typestr = "INT";
279          break;
280       case EDJE_MESSAGE_FLOAT:
281          typestr = "FLOAT";
282          break;
283       case EDJE_MESSAGE_STRING_SET:
284          typestr = "STRING_SET";
285          break;
286       case EDJE_MESSAGE_INT_SET:
287          typestr = "INT_SET";
288          break;
289       case EDJE_MESSAGE_FLOAT_SET:
290          typestr = "FLOAT_SET";
291          break;
292       case EDJE_MESSAGE_STRING_INT:
293          typestr = "STRING_INT";
294          break;
295       case EDJE_MESSAGE_STRING_FLOAT:
296          typestr = "STRING_FLOAT";
297          break;
298       case EDJE_MESSAGE_STRING_INT_SET:
299          typestr = "INT_SET";
300          break;
301       case EDJE_MESSAGE_STRING_FLOAT_SET:
302          typestr = "FLOAT_SET";
303          break;
304       default:
305          snprintf(buf, sizeof(buf), "UNKNOWN(%d)", type);
306          typestr = buf;
307      }
308
309    printf("MESSAGE: type=%s, id=%d", typestr, id);
310
311    switch (type)
312      {
313       case EDJE_MESSAGE_NONE: break;
314       case EDJE_MESSAGE_SIGNAL: break;
315       case EDJE_MESSAGE_STRING:
316         {
317            Edje_Message_String *m = msg;
318            printf(" \"%s\"", m->str);
319         }
320         break;
321       case EDJE_MESSAGE_INT:
322         {
323            Edje_Message_Int *m = msg;
324            printf(" %d", m->val);
325         }
326         break;
327       case EDJE_MESSAGE_FLOAT:
328         {
329            Edje_Message_Float *m = msg;
330            printf(" %f", m->val);
331         }
332         break;
333       case EDJE_MESSAGE_STRING_SET:
334         {
335            Edje_Message_String_Set *m = msg;
336            int i;
337            for (i = 0; i < m->count; i++)
338              printf(" \"%s\"", m->str[i]);
339         }
340         break;
341       case EDJE_MESSAGE_INT_SET:
342         {
343            Edje_Message_Int_Set *m = msg;
344            int i;
345            for (i = 0; i < m->count; i++)
346              printf(" %d", m->val[i]);
347         }
348         break;
349       case EDJE_MESSAGE_FLOAT_SET:
350         {
351            Edje_Message_Float_Set *m = msg;
352            int i;
353            for (i = 0; i < m->count; i++)
354              printf(" %f", m->val[i]);
355         }
356         break;
357       case EDJE_MESSAGE_STRING_INT:
358         {
359            Edje_Message_String_Int *m = msg;
360            printf(" \"%s\" %d", m->str, m->val);
361         }
362         break;
363       case EDJE_MESSAGE_STRING_FLOAT:
364         {
365            Edje_Message_String_Float *m = msg;
366            printf(" \"%s\" %f", m->str, m->val);
367         }
368         break;
369       case EDJE_MESSAGE_STRING_INT_SET:
370         {
371            Edje_Message_String_Int_Set *m = msg;
372            int i;
373            printf(" \"%s\"", m->str);
374            for (i = 0; i < m->count; i++)
375              printf(" %d", m->val[i]);
376         }
377         break;
378       case EDJE_MESSAGE_STRING_FLOAT_SET:
379         {
380            Edje_Message_String_Float_Set *m = msg;
381            int i;
382            printf(" \"%s\"", m->str);
383            for (i = 0; i < m->count; i++)
384              printf(" %f", m->val[i]);
385         }
386         break;
387       default:
388         break;
389      }
390
391    putchar('\n');
392 }
393
394 static void
395 _reset_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *stack, void *event_info __UNUSED__)
396 {
397    Evas_Coord minw, minh;
398    Evas_Object *edje = data;
399
400    edje_object_size_min_get(edje, &minw, &minh);
401    if ((minw <= 0) && (minh <= 0))
402      edje_object_size_min_calc(edje, &minw, &minh);
403
404    evas_object_size_hint_min_set(stack, minw, minh);
405 }
406
407 static void
408 _key_down(void *data, Evas *e __UNUSED__, Evas_Object *stack __UNUSED__, void *event_info)
409 {
410    Evas_Event_Key_Down *ev = event_info;
411    struct opts *opts = data;
412
413    if ((!strcmp(ev->keyname, "equal")) ||
414        (!strcmp(ev->keyname, "plus")))
415       opts->scale += 0.1;
416    else if ((!strcmp(ev->keyname, "minus")) ||
417             (!strcmp(ev->keyname, "underscore")))
418       opts->scale -= 0.1;
419    else if ((!strcmp(ev->keyname, "0")))
420       opts->scale = 1.0;
421    if (opts->scale < 0.1) opts->scale = 0.1;
422    else if (opts->scale > 10.0) opts->scale = 1.0;
423    edje_scale_set(opts->scale);
424 }
425
426 static Evas_Object *
427 _create_stack(Evas *evas, const struct opts *opts)
428 {
429    Evas_Object *stack = evas_object_box_add(evas);
430    if (!stack)
431      {
432         fputs("ERROR: could not create object stack (box).\n", stderr);
433         return NULL;
434      }
435    evas_object_box_layout_set(stack, evas_object_box_layout_stack, NULL, NULL);
436    evas_object_resize(stack, opts->size.w, opts->size.h);
437    evas_object_size_hint_weight_set(stack, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
438    evas_object_size_hint_align_set(stack, EVAS_HINT_FILL, EVAS_HINT_FILL);
439    evas_object_show(stack);
440    return stack;
441 }
442
443 static Evas_Object *
444 _create_bg(Evas *evas, const struct opts *opts)
445 {
446    const unsigned char *color = opts->color;
447    Evas_Object *bg = evas_object_rectangle_add(evas);
448    if (!bg)
449      {
450         fputs("ERROR: could not create background.\n", stderr);
451         return NULL;
452      }
453    evas_object_resize(bg, opts->size.w, opts->size.h);
454    evas_object_color_set(bg, color[0], color[1], color[2], 255);
455    evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
456    evas_object_size_hint_align_set(bg, EVAS_HINT_FILL, EVAS_HINT_FILL);
457    evas_object_show(bg);
458    return bg;
459 }
460
461 static void
462 _edje_reload(void *data __UNUSED__, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__)
463 {
464    const char *file;
465    const char *group;
466    edje_object_signal_callback_del(obj, "edje,change,file", "edje", _edje_reload);
467
468    edje_object_file_get(obj, &file, &group);
469    _edje_load_or_show_error(obj, file, group);
470 }
471
472 static Eina_Bool
473 _edje_load_or_show_error(Evas_Object *edje, const char *file, const char *group)
474 {
475    const char *errmsg;
476    int err;
477
478    if (edje_object_file_set(edje, file, group))
479      {
480         edje_object_signal_callback_add(edje, "edje,change,file", "edje", _edje_reload, NULL);;
481         evas_object_focus_set(edje, EINA_TRUE);
482         return EINA_TRUE;
483      }
484
485    err = edje_object_load_error_get(edje);
486    errmsg = edje_load_error_str(err);
487    fprintf(stderr, "ERROR: could not load edje file '%s', group '%s': %s\n",
488            file, group, errmsg);
489    return EINA_FALSE;
490 }
491
492 static Evas_Object *
493 _create_edje(Evas *evas, const struct opts *opts)
494 {
495    Evas_Coord minw, minh, maxw, maxh;
496    Evas_Object *edje = edje_object_add(evas);
497    if (!edje)
498      {
499         fputs("ERROR: could not create edje.\n", stderr);
500         return NULL;
501      }
502
503    if (opts->group)
504      {
505         if (!_edje_load_or_show_error(edje, opts->file, opts->group))
506           {
507              evas_object_del(edje);
508              return NULL;
509           }
510         if (!opts->title) _win_title_set(opts->group, opts->file);
511      }
512    else
513      {
514         if (edje_file_group_exists(opts->file, "main"))
515           {
516              if (!_edje_load_or_show_error(edje, opts->file, "main"))
517                {
518                   evas_object_del(edje);
519                   return NULL;
520                }
521              if (!opts->title) _win_title_set("main", opts->file);
522           }
523         else
524           {
525              Eina_List *groups = edje_file_collection_list(opts->file);
526              const char *group;
527              if (!groups)
528                {
529                   fprintf(stderr, "ERROR: file '%s' has no groups!\n",
530                           opts->file);
531                   evas_object_del(edje);
532                   return NULL;
533                }
534              group = groups->data;
535              if (!_edje_load_or_show_error(edje, opts->file, group))
536                {
537                   edje_file_collection_list_free(groups);
538                   evas_object_del(edje);
539                   return NULL;
540                }
541              if (!opts->title) _win_title_set(group, opts->file);
542              edje_file_collection_list_free(groups);
543           }
544      }
545
546    edje_object_size_max_get(edje, &maxw, &maxh);
547    edje_object_size_min_get(edje, &minw, &minh);
548    if ((minw <= 0) && (minh <= 0))
549      edje_object_size_min_calc(edje, &minw, &minh);
550
551    evas_object_size_hint_max_set(edje, maxw, maxh);
552    evas_object_size_hint_min_set(edje, minw, minh);
553
554    evas_object_size_hint_weight_set(edje, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
555    evas_object_size_hint_align_set(edje, EVAS_HINT_FILL, EVAS_HINT_FILL);
556    evas_object_show(edje);
557
558    return edje;
559 }
560
561 static unsigned char _parse_color(__UNUSED__ const Ecore_Getopt *parser, __UNUSED__ const Ecore_Getopt_Desc *desc, const char *str, __UNUSED__ void *data, Ecore_Getopt_Value *storage)
562 {
563    unsigned char *color = (unsigned char *)storage->ptrp;
564
565    if (sscanf(str, "%hhu,%hhu,%hhu", color, color + 1, color + 2) != 3)
566      {
567         fprintf(stderr, "ERROR: incorrect color value '%s'\n", str);
568         return 0;
569      }
570
571    return 1;
572 }
573
574 static void _cb_delete(__UNUSED__ Ecore_Evas *ee)
575 {
576   ecore_main_loop_quit();
577 }
578
579 const Ecore_Getopt optdesc = {
580   "edje_player",
581   "%prog [options] <filename.edj>",
582   PACKAGE_VERSION,
583   "(C) 2010 Enlightenment",
584   "BSD with advertisement clause",
585   "Simple application to view edje files.",
586   0,
587   {
588     ECORE_GETOPT_STORE_STR
589     ('g', "group", "The edje group to view (defaults to 'main')."),
590     ECORE_GETOPT_STORE_TRUE
591     ('G', "list-groups", "The groups in the given file."),
592     ECORE_GETOPT_STORE_STR
593     ('e', "engine", "The Ecore-Evas engine to use (see --list-engines)"),
594     ECORE_GETOPT_CALLBACK_NOARGS
595     ('E', "list-engines", "list Ecore-Evas engines",
596      ecore_getopt_callback_ecore_evas_list_engines, NULL),
597     ECORE_GETOPT_CALLBACK_ARGS
598     ('Z', "size", "size to use in wxh form.", "WxH",
599      ecore_getopt_callback_size_parse, NULL),
600     ECORE_GETOPT_CALLBACK_ARGS
601     ('c', "bg-color", "Color of the background (if not shaped or alpha)",
602      "RRGGBB", _parse_color, NULL),
603     ECORE_GETOPT_STORE_TRUE
604     ('b', "borderless", "Display window without border."),
605     ECORE_GETOPT_STORE_TRUE
606     ('y', "sticky", "Display window sticky."),
607     ECORE_GETOPT_STORE_TRUE
608     ('s', "shaped", "Display window shaped."),
609     ECORE_GETOPT_STORE_TRUE
610     ('a', "alpha", "Display window with alpha channel "
611      "(needs composite manager!)"),
612     ECORE_GETOPT_STORE_STR
613     ('t', "title", "Define the window title string"),
614     ECORE_GETOPT_STORE_TRUE
615     ('p', "print", "Print signals and messages to stdout"),
616     ECORE_GETOPT_STORE_TRUE
617     ('S', "slave-mode", "Listen for commands on stdin"),
618     ECORE_GETOPT_STORE_DOUBLE
619     ('z', "scale", "Set scale factor"),
620     ECORE_GETOPT_LICENSE('L', "license"),
621     ECORE_GETOPT_COPYRIGHT('C', "copyright"),
622     ECORE_GETOPT_VERSION('V', "version"),
623     ECORE_GETOPT_HELP('h', "help"),
624     ECORE_GETOPT_SENTINEL
625   }
626 };
627
628 int main(int argc, char **argv)
629 {
630    Evas *evas;
631    Evas_Object *stack, *edje;
632    struct opts opts;
633    Eina_Bool quit_option = EINA_FALSE;
634    int args;
635    Ecore_Getopt_Value values[] = {
636      ECORE_GETOPT_VALUE_STR(opts.group),
637      ECORE_GETOPT_VALUE_BOOL(opts.list_groups),
638      ECORE_GETOPT_VALUE_STR(opts.engine),
639      ECORE_GETOPT_VALUE_BOOL(quit_option),
640      ECORE_GETOPT_VALUE_PTR_CAST(opts.size),
641      ECORE_GETOPT_VALUE_PTR_CAST(opts.color),
642      ECORE_GETOPT_VALUE_BOOL(opts.borderless),
643      ECORE_GETOPT_VALUE_BOOL(opts.sticky),
644      ECORE_GETOPT_VALUE_BOOL(opts.shaped),
645      ECORE_GETOPT_VALUE_BOOL(opts.alpha),
646      ECORE_GETOPT_VALUE_STR(opts.title),
647      ECORE_GETOPT_VALUE_BOOL(opts.print),
648      ECORE_GETOPT_VALUE_BOOL(opts.slave_mode),
649      ECORE_GETOPT_VALUE_DOUBLE(opts.scale),
650      ECORE_GETOPT_VALUE_BOOL(quit_option),
651      ECORE_GETOPT_VALUE_BOOL(quit_option),
652      ECORE_GETOPT_VALUE_BOOL(quit_option),
653      ECORE_GETOPT_VALUE_BOOL(quit_option),
654      ECORE_GETOPT_VALUE_NONE
655    };
656
657    memset(&opts, 0, sizeof(opts));
658    opts.scale = 1.0;
659
660    if (!ecore_evas_init())
661      return EXIT_FAILURE;
662    if (!edje_init())
663      goto shutdown_ecore_evas;
664    edje_frametime_set(1.0/60.0);
665
666    args = ecore_getopt_parse(&optdesc, values, argc, argv);
667    if (args < 0)
668      {
669         fputs("Could not parse arguments.\n", stderr);
670         goto shutdown_edje;
671      }
672    else if (quit_option)
673      {
674         goto end;
675      }
676    else if (args >= argc)
677      {
678         fputs("Missing edje file to load.\n", stderr);
679         goto shutdown_edje;
680      }
681
682    ecore_app_args_set(argc, (const char **)argv);
683    edje_scale_set(opts.scale);
684
685    // check if the given edj file is there
686    if (access(argv[args], R_OK) == -1)
687      {
688        int e = errno;
689        fprintf(stderr, "ERROR: file '%s' not accessible, error %d (%s).\n",
690                argv[args], e, strerror(e));
691        goto end;
692      }
693
694    opts.file = argv[args];
695    if (opts.list_groups)
696      {
697         Eina_List *groups, *n;
698         const char *group;
699         groups = edje_file_collection_list(opts.file);
700         printf("%d groups in file '%s':\n", eina_list_count(groups), opts.file);
701         EINA_LIST_FOREACH(groups, n, group)
702           printf("\t'%s'\n", group);
703         edje_file_collection_list_free(groups);
704         goto end;
705      }
706
707    if (opts.size.w <= 0) opts.size.w = 320;
708    if (opts.size.h <= 0) opts.size.h = 240;
709    win = ecore_evas_new(opts.engine, 0, 0, opts.size.w, opts.size.h, NULL);
710    if (!win)
711      {
712         fprintf(stderr,
713                 "ERROR: could not create window of "
714                 "size %dx%d using engine %s.\n",
715                 opts.size.w, opts.size.h, opts.engine ? opts.engine : "(auto)");
716         goto shutdown_edje;
717      }
718
719    ecore_evas_callback_delete_request_set(win, _cb_delete);
720    evas = ecore_evas_get(win);
721    stack = _create_stack(evas, &opts);
722    if (!stack)
723      {
724         goto free_ecore_evas;
725      }
726
727    ecore_evas_object_associate(win, stack, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);
728
729    if (opts.alpha)
730      ecore_evas_alpha_set(win, EINA_TRUE);
731    else if (opts.shaped)
732      ecore_evas_shaped_set(win, EINA_TRUE);
733    else
734      {
735         Evas_Object *bg = _create_bg(evas, &opts);
736         if (bg) evas_object_box_append(stack, bg);
737      }
738
739    edje = _create_edje(evas, &opts);
740    if (edje)
741      evas_object_box_append(stack, edje);
742    else
743      {
744         goto free_ecore_evas;
745      }
746
747    evas_object_focus_set(stack, EINA_TRUE);
748    evas_object_event_callback_add(stack, EVAS_CALLBACK_KEY_DOWN,
749                                   _key_down, &opts);
750    evas_object_event_callback_add(stack, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
751                                   _reset_size_hints, edje);
752
753    if (opts.print)
754      {
755         edje_object_signal_callback_add(edje, "*", "*", _print_signal, NULL);
756         edje_object_message_handler_set(edje, _print_message, NULL);
757      }
758
759    if (opts.slave_mode)
760      {
761 #ifndef _WIN32
762         int flags;
763         flags = fcntl(STDIN_FILENO, F_GETFL, 0);
764         flags |= O_NONBLOCK;
765         if (fcntl(STDIN_FILENO, F_SETFL, flags) < 0)
766           {
767              fprintf(stderr, "ERROR: Could not set stdin to non-block: %s\n",
768                      strerror(errno));
769              goto free_ecore_evas;
770           }
771         ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ | ECORE_FD_ERROR,
772                                   _slave_mode, edje, NULL, NULL);
773 #else
774         /* TODO: port the code above to Windows */
775         fprintf (stderr, "ERROR: slave mode not working on Windows\n");
776         goto free_ecore_evas;
777 #endif
778      }
779
780    ecore_evas_borderless_set(win, opts.borderless);
781    ecore_evas_sticky_set(win, opts.sticky);
782    if (opts.title)
783      ecore_evas_title_set(win, opts.title);
784
785    ecore_evas_show(win);
786    ecore_main_loop_begin();
787
788    ecore_evas_free(win);
789  end:
790    edje_shutdown();
791    ecore_evas_shutdown();
792
793    return 0;
794
795  free_ecore_evas:
796    ecore_evas_free(win);
797  shutdown_edje:
798    edje_shutdown();
799  shutdown_ecore_evas:
800    ecore_evas_shutdown();
801    return EXIT_FAILURE;
802 }