#ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "extra.h" #include "jsmn/jsmn.h" #include "extra_private.h" static int _extra_init = 0; static Ecore_Event_Handler *_data; static Ecore_Event_Handler *_complete; int _extra_lib_log_dom = -1; Eina_List *_theme_list; #define sec_strdup(v) v ? eina_strbuf_string_steal(v) : NULL void _extra_theme_cache_load(); #define PREVIEW_DOWNLOAD 1 #define THEME_DOWNLOAD 2 typedef struct { Extra_Theme theme; char state; //indicates if some downloads are in progress } Extra_Theme_Private; typedef struct { Extra_Theme_Private *theme; Extra_Progress *progress; char nand_mask; //will be applied to the status char of the theme } Extra_Download_Job; static void _extra_theme_add(Eina_Strbuf *id, Eina_Strbuf *name, Eina_Strbuf *author, Eina_Strbuf *description, int version) { Extra_Theme_Private *theme; theme = malloc(sizeof(*theme)); theme->theme.id = sec_strdup(id); theme->theme.name = sec_strdup(name); theme->theme.author = sec_strdup(author); theme->theme.description = sec_strdup(description); theme->theme.version = version; theme->state = 0; _theme_list = eina_list_append(_theme_list, theme); } EAPI int extra_init(void) { _extra_init++; if (_extra_init > 1) return _extra_init; eina_init(); _extra_lib_log_dom = eina_log_domain_register("extra", EINA_COLOR_CYAN); if (_extra_lib_log_dom < 0) { EINA_LOG_ERR("extra can not create its log domain."); goto shutdown_eina; } //we are putting stuff into /extra/ lets make sure its created { Eina_Strbuf *buf; buf = eina_strbuf_new(); eina_strbuf_append(buf, efreet_cache_home_get()); eina_strbuf_append(buf, "/extra/"); ecore_file_mkdir(eina_strbuf_string_get(buf)); eina_strbuf_free(buf); } // Put here your initialization logic of your library _extra_theme_cache_load(); eina_log_timing(_extra_lib_log_dom, EINA_LOG_STATE_STOP, EINA_LOG_STATE_INIT); return _extra_init; shutdown_eina: eina_shutdown(); _extra_init--; return _extra_init; } EAPI int extra_shutdown(void) { _extra_init--; if (_extra_init != 0) return _extra_init; eina_log_timing(_extra_lib_log_dom, EINA_LOG_STATE_START, EINA_LOG_STATE_SHUTDOWN); // Put here your shutdown logic eina_log_domain_unregister(_extra_lib_log_dom); _extra_lib_log_dom = -1; eina_shutdown(); return _extra_init; } static Eina_Bool _url_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event_info) { Ecore_Con_Event_Url_Data *url_data = event_info; Eina_Strbuf *buf = ecore_con_url_data_get(url_data->url_con); eina_strbuf_append_n(buf, (char*)url_data->data, url_data->size); return EINA_TRUE; } static int _string_tuple_get(Eina_Strbuf *c, jsmntok_t *array, int i, Eina_Strbuf **name, Eina_Strbuf **value) { if (array[i].type != JSMN_STRING || array[i].size != 1) { printf("expected string type with children\n"); return 0; } *name = eina_strbuf_substr_get(c, array[i].start, array[i].end - array[i].start); if ((array[i + 1].type != JSMN_STRING && array[i + 1].type != JSMN_PRIMITIVE) || array[i + 1].size != 0) { printf("Expected string type without children\n"); return 0; } *value = eina_strbuf_substr_get(c, array[i + 1].start, array[i + 1].end - array[i + 1].start); return 2; } static Eina_Bool _fill_themes(Eina_Strbuf *buf) { jsmn_parser parser; jsmntok_t parts[201]; int n, c = 0; jsmn_init(&parser); const char *string = eina_strbuf_string_get(buf); n = jsmn_parse(&parser, string, strlen(string), parts, 201); if (n == 0) { printf("No themes received\n"); return EINA_FALSE; } if (parts[0].type != JSMN_OBJECT) { printf("Root node should be a object\n"); return EINA_FALSE; } c += 1; eina_list_free(_theme_list); _theme_list = NULL; for (int i = 0; i < parts[0].size; ++i) { Eina_Strbuf *id = NULL, *name = NULL, *version = NULL, *description = NULL, *author = NULL; int versionNumb; //expect string object tuple if (parts[c].type != JSMN_STRING || parts[c].size != 1) { printf("Expected String type with one child\n"); return EINA_FALSE; } c += 1; if (parts[c].type != JSMN_OBJECT || parts[c].size <= 0) { printf("Expected Object type with more than 0 children\n"); return EINA_FALSE; } int max = parts[c].size; c+=1; for (int i2 = 0; i2 < max; ++i2) { Eina_Strbuf *value = NULL, *property = NULL; const char *v; int j = _string_tuple_get(buf, parts, c, &value, &property); if (!j) return EINA_FALSE; c += j; v = eina_strbuf_string_steal(value); if (!strcmp(v, "description")) description = property; else if (!strcmp(v, "author")) author = property; else if (!strcmp(v, "name")) name = property; else if (!strcmp(v, "version")) version = property; else if (!strcmp(v, "theme_id")) id = property; else eina_strbuf_free(property); eina_strbuf_free(value); } versionNumb = atoi(eina_strbuf_string_steal(version)); _extra_theme_add(id, name, author, description, versionNumb); eina_strbuf_free(id); eina_strbuf_free(name); eina_strbuf_free(author); eina_strbuf_free(description); eina_strbuf_free(version); } return EINA_TRUE; } static char * _theme_cache_path_get() { char *path; path = malloc(PATH_MAX * sizeof(char)); sprintf(path, "%s/%s/%s.json", efreet_cache_home_get(), PACKAGE_NAME, "themes"); return path; } static Eina_Bool _url_complete_cb(void *data, int type EINA_UNUSED, void *event_info) { Extra_Progress *progress = data;; Ecore_Con_Event_Url_Complete *complete = event_info; Eina_Strbuf *buf; Eina_Bool parsed; buf = ecore_con_url_data_get(complete->url_con); parsed = _fill_themes(buf); if (parsed) { FILE *cache; char *cache_path = _theme_cache_path_get(); const char *content = eina_strbuf_string_get(buf); cache = fopen(cache_path, "w+"); fprintf(cache, "%s", content); fclose(cache); free(cache_path); } if (progress->done_cb) progress->done_cb(); ecore_event_handler_del(_data); ecore_event_handler_del(_complete); return EINA_TRUE; } EAPI void extra_sync(Extra_Progress *progress) { Ecore_Con_Url *url; url = ecore_con_url_custom_new("http://" HOSTNAME "/v1/themes/", "GET"); ecore_con_url_additional_header_add(url, "Accept", "text/json"); ecore_con_url_data_set(url, eina_strbuf_new()); _data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, _url_data_cb, NULL); _complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _url_complete_cb, progress); ecore_con_url_get(url); } EAPI Eina_List * extra_themes_list(void) { return _theme_list; } EAPI Extra_Theme *extra_theme_get(const char *id) { Extra_Theme *theme; Eina_List *item; EINA_LIST_FOREACH(extra_themes_list(), item, theme) if (!strcmp(id, theme->id)) return theme; return NULL; } EAPI Eina_Bool extra_theme_installed(Extra_Theme *theme) { char *path; Eina_Bool exists; EINA_SAFETY_ON_NULL_RETURN_VAL(theme, EINA_FALSE); path = extra_theme_install_path_get(theme); exists = ecore_file_exists(path); free(path); return exists; } EAPI char * extra_theme_install_path_get(Extra_Theme *theme) { Eina_Strbuf *buf; char *path; EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL); buf = eina_strbuf_new(); eina_strbuf_append(buf, elm_theme_user_dir_get()); eina_strbuf_append_printf(buf, "/%s-%d.edj", theme->id, theme->version); path = eina_strbuf_string_steal(buf); eina_strbuf_free(buf); return path; } static void _download_complete_cb(void *data, const char *file EINA_UNUSED, int status EINA_UNUSED) { Extra_Download_Job *job = data; job->theme->state &= (~job->nand_mask); if (job->progress->done_cb) job->progress->done_cb(); } static int _download_progress_cb(void *data EINA_UNUSED, const char *file EINA_UNUSED, long int dltotal EINA_UNUSED, long int dlnow EINA_UNUSED, long int ultotal EINA_UNUSED, long int ulnow EINA_UNUSED) { Extra_Download_Job *job = data; double percent = 0.f; if (dlnow > 0.f) percent = ((double)(double)dlnow / (double)dltotal); if (job->progress->progress_cb) job->progress->progress_cb(percent); return ECORE_FILE_PROGRESS_CONTINUE; } static char * _extra_theme_preview_remote_generate(Extra_Theme *theme) { const char *pattern = "http://" HOSTNAME "/themes/preview/%s.png"; char *url; EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL); url = malloc((strlen(pattern) + strlen(theme->id) - 1) * sizeof(char)); sprintf(url, pattern, theme->id); return url; } static char * _extra_theme_preview_local_generate(Extra_Theme *theme) { Eina_Strbuf *local_preview; char *tmp; local_preview = eina_strbuf_new(); eina_strbuf_append(local_preview, efreet_cache_home_get()); eina_strbuf_append_printf(local_preview, "/%s/%s-%d.png", PACKAGE_NAME, theme->id, theme->version); tmp = eina_strbuf_string_steal(local_preview); eina_strbuf_free(local_preview); return tmp; } EAPI char* extra_theme_preview_get(Extra_Theme *theme) { char *local; Extra_Theme_Private *priv = ((Extra_Theme_Private*) theme); //download is in progress do not return the path if (priv->state & PREVIEW_DOWNLOAD) return NULL; local = _extra_theme_preview_local_generate(theme); if (!ecore_file_exists(local)) { free(local); local = NULL; } return local; } EAPI void extra_theme_preview_download(Extra_Progress *progress, Extra_Theme *theme) { char *remote, *dst; Extra_Download_Job *job; Extra_Theme_Private *priv = ((Extra_Theme_Private*) theme); if (priv->state & PREVIEW_DOWNLOAD) return; job = calloc(1, sizeof(Extra_Download_Job)); job->progress = progress; job->theme = priv; job->nand_mask = PREVIEW_DOWNLOAD; priv->state |= PREVIEW_DOWNLOAD; remote = _extra_theme_preview_remote_generate(theme); dst = _extra_theme_preview_local_generate(theme); ecore_file_download(remote, dst, _download_complete_cb, _download_progress_cb, job, NULL); free(remote); free(dst); } EAPI char * extra_theme_download_url_get(Extra_Theme *theme) { const char *pattern = "http://" HOSTNAME "/themes/%s-%d.edj"; char *url; EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL); url = malloc((strlen(pattern) + strlen(theme->id) - 1 + (int)(log10(theme->version))) * sizeof(char)); sprintf(url, pattern, theme->id, theme->version); return url; } EAPI void extra_theme_download(Extra_Progress *progress, Extra_Theme *theme) { char *path, *urlstr = NULL; Extra_Theme_Private *priv = ((Extra_Theme_Private*) theme); if (theme) { if (priv->state & THEME_DOWNLOAD) return; Extra_Download_Job *job = calloc(1, sizeof(Extra_Download_Job)); job->progress = progress; job->theme = priv; job->nand_mask = THEME_DOWNLOAD; priv->state |= THEME_DOWNLOAD; urlstr = extra_theme_download_url_get(theme); path = extra_theme_install_path_get(theme); ecore_file_download(urlstr, path, _download_complete_cb, _download_progress_cb, job, NULL); free(urlstr); free(path); } } void _extra_theme_cache_load() { char *cache_path = _theme_cache_path_get(); if (ecore_file_exists(cache_path)) { Eina_File *cache; Eina_File_Line *line; Eina_Iterator *it; Eina_Strbuf *buf; INF("Loading themes from cache"); cache = eina_file_open(cache_path, EINA_FALSE); it = eina_file_map_lines(cache); buf = eina_strbuf_new(); EINA_ITERATOR_FOREACH(it, line) { eina_strbuf_append_length(buf, line->start, line->length); } _fill_themes(buf); eina_strbuf_free(buf); eina_file_close(cache); } else INF("No theme cache found"); free(cache_path); } static Eina_Bool _enlightenment_restart(void *data EINA_UNUSED) { char *cmd = "enlightenment_remote -restart"; ecore_exe_run(cmd, NULL); return EINA_FALSE; } EAPI void extra_theme_use(Extra_Theme *t) { char *path; EINA_SAFETY_ON_NULL_RETURN(t); path = extra_theme_install_path_get(t); elm_theme_set(NULL, path); elm_config_all_flush(); elm_config_save(); free(path); ecore_timer_add(3, _enlightenment_restart, NULL); }