summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Blumenkrantz <zmike@osg.samsung.com>2017-11-03 11:47:35 -0400
committerMike Blumenkrantz <zmike@osg.samsung.com>2017-11-03 11:47:35 -0400
commitf4470e002e06ea4cbab690cbca27bd73be145934 (patch)
tree152d97032a54675b8d693e3f35651eaf05dbe4df
parent9302f23a56cdd7bf1718dc85d9e0b5a489725325 (diff)
downloadenlightenment-f4470e002e06ea4cbab690cbca27bd73be145934.tar.gz
add gadget sandboxing
docs in progress, tasks https://phab.enlightenment.org/project/board/179/
-rw-r--r--meson.build2
-rw-r--r--src/Makefile.mk1
-rw-r--r--src/bin/Makefile.mk5
-rw-r--r--src/bin/e_gadget.c12
-rw-r--r--src/bin/e_gadget_loader.c310
-rw-r--r--src/bin/e_gadget_runner.c942
-rw-r--r--src/bin/generated/meson.build13
-rw-r--r--src/bin/meson.build14
-rw-r--r--src/protocol/e-gadget.xml38
9 files changed, 1336 insertions, 1 deletions
diff --git a/meson.build b/meson.build
index 372ced6294..02dbdede77 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 e4cf2bb8cf..a3ebd62241 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 4235b821c5..cc558ed2df 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 1de22e091d..bddc1387e9 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 0000000000..1b32304f5d
--- /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 0000000000..965bc3d35d
--- /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 2872c63622..0b373503e7 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 52181abcbe..d20165c6a3 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 0000000000..837c2606b8
--- /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>