#include "e.h"
#ifdef __linux__
# include <sys/prctl.h>
#endif

#include "valgrind.h"

#define MAX_LEVEL 80

#define TS_DO
#ifdef TS_DO
# define TS(x)                                                    \
  {                                                               \
     t1 = ecore_time_unix_get();                                  \
     printf("ESTART: %1.5f [%1.5f] - %s\n", t1 - t0, t1 - t2, x); \
     t2 = t1;                                                     \
  }
static double t0, t1, t2;
#else
# define TS(x)
#endif

/*
 * i need to make more use of these when i'm baffled as to when something is
 * up. other hooks:
 *
 *      void *(*__malloc_hook)(size_t size, const void *caller);
 *
 *      void *(*__realloc_hook)(void *ptr, size_t size, const void *caller);
 *
 *      void *(*__memalign_hook)(size_t alignment, size_t size,
 *                               const void *caller);
 *
 *      void (*__free_hook)(void *ptr, const void *caller);
 *
 *      void (*__malloc_initialize_hook)(void);
 *
 *      void (*__after_morecore_hook)(void);
 *

   static void my_init_hook(void);
   static void my_free_hook(void *p, const void *caller);

   static void (*old_free_hook)(void *ptr, const void *caller) = NULL;
   void (*__free_hook)(void *ptr, const void *caller);

   void (*__malloc_initialize_hook) (void) = my_init_hook;
   static void
   my_init_hook(void)
   {
   old_free_hook = __free_hook;
   __free_hook = my_free_hook;
   }

   //void *magicfree = NULL;

   static void
   my_free_hook(void *p, const void *caller)
   {
   __free_hook = old_free_hook;
   //   if ((p) && (p == magicfree))
   //     {
   //	printf("CAUGHT!!!!! %p ...\n", p);
   //	abort();
   //     }
   free(p);
   __free_hook = my_free_hook;
   }
 */

/* local function prototypes */
static void      _e_main_shutdown(int errcode);
static void      _e_main_shutdown_push(int (*func)(void));
static void      _e_main_parse_arguments(int argc, char **argv);
static Eina_Bool _e_main_cb_signal_exit(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev EINA_UNUSED);
static Eina_Bool _e_main_cb_signal_hup(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev EINA_UNUSED);
static Eina_Bool _e_main_cb_signal_user(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev);
static int       _e_main_dirs_init(void);
static int       _e_main_dirs_shutdown(void);
static int       _e_main_path_init(void);
static int       _e_main_path_shutdown(void);
static int       _e_main_screens_init(void);
static int       _e_main_screens_shutdown(void);
static void      _e_main_desk_save(void);
static void      _e_main_desk_restore(void);
static void      _e_main_efreet_paths_init(void);
static void      _e_main_modules_load(Eina_Bool safe_mode);
static Eina_Bool _e_main_cb_idle_before(void *data EINA_UNUSED);
static Eina_Bool _e_main_cb_idle_after(void *data EINA_UNUSED);
static Eina_Bool _e_main_cb_startup_fake_end(void *data EINA_UNUSED);

/* local variables */
static Eina_Bool really_know = EINA_FALSE;
static Eina_Bool locked = EINA_FALSE;
static Eina_Bool inloop = EINA_FALSE;
static jmp_buf x_fatal_buff;

static int _e_main_lvl = 0;
static int(*_e_main_shutdown_func[MAX_LEVEL]) (void);

static Ecore_Idle_Enterer *_idle_before = NULL;
static Ecore_Idle_Enterer *_idle_after = NULL;

static Ecore_Event_Handler *mod_init_end = NULL;

/* external variables */
E_API Eina_Bool e_precache_end = EINA_FALSE;
E_API Eina_Bool x_fatal = EINA_FALSE;
E_API Eina_Bool good = EINA_FALSE;
E_API Eina_Bool evil = EINA_FALSE;
E_API Eina_Bool starting = EINA_TRUE;
E_API Eina_Bool stopping = EINA_FALSE;
E_API Eina_Bool restart = EINA_FALSE;
E_API Eina_Bool e_nopause = EINA_FALSE;
E_API Eina_Bool after_restart = EINA_FALSE;
E_API Eina_Bool e_main_loop_running = EINA_FALSE;
EINTERN const char *e_first_frame = NULL;
EINTERN double e_first_frame_start_time = -1;

static Eina_Bool
_xdg_check_str(const char *env, const char *str)
{
   const char *p;
   size_t len;

   len = strlen(str);
   for (p = strstr(env, str); p; p++, p = strstr(p, str))
     {
        if ((!p[len]) || (p[len] == ':')) return EINA_TRUE;
     }
   return EINA_FALSE;
}

static void
_xdg_data_dirs_augment(void)
{
   const char *s;
   const char *p = e_prefix_get();
   char newpath[PATH_MAX], buf[PATH_MAX + PATH_MAX + 200];

   if (!p) return;

   s = getenv("XDG_DATA_DIRS");
   if (s)
     {
        Eina_Bool pfxdata, pfx;

        pfxdata = !_xdg_check_str(s, e_prefix_data_get());
        snprintf(newpath, sizeof(newpath), "%s/share", p);
        pfx = !_xdg_check_str(s, newpath);
        if (pfxdata || pfx)
          {
             snprintf(buf, sizeof(buf), "%s%s%s%s%s",
               pfxdata ? e_prefix_data_get() : "",
               pfxdata ? ":" : "",
               pfx ? newpath : "",
               pfx ? ":" : "",
               s);
             e_util_env_set("XDG_DATA_DIRS", buf);
          }
     }
   else
     {
        snprintf(buf, sizeof(buf), "%s:%s/share:/usr/local/share:/usr/share", e_prefix_data_get(), p);
        e_util_env_set("XDG_DATA_DIRS", buf);
     }

   s = getenv("XDG_CONFIG_DIRS");
   snprintf(newpath, sizeof(newpath), "%s/etc/xdg", p);
   if (s)
     {
        if (!_xdg_check_str(s, newpath))
          {
             snprintf(buf, sizeof(buf), "%s:%s", newpath, s);
             e_util_env_set("XDG_CONFIG_DIRS", buf);
          }
     }
   else
     {
        snprintf(buf, sizeof(buf), "%s:/etc/xdg", newpath);
        e_util_env_set("XDG_CONFIG_DIRS", buf);
     }

   // N.B. XDG_RUNTIME_DIR is taken care of in e_start

   /* set menu prefix so we get our e menu */
   if (!getenv("XDG_MENU_PREFIX"))
     {
        e_util_env_set("XDG_MENU_PREFIX", "e-");
     }
}

static Eina_Bool
_precache_file(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data EINA_UNUSED, void *fdata)
{
   Eina_List **precache_files = fdata;
   *precache_files = eina_list_append(*precache_files, strdup(key));
   return EINA_TRUE;
}

static void *
_precache_thread(void *data, Eina_Thread thr EINA_UNUSED)
{
   Eina_List *precache_files = data;
   char *path;
   double t = ecore_time_get();
   unsigned int sum = 0;
   unsigned int reads = 0;


   printf("PRECACHE: BEGIN\n");
   EINA_LIST_FREE(precache_files, path)
     {
        double tt = ecore_time_get();
        FILE *f = fopen(path, "r");
        unsigned char buf[4096];

        if (f)
          {
             size_t sz;

             while ((sz = fread(buf, 1, sizeof(buf), f)) > 0)
               {
                  reads ++;
                  sum = ((sum << 1) ^ buf[0]) + reads;
               }
             fclose(f);
          }
        printf("PRECACHE: [%1.5f] [%s] DONE\n", ecore_time_get() - tt, path);
        free(path);
     }
   printf("PRECACHE: TOTAL [%1.5f]\n", ecore_time_get() - t);
   printf("PRECACHE: SUM=%08x, READS=%i\n", sum, reads);
   return NULL;
}

/* externally accessible functions */
int
main(int argc, char **argv)
{
   Eina_Bool nostartup = EINA_FALSE;
   Eina_Bool safe_mode = EINA_FALSE;
   Eina_Bool waslocked = EINA_FALSE;
   Eina_Stringshare *strshare;
   char *s = NULL;
   struct sigaction action;

#ifdef __linux__
# ifdef PR_SET_PTRACER
#  ifdef PR_SET_PTRACER_ANY
   prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
#  endif
# endif
#endif

#ifdef TS_DO
   t0 = t1 = t2 = ecore_time_unix_get();
#endif
   TS("Begin Startup");

   /* trap deadly bug signals and allow some form of sane recovery */
   /* or ability to gdb attach and debug at this point - better than your */
   /* wm/desktop vanishing and not knowing what happened */
   if (!getenv("NOTIFY_SOCKET"))
     {
        TS("Signal Trap");
        action.sa_sigaction = e_sigseg_act;
	action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
	sigemptyset(&action.sa_mask);
	sigaction(SIGSEGV, &action, NULL);

	action.sa_sigaction = e_sigill_act;
	action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
	sigemptyset(&action.sa_mask);
	sigaction(SIGILL, &action, NULL);

	action.sa_sigaction = e_sigfpe_act;
	action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
	sigemptyset(&action.sa_mask);
	sigaction(SIGFPE, &action, NULL);

#ifndef HAVE_WAYLAND_ONLY
	action.sa_sigaction = e_sigbus_act;
	action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
	sigemptyset(&action.sa_mask);
	sigaction(SIGBUS, &action, NULL);
#endif

	action.sa_sigaction = e_sigabrt_act;
	action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
	sigemptyset(&action.sa_mask);
	sigaction(SIGABRT, &action, NULL);
	TS("Signal Trap Done");
     }

   TS("Eina Init");
   if (!eina_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Eina!\n"));
        _e_main_shutdown(101);
     }
   eina_file_statgen_enable();

   _e_main_shutdown_push(eina_shutdown);

   if (!e_log_init())
     {
        e_error_message_show(_("Enlightenment could not create a logging domain!\n"));
        _e_main_shutdown(101);
     }
#ifdef TS_DO
#undef TS
# define TS(x)                                                    \
  {                                                               \
     t1 = ecore_time_unix_get();                                  \
     printf("ESTART: %1.5f [%1.5f] - %s\n", t1 - t0, t1 - t2, x); \
     t2 = t1;                                                     \
  }
#endif
   TS("Eina Init Done");
   _e_main_shutdown_push(e_log_shutdown);

   TS("Determine Prefix");
   if (!e_prefix_determine(argv[0]))
     {
        fprintf(stderr,
                "ERROR: Enlightenment cannot determine it's installed\n"
                "       prefix from the system or argv[0].\n"
                "       This is because it is not on Linux AND has been\n"
                "       executed strangely. This is unusual.\n");
     }
   TS("Determine Prefix Done");

   /* Eio's eio_init internally calls efreet_init. Set XDG_MENU_PREFIX here      */
   /* else efreet's efreet_menu_prefix symbol is set erroneously during eio_init. */
   _xdg_data_dirs_augment();

   /* for debugging by redirecting stdout of e to a log file to tail */
   setvbuf(stdout, NULL, _IONBF, 0);

   TS("Environment Variables");
   if (getenv("E_RESTART")) after_restart = EINA_TRUE;
   if (getenv("DESKTOP_STARTUP_ID"))
     e_util_env_set("DESKTOP_STARTUP_ID", NULL);
   e_util_env_set("E_RESTART_OK", NULL);
   e_util_env_set("PANTS", "ON");
   e_util_env_set("DESKTOP", "Enlightenment");
   e_util_env_set("XDG_CURRENT_DESKTOP", "Enlightenment");
   if (getenv("E_ALERT_FONT_DIR"))
     e_util_env_set("E_ALERT_FONT_DIR", NULL);
   if (getenv("E_ALERT_SYSTEM_BIN"))
     e_util_env_set("E_ALERT_SYSTEM_BIN", NULL);
   strshare = eina_stringshare_printf("%s/enlightenment_askpass",
                                      e_prefix_bin_get());
   if (strshare)
     {
        e_util_env_set("SUDO_ASKPASS", strshare);
        e_util_env_set("SSH_ASKPASS", strshare);
        eina_stringshare_del(strshare);
     }
   TS("Environment Variables Done");

   TS("Parse Arguments");
   _e_main_parse_arguments(argc, argv);
   TS("Parse Arguments Done");

   /*** Initialize Core EFL Libraries We Need ***/

   TS("Eet Init");
   if (!eet_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Eet!\n"));
        _e_main_shutdown(101);
     }
   TS("Eet Init Done");
   _e_main_shutdown_push(eet_shutdown);

   TS("Ecore Init");
   if (!ecore_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Ecore!\n"));
        _e_main_shutdown(101);
     }
   TS("Ecore Init Done");
   _e_main_shutdown_push(ecore_shutdown);

   TS("E Comp Canvas Intercept Init");
   e_comp_canvas_intercept();
   TS("E Comp Canvas Intercept Init Done");

   e_first_frame = getenv("E_FIRST_FRAME");
   if (e_first_frame && e_first_frame[0])
     e_first_frame_start_time = ecore_time_get();
   else
     e_first_frame = NULL;

   TS("EFX Init");
   if (!e_efx_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize EFX!\n"));
        _e_main_shutdown(101);
     }
   TS("EFX Init Done");
   _e_main_shutdown_push((void*)e_efx_shutdown);

   TS("EIO Init");
   if (!eio_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize EIO!\n"));
        _e_main_shutdown(101);
     }
   TS("EIO Init Done");
   _e_main_shutdown_push(eio_shutdown);

   ecore_app_args_set(argc, (const char **)argv);

   TS("Ecore Event Handlers");
   if (!ecore_event_handler_add(ECORE_EVENT_SIGNAL_EXIT,
                                _e_main_cb_signal_exit, NULL))
     {
        e_error_message_show(_("Enlightenment cannot set up an exit signal handler.\n"
                               "Perhaps you are out of memory?"));
        _e_main_shutdown(101);
     }
   if (!ecore_event_handler_add(ECORE_EVENT_SIGNAL_HUP,
                                _e_main_cb_signal_hup, NULL))
     {
        e_error_message_show(_("Enlightenment cannot set up a HUP signal handler.\n"
                               "Perhaps you are out of memory?"));
        _e_main_shutdown(101);
     }
   if (!ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER,
                                _e_main_cb_signal_user, NULL))
     {
        e_error_message_show(_("Enlightenment cannot set up a USER signal handler.\n"
                               "Perhaps you are out of memory?"));
        _e_main_shutdown(101);
     }
   TS("Ecore Event Handlers Done");

   TS("Ecore_File Init");
   if (!ecore_file_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Ecore_File!\n"));
        _e_main_shutdown(101);
     }
   TS("Ecore_File Init Done");
   _e_main_shutdown_push(ecore_file_shutdown);

   TS("Ecore_Con Init");
   if (!ecore_con_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Ecore_Con!\n"));
        _e_main_shutdown(101);
     }
   TS("Ecore_Con Init Done");
   _e_main_shutdown_push(ecore_con_shutdown);

   TS("Ecore_Ipc Init");
   if (!ecore_ipc_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Ecore_Ipc!\n"));
        _e_main_shutdown(101);
     }
   TS("Ecore_Ipc Init Done");
   _e_main_shutdown_push(ecore_ipc_shutdown);

   _idle_before = ecore_idle_enterer_before_add(_e_main_cb_idle_before, NULL);

   TS("Ecore_Evas Init");
   if (!ecore_evas_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Ecore_Evas!\n"));
        _e_main_shutdown(101);
     }
   TS("Ecore_Evas Init Done");
//   _e_main_shutdown_push(ecore_evas_shutdown);

   TS("Elementary Init");
   if (!elm_init(argc, argv))
     {
        e_error_message_show(_("Enlightenment cannot initialize Elementary!\n"));
        _e_main_shutdown(101);
     }
   TS("Elementary Init Done");
   //_e_main_shutdown_push(elm_shutdown);

   TS("Emotion Init");
   if (!emotion_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize Emotion!\n"));
        _e_main_shutdown(101);
     }
   TS("Emotion Init Done");
   /* triggers event flush: do not call */
   //_e_main_shutdown_push((void *)emotion_shutdown);

   /* e doesn't sync to compositor - it should be one */
   ecore_evas_app_comp_sync_set(0);

   TS("Ecore_Evas Engine Check");
#ifdef HAVE_WAYLAND_ONLY
   if (!ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM))
     {
        e_error_message_show(_("Enlightenment found ecore_evas doesn't support the Wayland SHM\n"
                               "rendering in Evas. Please check your installation of Evas and\n"
                                "Ecore and check they support the Wayland SHM rendering engine."));
        _e_main_shutdown(101);
     }
#else
   if (!ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_SOFTWARE_XCB))
     {
        if (!ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_SOFTWARE_XLIB))
          {
             e_error_message_show(_("Enlightenment found ecore_evas doesn't support the Software X11\n"
                                    "rendering in Evas. Please check your installation of Evas and\n"
                                    "Ecore and check they support the Software X11 rendering engine."));
             _e_main_shutdown(101);
          }
     }
#endif
   if (!ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_SOFTWARE_BUFFER))
     {
        e_error_message_show(_("Enlightenment found ecore_evas doesn't support the Software Buffer\n"
                               "rendering in Evas. Please check your installation of Evas and\n"
                               "Ecore and check they support the Software Buffer rendering engine."));
        _e_main_shutdown(101);
     }
   TS("Ecore_Evas Engine Check Done");

   edje_freeze();

   /*** Initialize E Subsystems We Need ***/

   TS("E Intl Init");
   if (!e_intl_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize E_Intl!\n"));
        _e_main_shutdown(101);
     }
   TS("E Intl Init Done");
   _e_main_shutdown_push(e_intl_shutdown);

#ifndef HAVE_WAYLAND_ONLY
   /* init white box of death alert */
   TS("E_Alert Init");
   if (!e_alert_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize its emergency alert system.\n"
                               "Have you set your DISPLAY variable?"));
        _e_main_shutdown(101);
     }
   TS("E_Alert Init Done");
   _e_main_shutdown_push(e_alert_shutdown);
#endif

   TS("E Directories Init");
   /* setup directories we will be using for configurations storage etc. */
   if (!_e_main_dirs_init())
     {
        e_error_message_show(_("Enlightenment cannot create directories in your home directory.\n"
                               "Perhaps you have no home directory or the disk is full?"));
        _e_main_shutdown(101);
     }
   TS("E Directories Init Done");
   _e_main_shutdown_push(_e_main_dirs_shutdown);

   TS("E_Filereg Init");
   if (!e_filereg_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its file registry system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Filereg Init Done");
   _e_main_shutdown_push(e_filereg_shutdown);

   TS("E_Config Init");
   if (!e_config_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its config system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Config Init Done");
   _e_main_shutdown_push(e_config_shutdown);

   if (e_config->xsettings.match_e17_theme)
     {

        /* KDE5 applications don't understand anything other then gnome or kde     */
        /* They expect everyone else to set QT_QPA_PLATFORMTHEME to tell them how  */
        /* to theme there apps otherwise they use a fallback mode which results in */
        /* missing icons and a inability to change the appearance of applications  */
        /* see https://bugzilla.suse.com/show_bug.cgi?id=920792 for more info.     */
        /* There are two sensible defaults for this variable, "kde" which will     */
        /* make apps appear the same as they do if they are run in kde. and gtk2   */
        /* which will make kde applications follow the gtk/gnome theme, we have    */
        /* decided on choosing gtk2 as it means that kde/qt apps will follow the   */
        /* app and icon theme set in the enlightenment settings dialog. Some users */
        /* who wish to use Qt apps without any gnome or gtk usage may choose to    */
        /* install qt5ct and overwrite this variable with qt5ct and use that to    */
        /* configure there Qt5 applications.                                       */
        e_util_env_set("QT_QPA_PLATFORMTHEME", "gtk2");
        e_util_env_set("QT_STYLE_OVERRIDE", "gtk2");
     }
   // make fonts NOT BLURRY. after 35 (v 38, v40 of interpreter) fonts become
   // horizontally blurry - they seemingly want the interpreter for hinting to
   // sub-pixel position on sub-pixel boundaries. this ends up with blurry
   // horizontal positioning/hinting that is on a sub-pixel. yes - this
   // requires logging out and logging in to get e to not set this env var.
   // for now that's good enough. the aim is to get everyone to render the
   // same way and this does it. efl, gtk, qt, chromium, firtefox, ...
   if (e_config->scale.set_xapp_dpi)
     {
        s = getenv("FREETYPE_PROPERTIES");
        if (!s)
          {
             e_util_env_set("FREETYPE_PROPERTIES", "truetype:interpreter-version=35");
          }
     }

   TS("E_Env Init");
   if (!e_env_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its environment.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Env Init Done");
   _e_main_shutdown_push(e_env_shutdown);

   efreet_desktop_environment_set(e_config->desktop_environment);
   e_util_env_set("E_ICON_THEME", e_config->icon_theme);
   if (e_config->icon_theme) elm_config_icon_theme_set(e_config->icon_theme);
   ecore_exe_run_priority_set(e_config->priority);
   locked |= e_config->desklock_start_locked;

   s = getenv("E_DESKLOCK_LOCKED");
   if ((s) && (!strcmp(s, "locked"))) waslocked = EINA_TRUE;
   putenv("E_DESKLOCK_LOCKED=");

   TS("E Paths Init");
   if (!_e_main_path_init())
     {
        e_error_message_show(_("Enlightenment cannot set up paths for finding files.\n"
                               "Perhaps you are out of memory?"));
        _e_main_shutdown(101);
     }
   TS("E Paths Init Done");
   _e_main_shutdown_push(_e_main_path_shutdown);

   TS("E_Precache");
   if (!getenv("E_NO_PRECACHE"))
     {
        const Eina_List *l;
        const Eina_List *theme_items = elm_theme_list_get(NULL);
        Eina_List *precache_files = NULL;
        Eina_Hash *files = eina_hash_string_superfast_new(NULL);
        Eina_Thread thr;
        int scr, dx, dy;
        // find all theme edj files to precache
        EINA_LIST_FOREACH(theme_items, l, s)
          {
             Eina_Bool search = EINA_FALSE;
             char *path = elm_theme_list_item_path_get(s, &search);
             if (path)
               {
                  eina_hash_del(files, path, files);
                  eina_hash_add(files, path, files);
                  free(path);
               }
          }
        // go over the first 4 screens and all desks and find possible
        // background files and add them to our hash to precache
        for (scr = 0; scr < 4; scr++)
          {
             for (dy = 0; dy < e_config->zone_desks_y_count; dy++)
               {
                  for (dx = 0; dx < e_config->zone_desks_x_count; dx++)
                    {
                       const char *bgfile = e_bg_file_get(scr, dx, dy);
                       eina_hash_del(files, bgfile, files);
                       eina_hash_add(files, bgfile, files);
                       eina_stringshare_del(bgfile);
                    }
               }
          }
        eina_hash_foreach(files, _precache_file, &precache_files);
        eina_hash_free(files);
        printf("PRECACHE: SPAWN\n");
        if (!eina_thread_create(&thr, EINA_THREAD_BACKGROUND, -1,
                                _precache_thread, precache_files))
          {
             ERR("Can't spawn file precache thread");
          }
     }
   TS("E_Precache Done");

   TS("E_Ipc Init");
   if (!e_ipc_init()) _e_main_shutdown(101);
   TS("E_Ipc Init Done");
   _e_main_shutdown_push(e_ipc_shutdown);

   TS("E_State Init");
   e_state_init();
   TS("E_State Init Done");
   _e_main_shutdown_push(e_state_shutdown);

   TS("E_Font Init");
   if (!e_font_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its font system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Font Init Done");
   _e_main_shutdown_push(e_font_shutdown);

   TS("E_Font Apply");
   e_font_apply();
   TS("E_Font Apply Done");

   TS("E_Theme Init");
   if (!e_theme_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its theme system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Theme Init Done");
   _e_main_shutdown_push(e_theme_shutdown);

   TS("E_Moveresize Init");
   e_moveresize_init();
   TS("E_Moveresize Init Done");
   _e_main_shutdown_push(e_moveresize_shutdown);

   TS("E_Msgbus Init");
   if (!e_msgbus_init())
     {
        if (!getenv("E_NO_DBUS_SESSION"))
          {
             e_error_message_show(_("Enlightenment cannot create a dbus session connection.\n"
                                    "At best this will break many things, at worst it will hard lock your machine.\n"
                                    "If you're sure you know what you're doing, export E_NO_DBUS_SESSION=1"));
             _e_main_shutdown(101);
          }
     }
   else
     _e_main_shutdown_push(e_msgbus_shutdown);
   TS("E_Msgbus Init Done");

   TS("Efreet Init");
   if (!efreet_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the FDO desktop system.\n"
                               "Perhaps you lack permissions on ~/.cache/efreet or are\n"
                               "out of memory or disk space?"));
        _e_main_shutdown(101);
     }
   TS("Efreet Init Done");
   _e_main_shutdown_push(efreet_shutdown);

   TS("E_Intl Post Init");
   if (!e_intl_post_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its intl system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Intl Post Init Done");
   _e_main_shutdown_push(e_intl_post_shutdown);

   TS("E_Configure Init");
   e_configure_init();
   TS("E_Configure Init Done");

   e_screensaver_preinit();

   TS("E_Actions Init");
   if (!e_actions_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its actions system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Actions Init Done");
   _e_main_shutdown_push(e_actions_shutdown);

   /* these just add event handlers and can't fail
    * timestamping them is dumb.
    */
   e_zone_init();
   e_desk_init();
   e_exehist_init();

   TS("E_System Init");
   if (!e_system_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the Privilege System access system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_System Init Done");
   _e_main_shutdown_push(e_system_shutdown);

   TS("E_Powersave Init");
   if (!e_powersave_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its powersave modes.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Powersave Init Done");
   _e_main_shutdown_push(e_powersave_shutdown);

   TS("E_Sound Init");
   e_sound_init();
   _e_main_shutdown_push(e_sound_shutdown);

   TS("Screens Init");
   if (!_e_main_screens_init())
     {
        e_error_message_show(_("Enlightenment set up window management for all the screens on your system\n"
                               "failed. Perhaps another window manager is running?\n"));
        _e_main_shutdown(101);
     }
   TS("Screens Init Done");
   _e_main_shutdown_push(_e_main_screens_shutdown);

   TS("E_Pointer Init");
   if (!e_pointer_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its pointer system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Pointer Init Done");
   _e_main_shutdown_push(e_pointer_shutdown);
   e_menu_init();

   TS("E_Scale Init");
   if (!e_scale_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its scale system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Scale Init Done");
   _e_main_shutdown_push(e_scale_shutdown);

   TS("E_Splash Init");
   if (!e_init_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its init screen.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Splash Init Done");
   _e_main_shutdown_push(e_init_shutdown);
   if (!after_restart)
     {
        TS("E_Splash Show");
        e_init_show();
        TS("E_Splash Show Done");
     }

   TS("Add Icon Extensions");
   efreet_icon_extension_add(".svg");
   efreet_icon_extension_add(".svgz");
   efreet_icon_extension_add(".svg.gz");
   efreet_icon_extension_add(".jpg");
   efreet_icon_extension_add(".jpeg");
   efreet_icon_extension_add(".png");
   efreet_icon_extension_add(".edj");
   TS("Add Icon Extensions Done");

   TS("E_Acpi Init");
   e_acpi_init();
   TS("E_Acpi Init Done");
   _e_main_shutdown_push(e_acpi_shutdown);

   TS("E_Auth Init");
   e_auth_init();
   TS("E_Auth Init Done");
   _e_main_shutdown_push(e_auth_shutdown);

   TS("E_Backlight Init");
   if (!e_backlight_init())
     {
        e_error_message_show(_("Enlightenment cannot configure the backlight.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Backlight Init Done");

   TS("E_Dpms Init");
   if (!e_dpms_init())
     {
        e_error_message_show(_("Enlightenment cannot configure the DPMS settings.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Dpms Init Done");
   _e_main_shutdown_push(e_dpms_shutdown);

   TS("E_Desklock Init");
   if (!e_desklock_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its desk locking system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Desklock Init Done");
   _e_main_shutdown_push(e_desklock_shutdown);

   if (waslocked || (locked && ((!after_restart))))
     {
        e_desklock_show_manual(EINA_TRUE);
        e_screensaver_update();
        e_dpms_force_update();
     }

   TS("Efreet Paths");
   _e_main_efreet_paths_init();
   TS("Efreet Paths Done");

   TS("E_Sys Init");
   if (!e_sys_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the System Command system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Sys Init Done");
   _e_main_shutdown_push(e_sys_shutdown);

   TS("E_Exec Init");
   if (!e_exec_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its exec system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Exec Init Done");

   TS("E_Comp Freeze");
   e_comp_all_freeze();
   TS("E_Comp Freeze Done");

   TS("E_Fm2 Init");
   if (!e_fm2_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the File manager.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Fm2 Init Done");
   _e_main_shutdown_push(e_fm2_shutdown);

   TS("E_Msg Init");
   if (!e_msg_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its msg system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Msg Init Done");
   _e_main_shutdown_push(e_msg_shutdown);

   TS("E_Grabinput Init");
   if (!e_grabinput_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its grab input handling system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Grabinput Init Done");
   _e_main_shutdown_push(e_grabinput_shutdown);

   TS("E_Module Init");
   if (!e_module_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its module system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Module Init Done");
   _e_main_shutdown_push(e_module_shutdown);

   TS("E_Remember Init");
   if (!e_remember_init(after_restart ? E_STARTUP_RESTART : E_STARTUP_START))
     {
        e_error_message_show(_("Enlightenment cannot setup remember settings.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Remember Init Done");
   _e_main_shutdown_push(e_remember_shutdown);

   TS("E_Gadcon Init");
   if (!e_gadcon_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its gadget control system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Gadcon Init Done");
   _e_main_shutdown_push(e_gadcon_shutdown);

   TS("E_Toolbar Init");
   if (!e_toolbar_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its toolbars.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Toolbar Init Done");
   _e_main_shutdown_push(e_toolbar_shutdown);

   TS("E_Bg Init");
   if (!e_bg_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its desktop background system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Bg Init Done");
   _e_main_shutdown_push(e_bg_shutdown);

   TS("E_Mouse Init");
   if (!e_mouse_update())
     {
        e_error_message_show(_("Enlightenment cannot configure the mouse settings.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Mouse Init Done");

   TS("E_Bindings Init");
   if (!e_bindings_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its bindings system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Bindings Init Done");
   _e_main_shutdown_push(e_bindings_shutdown);

   TS("E_Thumb Init");
   if (!e_thumb_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the Thumbnailing system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Thumb Init Done");
   _e_main_shutdown_push(e_thumb_shutdown);

   TS("E_Icon Init");
   if (!e_icon_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the Icon Cache system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Icon Init Done");
   _e_main_shutdown_push(e_icon_shutdown);

   TS("E_Update Init");
   if (!e_update_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize the Update system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Update Init Done");
   _e_main_shutdown_push(e_update_shutdown);

   TS("E_Deskenv Init");
   if (!e_deskenv_init())
     {
        e_error_message_show(_("Enlightenment cannot initialize its desktop environment.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Deskenv Init Done");
   _e_main_shutdown_push(e_deskenv_shutdown);

   TS("E_Order Init");
   if (!e_order_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its order file system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Order Init Done");
   _e_main_shutdown_push(e_order_shutdown);

   TS("E_Comp_Canvas Keys Grab");
   e_comp_canvas_keys_grab();
   TS("E_Comp_Canvas Keys Grab Done");
#ifdef HAVE_ELPUT
   if (e_config->gesture_open_input_devices)
     {
        TS("E_Gesture Init");
        e_gesture_init();
        TS("E_Gesture Init Done");
        _e_main_shutdown_push(e_gesture_shutdown);
     }
#endif

   TS("Run Startup Apps");
   if (!nostartup)
     {
        if (after_restart)
          e_startup_mode_set(E_STARTUP_RESTART);
        else
          e_startup_mode_set(E_STARTUP_START);
     }
   TS("Run Startup Apps Done");

   TS("E_Comp Thaw");
   e_comp_all_thaw();
   TS("E_Comp Thaw Done");

   TS("E_Test Init");
   e_test();
   TS("E_Test Done");

   TS("Load Modules");
   _e_main_modules_load(safe_mode);
   TS("Load Modules Done");

   TS("E_Shelf Init");
   if (!e_shelf_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its module system.\n"));
        _e_main_shutdown(101);
     }
   e_shelf_config_update();
   TS("E_Shelf Init Done");

   _idle_after = ecore_idle_enterer_add(_e_main_cb_idle_after, NULL);

   e_init_status_set(_("Welcome to Enlightenment"));

   starting = EINA_FALSE;
   inloop = EINA_TRUE;

   e_util_env_set("E_RESTART", "1");

   if (!after_restart)
     ecore_timer_add(2.0, _e_main_cb_startup_fake_end, NULL);

   if (after_restart)
     {
        E_LIST_FOREACH(e_comp->zones, e_comp_canvas_zone_restarted);
     }

   e_watchdog_begin();
   TS("MAIN LOOP AT LAST");
   if (!setjmp(x_fatal_buff))
     {
        e_main_loop_running = EINA_TRUE;
        ecore_main_loop_begin();
     }
   else
     CRI("FATAL: X Died. Connection gone. Abbreviated Shutdown\n");
   e_watchdog_end();
   e_main_loop_running = EINA_FALSE;

   inloop = EINA_FALSE;
   stopping = EINA_TRUE;

   //if (!x_fatal) e_canvas_idle_flush();

   e_config_save_flush();
   _e_main_desk_save();
   e_remember_internal_save();
   e_comp_internal_save();

   _e_main_shutdown(0);

   if (restart)
     {
        e_system_shutdown();
        exit(111); // return code so e_start restrts e freshly
     }

   e_prefix_shutdown();

   return 0;
}

E_API double
e_main_ts(const char *str)
{
   double ret;
   t1 = ecore_time_unix_get();
   printf("ESTART: %1.5f [%1.5f] - %s\n", t1 - t0, t1 - t2, str);
   ret = t1 - t2;
   t2 = t1;
   return ret;
}

/* local functions */
static void
_e_main_shutdown(int errcode)
{
   int i = 0;

   printf("E: Begin Shutdown Procedure!\n");

   if (_idle_before) ecore_idle_enterer_del(_idle_before);
   _idle_before = NULL;
   if (_idle_after) ecore_idle_enterer_del(_idle_after);
   _idle_after = NULL;

   for (i = (_e_main_lvl - 1); i >= 0; i--)
     (*_e_main_shutdown_func[i])();
   if (errcode != 0) exit(errcode);
}

static void
_e_main_shutdown_push(int (*func)(void))
{
   _e_main_lvl++;
   if (_e_main_lvl > MAX_LEVEL)
     {
        _e_main_lvl--;
        e_error_message_show("WARNING: too many init levels. MAX = %i\n",
                             MAX_LEVEL);
        return;
     }
   _e_main_shutdown_func[_e_main_lvl - 1] = func;
}

static void
_e_main_parse_arguments(int argc, char **argv)
{
   char *s = NULL;
   int i = 0;

   /* handle some command-line parameters */
   for (i = 1; i < argc; i++)
     {
        if ((!strcmp(argv[i], "-display")) && (i < (argc - 1)))
          {
             i++;
             e_util_env_set("DISPLAY", argv[i]);
          }
        else if ((!strcmp(argv[i], "-fake-xinerama-screen")) && (i < (argc - 1)))
          {
             int x, y, w, h;

             i++;
             if (sscanf(argv[i], "%ix%i+%i+%i", &w, &h, &x, &y) == 4)
               e_xinerama_fake_screen_add(x, y, w, h);
          }
        else if (!strcmp(argv[i], "-good"))
          {
             good = EINA_TRUE;
             evil = EINA_FALSE;
             printf("LA LA LA\n");
          }
        else if (!strcmp(argv[i], "-evil"))
          {
             good = EINA_FALSE;
             evil = EINA_TRUE;
             printf("MUHAHAHAHHAHAHAHAHA\n");
          }
        else if (!strcmp(argv[i], "-psychotic"))
          {
             good = EINA_TRUE;
             evil = EINA_TRUE;
             printf("MUHAHALALALALALALALA\n");
          }
        else if ((!strcmp(argv[i], "-profile")) && (i < (argc - 1)))
          {
             i++;
             if (!getenv("E_CONF_PROFILE"))
               e_util_env_set("E_CONF_PROFILE", argv[i]);
          }
        else if (!strcmp(argv[i], "-i-really-know-what-i-am-doing-and-accept-full-responsibility-for-it"))
          really_know = EINA_TRUE;
        else if (!strcmp(argv[i], "-locked"))
          locked = EINA_TRUE;
        else if (!strcmp(argv[i], "-nopause"))
          e_nopause = EINA_TRUE;
        else if ((!strcmp(argv[i], "-version")) ||
                 (!strcmp(argv[i], "--version")))
          {
             printf(_("Version: %s\n"), PACKAGE_VERSION);
             _e_main_shutdown(11);
          }
        else if ((!strcmp(argv[i], "-h")) ||
                 (!strcmp(argv[i], "-help")) ||
                 (!strcmp(argv[i], "--help")))
          {
             printf
               (_(
                 "Options:\n"
                 "  -display DISPLAY\n"
                 "    Connect to display named DISPLAY.\n"
                 "    EG: -display :1.0\n"
                 "  -fake-xinerama-screen WxH+X+Y\n"
                 "    Add a FAKE xinerama screen (instead of the real ones)\n"
                 "    given the geometry. Add as many as you like. They all\n"
                 "    replace the real xinerama screens, if any. This can\n"
                 "    be used to simulate xinerama.\n"
                 "    EG: -fake-xinerama-screen 800x600+0+0 -fake-xinerama-screen 800x600+800+0\n"
                 "  -profile CONF_PROFILE\n"
                 "    Use the configuration profile CONF_PROFILE instead of the user selected default or just \"default\".\n"
                 "  -good\n"
                 "    Be good.\n"
                 "  -evil\n"
                 "    Be evil.\n"
                 "  -psychotic\n"
                 "    Be psychotic.\n"
                 "  -locked\n"
                 "    Start with desklock on, so password will be asked.\n"
                 "  -i-really-know-what-i-am-doing-and-accept-full-responsibility-for-it\n"
                 "    If you need this help, you don't need this option.\n"
                 "  -version\n"
                 )
               );
             _e_main_shutdown(11);
          }
     }

   /* fix up DISPLAY to be :N.0 if no .screen is in it */
   s = getenv("DISPLAY");
   if (s)
     {
        char *p, buff[4096];

        if (!(p = strrchr(s, ':')))
          {
             snprintf(buff, sizeof(buff), "%s:0.0", s);
             e_util_env_set("DISPLAY", buff);
          }
        else
          {
             if (!strrchr(p, '.'))
               {
                  snprintf(buff, sizeof(buff), "%s.0", s);
                  e_util_env_set("DISPLAY", buff);
               }
          }
     }

   /* we want to have been launched by enlightenment_start. there is a very */
   /* good reason we want to have been launched this way, thus check */
   if ((!getenv("E_START")) && (!RUNNING_ON_VALGRIND))
     {
        e_error_message_show(_("You are executing enlightenment directly. This is\n"
                               "bad. Please do not execute the \"enlightenment\"\n"
                               "binary. Use the \"enlightenment_start\" launcher. It\n"
                               "will handle setting up environment variables, paths,\n"
                               "and launching any other required services etc.\n"
                               "before enlightenment itself begins running.\n"));
        _e_main_shutdown(101);
     }
}

EINTERN void
_e_main_cb_x_fatal(void *data EINA_UNUSED)
{
   fprintf(stderr, "X I/O Error - fatal. Exiting.\n");
   exit(101);
}

static Eina_Bool
_e_main_cb_signal_exit(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev EINA_UNUSED)
{
   /* called on ctrl-c, kill (pid) (also SIGINT, SIGTERM and SIGQIT) */
   e_sys_action_do(E_SYS_EXIT, NULL);
   return ECORE_CALLBACK_RENEW;
}

static Ecore_Timer *hup_timer = NULL;

static Eina_Bool
_cb_hup_timer(void *data EINA_UNUSED)
{
   hup_timer = NULL;
   if (!e_sys_on_the_way_out_get()) e_sys_action_do(E_SYS_RESTART, NULL);
   return EINA_FALSE;
}

static Eina_Bool
_e_main_cb_signal_hup(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev EINA_UNUSED)
{
   if (hup_timer) ecore_timer_del(hup_timer);
   hup_timer = ecore_timer_add(0.5, _cb_hup_timer, NULL);
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_main_cb_signal_user(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev)
{
   Ecore_Event_Signal_User *e = ev;

   if (e->number == 1)
     {
//        E_Action *a = e_action_find("configuration");
//        if ((a) && (a->func.go)) a->func.go(NULL, NULL);
     }
   else if (e->number == 2)
     {
        // comp module has its own handler for this for enabling/disabling fps debug
     }
   return ECORE_CALLBACK_RENEW;

}

static int
_e_main_dirs_init(void)
{
   const char *base;
   const char *dirs[] =
   {
      "images",
      "fonts",
      "themes",
      "icons",
      "backgrounds",
      "applications",
      "applications/menu",
      "applications/menu/favorite",
      "applications/menu/all",
      "applications/bar",
      "applications/bar/default",
      "applications/startup",
      "applications/restart",
      "applications/trash",
      "applications/desk-lock",
      "applications/desk-unlock",
      "modules",
      "config",
      "locale",
      "input_methods",
      NULL
   };

   base = e_user_dir_get();
   if (ecore_file_mksubdirs(base, dirs) != sizeof(dirs) / sizeof(dirs[0]) - 1)
     {
        e_error_message_show("Could not create one of the required "
                             "subdirectories of '%s'\n", base);
        return 0;
     }

   return 1;
}

static int
_e_main_dirs_shutdown(void)
{
   return 1;
}

static int
_e_main_path_init(void)
{
   char buf[PATH_MAX];

   /* setup data paths */
   path_data = e_path_new();
   if (!path_data)
     {
        e_error_message_show("Cannot allocate path for path_data\n");
        return 0;
     }
   e_prefix_data_concat_static(buf, "data");
   e_path_default_path_append(path_data, buf);
   e_path_user_path_set(path_data, &(e_config->path_append_data));

   /* setup image paths */
   path_images = e_path_new();
   if (!path_images)
     {
        e_error_message_show("Cannot allocate path for path_images\n");
        return 0;
     }
   e_user_dir_concat_static(buf, "/images");
   e_path_default_path_append(path_images, buf);
   e_prefix_data_concat_static(buf, "data/images");
   e_path_default_path_append(path_images, buf);
   e_path_user_path_set(path_images, &(e_config->path_append_images));

   /* setup font paths */
   path_fonts = e_path_new();
   if (!path_fonts)
     {
        e_error_message_show("Cannot allocate path for path_fonts\n");
        return 0;
     }
   e_user_dir_concat_static(buf, "/fonts");
   e_path_default_path_append(path_fonts, buf);
   e_prefix_data_concat_static(buf, "data/fonts");
   e_path_default_path_append(path_fonts, buf);
   e_path_user_path_set(path_fonts, &(e_config->path_append_fonts));

   /* setup icon paths */
   path_icons = e_path_new();
   if (!path_icons)
     {
        e_error_message_show("Cannot allocate path for path_icons\n");
        return 0;
     }
   e_user_dir_concat_static(buf, "/icons");
   e_path_default_path_append(path_icons, buf);
   e_prefix_data_concat_static(buf, "data/icons");
   e_path_default_path_append(path_icons, buf);
   e_path_user_path_set(path_icons, &(e_config->path_append_icons));

   /* setup module paths */
   path_modules = e_path_new();
   if (!path_modules)
     {
        e_error_message_show("Cannot allocate path for path_modules\n");
        return 0;
     }
   e_user_dir_concat_static(buf, "/modules");
   e_path_default_path_append(path_modules, buf);
   snprintf(buf, sizeof(buf), "%s/enlightenment/modules", e_prefix_lib_get());
   e_path_default_path_append(path_modules, buf);
   /* FIXME: eventually this has to go - moduels should have installers that
    * add appropriate install paths (if not installed to user homedir) to
    * e's module search dirs
    */
   snprintf(buf, sizeof(buf), "%s/enlightenment/modules_extra", e_prefix_lib_get());
   e_path_default_path_append(path_modules, buf);
   e_path_user_path_set(path_modules, &(e_config->path_append_modules));

   /* setup background paths */
   path_backgrounds = e_path_new();
   if (!path_backgrounds)
     {
        e_error_message_show("Cannot allocate path for path_backgrounds\n");
        return 0;
     }
   e_user_dir_concat_static(buf, "/backgrounds");
   e_path_default_path_append(path_backgrounds, buf);
   e_prefix_data_concat_static(buf, "data/backgrounds");
   e_path_default_path_append(path_backgrounds, buf);
   e_path_user_path_set(path_backgrounds, &(e_config->path_append_backgrounds));

   path_messages = e_path_new();
   if (!path_messages)
     {
        e_error_message_show("Cannot allocate path for path_messages\n");
        return 0;
     }
   e_user_dir_concat_static(buf, "/locale");
   e_path_default_path_append(path_messages, buf);
   e_path_default_path_append(path_messages, e_prefix_locale_get());
   e_path_user_path_set(path_messages, &(e_config->path_append_messages));

   return 1;
}

static int
_e_main_path_shutdown(void)
{
   if (path_data)
     {
        e_object_del(E_OBJECT(path_data));
        path_data = NULL;
     }
   if (path_images)
     {
        e_object_del(E_OBJECT(path_images));
        path_images = NULL;
     }
   if (path_fonts)
     {
        e_object_del(E_OBJECT(path_fonts));
        path_fonts = NULL;
     }
   if (path_icons)
     {
        e_object_del(E_OBJECT(path_icons));
        path_icons = NULL;
     }
   if (path_modules)
     {
        e_object_del(E_OBJECT(path_modules));
        path_modules = NULL;
     }
   if (path_backgrounds)
     {
        e_object_del(E_OBJECT(path_backgrounds));
        path_backgrounds = NULL;
     }
   if (path_messages)
     {
        e_object_del(E_OBJECT(path_messages));
        path_messages = NULL;
     }
   return 1;
}

static int
_e_main_screens_init(void)
{
   TS("  screens: client");
   if (!e_client_init()) return 0;
   TS("E_Screensaver Init");
   if (!e_screensaver_init()) return 0;
   TS("  screens: client volume");
   if (!e_client_volume_init()) return 0;
   TS("  screens: win");
   if (!e_win_init()) return 0;
   TS("Compositor Init");
   if (!e_comp_init())
     {
        e_error_message_show(_("Enlightenment cannot create a compositor.\n"));
        _e_main_shutdown(101);
     }
   TS("Compositor Init Done");

   TS("Desk Restore");
   _e_main_desk_restore();
   TS("Desk Restore Done");

#ifndef HAVE_WAYLAND_ONLY
   TS("E_Dnd Init");
   if (!e_dnd_init())
     {
        e_error_message_show(_("Enlightenment cannot set up its dnd system.\n"));
        _e_main_shutdown(101);
     }
   TS("E_Dnd Init Done");
#endif

   return 1;
}

static int
_e_main_screens_shutdown(void)
{
   e_win_shutdown();
   e_menu_shutdown();
   e_shelf_shutdown();
   e_dnd_shutdown();
   e_comp_shutdown();
   e_client_volume_shutdown();
   e_screensaver_shutdown();
   e_client_shutdown();
   e_exehist_shutdown();
   e_backlight_shutdown();
   e_exec_shutdown();

   e_desk_shutdown();
   e_zone_shutdown();
   return 1;
}

static void
_e_main_desk_save(void)
{
#ifndef HAVE_WAYLAND_ONLY
   if (e_comp_util_has_x())
     {
        const Eina_List *l;
        E_Zone *zone;
        char buf[256];
        unsigned int desk[2];
        Ecore_X_Atom a;

        EINA_LIST_FOREACH(e_comp->zones, l, zone)
          {
             desk[0] = zone->desk_x_current;
             desk[1] = zone->desk_y_current;
             snprintf(buf, sizeof(buf), "E_DESK_%i", zone->num);
             a = ecore_x_atom_get(buf);
             ecore_x_window_prop_card32_set(e_comp->root, a, desk, 2);
          }
     }
#endif
}

static void
_e_main_desk_restore(void)
{
#ifndef HAVE_WAYLAND_ONLY
   if (e_comp_util_has_x())
     {
        const Eina_List *l;
        E_Zone *zone;
        E_Client *ec;
        E_Desk *desk;
        int ret;
        char buf[256];
        unsigned int desks[2];
        Ecore_X_Atom a;

        // hide all windows first as desk show will restore
        E_CLIENT_FOREACH(ec)
          {
             ec->hidden = 1;
             evas_object_show(ec->frame);
             ec->changes.visible = 0;
             evas_object_hide(ec->frame);
             e_client_comp_hidden_set(ec, EINA_TRUE);
          }
        EINA_LIST_FOREACH(e_comp->zones, l, zone)
          {
             desk = NULL;

             snprintf(buf, sizeof(buf), "E_DESK_%i", zone->num);
             a = ecore_x_atom_get(buf);
             ret = ecore_x_window_prop_card32_get(e_comp->root, a, desks, 2);
             if (ret == 2)
               {
                  desk = e_desk_at_xy_get(zone, desks[0], desks[1]);
                  if (desk) e_desk_show(desk);
               }
             if (!desk) desk = e_desk_current_get(zone);
             if (desk)
               {
                  // ensure windows for this desk are shown
                  E_CLIENT_REVERSE_FOREACH(ec)
                    {
                       if (ec->iconic) continue;
                       if ((ec->desk == desk) || (ec->sticky))
                         {
                            ec->hidden = 0;
                            e_client_comp_hidden_set(ec, ec->hidden || ec->shaded);
                            evas_object_show(ec->frame);
                         }
                    }
               }
          }
        E_CLIENT_REVERSE_FOREACH(ec)
          {
             if ((!e_client_util_ignored_get(ec)) &&
                 e_client_util_desk_visible(ec, e_desk_current_get(ec->zone)))
               {
                  ec->want_focus = ec->take_focus = 1;
                  break;
               }
          }
     }
#endif
}

static void
_e_main_efreet_paths_init(void)
{
   Eina_List **list;

   if ((list = efreet_icon_extra_list_get()))
     {
        char buff[PATH_MAX];

        e_user_dir_concat_static(buff, "icons");
        *list = eina_list_prepend(*list, (void *)eina_stringshare_add(buff));
        e_prefix_data_concat_static(buff, "data/icons");
        *list = eina_list_prepend(*list, (void *)eina_stringshare_add(buff));
     }
}

static Eina_Bool
_e_main_modules_load_after(void *d EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED)
{
   e_int_config_modules(NULL, NULL);
   E_FREE_FUNC(mod_init_end, ecore_event_handler_del);
   return ECORE_CALLBACK_RENEW;
}

static void
_e_main_modules_load(Eina_Bool safe_mode)
{
   if (!safe_mode)
     e_module_all_load();
   else
     {
        E_Module *m;
        char *crashmodule;

        crashmodule = getenv("E_MODULE_LOAD");
        if (crashmodule) m = e_module_new(crashmodule);

        if ((crashmodule) && (m))
          {
             e_module_disable(m);
             e_object_del(E_OBJECT(m));

             e_error_message_show
               (_("Enlightenment crashed early on start and has<ps/>"
                  "been restarted. There was an error loading the<ps/>"
                  "module named: %s. This module has been disabled<ps/>"
                  "and will not be loaded."), crashmodule);
             e_util_dialog_show
               (_("Enlightenment crashed early on start and has been restarted"),
               _("Enlightenment crashed early on start and has been restarted.<ps/>"
                 "There was an error loading the module named: %s<ps/><ps/>"
                 "This module has been disabled and will not be loaded."), crashmodule);
             e_module_all_load();
          }
        else
          {
             e_error_message_show
               (_("Enlightenment crashed early on start and has<ps/>"
                  "been restarted. All modules have been disabled<ps/>"
                  "and will not be loaded to help remove any problem<ps/>"
                  "modules from your configuration. The module<ps/>"
                  "configuration dialog should let you select your<ps/>"
                  "modules again.\n"));
             e_util_dialog_show
               (_("Enlightenment crashed early on start and has been restarted"),
               _("Enlightenment crashed early on start and has been restarted.<ps/>"
                 "All modules have been disabled and will not be loaded to help<ps/>"
                 "remove any problem modules from your configuration.<ps/><ps/>"
                 "The module configuration dialog should let you select your<ps/>"
                 "modules again."));
          }
        mod_init_end = ecore_event_handler_add(E_EVENT_MODULE_INIT_END, _e_main_modules_load_after, NULL);
     }
}

static Eina_Bool
_e_main_cb_idle_before(void *data EINA_UNUSED)
{
   e_menu_idler_before();
   e_client_idler_before();
   e_pointer_idler_before();
   edje_thaw();
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_main_cb_idle_after(void *data EINA_UNUSED)
{
   static int first_idle = 1;

   eet_clearcache();
   edje_freeze();

   if (first_idle)
     {
        TS("SLEEP");
        first_idle = 0;
     }
   e_precache_end = EINA_TRUE;

// every now and again trim malloc memory to stay lean-ish
#ifdef HAVE_MALLOC_TRIM
   static double t_last_clean = 0.0;
   double t = ecore_time_get();
   if ((t - t_last_clean) > 10.0)
     {
        t_last_clean = t;
        malloc_trim(0);
     }
#endif

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_main_cb_startup_fake_end(void *data EINA_UNUSED)
{
   e_init_done();
   return ECORE_CALLBACK_CANCEL;
}
