discomfitor pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=f4470e002e06ea4cbab690cbca27bd73be145934

commit f4470e002e06ea4cbab690cbca27bd73be145934
Author: Mike Blumenkrantz <[email protected]>
Date:   Fri Nov 3 11:47:35 2017 -0400

    add gadget sandboxing
    
    docs in progress, tasks https://phab.enlightenment.org/project/board/179/
---
 meson.build                   |   2 +
 src/Makefile.mk               |   1 +
 src/bin/Makefile.mk           |   5 +-
 src/bin/e_gadget.c            |  12 +
 src/bin/e_gadget_loader.c     | 310 ++++++++++++++
 src/bin/e_gadget_runner.c     | 942 ++++++++++++++++++++++++++++++++++++++++++
 src/bin/generated/meson.build |  13 +
 src/bin/meson.build           |  14 +
 src/protocol/e-gadget.xml     |  38 ++
 9 files changed, 1336 insertions(+), 1 deletion(-)

diff --git a/meson.build b/meson.build
index 372ced629..02dbdede7 100644
--- a/meson.build
+++ b/meson.build
@@ -271,6 +271,7 @@ if get_option('wayland') == true
   dir_wayland_protocols = 
wayland_protocols.get_pkgconfig_variable('pkgdatadir')
   wayland_version = '>= 1.11.0'
   dep_wayland = [ dependency('ecore-wl2'),
+                  dependency('efl-wl'),
                   dependency('wayland-server' , version: wayland_version),
                   dependency('wayland-client' , version: wayland_version),
                   wayland_protocols,
@@ -286,6 +287,7 @@ if get_option('wayland') == true
   endif
   requires_wayland = ' '.join([ 'wayland-protocols >= 1.9',
                                 'ecore-wl2',
+                                'efl-wl',
                                 requires_drm,
                                 ' '.join(['wayland-server' , wayland_version]),
                                 ' '.join(['wayland-client' , wayland_version]),
diff --git a/src/Makefile.mk b/src/Makefile.mk
index e4cf2bb8c..a3ebd6224 100644
--- a/src/Makefile.mk
+++ b/src/Makefile.mk
@@ -7,4 +7,5 @@ src/protocol/session-recovery.xml \
 src/protocol/efl-aux-hints.xml \
 src/protocol/www.xml \
 src/protocol/action_route.xml \
+src/protocol/e-gadget.xml \
 src/protocol/xdg-foreign-unstable-v1.xml
diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk
index 4235b821c..cc558ed2d 100644
--- a/src/bin/Makefile.mk
+++ b/src/bin/Makefile.mk
@@ -1,6 +1,9 @@
 DISTCLEANFILES += src/bin/e_fm_shared_types.h
 
-EXTRA_DIST += src/bin/e_drm2.x
+EXTRA_DIST += \
+src/bin/e_drm2.x \
+src/bin/e_gadget_runner.c \
+src/bin/e_gadget_loader.c
 
 efx_files = \
 src/bin/efx/efx_bumpmapping.c \
diff --git a/src/bin/e_gadget.c b/src/bin/e_gadget.c
index 1de22e091..bddc1387e 100644
--- a/src/bin/e_gadget.c
+++ b/src/bin/e_gadget.c
@@ -1,5 +1,11 @@
 #include "e.h"
 
+
+#ifdef HAVE_WAYLAND
+EINTERN void e_gadget_runner_init(void);
+EINTERN void e_gadget_runner_shutdown(void);
+#endif
+
 #define SNAP_DISTANCE 5
 #define E_GADGET_TYPE 0xE31337
 
@@ -2492,6 +2498,9 @@ e_gadget_init(void)
    evas_object_event_callback_add(e_comp->canvas->resize_object, 
EVAS_CALLBACK_RESIZE, _comp_site_resize, comp_site);
    evas_object_layer_set(comp_site, E_LAYER_DESKTOP);
    evas_object_resize(comp_site, e_comp->w, e_comp->h);
+#ifdef HAVE_WAYLAND
+   e_gadget_runner_init();
+#endif
 }
 
 EINTERN void
@@ -2499,6 +2508,9 @@ e_gadget_shutdown(void)
 {
    E_Gadget_Site *zgs;
 
+#ifdef HAVE_WAYLAND
+   e_gadget_runner_shutdown();
+#endif
    E_FREE_LIST(handlers, ecore_event_handler_del);
    E_CONFIG_DD_FREE(edd_gadget);
    E_CONFIG_DD_FREE(edd_site);
diff --git a/src/bin/e_gadget_loader.c b/src/bin/e_gadget_loader.c
new file mode 100644
index 000000000..1b32304f5
--- /dev/null
+++ b/src/bin/e_gadget_loader.c
@@ -0,0 +1,310 @@
+#include "config.h"
+#define EFL_BETA_API_SUPPORT
+#include <Ecore_Wl2.h>
+#include <Elementary.h>
+#include <dlfcn.h>
+#include "e-gadget-client-protocol.h"
+#include "action_route-client-protocol.h"
+#include <uuid.h>
+
+static Ecore_Event_Handler *handler;
+
+static Eina_Hash *wins;
+static Eina_Hash *gadget_globals;
+static Eina_Hash *ar_globals;
+static Eina_Hash *display_actions;
+
+typedef struct Gadget_Action
+{
+   Ecore_Wl2_Display *d;
+   Eina_Stringshare *action;
+   char handle[37];
+   Eina_List *requestors;
+   struct action_route_bind *ar_bind;
+} Gadget_Action;
+
+static inline Ecore_Wl2_Display *
+win_display_get(Evas_Object *win)
+{
+   Ecore_Wl2_Window *ww;
+   ww = elm_win_wl_window_get(win);
+   return ecore_wl2_window_display_get(ww);
+}
+
+static void
+wins_emit(Ecore_Wl2_Display *d, const char *sig, uint32_t val)
+{
+   Eina_List *l, *ll;
+   Evas_Object *win;
+
+   l = eina_hash_find(wins, &d);
+   EINA_LIST_FOREACH(l, ll, win)
+     evas_object_smart_callback_call(win, sig, (uintptr_t*)(uintptr_t)val);
+}
+
+static void
+_gadget_anchor(void *data, struct e_gadget *e_gadget EINA_UNUSED, uint32_t 
anchor)
+{
+   wins_emit(data, "gadget_site_anchor", anchor);
+}
+
+static void
+_gadget_orient(void *data, struct e_gadget *e_gadget EINA_UNUSED, uint32_t 
orient)
+{
+   wins_emit(data, "gadget_site_orient", orient);
+}
+
+static void
+_gadget_gravity(void *data, struct e_gadget *e_gadget EINA_UNUSED, uint32_t 
gravity)
+{
+   wins_emit(data, "gadget_site_gravity", gravity);
+}
+
+static const struct e_gadget_listener _gadget_listener =
+{
+   _gadget_anchor,
+   _gadget_orient,
+   _gadget_gravity,
+};
+
+static void
+_gadget_global_bind(Ecore_Wl2_Display *d, uint32_t id)
+{
+   struct e_gadget *gadget_global = 
wl_registry_bind(ecore_wl2_display_registry_get(d), id, &e_gadget_interface, 1);
+   e_gadget_add_listener(gadget_global, &_gadget_listener, d);
+   eina_hash_add(gadget_globals, &d, gadget_global);
+}
+
+static void
+_ar_global_bind(Ecore_Wl2_Display *d, uint32_t id)
+{
+   struct action_route *ar_global = 
wl_registry_bind(ecore_wl2_display_registry_get(d), id, 
&action_route_interface, 1);
+   eina_hash_add(ar_globals, &d, ar_global);
+}
+
+static Eina_Bool
+_global_added(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Wl2_Event_Global 
*ev)
+{
+   if (eina_streq(ev->interface, "e_gadget"))
+     _gadget_global_bind(ev->display, ev->id);
+   else if (eina_streq(ev->interface, "action_route"))
+     _ar_global_bind(ev->display, ev->id);
+   return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_gadget_init(Ecore_Wl2_Display *d)
+{
+   Eina_Iterator *it;
+   Ecore_Wl2_Global *g;
+
+   if (wins)
+     {
+        if (eina_hash_find(gadget_globals, &d)) return;
+     }
+   else
+     {
+        gadget_globals = eina_hash_pointer_new(NULL);
+        ar_globals = eina_hash_pointer_new(NULL);
+     }
+   it = ecore_wl2_display_globals_get(d);
+   EINA_ITERATOR_FOREACH(it, g)
+     {
+        if (eina_streq(g->interface, "e_gadget"))
+          _gadget_global_bind(d, g->id);
+        else if (eina_streq(g->interface, "action_route"))
+          _ar_global_bind(d, g->id);
+     }
+   eina_iterator_free(it);
+   if (!handler)
+     handler = ecore_event_handler_add(ECORE_WL2_EVENT_GLOBAL_ADDED, 
(Ecore_Event_Handler_Cb)_global_added, NULL);
+}
+
+static void
+_ar_bind_activate(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+   const char *params = event_info;
+   Gadget_Action *ga = data;
+
+   if (params && (!params[0])) params = NULL;
+   action_route_bind_activate(ga->ar_bind, params);
+}
+
+static void
+_ar_bind_del(Gadget_Action *ga)
+{
+   Evas_Object *r;
+   eina_stringshare_del(ga->action);
+   EINA_LIST_FREE(ga->requestors, r)
+     evas_object_smart_callback_del_full(r, ga->handle, _ar_bind_activate, ga);
+   free(ga);
+}
+
+static void
+_ar_bind_end(void *data, struct action_route_bind *action_route_bind 
EINA_UNUSED)
+{
+   Gadget_Action *ga = data;
+   Eina_List *l;
+   Evas_Object *r;
+
+   EINA_LIST_FOREACH(ga->requestors, l, r)
+     evas_object_smart_callback_call(r, "gadget_action_end", ga->handle);
+}
+
+static void
+_ar_bind_status(void *data, struct action_route_bind *action_route_bind, 
uint32_t state)
+{
+   uuid_t u;
+   Gadget_Action *ga = data;
+   Evas_Object *r;
+
+   if (state == ACTION_ROUTE_BIND_STATE_REJECTED)
+     {
+        Eina_Hash *h;
+        Eina_List *l;
+        h = eina_hash_find(display_actions, &ga->d);
+        EINA_LIST_FOREACH(ga->requestors, l, r)
+          {
+             if (ga->handle[0])
+               evas_object_smart_callback_call(r, "gadget_action_deleted", 
ga->handle);
+             else
+               evas_object_smart_callback_call(r, "gadget_action", NULL);
+          }
+        eina_hash_del_by_key(h, ga->action);
+        return;
+     }
+   uuid_generate(u);
+   uuid_unparse_lower(u, ga->handle);
+   ga->ar_bind = action_route_bind;
+   r = eina_list_data_get(ga->requestors);
+   evas_object_smart_callback_add(r, ga->handle, _ar_bind_activate, ga);
+   evas_object_smart_callback_call(r, "gadget_action", ga->handle);
+}
+
+static const struct action_route_bind_listener _ar_bind_interface =
+{
+   _ar_bind_status,
+   _ar_bind_end
+};
+
+static void
+uriopen_request(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+   Ecore_Wl2_Display *d = data;
+   const char *uri = event_info;
+   struct e_gadget *gadget_global = eina_hash_find(gadget_globals, &d);
+
+   e_gadget_open_uri(gadget_global, uri);
+}
+
+static void
+action_request(void *data, Evas_Object *obj, void *event_info)
+{
+   Gadget_Action *ga;
+   const char *action = event_info;
+   Ecore_Wl2_Display *d = data;
+   void *ar_global;
+   struct action_route_bind *ar_bind;
+   Eina_Hash *h;
+
+   if ((!action) || (!action[0]))
+     {
+        evas_object_smart_callback_call(obj, "gadget_action", NULL);
+        return;
+     }
+   if (display_actions)
+     {
+        h = eina_hash_find(display_actions, &d);
+        if (h)
+          {
+             ga = eina_hash_find(h, action);
+             if (ga && (!eina_list_data_find(ga->requestors, obj)))
+               {
+                  ga->requestors = eina_list_append(ga->requestors, obj);
+                  evas_object_smart_callback_add(obj, ga->handle, 
_ar_bind_activate, ga);
+               }
+             evas_object_smart_callback_call(obj, "gadget_action", ga ? 
ga->handle : NULL);
+             return;
+          }
+     }
+   ar_global = eina_hash_find(ar_globals, &d);
+   if (!ar_global)
+     {
+        evas_object_smart_callback_call(obj, "gadget_action", NULL);
+        return;
+     }
+   ga = calloc(1, sizeof(Gadget_Action));
+   ga->d = d;
+   ga->requestors = eina_list_append(ga->requestors, obj);
+   ga->action = eina_stringshare_add(action);
+   if (!display_actions)
+     display_actions = eina_hash_string_superfast_new(NULL);
+   h = eina_hash_find(display_actions, &d);
+   if (!h)
+     {
+        h = eina_hash_pointer_new((Eina_Free_Cb)_ar_bind_del);
+        eina_hash_add(display_actions, &d, h);
+     }
+   
+   ar_bind = action_route_bind_action(ar_global, action);
+   action_route_bind_add_listener(ar_bind, &_ar_bind_interface, ga);
+   wl_display_roundtrip(ecore_wl2_display_get(d));
+}
+
+static void
+win_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
+{
+   Ecore_Wl2_Display *d = win_display_get(obj);
+   eina_hash_list_remove(wins, &d, obj);
+}
+
+static Evas_Object *
+win_add(Evas_Object *win)
+{
+   Ecore_Wl2_Display *d;
+   if (!win) return NULL;
+   d = win_display_get(win);
+   _gadget_init(d);
+   if (!wins)
+     wins = eina_hash_pointer_new(NULL);
+   eina_hash_list_append(wins, &d, win);
+   evas_object_smart_callback_add(win, "gadget_action_request", 
action_request, d);
+   evas_object_smart_callback_add(win, "gadget_open_uri", uriopen_request, d);
+   evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, win_del, NULL);
+   elm_win_borderless_set(win, 1);
+   return win;
+}
+
+int
+eina_init(void)
+{
+   int (*_eina_init)(void) = dlsym(RTLD_NEXT, __func__);
+
+   if (wins) return _eina_init();
+   if (getenv("RUNNER_DEBUG")) raise(SIGSTOP);
+   return _eina_init();
+}
+
+Evas_Object *
+elm_win_util_dialog_add(Evas_Object *parent, const char *name, const char 
*title)
+{
+   Evas_Object *(*_elm_win_util_dialog_add)(Evas_Object *, const char *, const 
char *) = dlsym(RTLD_NEXT, __func__);
+
+   return win_add(_elm_win_util_dialog_add(parent, name, title));
+}
+
+Evas_Object *
+elm_win_util_standard_add(const char *name, const char *title)
+{
+   Evas_Object *(*_elm_win_util_standard_add)(const char *, const char *) = 
dlsym(RTLD_NEXT, __func__);
+
+   return win_add(_elm_win_util_standard_add(name, title));
+}
+
+Evas_Object *
+elm_win_add(Evas_Object *parent, const char *name, Elm_Win_Type type)
+{
+   Evas_Object *(*_elm_win_add)(Evas_Object *,const char*, Elm_Win_Type) = 
dlsym(RTLD_NEXT, __func__);
+
+   return win_add(_elm_win_add(parent, name, type));
+}
diff --git a/src/bin/e_gadget_runner.c b/src/bin/e_gadget_runner.c
new file mode 100644
index 000000000..965bc3d35
--- /dev/null
+++ b/src/bin/e_gadget_runner.c
@@ -0,0 +1,942 @@
+#include "e.h"
+#include <Efl_Wl.h>
+#include "e-gadget-server-protocol.h"
+#include "action_route-server-protocol.h"
+#include <sched.h>
+
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wformat-truncation"
+#endif
+
+typedef enum
+{
+   EXIT_MODE_RESTART,
+   EXIT_MODE_DELETE,
+} Exit_Mode;
+
+typedef struct Config_Item
+{
+   int id;
+   int exit_mode;
+   Eina_Stringshare *cmd;
+   void *inst;
+   Eina_Bool cmd_changed : 1;
+} Config_Item;
+
+typedef struct Instance
+{
+   Evas_Object *box;
+   Evas_Object *obj;
+   Ecore_Exe *exe;
+   Config_Item *ci;
+   Eina_Hash *allowed_pids;
+   void *gadget_resource;
+   Evas_Object *popup;
+   Evas_Object *ctxpopup;
+   Eina_List *extracted;
+} Instance;
+
+typedef struct RConfig
+{
+   Eina_List *items;
+   Evas_Object *config_dialog;
+} RConfig;
+
+static E_Config_DD *conf_edd = NULL;
+static E_Config_DD *conf_item_edd = NULL;
+
+static int ns_fd = -1;
+
+static RConfig *rconfig;
+static Eina_List *instances;
+static Eina_List *wizards;
+
+static Eina_Hash *sandbox_gadgets;
+
+static Eina_List *handlers;
+static Eio_Monitor *gadget_monitor;
+static Eio_File *gadget_lister;
+
+typedef struct Wizard_Item
+{
+   Evas_Object *site;
+   Evas_Object *popup;
+   E_Gadget_Wizard_End_Cb cb;
+   void *data;
+   int id;
+   Eina_Bool sandbox : 1;
+} Wizard_Item;
+
+static void
+runner_run(Instance *inst)
+{
+   char *preload = eina_strdup(getenv("LD_PRELOAD"));
+   char buf[PATH_MAX];
+   int pid;
+
+   snprintf(buf, sizeof(buf), "%s/enlightenment/gadgets/%s/loader.so", 
e_prefix_lib_get(), MODULE_ARCH);
+   e_util_env_set("LD_PRELOAD", buf);
+
+   snprintf(buf, sizeof(buf), "%d", inst->ci->id);
+   e_util_env_set("E_GADGET_ID", buf);
+
+   unshare(CLONE_NEWPID);
+
+   inst->exe = efl_wl_run(inst->obj, inst->ci->cmd);
+
+   setns(ns_fd, CLONE_NEWPID);
+
+   e_util_env_set("E_GADGET_ID", NULL);
+   e_util_env_set("LD_PRELOAD", preload);
+   free(preload);
+   eina_hash_free_buckets(inst->allowed_pids);
+   pid = ecore_exe_pid_get(inst->exe);
+   eina_hash_add(inst->allowed_pids, &pid, (void*)1);
+}
+
+static void
+_config_close(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
+{
+   Config_Item *ci = data;
+   Instance *inst = ci->inst;
+
+   e_comp_ungrab_input(1, 1);
+   rconfig->config_dialog = NULL;
+   if (ci->cmd_changed)
+     {
+        char *cmd;
+
+        cmd = 
elm_entry_markup_to_utf8(elm_entry_entry_get(evas_object_data_get(obj, 
"entry")));
+        eina_stringshare_replace(&ci->cmd, cmd);
+        free(cmd);
+        e_config_save_queue();
+     }
+   if (!inst) ci->cmd_changed = 0;
+   if (!ci->cmd_changed) return;
+   ci->cmd_changed = 0;
+   if (inst->exe) ecore_exe_quit(inst->exe);
+   runner_run(inst);
+}
+
+static void
+_config_label_add(Evas_Object *tb, const char *txt, int row)
+{
+   Evas_Object *o;
+
+   o = elm_label_add(tb);
+   E_ALIGN(o, 0, 0.5);
+   elm_object_text_set(o, txt);
+   evas_object_show(o);
+   elm_table_pack(tb, o, 0, row, 1, 1);
+}
+
+static void
+_config_cmd_changed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info 
EINA_UNUSED)
+{
+   Config_Item *ci = data;
+
+   ci->cmd_changed = 1;
+}
+
+static void
+_config_cmd_activate(void *data, Evas_Object *obj, void *event_info 
EINA_UNUSED)
+{
+   Config_Item *ci = data;
+   Instance *inst = ci->inst;
+   char *cmd;
+
+   ci->cmd_changed = 0;
+   cmd = elm_entry_markup_to_utf8(elm_entry_entry_get(obj));
+   eina_stringshare_replace(&ci->cmd, cmd);
+   free(cmd);
+   e_config_save_queue();
+   if (!inst) return;
+   if (inst->exe) ecore_exe_quit(inst->exe);
+   runner_run(inst);
+}
+
+EINTERN Evas_Object *
+config_runner(Config_Item *ci, E_Zone *zone)
+{
+   Evas_Object *popup, *tb, *o, *ent, *rg;
+   int row = 0;
+
+   if (!zone) zone = e_zone_current_get();
+   popup = elm_popup_add(e_comp->elm);
+   E_EXPAND(popup);
+   evas_object_layer_set(popup, E_LAYER_POPUP);
+   elm_popup_allow_events_set(popup, 1);
+   elm_popup_scrollable_set(popup, 1);
+
+   tb = elm_table_add(popup);
+   elm_table_align_set(tb, 0, 0.5);
+   E_EXPAND(tb);
+   evas_object_show(tb);
+   elm_object_content_set(popup, tb);
+
+   o = evas_object_rectangle_add(e_comp->evas);
+   evas_object_size_hint_min_set(o, ELM_SCALE_SIZE(200), 1);
+   elm_table_pack(tb, o, 0, row++, 2, 1);
+
+   _config_label_add(tb, _("Command:"), row);
+   ent = o = elm_entry_add(tb);
+   E_FILL(o);
+   evas_object_show(o);
+   elm_entry_single_line_set(o, 1);
+   elm_entry_entry_set(o, ci->cmd);
+   evas_object_smart_callback_add(o, "changed,user", _config_cmd_changed, ci);
+   evas_object_smart_callback_add(o, "activated", _config_cmd_activate, ci);
+   elm_table_pack(tb, o, 1, row++, 1, 1);
+
+   _config_label_add(tb, _("On Exit:"), row);
+   o = rg = elm_radio_add(tb);
+   E_FILL(o);
+   evas_object_show(o);
+   elm_object_text_set(o, _("Restart"));
+   elm_radio_state_value_set(o, EXIT_MODE_RESTART);
+   elm_radio_value_pointer_set(o, &ci->exit_mode);
+   elm_table_pack(tb, o, 1, row++, 1, 1);
+
+   o = elm_radio_add(tb);
+   E_FILL(o);
+   elm_radio_group_add(o, rg);
+   evas_object_show(o);
+   elm_object_text_set(o, _("Delete"));
+   elm_radio_state_value_set(o, EXIT_MODE_DELETE);
+   elm_table_pack(tb, o, 1, row++, 1, 1);
+
+
+   popup = e_comp_object_util_add(popup, E_COMP_OBJECT_TYPE_NONE);
+   evas_object_layer_set(popup, E_LAYER_POPUP);
+   evas_object_move(popup, zone->x, zone->y);
+   evas_object_resize(popup, zone->w / 4, zone->h / 3);
+   e_comp_object_util_center(popup);
+   evas_object_show(popup);
+   e_comp_object_util_autoclose(popup, NULL, 
e_comp_object_util_autoclose_on_escape, NULL);
+   evas_object_event_callback_priority_add(popup, EVAS_CALLBACK_DEL, 
EVAS_CALLBACK_PRIORITY_BEFORE, _config_close, ci);
+   evas_object_data_set(popup, "entry", ent);
+   e_comp_grab_input(1, 1);
+
+   elm_object_focus_set(ent, 1);
+
+   return rconfig->config_dialog = popup;
+}
+
+static Config_Item *
+_conf_item_get(int *id)
+{
+   Config_Item *ci;
+   Eina_List *l;
+
+   if (*id > 0)
+     {
+        EINA_LIST_FOREACH(rconfig->items, l, ci)
+          if (*id == ci->id) return ci;
+     }
+
+   ci = E_NEW(Config_Item, 1);
+   if (!*id)
+     *id = ci->id = rconfig->items ? eina_list_count(rconfig->items) + 1 : 1;
+   else
+     ci->id = *id;
+
+   if (ci->id < 1) return ci;
+   rconfig->items = eina_list_append(rconfig->items, ci);
+   e_config_save_queue();
+
+   return ci;
+}
+
+static void
+wizard_site_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, 
void *event_info EINA_UNUSED)
+{
+   Wizard_Item *wi = data;
+   wi->site = NULL;
+   evas_object_hide(wi->popup);
+   evas_object_del(wi->popup);
+}
+
+static void
+_wizard_config_end(void *data, Evas *e EINA_UNUSED, Evas_Object *obj 
EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Wizard_Item *wi = data;
+   Eina_List *l;
+   Config_Item *ci;
+
+   EINA_LIST_FOREACH(rconfig->items, l, ci)
+     {
+        if (ci->id == wi->id)
+          {
+             if (ci->cmd) break;
+             wi->id = 0;
+             free(ci);
+             rconfig->items = eina_list_remove_list(rconfig->items, l);
+             break;
+          }
+     }
+
+   if (wi->site)
+     wi->cb(wi->data, wi->id);
+   wizards = eina_list_remove(wizards, wi);
+   if (wi->site)
+     evas_object_event_callback_del_full(wi->site, EVAS_CALLBACK_DEL, 
wizard_site_del, wi);
+   free(wi);
+}
+
+static Evas_Object *
+runner_wizard(E_Gadget_Wizard_End_Cb cb, void *data, Evas_Object *site)
+{
+   int id = 0;
+   Config_Item *ci;
+   Wizard_Item *wi;
+
+   wi = E_NEW(Wizard_Item, 1);
+   wi->cb = cb;
+   wi->data = data;
+   wi->site = site;
+   evas_object_event_callback_add(wi->site, EVAS_CALLBACK_DEL, 
wizard_site_del, wi);
+   wizards = eina_list_append(wizards, wi);
+
+   ci = _conf_item_get(&id);
+   wi->id = ci->id;
+   wi->popup = config_runner(ci, NULL);
+   evas_object_event_callback_add(wi->popup, EVAS_CALLBACK_DEL, 
_wizard_config_end, wi);
+   return wi->popup;
+}
+
+/////////////////////////////////////////
+
+static void
+mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void 
*event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   evas_object_focus_set(inst->obj, 1);
+}
+
+static void
+runner_removed(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+   Instance *inst = data;
+   if (inst->box != event_info) return;
+   rconfig->items = eina_list_remove(rconfig->items, inst->ci);
+   eina_stringshare_del(inst->ci->cmd);
+   E_FREE(inst->ci);
+}
+
+static void
+runner_site_gravity(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   if (inst->gadget_resource)
+     e_gadget_send_gadget_gravity(inst->gadget_resource, 
e_gadget_site_gravity_get(obj));
+}
+
+static void
+runner_site_anchor(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   if (inst->gadget_resource)
+     e_gadget_send_gadget_anchor(inst->gadget_resource, 
e_gadget_site_anchor_get(obj));
+}
+
+static void
+runner_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info 
EINA_UNUSED)
+{
+   Instance *inst = data;
+   Evas_Object *site = e_gadget_site_get(obj);
+
+   evas_object_smart_callback_del_full(site, "gadget_removed", runner_removed, 
inst);
+   evas_object_smart_callback_del_full(site, "gadget_site_anchor", 
runner_site_anchor, inst);
+   evas_object_smart_callback_del_full(site, "gadget_site_gravity", 
runner_site_gravity, inst);
+   if (inst->ci)
+     {
+        inst->ci->inst = NULL;
+        E_FREE_FUNC(inst->exe, ecore_exe_quit);
+     }
+   else
+     E_FREE_FUNC(inst->exe, ecore_exe_terminate);
+   instances = eina_list_remove(instances, inst);
+   eina_hash_free(inst->allowed_pids);
+   free(inst);
+}
+
+static Evas_Object *
+runner_gadget_configure(Evas_Object *g)
+{
+   Instance *inst = evas_object_data_get(g, "runner");
+   return config_runner(inst->ci, e_comp_object_util_zone_get(g));
+}
+
+static void
+runner_created(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   if (inst->box != event_info) return;
+   e_gadget_configure_cb_set(inst->box, runner_gadget_configure);
+   evas_object_smart_callback_del_full(obj, "gadget_created", runner_created, 
data);
+}
+
+
+static void
+gadget_unbind(struct wl_resource *resource)
+{
+   Instance *inst = wl_resource_get_user_data(resource);
+   inst->gadget_resource = NULL;
+}
+
+static void
+gadget_open_uri(struct wl_client *client EINA_UNUSED, struct wl_resource 
*resource EINA_UNUSED, const char *uri)
+{
+   //Instance *inst = wl_resource_get_user_data(resource);
+
+   /* FIXME: rate limit? */
+   e_util_open(uri, NULL);
+}
+
+static const struct e_gadget_interface _gadget_interface =
+{
+   .open_uri = gadget_open_uri,
+};
+
+static void
+gadget_bind(struct wl_client *client, void *data, uint32_t version, uint32_t 
id)
+{
+   struct wl_resource *res;
+   Instance *inst = data;
+   pid_t pid;
+   Evas_Object *site;
+
+   wl_client_get_credentials(client, &pid, NULL, NULL);
+   if (pid != ecore_exe_pid_get(inst->exe))
+     {
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   res = wl_resource_create(client, &e_gadget_interface, version, id);
+   wl_resource_set_implementation(res, &_gadget_interface, data, 
gadget_unbind);
+   inst->gadget_resource = res;
+   site = e_gadget_site_get(inst->box);
+   e_gadget_send_gadget_orient(res, e_gadget_site_orient_get(site));
+   e_gadget_send_gadget_gravity(res, e_gadget_site_gravity_get(site));
+   e_gadget_send_gadget_anchor(res, e_gadget_site_anchor_get(site));
+}
+
+static void
+ar_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+   struct wl_resource *res;
+   Instance *inst = data;
+   int v;
+   const void *ar_interface;
+   pid_t pid;
+
+   wl_client_get_credentials(client, &pid, NULL, NULL);
+   if (pid != ecore_exe_pid_get(inst->exe))
+     {
+        wl_client_post_no_memory(client);
+        return;
+     }
+   ar_interface = e_comp_wl_extension_action_route_interface_get(&v);
+
+   if (!(res = wl_resource_create(client, &action_route_interface, MIN(v, 
version), id)))
+     {
+        wl_client_post_no_memory(client);
+        return;
+     }
+
+   wl_resource_set_implementation(res, ar_interface, inst->allowed_pids, NULL);
+}
+
+static void
+child_close(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   Evas_Object *ext;
+
+   inst->popup = NULL;
+   ext = evas_object_data_get(obj, "extracted");
+   elm_box_unpack_all(obj);
+   inst->extracted = eina_list_remove(inst->extracted, ext);
+   evas_object_hide(ext);
+}
+
+static void
+child_added(void *data, Evas_Object *obj, void *event_info)
+{
+   Evas_Object *popup, *bx;
+   E_Zone *zone = e_comp_object_util_zone_get(obj);
+   Instance *inst = data;
+
+   if (!efl_wl_surface_extract(event_info)) return;
+   inst->extracted = eina_list_append(inst->extracted, event_info);
+
+   popup = elm_popup_add(e_comp->elm);
+   e_comp_object_util_del_list_append(event_info, popup);
+   E_EXPAND(popup);
+   evas_object_layer_set(popup, E_LAYER_POPUP);
+   elm_popup_allow_events_set(popup, 1);
+   elm_popup_scrollable_set(popup, 1);
+
+   bx = elm_box_add(popup);
+   E_EXPAND(event_info);
+   E_FILL(event_info);
+   elm_box_homogeneous_set(bx, 1);
+   evas_object_show(bx);
+   elm_box_pack_end(bx, event_info);
+   elm_object_content_set(popup, bx);
+
+   inst->popup = popup = e_comp_object_util_add(popup, 
E_COMP_OBJECT_TYPE_NONE);
+   evas_object_layer_set(popup, E_LAYER_POPUP);
+   evas_object_move(popup, zone->x, zone->y);
+   evas_object_resize(popup, zone->w / 4, zone->h / 3);
+   e_comp_object_util_center(popup);
+   evas_object_show(popup);
+   e_comp_canvas_feed_mouse_up(0);
+   e_comp_object_util_autoclose(popup, NULL, 
e_comp_object_util_autoclose_on_escape, NULL);
+   evas_object_event_callback_add(bx, EVAS_CALLBACK_DEL, child_close, inst);
+   evas_object_data_set(bx, "extracted", event_info);
+   evas_object_focus_set(event_info, 1);
+}
+
+static void
+popup_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info 
EINA_UNUSED)
+{
+   Instance *inst = data;
+   Evas_Object *ext;
+
+   inst->ctxpopup = NULL;
+   ext = evas_object_data_get(obj, "extracted");
+   elm_box_unpack_all(obj);
+   inst->extracted = eina_list_remove(inst->extracted, ext);
+   evas_object_hide(ext);
+}
+
+static void
+popup_dismissed(void *data EINA_UNUSED, Evas_Object *obj, void *event_info 
EINA_UNUSED)
+{
+   evas_object_del(obj);
+}
+
+static void
+popup_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void 
*event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   elm_ctxpopup_dismiss(inst->ctxpopup);
+   evas_object_del(elm_object_content_get(inst->ctxpopup));
+}
+
+static void
+popup_hints_update(Evas_Object *obj)
+{
+   double w, h;
+   E_Zone *zone = e_comp_object_util_zone_get(obj);
+
+   evas_object_size_hint_weight_get(obj, &w, &h);
+   w = E_CLAMP(w, 0, 0.5);
+   h = E_CLAMP(h, 0, 0.5);
+
+   if ((w > 0) && (h > 0))
+     {
+        evas_object_size_hint_min_set(obj, w * zone->w, h * zone->h);
+        evas_object_size_hint_max_set(obj, w * zone->w, h * zone->h);
+     }
+   if ((!EINA_DBL_NONZERO(w)) && (!EINA_DBL_NONZERO(h)))
+     {
+        int ww, hh;
+        evas_object_geometry_get(obj, NULL, NULL, &ww, &hh);
+        evas_object_size_hint_min_set(obj, ww, hh);
+     }
+   E_WEIGHT(obj, 0, 0);
+}
+
+static void
+popup_hints(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
+{
+   evas_object_event_callback_del_full(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, 
popup_hints, data);
+   popup_hints_update(obj);
+   evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, 
popup_hints, data);
+}
+
+static void
+popup_added(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+   Instance *inst = data;
+   Evas_Object *bx;
+
+   if (!efl_wl_surface_extract(event_info)) return;
+   inst->extracted = eina_list_append(inst->extracted, event_info);
+
+   inst->ctxpopup = elm_ctxpopup_add(inst->box);
+   elm_object_style_set(inst->ctxpopup, "noblock");
+   evas_object_smart_callback_add(inst->ctxpopup, "dismissed", 
popup_dismissed, inst);
+   evas_object_event_callback_add(event_info, EVAS_CALLBACK_DEL, popup_hide, 
inst);
+
+   bx = elm_box_add(inst->ctxpopup);
+   popup_hints_update(event_info);
+   E_FILL(event_info);
+   evas_object_event_callback_add(event_info, 
EVAS_CALLBACK_CHANGED_SIZE_HINTS, popup_hints, inst);
+   evas_object_show(bx);
+   elm_box_pack_end(bx, event_info);
+   elm_box_recalculate(bx);
+   evas_object_data_set(bx, "extracted", event_info);
+   evas_object_event_callback_add(bx, EVAS_CALLBACK_DEL, popup_del, inst);
+   elm_object_content_set(inst->ctxpopup, bx);
+
+   e_gadget_util_ctxpopup_place(inst->box, inst->ctxpopup, NULL);
+   evas_object_show(inst->ctxpopup);
+   evas_object_focus_set(event_info, 1);
+}
+
+static void
+runner_hints(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
+{
+   Instance *inst = data;
+   int w, h;
+   Evas_Aspect_Control aspect;
+
+   evas_object_size_hint_min_get(obj, &w, &h);
+   evas_object_size_hint_min_set(inst->box, w, h);
+   evas_object_size_hint_max_get(obj, &w, &h);
+   evas_object_size_hint_max_set(inst->box, w, h);
+   evas_object_size_hint_aspect_get(obj, &aspect, &w, &h);
+   evas_object_size_hint_aspect_set(inst->box, aspect, w, h);
+}
+
+static Evas_Object *
+gadget_create(Evas_Object *parent, Config_Item *ci, int *id, 
E_Gadget_Site_Orient orient EINA_UNUSED)
+{
+   Instance *inst;
+   int ar_version;
+
+   inst = E_NEW(Instance, 1);
+   instances = eina_list_append(instances, inst);
+   inst->ci = ci;
+   if (!inst->ci)
+     inst->ci = _conf_item_get(id);
+   inst->ci->inst = inst;
+   inst->allowed_pids = eina_hash_int32_new(NULL);
+   inst->obj = efl_wl_add(e_comp->evas);
+   E_EXPAND(inst->obj);
+   E_FILL(inst->obj);
+   evas_object_show(inst->obj);
+   efl_wl_aspect_set(inst->obj, 1);
+   efl_wl_minmax_set(inst->obj, 1);
+   efl_wl_global_add(inst->obj, &e_gadget_interface, 1, inst, gadget_bind);
+   evas_object_smart_callback_add(inst->obj, "child_added", child_added, inst);
+   evas_object_smart_callback_add(inst->obj, "popup_added", popup_added, inst);
+   e_comp_wl_extension_action_route_interface_get(&ar_version);
+   efl_wl_global_add(inst->obj, &action_route_interface, ar_version, inst, 
ar_bind);
+   evas_object_data_set(inst->obj, "runner", inst);
+   evas_object_event_callback_add(inst->obj, EVAS_CALLBACK_MOUSE_DOWN, 
mouse_down, inst);
+   evas_object_smart_callback_add(parent, "gadget_created", runner_created, 
inst);
+   evas_object_smart_callback_add(parent, "gadget_removed", runner_removed, 
inst);
+   evas_object_smart_callback_add(parent, "gadget_site_anchor", 
runner_site_anchor, inst);
+   evas_object_smart_callback_add(parent, "gadget_site_gravity", 
runner_site_gravity, inst);
+   runner_run(inst);
+   ecore_exe_data_set(inst->exe, inst);
+   inst->box = elm_box_add(e_comp->elm);
+   evas_object_event_callback_add(inst->box, EVAS_CALLBACK_DEL, runner_del, 
inst);
+   evas_object_event_callback_add(inst->obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, 
runner_hints, inst);
+   elm_box_homogeneous_set(inst->box, 1);
+   elm_box_pack_end(inst->box, inst->obj);
+   return inst->box;
+}
+
+static Evas_Object *
+runner_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient)
+{
+   Evas_Object *obj;
+   Config_Item *ci = NULL;
+
+   if (orient) return NULL;
+   if (*id > 0) ci = _conf_item_get(id);
+   if ((*id < 0) || ci->inst)
+     {
+        obj = elm_image_add(parent);
+        elm_image_file_set(obj, e_theme_edje_file_get(NULL, 
"e/icons/modules-launcher"), "e/icons/modules-launcher");
+        evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
+        return obj;
+     }
+   return gadget_create(parent, ci, id, orient);
+}
+
+static Eina_Bool
+runner_exe_del(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Exe_Event_Del *ev)
+{
+   Instance *inst = ecore_exe_data_get(ev->exe);
+
+   if ((!inst) || (!instances) || (!eina_list_data_find(instances, inst))) 
return ECORE_CALLBACK_RENEW;
+   switch (inst->ci->exit_mode)
+     {
+      case EXIT_MODE_RESTART:
+        /* FIXME: probably notify? */
+        if (ev->exit_code == 255) //exec error
+          e_gadget_del(inst->box);
+        else
+          {
+             runner_run(inst);
+             ecore_exe_data_set(inst->exe, inst);
+          }
+        break;
+      case EXIT_MODE_DELETE:
+        e_gadget_del(inst->box);
+        break;
+     }
+   return ECORE_CALLBACK_RENEW;
+}
+
+///////////////////////////////
+
+static Evas_Object *
+sandbox_create(Evas_Object *parent, const char *type, int *id, 
E_Gadget_Site_Orient orient)
+{
+   Efreet_Desktop *ed = eina_hash_find(sandbox_gadgets, type);
+   Config_Item *ci = NULL;
+
+   if (*id > 0) ci = _conf_item_get(id);
+   if ((*id < 0) || (ci && ci->inst))
+     {
+        if (ed->x)
+          {
+             const char *orients = eina_hash_find(ed->x, 
"X-Gadget-Orientations");
+
+             if (orients)
+               {
+                  const char *ostring[] =
+                  {
+                     "None",
+                     "Horizontal",
+                     "Vertical",
+                  };
+                  const char *v, *val = orients;
+                  Eina_Bool found = EINA_FALSE;
+
+                  v = strchr(val, ';');
+                  do
+                    {
+                       if (v)
+                         {
+                            if (!memcmp(val, ostring[orient], v - val))
+                              {
+                                 found = EINA_TRUE;
+                                 break;
+                              }
+                            val = v + 1;
+                            v = strchr(val, ';');
+                         }
+                       else
+                         {
+                            if (!strcmp(val, ostring[orient]))
+                              found = EINA_TRUE;
+                            break;
+                         }
+                    } while (val[0]);
+                  if (!found) return NULL;
+               }
+          }
+        if (ed->icon)
+          {
+             int w, h;
+             Eina_Bool fail = EINA_FALSE;
+             Evas_Object *obj;
+
+             obj = elm_image_add(parent);
+             if (ed->icon[0] == '/')
+               {
+                  if (eina_str_has_extension(ed->icon, ".edj"))
+                    fail = !elm_image_file_set(obj, ed->icon, "icon");
+                  else
+                    fail = !elm_image_file_set(obj, ed->icon, NULL);
+               }
+             else
+               {
+                  if (!elm_image_file_set(obj, e_theme_edje_file_get(NULL, 
ed->icon), ed->icon))
+                    fail = !elm_icon_standard_set(obj, ed->icon);
+               }
+             if (!fail)
+               {
+                  elm_image_object_size_get(obj, &w, &h);
+                  if (w && h)
+                    evas_object_size_hint_aspect_set(obj, 
EVAS_ASPECT_CONTROL_BOTH, w, h);
+                  return obj;
+               }
+             evas_object_del(obj);
+          }
+     }
+   if (!ci)
+     {
+        ci = _conf_item_get(id);
+        ci->cmd = eina_stringshare_add(ed->exec);
+        ci->exit_mode = EXIT_MODE_RESTART;
+     }
+   return gadget_create(parent, ci, id, orient);
+}
+
+static char *
+sandbox_name(const char *filename)
+{
+   Efreet_Desktop *ed = eina_hash_find(sandbox_gadgets, filename);
+   const char *name = ed->name ?: ed->generic_name;
+   char buf[1024];
+
+   if (name) return strdup(name);
+   strncpy(buf, ed->orig_path, sizeof(buf) - 1);
+   buf[0] = toupper(buf[0]);
+   return strdup(buf);
+}
+
+///////////////////////////////
+
+static void
+gadget_dir_add(const char *filename)
+{
+   const char *file;
+   char buf[PATH_MAX];
+   Efreet_Desktop *ed;
+
+   file = ecore_file_file_get(filename);
+   snprintf(buf, sizeof(buf), "%s/%s.desktop", filename, file);
+   ed = efreet_desktop_new(buf);
+   EINA_SAFETY_ON_NULL_RETURN(ed);
+   eina_hash_add(sandbox_gadgets, filename, ed);
+   e_gadget_external_type_add("runner_sandbox", filename, sandbox_create, 
NULL);
+   e_gadget_external_type_name_cb_set("runner_sandbox", filename, 
sandbox_name);
+}
+
+static Eina_Bool
+monitor_dir_create(void *d EINA_UNUSED, int t EINA_UNUSED, Eio_Monitor_Event 
*ev)
+{
+   if (!eina_hash_find(sandbox_gadgets, ev->filename))
+     gadget_dir_add(ev->filename);
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+monitor_dir_del(void *d EINA_UNUSED, int t EINA_UNUSED, Eio_Monitor_Event *ev)
+{
+   eina_hash_del_by_key(sandbox_gadgets, ev->filename);
+   e_gadget_external_type_del("runner_sandbox", ev->filename);
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+monitor_error(void *d EINA_UNUSED, int t EINA_UNUSED, Eio_Monitor_Error *ev 
EINA_UNUSED)
+{
+   /* panic? */
+   return ECORE_CALLBACK_RENEW;
+}
+
+
+static Eina_Bool
+list_filter_cb(void *d EINA_UNUSED, Eio_File *ls EINA_UNUSED, const 
Eina_File_Direct_Info *info)
+{
+   struct stat st;
+   char buf[PATH_MAX];
+
+   if (info->type != EINA_FILE_DIR) return EINA_FALSE;
+   if (info->path[info->name_start] == '.') return EINA_FALSE;
+   snprintf(buf, sizeof(buf), "%s/%s.desktop", info->path, info->path + 
info->name_start);
+   return !stat(info->path, &st);
+}
+
+static void
+list_main_cb(void *d EINA_UNUSED, Eio_File *ls EINA_UNUSED, const 
Eina_File_Direct_Info *info)
+{
+   gadget_dir_add(info->path);
+}
+
+static void
+list_done_cb(void *d EINA_UNUSED, Eio_File *ls EINA_UNUSED)
+{
+   gadget_lister = NULL;
+}
+
+static void
+list_error_cb(void *d EINA_UNUSED, Eio_File *ls EINA_UNUSED, int error 
EINA_UNUSED)
+{
+   gadget_lister = NULL;
+}
+
+EINTERN void
+e_gadget_runner_init(void)
+{
+   conf_item_edd = E_CONFIG_DD_NEW("Config_Item", Config_Item);
+#undef T
+#undef D
+#define T Config_Item
+#define D conf_item_edd
+   E_CONFIG_VAL(D, T, id, INT);
+   E_CONFIG_VAL(D, T, exit_mode, INT);
+   E_CONFIG_VAL(D, T, cmd, STR);
+
+   conf_edd = E_CONFIG_DD_NEW("RConfig", RConfig);
+#undef T
+#undef D
+#define T RConfig
+#define D conf_edd
+   E_CONFIG_LIST(D, T, items, conf_item_edd);
+
+   rconfig = e_config_domain_load("e_gadget_runner", conf_edd);
+   if (!rconfig) rconfig = E_NEW(RConfig, 1);
+
+   e_gadget_type_add("runner", runner_create, runner_wizard);
+   {
+      char buf[PATH_MAX];
+
+      snprintf(buf, sizeof(buf), "%s/enlightenment/gadgets/%s", 
e_prefix_lib_get(), MODULE_ARCH);
+      gadget_monitor = eio_monitor_add(buf);
+      gadget_lister = eio_file_direct_ls(buf, list_filter_cb, list_main_cb, 
list_done_cb, list_error_cb, NULL);
+   }
+   E_LIST_HANDLER_APPEND(handlers, ECORE_EXE_EVENT_DEL, runner_exe_del, NULL);
+   E_LIST_HANDLER_APPEND(handlers, EIO_MONITOR_DIRECTORY_CREATED, 
monitor_dir_create, NULL);
+   E_LIST_HANDLER_APPEND(handlers, EIO_MONITOR_DIRECTORY_DELETED, 
monitor_dir_del, NULL);
+   E_LIST_HANDLER_APPEND(handlers, EIO_MONITOR_ERROR, monitor_error, NULL);
+
+   sandbox_gadgets = 
eina_hash_string_superfast_new((Eina_Free_Cb)efreet_desktop_free);
+   {
+      char buf[PATH_MAX];
+
+      snprintf(buf, sizeof(buf), "/proc/%d/ns/pid", getpid());
+      ns_fd = open(buf, O_RDONLY);
+   }
+}
+
+EINTERN void
+e_gadget_runner_shutdown(void)
+{
+   e_gadget_type_del("runner");
+   e_gadget_external_type_del("runner_sandbox", NULL);
+
+   if (rconfig)
+     {
+        Config_Item *ci;
+
+        if (rconfig->config_dialog)
+          {
+             evas_object_hide(rconfig->config_dialog);
+             evas_object_del(rconfig->config_dialog);
+          }
+
+        EINA_LIST_FREE(rconfig->items, ci)
+          {
+             eina_stringshare_del(ci->cmd);
+             free(ci);
+          }
+
+     }
+   E_FREE(rconfig);
+   E_CONFIG_DD_FREE(conf_edd);
+   E_CONFIG_DD_FREE(conf_item_edd);
+   E_FREE_LIST(handlers, ecore_event_handler_del);
+   E_FREE_FUNC(sandbox_gadgets, eina_hash_free);
+   E_FREE_FUNC(gadget_lister, eio_file_cancel);
+   close(ns_fd);
+   ns_fd = -1;
+}
+
+EINTERN void
+runner_save(void)
+{
+   e_config_domain_save("e_gadget_runner", conf_edd, rconfig);
+}
diff --git a/src/bin/generated/meson.build b/src/bin/generated/meson.build
index 2872c6362..0b373503e 100644
--- a/src/bin/generated/meson.build
+++ b/src/bin/generated/meson.build
@@ -4,6 +4,7 @@ protos = [
   '../../protocol/www.xml',
   '../../protocol/efl-aux-hints.xml',
   '../../protocol/action_route.xml',
+  '../../protocol/e-gadget.xml',
   
'@0@/unstable/xdg-foreign/xdg-foreign-unstable-v1.xml'.format(dir_wayland_protocols),
   
'@0@/unstable/relative-pointer/relative-pointer-unstable-v1.xml'.format(dir_wayland_protocols),
   
'@0@/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'.format(dir_wayland_protocols),
@@ -17,5 +18,17 @@ foreach p: protos
   proto_c += gen_scanner_impl.process(p)
 endforeach
 
+gadget_loader_protos = [
+  '../../protocol/action_route.xml',
+  '../../protocol/e-gadget.xml',
+]
+
+gadget_loader_proto_files = []
+
+foreach p: protos
+  gadget_loader_proto_files += gen_scanner_client.process(p)
+  gadget_loader_proto_files += gen_scanner_impl.process(p)
+endforeach
+
 wayland_proto_c = proto_c
 wayland_proto_h = proto_h
diff --git a/src/bin/meson.build b/src/bin/meson.build
index 52181abcb..d20165c6a 100644
--- a/src/bin/meson.build
+++ b/src/bin/meson.build
@@ -400,6 +400,7 @@ if config_h.has('HAVE_WAYLAND') == true
     'e_comp_wl.c',
     'e_comp_wl_extensions.c',
     'e_comp_wl_extensions_tizen.c',
+    'e_gadget_runner.c',
     wayland_proto_c,
     wayland_proto_h
   ]
@@ -563,4 +564,17 @@ if freebsd == true
   suid_exes += join_paths(dir_e_utils, 'enlightenment_ckpasswd')
 endif
 
+if config_h.has('HAVE_WAYLAND') == true
+       shared_library('loader',
+                       ['e_gadget_loader.c', gadget_loader_proto_files],
+               name_prefix: '',
+               include_directories: include_directories('../..'),
+               dependencies: [
+                       dependency('elementary'),
+                       dependency('ecore-wl2'),
+                       dependency('wayland-client'),
+                       cc.find_library('uuid')],
+               install_dir: join_paths([dir_lib, 'enlightenment/gadgets/', 
module_arch]),
+               install: true)
+endif
 subdir('e_fm')
diff --git a/src/protocol/e-gadget.xml b/src/protocol/e-gadget.xml
new file mode 100644
index 000000000..837c2606b
--- /dev/null
+++ b/src/protocol/e-gadget.xml
@@ -0,0 +1,38 @@
+<protocol name="e_gadget">
+
+  <interface name="e_gadget" version="1">
+    <enum name="orient">
+      <entry name="none" value="0"/>
+      <entry name="horizontal" value="1"/>
+      <entry name="vertical" value="2"/>
+    </enum>
+    <enum name="gravity">
+      <entry name="none" value="0"/>
+      <entry name="left" value="1"/>
+      <entry name="right" value="2"/>
+      <entry name="top" value="3"/>
+      <entry name="bottom" value="4"/>
+      <entry name="center" value="5"/>
+    </enum>
+    <enum name="anchor" bitfield="true">
+      <entry name="none" value="0"/>
+      <entry name="left" value="1"/>
+      <entry name="right" value="2"/>
+      <entry name="top" value="4"/>
+      <entry name="bottom" value="8"/>
+    </enum>
+    <event name="gadget_anchor">
+      <arg name="anchor" type="uint" enum="anchor"/>
+    </event>
+    <event name="gadget_orient">
+      <arg name="orient" type="uint" enum="orient"/>
+    </event>
+    <event name="gadget_gravity">
+      <arg name="gravity" type="uint" enum="gravity"/>
+    </event>
+    <request name="open_uri">
+      <arg name="uri" type="string"/>
+    </request>
+  </interface>
+
+</protocol>

-- 


Reply via email to