#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #ifdef HAVE_EVIL # include #endif #include #include #include #include #include struct opts { char *file; char *group; Eina_Bool list_groups; char *engine; Eina_Rectangle size; unsigned char color[3]; Eina_Bool borderless; Eina_Bool sticky; Eina_Bool shaped; Eina_Bool alpha; Eina_Bool print; Eina_Bool slave_mode; double scale; char *title; }; static Eina_Bool _edje_load_or_show_error(Evas_Object *edje, const char *file, const char *group); static Ecore_Evas *win; static void _win_title_set(const char *group, const char *file) { char buf[1024]; snprintf(buf, sizeof(buf), "Edje_Player - %s of %s", group, file); ecore_evas_title_set(win, buf); } static char * _slave_mode_tok(char **p_arg) { char *s, *e; Eina_Bool is_quoted; if (!*p_arg) return NULL; s = *p_arg; while (isspace(*s)) s++; if (*s == '\0') { *p_arg = NULL; return NULL; } else if (*s == '"') { is_quoted = EINA_TRUE; s++; *p_arg = s; } else { is_quoted = EINA_FALSE; *p_arg = s; } for (e = s; *e != '\0'; e++) { if ((!is_quoted) && (isspace(*e))) break; else if ((is_quoted) && (*e == '"')) break; } if (*e == '\0') return NULL; *e = '\0'; return e + 1; } static void _slave_mode_signal(Evas_Object *edje, char *args) { char *emission, *source; emission = args; source = _slave_mode_tok(&emission); _slave_mode_tok(&source); if ((!emission) || (!source)) { fputs("ERROR: Invalid command arguments.\n", stderr); return; } edje_object_signal_emit(edje, emission, source); } static void _slave_mode_info(Evas_Object *edje, char *args) { _slave_mode_tok(&args); if (!args) { fputs("ERROR: Invalid command arguments.\n", stderr); return; } if (!edje_object_part_exists(edje, args)) { printf("INFO: \"%s\" does not exist.\n", args); } else { Evas_Coord x, y, w, h; edje_object_part_geometry_get(edje, args, &x, &y, &w, &h); printf("INFO: \"%s\" %d,%d,%d,%d\n", args, x, y, w, h); } } static void _slave_mode_quit(Evas_Object *edje __UNUSED__, char *args __UNUSED__) { puts("Bye!"); ecore_main_loop_quit(); } static void _slave_mode_help(Evas_Object *edje __UNUSED__, char *args __UNUSED__) { puts("Help:\n" "One command per line, arguments separated by space. Strings may have " "spaces if enclosed in quotes (\").\n" "\n" "\t [arguments]\n" "\n" "Available commands:\n" "\tsignal \n" "\t sends a signal to edje\n" "\tinfo \n" "\t Print part geometry: ,,,\n" "\tquit\n" "\t exit edje player.\n" "\thelp\n" "\t shows this message.\n"); /* * Extension ideas (are they useful?): * - message: send a message * - data: show data value * - color_class: set color class values (maybe also list?) * - text_class: set text class values (maybe also list?) * - play_set: change play state * - animation_set: change animation state */ } struct slave_cmd { const char *cmd; void (*func)(Evas_Object *edje, char *args); } _slave_mode_commands[] = { {"signal", _slave_mode_signal}, {"info", _slave_mode_info}, {"quit", _slave_mode_quit}, {"help", _slave_mode_help}, {NULL, NULL} }; static Eina_Bool _slave_mode(void *data, Ecore_Fd_Handler *fd_handler) { Evas_Object *edje = data; char buf[1024], *p; const struct slave_cmd *itr; size_t len; if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) { fputs("ERROR: error on stdin! Exit.\n", stderr); ecore_main_loop_quit(); return ECORE_CALLBACK_CANCEL; } if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) return ECORE_CALLBACK_RENEW; if (!fgets(buf, sizeof(buf), stdin)) { fputs("ERROR: end of stdin! Exit.\n", stderr); ecore_main_loop_quit(); return ECORE_CALLBACK_CANCEL; } len = strlen(buf); if (len < 1) { fputs("ERROR: no input! Try: help\n", stderr); return ECORE_CALLBACK_RENEW; } if (buf[len - 1] == '\n') { len--; buf[len] = '\0'; } p = strchr(buf, ' '); if (p) { *p = '\0'; p++; while (isspace(*p)) p++; if (*p == '\0') p = NULL; if (p) { char *q = p + strlen(p) - 1; while (isspace(*q)) { *q = '\0'; q--; } } } for (itr = _slave_mode_commands; itr->cmd; itr++) { if (strcmp(itr->cmd, buf) == 0) { itr->func(edje, p); break; } } return ECORE_CALLBACK_RENEW; } static void _print_signal(void *data __UNUSED__, Evas_Object *o __UNUSED__, const char *emission, const char *source) { printf("SIGNAL: \"%s\" \"%s\"\n", emission, source); } static void _print_message(void *data __UNUSED__, Evas_Object *edje __UNUSED__, Edje_Message_Type type, int id, void *msg) { const char *typestr; char buf[64]; switch (type) { case EDJE_MESSAGE_NONE: typestr = "NONE"; break; case EDJE_MESSAGE_SIGNAL: typestr = "SIGNAL"; break; case EDJE_MESSAGE_STRING: typestr = "STRING"; break; case EDJE_MESSAGE_INT: typestr = "INT"; break; case EDJE_MESSAGE_FLOAT: typestr = "FLOAT"; break; case EDJE_MESSAGE_STRING_SET: typestr = "STRING_SET"; break; case EDJE_MESSAGE_INT_SET: typestr = "INT_SET"; break; case EDJE_MESSAGE_FLOAT_SET: typestr = "FLOAT_SET"; break; case EDJE_MESSAGE_STRING_INT: typestr = "STRING_INT"; break; case EDJE_MESSAGE_STRING_FLOAT: typestr = "STRING_FLOAT"; break; case EDJE_MESSAGE_STRING_INT_SET: typestr = "INT_SET"; break; case EDJE_MESSAGE_STRING_FLOAT_SET: typestr = "FLOAT_SET"; break; default: snprintf(buf, sizeof(buf), "UNKNOWN(%d)", type); typestr = buf; } printf("MESSAGE: type=%s, id=%d", typestr, id); switch (type) { case EDJE_MESSAGE_NONE: break; case EDJE_MESSAGE_SIGNAL: break; case EDJE_MESSAGE_STRING: { Edje_Message_String *m = msg; printf(" \"%s\"", m->str); } break; case EDJE_MESSAGE_INT: { Edje_Message_Int *m = msg; printf(" %d", m->val); } break; case EDJE_MESSAGE_FLOAT: { Edje_Message_Float *m = msg; printf(" %f", m->val); } break; case EDJE_MESSAGE_STRING_SET: { Edje_Message_String_Set *m = msg; int i; for (i = 0; i < m->count; i++) printf(" \"%s\"", m->str[i]); } break; case EDJE_MESSAGE_INT_SET: { Edje_Message_Int_Set *m = msg; int i; for (i = 0; i < m->count; i++) printf(" %d", m->val[i]); } break; case EDJE_MESSAGE_FLOAT_SET: { Edje_Message_Float_Set *m = msg; int i; for (i = 0; i < m->count; i++) printf(" %f", m->val[i]); } break; case EDJE_MESSAGE_STRING_INT: { Edje_Message_String_Int *m = msg; printf(" \"%s\" %d", m->str, m->val); } break; case EDJE_MESSAGE_STRING_FLOAT: { Edje_Message_String_Float *m = msg; printf(" \"%s\" %f", m->str, m->val); } break; case EDJE_MESSAGE_STRING_INT_SET: { Edje_Message_String_Int_Set *m = msg; int i; printf(" \"%s\"", m->str); for (i = 0; i < m->count; i++) printf(" %d", m->val[i]); } break; case EDJE_MESSAGE_STRING_FLOAT_SET: { Edje_Message_String_Float_Set *m = msg; int i; printf(" \"%s\"", m->str); for (i = 0; i < m->count; i++) printf(" %f", m->val[i]); } break; default: break; } putchar('\n'); } static void _reset_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *stack, void *event_info __UNUSED__) { Evas_Coord minw, minh; Evas_Object *edje = data; edje_object_size_min_get(edje, &minw, &minh); if ((minw <= 0) && (minh <= 0)) edje_object_size_min_calc(edje, &minw, &minh); evas_object_size_hint_min_set(stack, minw, minh); } static void _key_down(void *data, Evas *e __UNUSED__, Evas_Object *stack __UNUSED__, void *event_info) { Evas_Event_Key_Down *ev = event_info; struct opts *opts = data; if ((!strcmp(ev->keyname, "equal")) || (!strcmp(ev->keyname, "plus"))) opts->scale += 0.1; else if ((!strcmp(ev->keyname, "minus")) || (!strcmp(ev->keyname, "underscore"))) opts->scale -= 0.1; else if ((!strcmp(ev->keyname, "0"))) opts->scale = 1.0; if (opts->scale < 0.1) opts->scale = 0.1; else if (opts->scale > 10.0) opts->scale = 1.0; edje_scale_set(opts->scale); } static Evas_Object * _create_stack(Evas *evas, const struct opts *opts) { Evas_Object *stack = evas_object_box_add(evas); if (!stack) { fputs("ERROR: could not create object stack (box).\n", stderr); return NULL; } evas_object_box_layout_set(stack, evas_object_box_layout_stack, NULL, NULL); evas_object_resize(stack, opts->size.w, opts->size.h); evas_object_size_hint_weight_set(stack, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(stack, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(stack); return stack; } static Evas_Object * _create_bg(Evas *evas, const struct opts *opts) { const unsigned char *color = opts->color; Evas_Object *bg = evas_object_rectangle_add(evas); if (!bg) { fputs("ERROR: could not create background.\n", stderr); return NULL; } evas_object_resize(bg, opts->size.w, opts->size.h); evas_object_color_set(bg, color[0], color[1], color[2], 255); evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(bg, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(bg); return bg; } static void _edje_reload(void *data __UNUSED__, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__) { const char *file; const char *group; edje_object_signal_callback_del(obj, "edje,change,file", "edje", _edje_reload); edje_object_file_get(obj, &file, &group); _edje_load_or_show_error(obj, file, group); } static Eina_Bool _edje_load_or_show_error(Evas_Object *edje, const char *file, const char *group) { const char *errmsg; int err; if (edje_object_file_set(edje, file, group)) { edje_object_signal_callback_add(edje, "edje,change,file", "edje", _edje_reload, NULL);; evas_object_focus_set(edje, EINA_TRUE); return EINA_TRUE; } err = edje_object_load_error_get(edje); errmsg = edje_load_error_str(err); fprintf(stderr, "ERROR: could not load edje file '%s', group '%s': %s\n", file, group, errmsg); return EINA_FALSE; } static Evas_Object * _create_edje(Evas *evas, const struct opts *opts) { Evas_Coord minw, minh, maxw, maxh; Evas_Object *edje = edje_object_add(evas); if (!edje) { fputs("ERROR: could not create edje.\n", stderr); return NULL; } if (opts->group) { if (!_edje_load_or_show_error(edje, opts->file, opts->group)) { evas_object_del(edje); return NULL; } if (!opts->title) _win_title_set(opts->group, opts->file); } else { if (edje_file_group_exists(opts->file, "main")) { if (!_edje_load_or_show_error(edje, opts->file, "main")) { evas_object_del(edje); return NULL; } if (!opts->title) _win_title_set("main", opts->file); } else { Eina_List *groups = edje_file_collection_list(opts->file); const char *group; if (!groups) { fprintf(stderr, "ERROR: file '%s' has no groups!\n", opts->file); evas_object_del(edje); return NULL; } group = groups->data; if (!_edje_load_or_show_error(edje, opts->file, group)) { edje_file_collection_list_free(groups); evas_object_del(edje); return NULL; } if (!opts->title) _win_title_set(group, opts->file); edje_file_collection_list_free(groups); } } edje_object_size_max_get(edje, &maxw, &maxh); edje_object_size_min_get(edje, &minw, &minh); if ((minw <= 0) && (minh <= 0)) edje_object_size_min_calc(edje, &minw, &minh); evas_object_size_hint_max_set(edje, maxw, maxh); evas_object_size_hint_min_set(edje, minw, minh); evas_object_size_hint_weight_set(edje, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(edje, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(edje); return edje; } 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) { unsigned char *color = (unsigned char *)storage->ptrp; if (sscanf(str, "%hhu,%hhu,%hhu", color, color + 1, color + 2) != 3) { fprintf(stderr, "ERROR: incorrect color value '%s'\n", str); return 0; } return 1; } static void _cb_delete(__UNUSED__ Ecore_Evas *ee) { ecore_main_loop_quit(); } const Ecore_Getopt optdesc = { "edje_player", "%prog [options] ", PACKAGE_VERSION, "(C) 2010 Enlightenment", "BSD with advertisement clause", "Simple application to view edje files.", 0, { ECORE_GETOPT_STORE_STR ('g', "group", "The edje group to view (defaults to 'main')."), ECORE_GETOPT_STORE_TRUE ('G', "list-groups", "The groups in the given file."), ECORE_GETOPT_STORE_STR ('e', "engine", "The Ecore-Evas engine to use (see --list-engines)"), ECORE_GETOPT_CALLBACK_NOARGS ('E', "list-engines", "list Ecore-Evas engines", ecore_getopt_callback_ecore_evas_list_engines, NULL), ECORE_GETOPT_CALLBACK_ARGS ('Z', "size", "size to use in wxh form.", "WxH", ecore_getopt_callback_size_parse, NULL), ECORE_GETOPT_CALLBACK_ARGS ('c', "bg-color", "Color of the background (if not shaped or alpha)", "RRGGBB", _parse_color, NULL), ECORE_GETOPT_STORE_TRUE ('b', "borderless", "Display window without border."), ECORE_GETOPT_STORE_TRUE ('y', "sticky", "Display window sticky."), ECORE_GETOPT_STORE_TRUE ('s', "shaped", "Display window shaped."), ECORE_GETOPT_STORE_TRUE ('a', "alpha", "Display window with alpha channel " "(needs composite manager!)"), ECORE_GETOPT_STORE_STR ('t', "title", "Define the window title string"), ECORE_GETOPT_STORE_TRUE ('p', "print", "Print signals and messages to stdout"), ECORE_GETOPT_STORE_TRUE ('S', "slave-mode", "Listen for commands on stdin"), ECORE_GETOPT_STORE_DOUBLE ('z', "scale", "Set scale factor"), ECORE_GETOPT_LICENSE('L', "license"), ECORE_GETOPT_COPYRIGHT('C', "copyright"), ECORE_GETOPT_VERSION('V', "version"), ECORE_GETOPT_HELP('h', "help"), ECORE_GETOPT_SENTINEL } }; int main(int argc, char **argv) { Evas *evas; Evas_Object *stack, *edje; struct opts opts; Eina_Bool quit_option = EINA_FALSE; int args; Ecore_Getopt_Value values[] = { ECORE_GETOPT_VALUE_STR(opts.group), ECORE_GETOPT_VALUE_BOOL(opts.list_groups), ECORE_GETOPT_VALUE_STR(opts.engine), ECORE_GETOPT_VALUE_BOOL(quit_option), ECORE_GETOPT_VALUE_PTR_CAST(opts.size), ECORE_GETOPT_VALUE_PTR_CAST(opts.color), ECORE_GETOPT_VALUE_BOOL(opts.borderless), ECORE_GETOPT_VALUE_BOOL(opts.sticky), ECORE_GETOPT_VALUE_BOOL(opts.shaped), ECORE_GETOPT_VALUE_BOOL(opts.alpha), ECORE_GETOPT_VALUE_STR(opts.title), ECORE_GETOPT_VALUE_BOOL(opts.print), ECORE_GETOPT_VALUE_BOOL(opts.slave_mode), ECORE_GETOPT_VALUE_DOUBLE(opts.scale), ECORE_GETOPT_VALUE_BOOL(quit_option), ECORE_GETOPT_VALUE_BOOL(quit_option), ECORE_GETOPT_VALUE_BOOL(quit_option), ECORE_GETOPT_VALUE_BOOL(quit_option), ECORE_GETOPT_VALUE_NONE }; memset(&opts, 0, sizeof(opts)); opts.scale = 1.0; if (!ecore_evas_init()) return EXIT_FAILURE; if (!edje_init()) goto shutdown_ecore_evas; edje_frametime_set(1.0/60.0); args = ecore_getopt_parse(&optdesc, values, argc, argv); if (args < 0) { fputs("Could not parse arguments.\n", stderr); goto shutdown_edje; } else if (quit_option) { goto end; } else if (args >= argc) { fputs("Missing edje file to load.\n", stderr); goto shutdown_edje; } ecore_app_args_set(argc, (const char **)argv); edje_scale_set(opts.scale); // check if the given edj file is there if (access(argv[args], R_OK) == -1) { int e = errno; fprintf(stderr, "ERROR: file '%s' not accessible, error %d (%s).\n", argv[args], e, strerror(e)); goto end; } opts.file = argv[args]; if (opts.list_groups) { Eina_List *groups, *n; const char *group; groups = edje_file_collection_list(opts.file); printf("%d groups in file '%s':\n", eina_list_count(groups), opts.file); EINA_LIST_FOREACH(groups, n, group) printf("\t'%s'\n", group); edje_file_collection_list_free(groups); goto end; } win = ecore_evas_new(opts.engine, 0, 0, opts.size.w, opts.size.h, NULL); if (!win) { fprintf(stderr, "ERROR: could not create window of " "size %dx%d using engine %s.\n", opts.size.w, opts.size.h, opts.engine ? opts.engine : "(auto)"); goto shutdown_edje; } ecore_evas_callback_delete_request_set(win, _cb_delete); evas = ecore_evas_get(win); stack = _create_stack(evas, &opts); if (!stack) { goto free_ecore_evas; } ecore_evas_object_associate(win, stack, ECORE_EVAS_OBJECT_ASSOCIATE_BASE); if (opts.alpha) ecore_evas_alpha_set(win, EINA_TRUE); else if (opts.shaped) ecore_evas_shaped_set(win, EINA_TRUE); else { Evas_Object *bg = _create_bg(evas, &opts); if (bg) evas_object_box_append(stack, bg); } edje = _create_edje(evas, &opts); if (edje) evas_object_box_append(stack, edje); else { goto free_ecore_evas; } evas_object_focus_set(stack, EINA_TRUE); evas_object_event_callback_add(stack, EVAS_CALLBACK_KEY_DOWN, _key_down, &opts); evas_object_event_callback_add(stack, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _reset_size_hints, edje); if (opts.print) { edje_object_signal_callback_add(edje, "*", "*", _print_signal, NULL); edje_object_message_handler_set(edje, _print_message, NULL); } if (opts.slave_mode) { #ifndef _WIN32 int flags; flags = fcntl(STDIN_FILENO, F_GETFL, 0); flags |= O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) < 0) { fprintf(stderr, "ERROR: Could not set stdin to non-block: %s\n", strerror(errno)); goto free_ecore_evas; } ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ | ECORE_FD_ERROR, _slave_mode, edje, NULL, NULL); #else /* TODO: port the code above to Windows */ fprintf (stderr, "ERROR: slave mode not working on Windows\n"); goto free_ecore_evas; #endif } ecore_evas_borderless_set(win, opts.borderless); ecore_evas_sticky_set(win, opts.sticky); if (opts.title) ecore_evas_title_set(win, opts.title); if (opts.size.w <= 0) opts.size.w = 320; if (opts.size.h <= 0) opts.size.h = 240; ecore_evas_resize(win, opts.size.w, opts.size.h); ecore_evas_show(win); ecore_main_loop_begin(); ecore_evas_free(win); end: edje_shutdown(); ecore_evas_shutdown(); return 0; free_ecore_evas: ecore_evas_free(win); shutdown_edje: edje_shutdown(); shutdown_ecore_evas: ecore_evas_shutdown(); return EXIT_FAILURE; }