e_process: added feature to handle process status 53/83953/3
authorDoyoun Kang <doyoun.kang@samsung.com>
Tue, 16 Aug 2016 04:05:37 +0000 (13:05 +0900)
committerGwanglim Lee <gl77.lee@samsung.com>
Tue, 16 Aug 2016 09:17:04 +0000 (02:17 -0700)
Change-Id: Ie12d0603367116977c5b923cf86727ba305e1af6

src/bin/Makefile.mk
src/bin/e_includes.h
src/bin/e_main.c
src/bin/e_process.c [new file with mode: 0644]
src/bin/e_process.h [new file with mode: 0644]

index 37fe0c1..9c3d11a 100644 (file)
@@ -102,7 +102,8 @@ src/bin/e_policy.h \
 src/bin/e_policy_keyboard.h \
 src/bin/e_policy_private_data.h \
 src/bin/e_policy_wl.h \
-src/bin/e_policy_wl_display.h
+src/bin/e_policy_wl_display.h \
+src/bin/e_process.h
 
 enlightenment_src = \
 src/bin/e_actions.c \
@@ -189,7 +190,8 @@ src/bin/e_policy_softkey.c \
 src/bin/e_policy_stack.c  \
 src/bin/e_policy_visibility.c \
 src/bin/e_policy_wl.c \
-src/bin/e_policy_wl_display.c
+src/bin/e_policy_wl_display.c \
+src/bin/e_process.c
 
 src_bin_enlightenment_CPPFLAGS = $(E_CPPFLAGS) -DEFL_BETA_API_SUPPORT -DEFL_EO_API_SUPPORT -DE_LOGGING=1 @WAYLAND_CFLAGS@ $(TTRACE_CFLAGS) $(DLOG_CFLAGS) $(POLICY_CFLAGS)
 if HAVE_WAYLAND_TBM
index 77f1ef9..5681cde 100644 (file)
@@ -59,3 +59,4 @@
 # include "e_comp_wl_tbm.h"
 #endif
 #include "e_policy.h"
+#include "e_process.h"
index eda6612..d926de5 100644 (file)
@@ -683,6 +683,15 @@ main(int argc, char **argv)
         _e_main_shutdown_push(e_policy_shutdown);
      }
 
+   TS("E_Process Init");
+   if (!e_process_init())
+     {
+        e_error_message_show(_("Enlightenment cannot setup process managing system!\n"));
+        _e_main_shutdown(-1);
+     }
+   TS("E_Process Init Done");
+   _e_main_shutdown_push(e_process_shutdown);
+
    TS("Load Modules");
    _e_main_modules_load(safe_mode);
    TS("Load Modules Done");
diff --git a/src/bin/e_process.c b/src/bin/e_process.c
new file mode 100644 (file)
index 0000000..8825ab6
--- /dev/null
@@ -0,0 +1,547 @@
+#include "e.h"
+
+static E_Process *_e_process_find(E_Process_Manager *pm, pid_t pid);
+static E_Process *_e_process_new(pid_t pid);
+static void       _e_process_del(E_Process *pinfo);
+
+static Eina_Bool  _e_process_client_info_add(E_Client *ec);
+static void       _e_process_client_info_del(E_Client *ec);
+
+static Eina_Bool  _e_process_cb_client_add(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+static Eina_Bool  _e_process_cb_client_remove(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+static Eina_Bool  _e_process_cb_client_iconify(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+static Eina_Bool  _e_process_cb_client_uniconify(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+static Eina_Bool  _e_process_cb_client_visibility_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+static Eina_Bool  _e_process_cb_client_focus_in(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+
+static void       _e_process_cb_hook_visibility(void *d EINA_UNUSED, E_Client *ec);
+
+static Eina_Bool  _e_process_freeze_condition_check(pid_t pid);
+static Eina_Bool  _e_process_freeze(pid_t pid);
+static Eina_Bool  _e_process_thaw(pid_t pid);
+
+static void       _e_process_action_change(E_Process *epro, E_Process_Action act);
+static void       _e_process_state_change(E_Process *epro, E_Process_State state);
+
+
+static void       _e_process_hooks_clean(void);
+static void       _e_process_hook_call(E_Process_Hook_Point hookpoint, E_Process *epro, void *user_data);
+
+
+static Eina_Inlist *_e_process_hooks[] =
+{
+   [E_PROCESS_HOOK_STATE_CHANGE] = NULL,
+   [E_PROCESS_HOOK_ACTION_CHANGE] = NULL,
+};
+
+static int _e_process_hooks_delete = 0;
+static int _e_process_hooks_walking = 0;
+
+static Eina_List *_e_process_ec_handlers = NULL;
+static Eina_List *_e_process_ec_hooks = NULL;
+
+E_Process_Manager *_e_process_manager;
+
+
+static E_Process *
+_e_process_find(E_Process_Manager *pm, pid_t pid)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pm, NULL);
+   return eina_hash_find(pm->pids_hash, &pid);
+}
+
+static E_Process *
+_e_process_new(pid_t pid)
+{
+   E_Process  *pinfo = NULL;
+
+   if (pid <= 0) return NULL;
+
+   pinfo = E_NEW(E_Process, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pinfo, NULL);
+
+   pinfo->pid = pid;
+   pinfo->state = E_PROCESS_STATE_UNKNOWN;
+
+   eina_hash_add(_e_process_manager->pids_hash, &pid, pinfo);
+   _e_process_manager->process_list = eina_inlist_append(_e_process_manager->process_list, EINA_INLIST_GET(pinfo));
+
+   return pinfo;
+}
+
+static void
+_e_process_del(E_Process *pinfo)
+{
+   pid_t pid;
+
+   EINA_SAFETY_ON_NULL_RETURN(pinfo);
+
+   pid = pinfo->pid;
+
+   _e_process_manager->process_list = eina_inlist_remove(_e_process_manager->process_list, EINA_INLIST_GET(pinfo));
+   eina_hash_del_by_key(_e_process_manager->pids_hash, &pid);
+
+   E_FREE(pinfo);
+}
+
+static Eina_Bool
+_e_process_client_info_add(E_Client *ec)
+{
+   E_Process *pinfo = NULL;
+   pid_t pid;
+
+   if (!ec) return EINA_FALSE;
+
+   pid = ec->netwm.pid;
+   if (pid <= 0) return EINA_FALSE;
+
+   pinfo = _e_process_find(_e_process_manager, pid);
+   if (!pinfo)
+     {
+        pinfo = _e_process_new(pid);
+        EINA_SAFETY_ON_NULL_RETURN_VAL(pinfo, EINA_FALSE);
+     }
+
+   if (!eina_list_data_find(pinfo->ec_list, ec))
+     pinfo->ec_list = eina_list_append(pinfo->ec_list, ec);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_process_client_info_del(E_Client *ec)
+{
+   E_Process *pinfo = NULL;
+   pid_t pid;
+
+   if (!ec) return;
+
+   pid = ec->netwm.pid;
+   if (pid <=0) return;
+
+   pinfo = _e_process_find(_e_process_manager, pid);
+   EINA_SAFETY_ON_NULL_RETURN(pinfo);
+
+   if (_e_process_manager->active_win == ec)
+     {
+        _e_process_manager->active_win = NULL;
+        ELOGF("PROCESS", "ACTION DEACTIVATE. PID:%d", NULL, NULL, pid);
+        _e_process_action_change(pinfo, E_PROCESS_ACT_DEACTIVATE);
+     }
+
+   pinfo->ec_list = eina_list_remove(pinfo->ec_list, ec);
+
+   if (!pinfo->ec_list)
+     _e_process_del(pinfo);
+
+   return;
+}
+
+static Eina_Bool
+_e_process_cb_client_add(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+   E_Client *ec;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   ec = ev->ec;
+   _e_process_client_info_add(ec);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_process_cb_client_remove(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+   E_Client *ec;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   ec = ev->ec;
+   _e_process_client_info_del(ec);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_process_cb_client_iconify(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+   E_Client *ec;
+   pid_t pid;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   ec = ev->ec;
+   if (!ec) return ECORE_CALLBACK_PASS_ON;
+
+   pid = ec->netwm.pid;
+
+   // check all ECs of its pid, if yes, freeze
+   if (_e_process_freeze_condition_check(pid))
+     _e_process_freeze(pid);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_process_cb_client_uniconify(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+   E_Client *ec;
+   pid_t pid;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   ec = ev->ec;
+   if (!ec) return ECORE_CALLBACK_PASS_ON;
+
+   pid = ec->netwm.pid;
+   _e_process_thaw(pid);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_process_cb_client_visibility_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+   E_Client *ec;
+   pid_t pid;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   ec = ev->ec;
+   if (!ec) return ECORE_CALLBACK_PASS_ON;
+
+   pid = ec->netwm.pid;
+   if (ec->visibility.obscured == E_VISIBILITY_UNOBSCURED)
+     _e_process_thaw(pid);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_process_cb_client_focus_in(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+   E_Client *ec;
+   E_Client *ec_deactive;
+   E_Process *pinfo  = NULL;
+   E_Process *pinfo_deactive = NULL;
+   Eina_Bool change_active = EINA_FALSE;
+   pid_t pid = -1;
+   pid_t pid_deactivate = -1;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   ec = ev->ec;
+   if (!ec) return ECORE_CALLBACK_PASS_ON;
+
+   pid = ec->netwm.pid;
+   if (pid <= 0) return EINA_FALSE;
+
+   pinfo = _e_process_find(_e_process_manager, pid);
+   if (!pinfo) return EINA_FALSE;
+
+   ec_deactive = _e_process_manager->active_win;
+   _e_process_manager->active_win = ec;
+
+   if (!ec_deactive)
+     {
+        change_active = EINA_TRUE;
+     }
+   else
+     {
+        pid_deactivate = ec_deactive->netwm.pid;
+        if (pid_deactivate != pid)
+          {
+             change_active = EINA_TRUE;
+          }
+     }
+
+   if (change_active)
+     {
+        ELOGF("PROCESS", "ACTION ACTIVATE. PID:%d", NULL, NULL, pid);
+        _e_process_action_change(pinfo, E_PROCESS_ACT_ACTIVATE);
+
+        if (ec_deactive)
+          {
+             pinfo_deactive = _e_process_find(_e_process_manager, ec_deactive->netwm.pid);
+             if (pinfo_deactive)
+               {
+                  ELOGF("PROCESS", "ACTION DEACTIVATE. PID:%d", NULL, NULL, pinfo_deactive->pid);
+                  _e_process_action_change(pinfo_deactive, E_PROCESS_ACT_DEACTIVATE);
+               }
+          }
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static void
+_e_process_cb_hook_visibility(void *d EINA_UNUSED, E_Client *ec)
+{
+   if (ec->visibility.changed)
+     {
+        if (ec->visibility.obscured == E_VISIBILITY_UNOBSCURED)
+          {
+             _e_process_thaw(ec->netwm.pid);
+          }
+     }
+}
+
+static Eina_Bool
+_e_process_freeze_condition_check(pid_t pid)
+{
+   E_Process *pinfo  = NULL;
+   E_Client *ec = NULL;
+   Eina_Bool freeze = EINA_TRUE;
+   Eina_List *l;
+
+   if (pid <= 0) return EINA_FALSE;
+
+   pinfo = _e_process_find(_e_process_manager, pid);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pinfo, EINA_FALSE);
+
+   if (pinfo->state == E_PROCESS_STATE_BACKGROUND) return EINA_FALSE;
+   if (!pinfo->ec_list) return EINA_FALSE;
+
+   EINA_LIST_FOREACH(pinfo->ec_list, l, ec)
+     {
+        if (!ec->iconic)
+          {
+             freeze = EINA_FALSE;
+             break;
+          }
+     }
+
+   return freeze;
+}
+
+static Eina_Bool
+_e_process_freeze(pid_t pid)
+{
+   E_Process  *pinfo  = NULL;
+
+   if (pid <= 0) return EINA_FALSE;
+
+   pinfo = _e_process_find(_e_process_manager, pid);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pinfo, EINA_FALSE);
+
+   if (pinfo->state != E_PROCESS_STATE_BACKGROUND)
+     {
+        ELOGF("PROCESS", "STATE  BACKGROUND. PID:%d", NULL, NULL, pid);
+        _e_process_state_change(pinfo, E_PROCESS_STATE_BACKGROUND);
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_process_thaw(pid_t pid)
+{
+   E_Process  *pinfo  = NULL;
+
+   if (pid <= 0) return EINA_FALSE;
+
+   pinfo = _e_process_find(_e_process_manager, pid);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pinfo, EINA_FALSE);
+
+   if (pinfo->state != E_PROCESS_STATE_FOREGROUND)
+     {
+        ELOGF("PROCESS", "STATE  FOREGROUND. PID:%d", NULL, NULL, pid);
+        _e_process_state_change(pinfo, E_PROCESS_STATE_FOREGROUND);
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_e_process_action_change(E_Process *epro, E_Process_Action act)
+{
+   EINA_SAFETY_ON_NULL_RETURN(epro);
+
+   _e_process_hook_call(E_PROCESS_HOOK_ACTION_CHANGE, epro, (E_Process_Action *)&act);
+}
+
+static void
+_e_process_state_change(E_Process *epro, E_Process_State state)
+{
+   EINA_SAFETY_ON_NULL_RETURN(epro);
+
+   if (epro->state != state)
+     {
+        epro->state = state;
+        _e_process_hook_call(E_PROCESS_HOOK_STATE_CHANGE, epro, NULL);
+
+        if (state == E_PROCESS_STATE_FOREGROUND)
+          {
+             ELOGF("PROCESS", "ACTION FOREGROUND. PID:%d", NULL, NULL, epro->pid);
+             _e_process_action_change(epro, E_PROCESS_ACT_FOREGROUND);
+          }
+        else if (state == E_PROCESS_STATE_BACKGROUND)
+          {
+             ELOGF("PROCESS", "ACTION BACKGROUND. PID:%d", NULL, NULL, epro->pid);
+             _e_process_action_change(epro, E_PROCESS_ACT_BACKGROUND);
+          }
+     }
+}
+
+static void
+_e_process_hooks_clean(void)
+{
+   Eina_Inlist *l;
+   E_Process_Hook *ph;
+   unsigned int x;
+
+   for (x = 0; x < E_PROCESS_HOOK_LAST; x++)
+     EINA_INLIST_FOREACH_SAFE(_e_process_hooks[x], l, ph)
+       {
+          if (!ph->delete_me) continue;
+          _e_process_hooks[x] = eina_inlist_remove(_e_process_hooks[x],
+                                                   EINA_INLIST_GET(ph));
+          free(ph);
+       }
+}
+
+static void
+_e_process_hook_call(E_Process_Hook_Point hookpoint, E_Process *epro, void *user_data)
+{
+   E_Process_Hook *ph;
+
+   _e_process_hooks_walking++;
+   EINA_INLIST_FOREACH(_e_process_hooks[hookpoint], ph)
+     {
+        if (ph->delete_me) continue;
+        ph->func(ph->data, epro, user_data);
+     }
+   _e_process_hooks_walking--;
+   if ((_e_process_hooks_walking == 0) && (_e_process_hooks_delete > 0))
+     _e_process_hooks_clean();
+}
+
+
+static E_Process_Manager *
+_e_process_manager_new(void)
+{
+   E_Process_Manager *pm;
+
+   pm = E_NEW(E_Process_Manager, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pm, NULL);
+
+   pm->pids_hash = eina_hash_pointer_new(NULL);
+   if (!pm->pids_hash) goto error;
+
+   return pm;
+
+error:
+
+   E_FREE(pm);
+   return NULL;
+
+}
+
+static void
+_e_process_manager_del(E_Process_Manager *pm)
+{
+   E_Process *pinfo = NULL;
+
+   if (!pm) return;
+
+   EINA_INLIST_FREE(pm->process_list, pinfo)
+     {
+        eina_list_free(pinfo->ec_list);
+        eina_hash_del_by_key(pm->pids_hash, &pinfo->pid);
+        E_FREE(pinfo);
+     }
+
+   if (pm->pids_hash)
+     {
+        eina_hash_free(pm->pids_hash);
+        pm->pids_hash = NULL;
+     }
+
+   E_FREE(pm);
+}
+
+
+E_API Eina_Bool
+e_process_init(void)
+{
+   E_Process_Manager *e_pm;
+   E_Client_Hook *hook;
+
+   e_pm = _e_process_manager_new();
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e_pm, EINA_FALSE);
+
+   E_LIST_HANDLER_APPEND(_e_process_ec_handlers, E_EVENT_CLIENT_ADD, _e_process_cb_client_add, NULL);
+   E_LIST_HANDLER_APPEND(_e_process_ec_handlers, E_EVENT_CLIENT_REMOVE, _e_process_cb_client_remove, NULL);
+   E_LIST_HANDLER_APPEND(_e_process_ec_handlers, E_EVENT_CLIENT_ICONIFY, _e_process_cb_client_iconify, NULL);
+   E_LIST_HANDLER_APPEND(_e_process_ec_handlers, E_EVENT_CLIENT_UNICONIFY, _e_process_cb_client_uniconify, NULL);
+   E_LIST_HANDLER_APPEND(_e_process_ec_handlers, E_EVENT_CLIENT_VISIBILITY_CHANGE, _e_process_cb_client_visibility_change, NULL);
+   E_LIST_HANDLER_APPEND(_e_process_ec_handlers, E_EVENT_CLIENT_FOCUS_IN, _e_process_cb_client_focus_in, NULL);
+
+   hook = e_client_hook_add(E_CLIENT_HOOK_EVAL_VISIBILITY, _e_process_cb_hook_visibility, NULL);
+   if (hook) _e_process_ec_hooks = eina_list_append(_e_process_ec_hooks, hook);
+
+   _e_process_manager = e_pm;
+
+   return EINA_TRUE;
+}
+
+E_API int
+e_process_shutdown(void)
+{
+   E_Client_Hook *hook;
+
+   if (!_e_process_manager) return 0;
+
+   E_FREE_LIST(_e_process_ec_handlers, ecore_event_handler_del);
+   EINA_LIST_FREE(_e_process_ec_hooks, hook)
+      e_client_hook_del(hook);
+
+   _e_process_manager_del(_e_process_manager);
+   _e_process_manager = NULL;
+
+   return 1;
+}
+
+E_API E_Process_Hook *
+e_process_hook_add(E_Process_Hook_Point hookpoint, E_Process_Hook_Cb func, const void *data)
+{
+   E_Process_Hook *ph;
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(hookpoint >= E_PROCESS_HOOK_LAST, NULL);
+
+   ph = E_NEW(E_Process_Hook, 1);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ph, NULL);
+
+   ph->hookpoint = hookpoint;
+   ph->func = func;
+   ph->data = (void*)data;
+
+   _e_process_hooks[hookpoint] = eina_inlist_append(_e_process_hooks[hookpoint], EINA_INLIST_GET(ph));
+   return ph;
+}
+
+E_API void
+e_process_hook_del(E_Process_Hook *ph)
+{
+   ph->delete_me = 1;
+   if (_e_process_hooks_walking == 0)
+     {
+        _e_process_hooks[ph->hookpoint] = eina_inlist_remove(_e_process_hooks[ph->hookpoint], EINA_INLIST_GET(ph));
+        free(ph);
+     }
+   else
+     _e_process_hooks_delete++;
+}
diff --git a/src/bin/e_process.h b/src/bin/e_process.h
new file mode 100644 (file)
index 0000000..bd3a084
--- /dev/null
@@ -0,0 +1,74 @@
+# ifdef E_TYPEDEFS
+
+typedef enum _E_Process_Action
+{
+   E_PROCESS_ACT_LAUNCH = 0,
+   E_PROCESS_ACT_RESUME = 1,
+   E_PROCESS_ACT_TERMINATE = 2,
+   E_PROCESS_ACT_FOREGROUND = 3,
+   E_PROCESS_ACT_BACKGROUND = 4,
+   E_PROCESS_ACT_ACTIVATE = 5,
+   E_PROCESS_ACT_DEACTIVATE = 6,
+} E_Process_Action;
+
+typedef enum _E_Process_State
+{
+   E_PROCESS_STATE_UNKNOWN,
+   E_PROCESS_STATE_BACKGROUND,
+   E_PROCESS_STATE_FOREGROUND,
+} E_Process_State;
+
+typedef enum _E_Process_Hook_Point
+{
+   E_PROCESS_HOOK_STATE_CHANGE,
+   E_PROCESS_HOOK_ACTION_CHANGE,
+   E_PROCESS_HOOK_LAST
+} E_Process_Hook_Point;
+
+typedef struct _E_Process_Manager E_Process_Manager;
+typedef struct _E_Process E_Process;
+
+typedef struct _E_Process_Hook E_Process_Hook;
+
+typedef void (*E_Process_Hook_Cb)(void *data, E_Process *epro, void *user);
+
+
+# else
+
+# ifndef E_PROCESSMGR_H
+# define E_PROCESSMGR_H
+
+struct _E_Process_Hook
+{
+   EINA_INLIST;
+   E_Process_Hook_Point hookpoint;
+   E_Process_Hook_Cb    func;
+   void                *data;
+   unsigned char        delete_me : 1;
+};
+
+struct _E_Process_Manager
+{
+   Eina_Hash         *pids_hash;
+   Eina_Inlist       *process_list;
+   E_Client          *active_win;
+};
+
+struct _E_Process
+{
+   EINA_INLIST;
+   pid_t            pid;
+   Eina_List       *ec_list;
+   E_Process_State  state;
+};
+
+E_API Eina_Bool  e_process_init(void);
+E_API int        e_process_shutdown(void);
+
+E_API E_Process_Hook *e_process_hook_add(E_Process_Hook_Point hookpoint, E_Process_Hook_Cb func, const void *data);
+E_API void            e_process_hook_del(E_Process_Hook *ph);
+
+
+#endif
+#endif
+