From: Carsten Haitzler (Rasterman) Date: Fri, 1 Apr 2016 01:45:07 +0000 (+0900) Subject: efl: vpath subsystem X-Git-Tag: upstream/1.20.0~6954 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6291c61556a531da874242830689e37362db1638;p=platform%2Fupstream%2Fefl.git efl: vpath subsystem this adds a core vpath subsystem to efl that allows paths like: ~/file.jpg ~user/file.jpg (:tmp/file.jpg (:config/file.jpg (:videos/file.mp4 (:pictures/file.jpg (:app.config/mycfg.cfg etc. to be translated/looked up. it is desitgned to be async and call event callbacks when ready. the reason for this complexity is fo in future also handle: file:///whatever/file.jpg http://blah.com/file.jpg https://blah.com/file.jpg ssh://blah.com:~/file.jpg etc. @feature --- diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am index d8683e1..17529a5 100644 --- a/src/Makefile_Efl.am +++ b/src/Makefile_Efl.am @@ -23,6 +23,11 @@ efl_eolian_files = \ lib/efl/interfaces/efl_gfx_filter.eo \ lib/efl/interfaces/efl_model_base.eo \ lib/efl/interfaces/efl_animator.eo \ + lib/efl/interfaces/efl_vpath.eo \ + lib/efl/interfaces/efl_vpath_manager.eo \ + lib/efl/interfaces/efl_vpath_file.eo \ + lib/efl/interfaces/efl_vpath_core.eo \ + lib/efl/interfaces/efl_vpath_file_core.eo \ $(efl_eolian_legacy_files) \ $(NULL) @@ -55,7 +60,11 @@ lib_LTLIBRARIES += lib/efl/libefl.la lib_efl_libefl_la_SOURCES = \ lib/efl/interfaces/efl_interfaces_main.c \ lib/efl/interfaces/efl_model_common.c \ -lib/efl/interfaces/efl_gfx_shape.c +lib/efl/interfaces/efl_gfx_shape.c \ +lib/efl/interfaces/efl_vpath_file.c \ +lib/efl/interfaces/efl_vpath_manager.c \ +lib/efl/interfaces/efl_vpath_core.c \ +lib/efl/interfaces/efl_vpath_file_core.c lib_efl_libefl_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl -I$(top_srcdir)/src/lib/efl @EFL_CFLAGS@ -DEFL_GFX_FILTER_BETA lib_efl_libefl_la_LIBADD = @EFL_LIBS@ diff --git a/src/lib/efl/Efl.h b/src/lib/efl/Efl.h index 2cb0bd7..651a5bd 100644 --- a/src/lib/efl/Efl.h +++ b/src/lib/efl/Efl.h @@ -50,6 +50,12 @@ typedef struct tm Efl_Time; #include +#include "interfaces/efl_vpath_file.eo.h" +#include "interfaces/efl_vpath.eo.h" +#include "interfaces/efl_vpath_core.eo.h" +#include "interfaces/efl_vpath_manager.eo.h" +#include "interfaces/efl_vpath_file_core.eo.h" + /* Data types */ #include "interfaces/efl_gfx_types.eot.h" typedef Efl_Gfx_Path_Command_Type Efl_Gfx_Path_Command; diff --git a/src/lib/efl/interfaces/efl_interfaces_main.c b/src/lib/efl/interfaces/efl_interfaces_main.c index 2299022..d01e8df 100644 --- a/src/lib/efl/interfaces/efl_interfaces_main.c +++ b/src/lib/efl/interfaces/efl_interfaces_main.c @@ -25,6 +25,8 @@ #include "interfaces/efl_gfx_filter.eo.c" +#include "interfaces/efl_vpath.eo.c" + EAPI const Eo_Event_Description _EFL_GFX_CHANGED = EO_EVENT_DESCRIPTION("Graphics changed"); diff --git a/src/lib/efl/interfaces/efl_vpath.eo b/src/lib/efl/interfaces/efl_vpath.eo new file mode 100644 index 0000000..021b317 --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath.eo @@ -0,0 +1,29 @@ +interface Efl.Vpath +{ + [[ VPath is the EFL "Virtual Path" system that maps paths and downloads + You can provide virtual paths common in unix shells like: + "~/file.jpg" + "~username/file.png" + And also other extended paths like: + "(:cache/file.png" + "(:videos/file.mp4" + ... + And in future: + "file:///blah.jpg" + "http://blah.com/file.jpg" + "https://blahblah.com/file.jpg" + "ssh://blahblah.com:~/file.jpg" + ... + ]] + legacy_prefix: null; + eo_prefix: efl_vpath; + methods { + fetch { + [[ Fetch a new Vpath File object from the Vpath system ]] + params { + path: const(char)*; [[ The input virtual file path to fetch ]] + } + return: own(Efl.Vpath_File *); [[ An object representing the file ]] + } + } +} diff --git a/src/lib/efl/interfaces/efl_vpath_core.c b/src/lib/efl/interfaces/efl_vpath_core.c new file mode 100644 index 0000000..5e7d525 --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_core.c @@ -0,0 +1,351 @@ +#include "config.h" +#include "Efl.h" + +#ifdef HAVE_GETPWENT +# include +# include +#endif + +#include +#include +#include +#include +#include + +#define MY_CLASS EFL_VPATH_CORE_CLASS + +typedef struct _Efl_Vpath_Core_Data Efl_Vpath_Core_Data; + +struct _Efl_Vpath_Core_Data +{ + Eina_Spinlock lock; + Eina_Hash *meta; +}; + +static Efl_Vpath_Core *vpath_core = NULL; + +EOLIAN static Eo_Base * +_efl_vpath_core_eo_base_constructor(Eo *obj, Efl_Vpath_Core_Data *pd) +{ + char buf[PATH_MAX], bufhome[PATH_MAX], *s; + const char *home; + + if (vpath_core) return NULL; + obj = eo_constructor(eo_super(obj, MY_CLASS)); + pd->meta = eina_hash_string_superfast_new + ((Eina_Free_Cb)eina_stringshare_del); + eina_spinlock_new(&(pd->lock)); + + vpath_core = obj; + + // $HOME / ~/ etc. + home = eina_environment_home_get(); + if (!home) + { + uid_t uid = getuid(); + struct stat st; + + snprintf(bufhome, sizeof(bufhome), "/tmp/%i", (int)uid); + mkdir(bufhome, S_IRUSR | S_IWUSR | S_IXUSR); + if (stat(bufhome, &st) == 0) home = bufhome; + else + { + if (stat("/tmp", &st) == 0) home = "/tmp"; + else home = "/"; + } + } + efl_vpath_core_meta_set(obj, "home", home); + // tmp dir - system wide + s = getenv("TMPDIR"); + if (!s) s = getenv("TMP"); + if (!s) s = getenv("TEMPDIR"); + if (!s) s = getenv("TEMP"); + if (!s) s = "/tmp"; + efl_vpath_core_meta_set(obj, "tmp", s); + +#define ENV_HOME_SET(_env, _dir, _meta) \ + if (!(s = getenv(_env))) { \ + snprintf(buf, sizeof(buf), "%s/"_dir, home); s = buf; \ + } efl_vpath_core_meta_set(obj, _meta, s); + // $XDG_DATA_HOME defines the base directory relative to which user + // specific data files should be stored. If $XDG_DATA_HOME is either + // not set or empty, a default equal to $HOME/.local/share should be + // used. + ENV_HOME_SET("XDG_DATA_HOME", ".local/share", "data"); + // $XDG_CONFIG_HOME defines the base directory relative to which user + // specific configuration files should be stored. If $XDG_CONFIG_HOME + // is either not set or empty, a default equal to $HOME/.config should + // be used. + ENV_HOME_SET("XDG_CONFIG_HOME", ".config", "config"); + // $XDG_CACHE_HOME defines the base directory relative to which + // user specific non-essential data files should be stored. If + // $XDG_CACHE_HOME is either not set or empty, a default equal to + // $HOME/.cache should be used. + ENV_HOME_SET("XDG_CACHE_HOME", ".cache", "cache"); + // $XDG_RUNTIME_DIR defines the base directory relative to which + // user-specific non-essential runtime files and other file objects + // (such as sockets, named pipes, ...) should be stored. The + // directory MUST be owned by the user, and he MUST be the only one + // having read and write access to it. Its Unix access mode MUST + // be 0700. + if (!(s = getenv("XDG_RUNTIME_DIR"))) + { + struct stat st; + + // fallback - make ~/.run + snprintf(buf, sizeof(buf), "%s/.run", home); + mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR); + // if mkdir worked - use, otherwse use /tmp + if (stat(bufhome, &st) == 0) s = buf; + else + { + uid_t uid; + + // use /tmp/.run-UID if ~/ dir cant be made + s = (char *)efl_vpath_core_meta_get(obj, "tmp"); + uid = getuid(); + snprintf(buf, sizeof(buf), "%s/.run-%i", s, (int)uid); + mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR); + // if ok - use it or fall back to /tmp + if (stat(bufhome, &st) == 0) s = buf; + else s = (char *)efl_vpath_core_meta_get(obj, "tmp"); + } + } + efl_vpath_core_meta_set(obj, "run", s); + // https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ + // https://wiki.archlinux.org/index.php/Xdg_user_directories + // ^^^^ we don't handle: + // /etc/xdg/user-dirs.conf + // /etc/xdg/user-dirs.defaults + // (:config/user-dirs.conf + // (:config/user-dirs.defaults + + // $XDG_DESKTOP_DIR="$HOME/Desktop" + ENV_HOME_SET("XDG_DESKTOP_DIR", "Desktop", "desktop"); + // $XDG_DOCUMENTS_DIR="$HOME/Documents" + ENV_HOME_SET("XDG_DOCUMENTS_DIR", "Documents", "documents"); + // $XDG_DOWNLOAD_DIR="$HOME/Downloads" + ENV_HOME_SET("XDG_DOWNLOAD_DIR", "Downloads", "downloads"); + // $XDG_MUSIC_DIR="$HOME/Music" + ENV_HOME_SET("XDG_MUSIC_DIR", "Music", "music"); + // $XDG_PICTURES_DIR="$HOME/Pictures" + ENV_HOME_SET("XDG_PICTURES_DIR", "Pictures", "pictures"); + // $XDG_PUBLICSHARE_DIR="$HOME/Public" + ENV_HOME_SET("XDG_PUBLIC_DIR", "Public", "public"); + // $XDG_TEMPLATES_DIR="$HOME/.Templates" + ENV_HOME_SET("XDG_TEMPLATES_DIR", ".Templates", "templates"); + // $XDG_VIDEOS_DIR="$HOME/Videos" + ENV_HOME_SET("XDG_VIDEOS_DIR", "Videos", "videos"); + + // Add ~/Applications for user-installed apps + ENV_HOME_SET("E_APPS_DIR", "Applications", "apps"); + + // XXX: do the below ... later + // + // FHS FOR APP: + // app.dir = PREFIX + // app.bin = PREFIX/bin + // app.lib = PREFIX/lib + // app.data = PREFIX/share/APPNAME + // app.locale = PREFIX/share/locale + // + // XXX: figure out how to merge these with XDG/FHS? + // Tizen: + // App Dir Structure: + // bin Executable binary pathOwner: Read + // lib Library pathOwner: Read + // data Used to store private data of an application. + // res Used to read resource files that are delivered with the application package. + // shared Parent directory of the data, res, and trusted sub-directories. Files in this directory cannot be delivered with the application package.Owner: Read + // shared/data Used to share data with other applications. + // shared/res Used to share resources with other applications. The resource files are delivered with the application package. + // shared/trusted Used to share data with family of trusted applications. The family applications signed with the same certificate can access data in the shared/trusted directory. + // + // XXX: figure out how to merge these with XDG? + // Media/...vvv + // Images Used for Image data.Read and Write + // Sounds Used for Sound data. + // Videos Used for Video data. + // Cameras Used for Camera pictures. + // Downloads Used for Downloaded data. + // Music Used for Music data. + // Documents Used for Documents. + // Others Used for other types. + // System Ringtones Used for System default ringtones.Read + // + // $TZ_SYS_HOME=/home + // $TZ_SYS_DB=/var/db + // $TZ_SYS_CONFIG=/var/kdb + // $TZ_SYS_CONFIG_VOLATILE=/run/kdb + // $TZ_SYS_APP=/usr/apps + // $TZ_SYS_DESKTOP_APP=/usr/share/applications + // + // $TS_USER_DB=/.tizen/db + // $TZ_USER_CONFIG=/.tizen/kdb + // $TZ_USER_APP=/.tizen/apps + // $TZ_USER_DESKTOP_APP=/.tizen/desktop + // $TZ_USER_DOCUMENTS=/Documents + // $TZ_USER_PICTURES=/Pictures + // $TZ_USER_VIDEOS=/Videos + // $TZ_USER_MUSIC=/Music + // $TZ_USER_DOWNLOADS=/Downloads + // $TZ_USER_PUBLIC=/Public + return obj; +} + +EOLIAN static void +_efl_vpath_core_eo_base_destructor(Eo *obj, Efl_Vpath_Core_Data *pd) +{ + eina_hash_free(pd->meta); + pd->meta = NULL; + eina_spinlock_free(&(pd->lock)); + if (vpath_core == obj) vpath_core = NULL; + eo_destructor(eo_super(obj, MY_CLASS)); +} + +EOLIAN static Efl_Vpath_Core * +_efl_vpath_core_get(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED) +{ + // no locks here as we expect efl to init this early in main "thread" + if (!vpath_core) vpath_core = eo_add(EFL_VPATH_CORE_CLASS, NULL); + return vpath_core; +} + +EOLIAN static const char * +_efl_vpath_core_meta_get(Eo *obj EINA_UNUSED, Efl_Vpath_Core_Data *pd, const char *key) +{ + const char *meta; + + if (!key) return NULL; + eina_spinlock_take(&(pd->lock)); + meta = eina_hash_find(pd->meta, key); + eina_spinlock_release(&(pd->lock)); + return meta; +} + +EOLIAN static void +_efl_vpath_core_meta_set(Eo *obj EINA_UNUSED, Efl_Vpath_Core_Data *pd, const char *key, const char *path) +{ + if (!key) return; + eina_spinlock_take(&(pd->lock)); + if (path) eina_hash_add(pd->meta, key, eina_stringshare_add(path)); + else eina_hash_del(pd->meta, key, NULL); + eina_spinlock_release(&(pd->lock)); +} + +EOLIAN static Efl_Vpath_File * +_efl_vpath_core_efl_vpath_fetch(Eo *obj, Efl_Vpath_Core_Data *pd EINA_UNUSED, const char *path) +{ + Efl_Vpath_File_Core *file; + + file = eo_add(EFL_VPATH_FILE_CORE_CLASS, obj); + efl_vpath_file_path_set(file, path); + // XXX: implement parse of path then look up in hash if not just create + // object where path and result are the same and return that with + // path set and result set to resolved path - return obj handler calls + // "do" on object to get the result inside fetched or failed callback. + // if it's a url then we need a new classs that overrides the do and + // begins a fetch and on finish calls the event cb or when wait is called + if (path) + { + // /* <- full path + if (path[0] == '/') + { + efl_vpath_file_result_set(file, path); + return file; + } + // .* + if (path[0] == '.') + { + // .[/]* <- current dir relative + if ((path[1] == '/') || (path[1] == 0)) + { + efl_vpath_file_result_set(file, path); + return file; + } + // ..[/]* <- parent dir relative + if ((path[1] == '.') && ((path[2] == '/') || (path[2] == 0))) + { + efl_vpath_file_result_set(file, path); + return file; + } + } + // ~* ... + if (path[0] == '~') + { + // ~/ <- home directory + if (path[1] == '/') + { + char buf[PATH_MAX]; + const char *home = efl_vpath_core_meta_get(obj, "home"); + + if (home) + { + snprintf(buf, sizeof(buf), "%s%s", home, path + 1); + efl_vpath_file_result_set(file, buf); + return file; + } + } +#ifdef HAVE_GETPWENT + // ~username/ <- homedir of user "username" + else + { + const char *p; + struct passwd pwent, *pwent2 = NULL; + char *name, buf[PATH_MAX], pwbuf[8129]; + + for (p = path + 1; *p; p++) + { + if (*p =='/') break; + } + name = alloca(p - path); + strncpy(name, path + 1, p - path - 1); + name[p - path - 1] = 0; + if (!getpwnam_r(name, &pwent, pwbuf, sizeof(pwbuf), &pwent2)) + { + if ((pwent2) && (pwent.pw_dir)) + { + snprintf(buf, sizeof(buf), "%s%s", pwent.pw_dir, p); + efl_vpath_file_result_set(file, buf); + return file; + } + } + } +#endif /* HAVE_GETPWENT */ + } + // (:xxx/* ... <- meta has table + if ((path[0] == '(') && (path[1] == ':')) + { + const char *p, *meta; + char *name, buf[PATH_MAX]; + + for (p = path + 2; *p; p++) + { + if (*p =='/') break; + } + name = alloca(p - path); + strncpy(name, path + 2, p - path - 2); + name[p - path - 2] = 0; + eina_spinlock_take(&(pd->lock)); + meta = eina_hash_find(pd->meta, name); + eina_spinlock_release(&(pd->lock)); + if (meta) + { + snprintf(buf, sizeof(buf), "%s%s", meta, p); + efl_vpath_file_result_set(file, buf); + return file; + } + } + // file:/// <- local file path uri + // file://localhost/ <- local file path uri + // file://hostname/ <- remove file path uri + // XXX: %c4,%17,%fe etc. are bytes escaped + // http://www.ietf.org/rfc/rfc2396.txt + // http://www.ietf.org/rfc/rfc1738.txt + // http://equinox-project.org/spec/file-uri-spec.txt + // http://en.wikipedia.org/wiki/File_URI_scheme + } + return file; +} + +#include "interfaces/efl_vpath_core.eo.c" diff --git a/src/lib/efl/interfaces/efl_vpath_core.eo b/src/lib/efl/interfaces/efl_vpath_core.eo new file mode 100644 index 0000000..d9910a4 --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_core.eo @@ -0,0 +1,31 @@ +class Efl.Vpath.Core (Eo.Base, Efl.Vpath) +{ + [[ Core EFL implementation of a Vpath system ]] + legacy_prefix: null; + eo_prefix: efl_vpath_core; + methods { + get @class { + [[ This gets the global EFL Core Vpath class - only 1 - singleton ]] + return: Efl.Vpath.Core *; [[ Get the singleton core vpath ]] + } + meta_set { + [[ A Meta key is a mapping from a virtual path to a real one ]] + params { + key: const(char)*; [[ The magic path key being looked up ]] + path: const(char)*; [[ The real path the key maps to ]] + } + } + meta_get { + [[ This returns the real path set for a Meta key, or NULL if not ]] + params { + key: const(char)*; [[ The magic path key being looked up ]] + } + return: const(char)*; + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Vpath.fetch; + } +} diff --git a/src/lib/efl/interfaces/efl_vpath_file.c b/src/lib/efl/interfaces/efl_vpath_file.c new file mode 100644 index 0000000..e62ae1e --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_file.c @@ -0,0 +1,61 @@ +#include "config.h" +#include "Efl.h" + +#define MY_CLASS EFL_VPATH_FILE_CLASS + +typedef struct _Efl_Vpath_File_Data Efl_Vpath_File_Data; + +struct _Efl_Vpath_File_Data +{ + const char *path; + const char *result; + Eina_Bool called : 1; +}; + +EOLIAN static void +_efl_vpath_file_path_set(Eo *obj EINA_UNUSED, Efl_Vpath_File_Data *pd, const char *path) +{ + eina_stringshare_replace(&(pd->path), path); +} + +EOLIAN static const char * +_efl_vpath_file_path_get(Eo *obj EINA_UNUSED, Efl_Vpath_File_Data *pd) +{ + return pd->path; +} + +EOLIAN static void +_efl_vpath_file_result_set(Eo *obj EINA_UNUSED, Efl_Vpath_File_Data *pd, const char *path) +{ + eina_stringshare_replace(&(pd->result), path); +} + +EOLIAN static const char * +_efl_vpath_file_result_get(Eo *obj EINA_UNUSED, Efl_Vpath_File_Data *pd) +{ + return pd->result; +} + +EOLIAN static Eina_Bool +_efl_vpath_file_do(Eo *obj EINA_UNUSED, Efl_Vpath_File_Data *pd) +{ + if (pd->called) return EINA_FALSE; + pd->called = EINA_TRUE; + eo_event_callback_call(obj, EFL_VPATH_FILE_EVENT_FETCHED, NULL); + return EINA_TRUE; +} + +EOLIAN static void +_efl_vpath_file_wait(Eo *obj EINA_UNUSED, Efl_Vpath_File_Data *pd EINA_UNUSED) +{ + if (!pd->called) + { + pd->called = EINA_TRUE; + if (pd->result) + eo_event_callback_call(obj, EFL_VPATH_FILE_EVENT_FETCHED, NULL); + else + eo_event_callback_call(obj, EFL_VPATH_FILE_EVENT_FAILED, NULL); + } +} + +#include "interfaces/efl_vpath_file.eo.c" diff --git a/src/lib/efl/interfaces/efl_vpath_file.eo b/src/lib/efl/interfaces/efl_vpath_file.eo new file mode 100644 index 0000000..7cdb7ce --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_file.eo @@ -0,0 +1,46 @@ +class Efl.Vpath_File (Eo.Base) +{ + [[ VPath File is an actual representation of a downloaded/mapped vpath file + + Keep this object around for as long as you need to use the file as it + may have been downloaded and kept as a local temporary file and + deletion may remove it. + + When you have a Vpath File object for the first time, call the do() + method on it to actually begin/do the mapping. From here the + fetched or failed event callbacks will be called, inside of which or + afterwards you can fetch the resulting local file path by getting the + result property. + ]] + legacy_prefix: null; + eo_prefix: efl_vpath_file; + methods { + @property path { + [[ The original source path provided to lookup/fetch from ]] + set {} + get {} + values { + path: const(char)*; [[ The input virtual path to a file ]] + } + } + @property result { + [[ The resulting real local file path to open/read ]] + set {} + get {} + values { + path: const(char)*; [[ The resulting destination file ]] + } + } + do { + [[ Actually begin the resolving here - emit event now or do later ]] + return: bool; [[ Result callback already called ]] + } + wait { + [[ If not fteched yet, wait until it is and call result cb ]] + } + } + events { + fetched; [[ File successfully mapped/fetched ]] + failed; [[ File fetch or mapping failed ]] + } +} diff --git a/src/lib/efl/interfaces/efl_vpath_file_core.c b/src/lib/efl/interfaces/efl_vpath_file_core.c new file mode 100644 index 0000000..30c6e08 --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_file_core.c @@ -0,0 +1,66 @@ +#include "config.h" +#include "Efl.h" + +#define MY_CLASS EFL_VPATH_FILE_CORE_CLASS + +typedef struct _Efl_Vpath_File_Core_Data Efl_Vpath_File_Core_Data; + +struct _Efl_Vpath_File_Core_Data +{ + int dummy; +}; + +EOLIAN static Eo_Base * +_efl_vpath_file_core_eo_base_constructor(Eo *obj, Efl_Vpath_File_Core_Data *pd) +{ + obj = eo_constructor(eo_super(obj, MY_CLASS)); + pd->dummy = 0; + return obj; +} + +EOLIAN static void +_efl_vpath_file_core_eo_base_destructor(Eo *obj, Efl_Vpath_File_Core_Data *pd) +{ + pd->dummy = 0; + eo_destructor(eo_super(obj, MY_CLASS)); +} + +EOLIAN static Eina_Bool +_efl_vpath_file_core_efl_vpath_file_do(Eo *obj, Efl_Vpath_File_Core_Data *pd) +{ + const char *path; + + if (efl_vpath_file_result_get(obj)) + return efl_vpath_file_do(eo_super(obj, MY_CLASS)); + // vpath core didn't find a match, so it'ss likely a protocol like + // http:// etc. etc. so deal with that here + path = efl_vpath_file_path_get(obj); + if (path) + { + if ((!strncasecmp(path, "http://", 7)) || + (!strncasecmp(path, "https://", 8))) + { + // XXX: handle urls --- need a loop object + } + } + // ... + pd->dummy = 0; + + // not a magic path - just set result to path + efl_vpath_file_result_set(obj, efl_vpath_file_path_get(obj)); + return efl_vpath_file_do(eo_super(obj, MY_CLASS)); +} + +EOLIAN static void +_efl_vpath_file_core_efl_vpath_file_wait(Eo *obj, Efl_Vpath_File_Core_Data *pd) +{ + if (efl_vpath_file_result_get(obj)) + { + efl_vpath_file_do(eo_super(obj, MY_CLASS)); + return; + } + pd->dummy = 0; + // XXX: not found yet, so do what is necessary +} + +#include "interfaces/efl_vpath_file_core.eo.c" diff --git a/src/lib/efl/interfaces/efl_vpath_file_core.eo b/src/lib/efl/interfaces/efl_vpath_file_core.eo new file mode 100644 index 0000000..a77b4c9 --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_file_core.eo @@ -0,0 +1,12 @@ +class Efl.Vpath_File.Core (Efl.Vpath_File) +{ + [[ Core EFL implementation of a Vpath File ]] + legacy_prefix: null; + eo_prefix: efl_vpath_file_core; + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Vpath_File.do; + Efl.Vpath_File.wait; + } +} diff --git a/src/lib/efl/interfaces/efl_vpath_manager.c b/src/lib/efl/interfaces/efl_vpath_manager.c new file mode 100644 index 0000000..fdf5e9a --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_manager.c @@ -0,0 +1,89 @@ +#include "config.h" +#include "Efl.h" + +#define MY_CLASS EFL_VPATH_MANAGER_CLASS + +typedef struct _Efl_Vpath_Manager_Data Efl_Vpath_Manager_Data; +typedef struct _Efl_Vpath_Manager_Entry Efl_Vpath_Manager_Entry; + +struct _Efl_Vpath_Manager_Data +{ + Eina_List *list; +}; + +struct _Efl_Vpath_Manager_Entry +{ + Efl_Vpath *vpath; + int priority; +}; + +static Efl_Vpath_Manager_Data vpath_manager = +{ + NULL +}; + +EOLIAN static Efl_Vpath_File * +_efl_vpath_manager_fetch(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED, const char *path) +{ + Efl_Vpath_Manager_Entry *entry; + Eina_List *l; + Efl_Vpath_File *file; + + EINA_LIST_FOREACH(vpath_manager.list, l, entry) + { + file = efl_vpath_fetch(entry->vpath, path); + if (file) return file; + } + file = eo_add(EFL_VPATH_FILE_CLASS, NULL); + if (file) + { + efl_vpath_file_path_set(file, path); + efl_vpath_file_result_set(file, path); + } + return file; +} + +static int +_register_sort_cb(Efl_Vpath_Manager_Entry *e1, Efl_Vpath_Manager_Entry *e2) +{ + // sort higher numbers first in list + return (e2->priority - e1->priority); +} + +static Eina_Bool +_cb_vpath_del(void *data, const Eo_Event *event) +{ + efl_vpath_manager_unregister(data, event->obj); + eo_event_callback_del(event->obj, EO_BASE_EVENT_DEL, _cb_vpath_del, data); + return EINA_TRUE; +} + +EOLIAN static void +_efl_vpath_manager_register(Eo *obj, void *pd EINA_UNUSED, int priority, Efl_Vpath *vpath) +{ + Efl_Vpath_Manager_Entry *entry = malloc(sizeof(Efl_Vpath_Manager_Entry)); + entry->vpath = vpath; + entry->priority = priority; + eo_event_callback_add(vpath, EO_BASE_EVENT_DEL, _cb_vpath_del, obj); + vpath_manager.list = eina_list_sorted_insert + (vpath_manager.list, EINA_COMPARE_CB(_register_sort_cb), entry); +} + +EOLIAN static void +_efl_vpath_manager_unregister(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED, Efl_Vpath *vpath) +{ + Efl_Vpath_Manager_Entry *entry; + Eina_List *l; + + EINA_LIST_FOREACH(vpath_manager.list, l, entry) + { + if (entry->vpath == vpath) + { + vpath_manager.list = eina_list_remove_list(vpath_manager.list, l); + free(entry); + return; + } + } +} + +#include "interfaces/efl_vpath_manager.eo.c" diff --git a/src/lib/efl/interfaces/efl_vpath_manager.eo b/src/lib/efl/interfaces/efl_vpath_manager.eo new file mode 100644 index 0000000..532f631 --- /dev/null +++ b/src/lib/efl/interfaces/efl_vpath_manager.eo @@ -0,0 +1,28 @@ +class Efl.Vpath_Manager (Eo.Base) +{ + [[ Vpath Manager manages multiple VPath objects that remap/download ]] + legacy_prefix: null; + eo_prefix: efl_vpath_manager; + methods { + fetch @class { + [[ This class function fetches a Vpath File given an input path ]] + params { + path: const(char)*; [[ The input virtual file path to fetch ]] + } + return: own(Efl.Vpath_File *); [[ An object representing the file ]] + } + register @class { + [[ ]] + params { + priority: int; [[ Search order - higher values tired first ]] + vpath: Efl.Vpath * @nonull; [[ A Vpath implementation object ]] + } + } + unregister @class { + [[ ]] + params { + vpath: Efl.Vpath * @nonull; [[ A Vpath implementation object ]] + } + } + } +}