summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <marcel@osg.samsung.com>2020-07-02 12:18:42 +0200
committerMarcel Hollerbach <marcel@osg.samsung.com>2020-07-21 10:05:32 +0200
commit304afe7ea3daf5b2b651ff01e3127304692be950 (patch)
tree3bef0cd7d2238386f4a122a32288d8088566d209
parent1fa239b404ead097c1e019f6cdad6eb5f9f2f7e0 (diff)
downloadenlightenment-devs/bu5hm4n/recognition.tar.gz
Introduce gesture recognitiondevs/bu5hm4n/recognition
This is currently using libinputs gesture recognition. And offers a config screen to setup new gestures. 1. No default gesture bindings are setup 2. When libinput is not available the module is not going to be loaded, and nothing is recognited.+ 3. Only swipe gestures are recognized yet. 4. For now, you are required to be part of the input group, otherwise we cannot get the libinput events. (See Todo 1) 5. The visual representation is not really good. In terms of UI, it is visually showing a value coming from left to right, which is indicating a direction, which is not always the direction of the gesture, which is kind of bad. More improvements needed here. Some things that still can be done: 1. The whole libinput things should be handled by elput, either with the input group hack, or logind, or simply by root. The ideal idea would be that e_sys is creating the elput context, which also listens for new devices etc.. When all this is done, and it recognizes a new device, it can simply sent a message from e_sys to e, that there is some new device, with a opened fd. (However, this all needs to be locked up in a way that e_sys cannot be abused) -
-rw-r--r--meson_options.txt4
-rw-r--r--src/bin/e_bindings.c141
-rw-r--r--src/bin/e_bindings.h30
-rw-r--r--src/bin/e_config.c28
-rw-r--r--src/bin/e_config.h12
-rw-r--r--src/modules/conf_bindings/e_int_config_swipebindings.c978
-rw-r--r--src/modules/conf_bindings/e_mod_main.c4
-rw-r--r--src/modules/conf_bindings/e_mod_main.h2
-rw-r--r--src/modules/conf_bindings/meson.build1
-rw-r--r--src/modules/gesture_recognition/e_mod_main.c287
-rw-r--r--src/modules/gesture_recognition/meson.build5
-rw-r--r--src/modules/gesture_recognition/module.desktop6
-rw-r--r--src/modules/meson.build1
13 files changed, 1498 insertions, 1 deletions
diff --git a/meson_options.txt b/meson_options.txt
index 016dd81249..faedfeada8 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -297,3 +297,7 @@ option('conf-window-remembers',
type: 'boolean',
value: true,
description: 'enable conf-window-remembers module: (default=true)')
+option('gesture-recognition',
+ type: 'boolean',
+ value: true,
+ description: 'Enable gesture recognition using libinput, needed to get swipe bindings beeing detected.')
diff --git a/src/bin/e_bindings.c b/src/bin/e_bindings.c
index 084e6ef51b..7e4c1408ec 100644
--- a/src/bin/e_bindings.c
+++ b/src/bin/e_bindings.c
@@ -7,6 +7,7 @@ static void _e_bindings_edge_free(E_Binding_Edge *bind);
static void _e_bindings_signal_free(E_Binding_Signal *bind);
static void _e_bindings_wheel_free(E_Binding_Wheel *bind);
static void _e_bindings_acpi_free(E_Binding_Acpi *bind);
+static void _e_bindings_swipe_free(E_Binding_Swipe *bind);
static Eina_Bool _e_bindings_edge_cb_timer(void *data);
/* local subsystem globals */
@@ -17,9 +18,13 @@ static Eina_List *edge_bindings = NULL;
static Eina_List *signal_bindings = NULL;
static Eina_List *wheel_bindings = NULL;
static Eina_List *acpi_bindings = NULL;
+static Eina_List *swipe_bindings = NULL;
static unsigned int bindings_disabled = 0;
+static E_Bindings_Swipe_Live_Update live_update;
+static E_Bindings_Swipe_Live_Update live_update_data;
+
EINTERN E_Action *(*e_binding_key_list_cb)(E_Binding_Context, Ecore_Event_Key*, E_Binding_Modifier, E_Binding_Key **);
typedef struct _E_Binding_Edge_Data E_Binding_Edge_Data;
@@ -43,6 +48,7 @@ e_bindings_init(void)
E_Config_Binding_Edge *ebe;
E_Config_Binding_Key *ebk;
E_Config_Binding_Acpi *eba;
+ E_Config_Binding_Acpi *ebsw;
Eina_List *l;
EINA_LIST_FOREACH(e_bindings->mouse_bindings, l, ebm)
@@ -86,6 +92,8 @@ e_bindings_init(void)
e_bindings_acpi_add(eba->context, eba->type, eba->status,
eba->action, eba->params);
+ e_bindings_swipe_reset();
+
return 1;
}
@@ -98,6 +106,7 @@ e_bindings_shutdown(void)
E_FREE_LIST(signal_bindings, _e_bindings_signal_free);
E_FREE_LIST(wheel_bindings, _e_bindings_wheel_free);
E_FREE_LIST(acpi_bindings, _e_bindings_acpi_free);
+ E_FREE_LIST(swipe_bindings, _e_bindings_swipe_free);
return 1;
}
@@ -325,6 +334,18 @@ e_bindings_key_reset(void)
}
E_API void
+e_bindings_swipe_reset(void)
+{
+ E_Config_Binding_Swipe *eswipe;
+ Eina_List *l;
+
+ E_FREE_LIST(swipe_bindings, _e_bindings_swipe_free);
+
+ EINA_LIST_FOREACH(e_bindings->swipe_bindings, l, eswipe)
+ e_bindings_swipe_add(eswipe->context, eswipe->direction, eswipe->length, eswipe->fingers, eswipe->error, eswipe->action, eswipe->params);
+}
+
+E_API void
e_bindings_reset(void)
{
e_bindings_signal_reset();
@@ -332,6 +353,7 @@ e_bindings_reset(void)
e_bindings_wheel_reset();
e_bindings_edge_reset();
e_bindings_key_reset();
+ e_bindings_swipe_reset();
}
E_API void
@@ -1514,6 +1536,14 @@ _e_bindings_acpi_free(E_Binding_Acpi *binding)
E_FREE(binding);
}
+static void
+_e_bindings_swipe_free(E_Binding_Swipe *binding)
+{
+ if (binding->action) eina_stringshare_del(binding->action);
+ if (binding->params) eina_stringshare_del(binding->params);
+ E_FREE(binding);
+}
+
E_API int
e_bindings_context_match(E_Binding_Context bctxt, E_Binding_Context ctxt)
{
@@ -1583,3 +1613,114 @@ _e_bindings_edge_cb_timer(void *data)
return ECORE_CALLBACK_CANCEL;
}
+
+E_API void
+e_bindings_swipe_add(E_Binding_Context ctxt, double direction, double length, unsigned int fingers, double error, const char *action, const char *params)
+{
+ E_Binding_Swipe *binding = E_NEW(E_Binding_Swipe, 1);
+
+ binding->ctxt = ctxt;
+ binding->direction = direction;
+ binding->length = length;
+ binding->fingers = fingers;
+ binding->error = error;
+ if (action)
+ binding->action = eina_stringshare_add(action);
+ if (params)
+ binding->params = eina_stringshare_add(params);
+
+ swipe_bindings = eina_list_append(swipe_bindings, binding);
+}
+
+E_API void
+e_bindings_swipe_del(E_Binding_Context ctxt, double direction, double length, unsigned int fingers, double error, const char *action, const char *params)
+{
+ E_Binding_Swipe *binding;
+ Eina_List *n;
+
+ EINA_LIST_FOREACH(swipe_bindings, n, binding)
+ {
+ if (binding->ctxt == ctxt &&
+ binding->action == action &&
+ binding->params == params &&
+ EINA_DBL_EQ(direction, binding->direction) &&
+ EINA_DBL_EQ(length, binding->length) &&
+ EINA_DBL_EQ(fingers, binding->fingers) &&
+ EINA_DBL_EQ(error, binding->error))
+ {
+ _e_bindings_swipe_free(binding);
+ swipe_bindings = eina_list_remove_list(swipe_bindings, n);
+ break;
+ }
+ }
+}
+
+E_API Eina_Bool
+e_bindings_swipe_available(void)
+{
+ return eina_list_count(swipe_bindings) > 0;
+}
+
+E_API E_Action*
+e_bindings_swipe_handle(E_Binding_Context ctxt, E_Object *obj, double direction, double length, unsigned int fingers)
+{
+ E_Binding_Swipe *binding;
+ Eina_List *n;
+ E_Action *act = NULL;
+
+ EINA_LIST_FOREACH(swipe_bindings, n, binding)
+ {
+ if (binding->ctxt == ctxt &&
+ binding->length < length &&
+ EINA_DBL_EQ(fingers, binding->fingers) &&
+ fmod(binding->direction - binding->error, 2*M_PI) < direction &&
+ fmod(binding->direction + binding->error, 2*M_PI) > direction)
+ {
+ act = e_action_find(binding->action);
+ act->func.go(obj, binding->params);
+ }
+ }
+ return act;
+}
+
+
+E_API Eina_Inarray*
+e_bindings_swipe_find_candidates(E_Binding_Context ctxt, double direction, double length, unsigned int fingers)
+{
+ Eina_Inarray *ret = eina_inarray_new(sizeof(E_Binding_Swipe_Candidate), 10);
+ E_Binding_Swipe *binding;
+ Eina_List *n;
+
+ EINA_LIST_FOREACH(swipe_bindings, n, binding)
+ {
+ if (binding->ctxt == ctxt &&
+ EINA_DBL_EQ(fingers, binding->fingers) &&
+ fmod(binding->direction - binding->error, 2*M_PI) < direction &&
+ fmod(binding->direction + binding->error, 2*M_PI) > direction)
+ {
+ E_Binding_Swipe_Candidate cad = {binding->action, length / binding->length};
+ eina_inarray_push(ret, &cad);
+ }
+ }
+
+ return ret;
+}
+
+E_API void
+e_bindings_swipe_live_update_hook_set(E_Bindings_Swipe_Live_Update update, void *data)
+{
+ live_update = update;
+ live_update_data = data;
+}
+
+E_API E_Bindings_Swipe_Live_Update
+e_bindings_swipe_live_update_hook_get(void)
+{
+ return live_update;
+}
+
+E_API void*
+e_bindings_swipe_live_update_hook_data_get(void)
+{
+ return live_update_data;
+}
diff --git a/src/bin/e_bindings.h b/src/bin/e_bindings.h
index 043118cbfa..b65fabe38f 100644
--- a/src/bin/e_bindings.h
+++ b/src/bin/e_bindings.h
@@ -35,6 +35,7 @@ typedef struct _E_Binding_Edge E_Binding_Edge;
typedef struct _E_Binding_Signal E_Binding_Signal;
typedef struct _E_Binding_Wheel E_Binding_Wheel;
typedef struct _E_Binding_Acpi E_Binding_Acpi;
+typedef struct _E_Binding_Swipe E_Binding_Swipe;
typedef struct E_Binding_Event_Mouse_Button E_Binding_Event_Mouse_Button;
typedef struct E_Binding_Event_Wheel E_Binding_Event_Wheel;
@@ -131,6 +132,15 @@ struct _E_Binding_Acpi
const char *action, *params;
};
+struct _E_Binding_Swipe
+{
+ E_Binding_Context ctxt;
+ double direction, length, error;
+ unsigned int fingers;
+ const char *action, *params;
+};
+
+
EINTERN int e_bindings_init(void);
EINTERN int e_bindings_shutdown(void);
@@ -139,6 +149,7 @@ E_API void e_bindings_key_reset(void);
E_API void e_bindings_wheel_reset(void);
E_API void e_bindings_edge_reset(void);
E_API void e_bindings_signal_reset(void);
+E_API void e_bindings_swipe_reset(void);
E_API void e_bindings_reset(void);
E_API void e_bindings_mouse_add(E_Binding_Context ctxt, int button, E_Binding_Modifier mod, int any_mod, const char *action, const char *params);
@@ -196,6 +207,25 @@ E_API void e_bindings_acpi_del(E_Binding_Context ctxt, int type, int status, con
E_API E_Action *e_bindings_acpi_find(E_Binding_Context ctxt, E_Event_Acpi *ev, E_Binding_Acpi **bind_ret);
E_API E_Action *e_bindings_acpi_event_handle(E_Binding_Context ctxt, E_Object *obj, E_Event_Acpi *ev);
+typedef struct {
+ const char *name;
+ double acceptance; //0 to 1
+} E_Binding_Swipe_Candidate;
+
+typedef void (*E_Bindings_Swipe_Live_Update)(void *data, Eina_Bool end, double direction, double length, double error, unsigned int fingers);
+
+/**
+ * Direction is in radiens, 0 is pointing to the right. Going clockwise. (Only positive range)
+ */
+E_API Eina_Bool e_bindings_swipe_available(void);
+E_API void e_bindings_swipe_add(E_Binding_Context ctxt, double direction, double length, unsigned int fingers, double error, const char *action, const char *params);
+E_API void e_bindings_swipe_del(E_Binding_Context ctxt, double direction, double length, unsigned int fingers, double error, const char *action, const char *params);
+E_API E_Action* e_bindings_swipe_handle(E_Binding_Context ctxt, E_Object *obj, double direction, double length, unsigned int fingers);
+E_API Eina_Inarray/*<E_Bindings_Swipe_Candidate>*/* e_bindings_swipe_find_candidates(E_Binding_Context ctxt, double direction, double lenght, unsigned int fingers);
+E_API void e_bindings_swipe_live_update_hook_set(E_Bindings_Swipe_Live_Update update, void *data);
+E_API E_Bindings_Swipe_Live_Update e_bindings_swipe_live_update_hook_get(void);
+E_API void* e_bindings_swipe_live_update_hook_data_get(void);
+
E_API int e_bindings_evas_modifiers_convert(Evas_Modifier *modifiers);
E_API int e_bindings_modifiers_to_ecore_convert(E_Binding_Modifier modifiers);
E_API void e_bindings_evas_event_mouse_button_convert(const Evas_Event_Mouse_Down *ev, E_Binding_Event_Mouse_Button *event);
diff --git a/src/bin/e_config.c b/src/bin/e_config.c
index 6c792d4f20..3e5d68f76f 100644
--- a/src/bin/e_config.c
+++ b/src/bin/e_config.c
@@ -28,6 +28,7 @@ static E_Config_DD *_e_config_bindings_edge_edd = NULL;
static E_Config_DD *_e_config_bindings_signal_edd = NULL;
static E_Config_DD *_e_config_bindings_wheel_edd = NULL;
static E_Config_DD *_e_config_bindings_acpi_edd = NULL;
+static E_Config_DD *_e_config_bindings_swipe_edd = NULL;
static E_Config_DD *_e_config_path_append_edd = NULL;
static E_Config_DD *_e_config_desktop_bg_edd = NULL;
static E_Config_DD *_e_config_desklock_bg_edd = NULL;
@@ -460,6 +461,7 @@ _e_config_edd_init(Eina_Bool old)
E_CONFIG_LIST(D, T, signal_bindings, _e_config_bindings_signal_edd); /**/
E_CONFIG_LIST(D, T, wheel_bindings, _e_config_bindings_wheel_edd); /**/
E_CONFIG_LIST(D, T, acpi_bindings, _e_config_bindings_acpi_edd); /**/
+ E_CONFIG_LIST(D, T, swipe_bindings, _e_config_bindings_swipe_edd); /**/
E_CONFIG_LIST(D, T, path_append_data, _e_config_path_append_edd); /**/
E_CONFIG_LIST(D, T, path_append_images, _e_config_path_append_edd); /**/
E_CONFIG_LIST(D, T, path_append_fonts, _e_config_path_append_edd); /**/
@@ -924,6 +926,21 @@ e_config_init(void)
E_CONFIG_VAL(D, T, action, STR);
E_CONFIG_VAL(D, T, params, STR);
+ _e_config_bindings_swipe_edd = E_CONFIG_DD_NEW("E_Config_Binding_Swipe",
+ E_Config_Binding_Swipe);
+#undef T
+#undef D
+#define T E_Config_Binding_Swipe
+#define D _e_config_bindings_swipe_edd
+
+ E_CONFIG_VAL(D, T, context, INT);
+ E_CONFIG_VAL(D, T, fingers, UINT);
+ E_CONFIG_VAL(D, T, direction, DOUBLE);
+ E_CONFIG_VAL(D, T, length, DOUBLE);
+ E_CONFIG_VAL(D, T, error, DOUBLE);
+ E_CONFIG_VAL(D, T, action, STR);
+ E_CONFIG_VAL(D, T, params, STR);
+
_e_config_edd_init(EINA_FALSE);
_e_config_binding_edd = E_CONFIG_DD_NEW("E_Config_Bindings", E_Config_Bindings);
@@ -938,6 +955,7 @@ e_config_init(void)
E_CONFIG_LIST(D, T, signal_bindings, _e_config_bindings_signal_edd); /**/
E_CONFIG_LIST(D, T, wheel_bindings, _e_config_bindings_wheel_edd); /**/
E_CONFIG_LIST(D, T, acpi_bindings, _e_config_bindings_acpi_edd); /**/
+ E_CONFIG_LIST(D, T, swipe_bindings, _e_config_bindings_swipe_edd); /**/
e_config_load();
@@ -2258,6 +2276,16 @@ e_config_mode_changed(void)
}
E_API void
+e_config_binding_swipe_free(E_Config_Binding_Swipe *eba)
+{
+ if (!eba) return;
+ eina_stringshare_del(eba->action);
+ eina_stringshare_del(eba->params);
+ free(eba);
+}
+
+
+E_API void
e_config_binding_acpi_free(E_Config_Binding_Acpi *eba)
{
if (!eba) return;
diff --git a/src/bin/e_config.h b/src/bin/e_config.h
index deae01d318..3b1c304ca6 100644
--- a/src/bin/e_config.h
+++ b/src/bin/e_config.h
@@ -10,6 +10,7 @@ typedef struct _E_Config_Binding_Edge E_Config_Binding_Edge;
typedef struct _E_Config_Binding_Signal E_Config_Binding_Signal;
typedef struct _E_Config_Binding_Wheel E_Config_Binding_Wheel;
typedef struct _E_Config_Binding_Acpi E_Config_Binding_Acpi;
+typedef struct _E_Config_Binding_Swipe E_Config_Binding_Swipe;
typedef struct _E_Config_Desktop_Background E_Config_Desktop_Background;
typedef struct _E_Config_Desklock_Background E_Config_Desklock_Background;
typedef struct _E_Config_Desktop_Name E_Config_Desktop_Name;
@@ -87,6 +88,7 @@ struct _E_Config
Eina_List *signal_bindings; // GUI
Eina_List *wheel_bindings; // GUI
Eina_List *acpi_bindings; // GUI
+ Eina_List *swipe_bindings; // GUI
Eina_List *path_append_data; // GUI
Eina_List *path_append_images; // GUI
@@ -452,6 +454,7 @@ struct E_Config_Bindings
Eina_List *signal_bindings; // GUI
Eina_List *wheel_bindings; // GUI
Eina_List *acpi_bindings; // GUI
+ Eina_List *swipe_bindings;
};
struct _E_Config_Desklock_Background
@@ -542,6 +545,14 @@ struct _E_Config_Binding_Acpi
const char *action, *params;
};
+struct _E_Config_Binding_Swipe
+{
+ int context;
+ unsigned int fingers;
+ double direction, length, error;
+ const char *action, *params;
+};
+
struct _E_Config_Desktop_Background
{
int zone;
@@ -675,6 +686,7 @@ E_API void e_config_binding_mouse_free(E_Config_Binding_Mouse *ebm);
E_API void e_config_binding_edge_free(E_Config_Binding_Edge *ebe);
E_API void e_config_binding_key_free(E_Config_Binding_Key *ebk);
E_API void e_config_binding_acpi_free(E_Config_Binding_Acpi *eba);
+E_API void e_config_binding_swipe_free(E_Config_Binding_Swipe *eba);
extern E_API E_Config *e_config;
extern E_API E_Config_Bindings *e_bindings;
diff --git a/src/modules/conf_bindings/e_int_config_swipebindings.c b/src/modules/conf_bindings/e_int_config_swipebindings.c
new file mode 100644
index 0000000000..7c47aaa928
--- /dev/null
+++ b/src/modules/conf_bindings/e_int_config_swipebindings.c
@@ -0,0 +1,978 @@
+#include "e.h"
+
+#define TEXT_NO_PARAMS _("<None>")
+
+struct _E_Config_Dialog_Data
+{
+ Evas *evas;
+ struct
+ {
+ Eina_List *swipe;
+ } binding;
+ struct
+ {
+ const char *binding, *action;
+ char *params;
+ const char *cur;
+ int button;
+ int cur_act;
+ const char *swipe;
+ const char *source;
+
+ E_Dialog *dia;
+ double degree;
+ double error;
+ double length;
+ unsigned int fingers;
+ } locals;
+ struct
+ {
+ Evas_Object *o_add, *o_del, *o_del_all;
+ Evas_Object *o_binding_list, *o_action_list;
+ Evas_Object *o_params, *o_selector;
+ } gui;
+
+ const char *params;
+
+ int fullscreen_flip;
+ int multiscreen_flip;
+
+ E_Config_Dialog *cfd;
+};
+
+static E_Config_Binding_Swipe *
+_swipe_binding_copy(E_Config_Binding_Swipe *bi)
+{
+ E_Config_Binding_Swipe *bi2;
+ if (!bi) return NULL;
+
+ bi2 = E_NEW(E_Config_Binding_Swipe, 1);
+ bi2->context = bi->context;
+ bi2->direction = bi->direction;
+ bi2->length = bi->length;
+ bi2->fingers = bi->fingers;
+ bi2->error = bi->error;
+ bi2->action = bi->action;
+ bi2->params = bi->params;
+ return bi2;
+}
+
+static void
+_swipe_binding_free(E_Config_Binding_Swipe *bi)
+{
+ if (!bi) return;
+ eina_stringshare_del(bi->action);
+ eina_stringshare_del(bi->params);
+ free(bi);
+}
+
+
+static void
+_auto_apply_changes(E_Config_Dialog_Data *cfdata)
+{
+ int n, g, a, ok;
+ E_Config_Binding_Swipe *bi;
+ E_Action_Group *actg;
+ E_Action_Description *actd;
+
+ if ((!cfdata->locals.cur) || (!cfdata->locals.cur[0]) ||
+ (!cfdata->locals.action) || (!cfdata->locals.action[0])) return;
+
+ if (sscanf(cfdata->locals.cur, "s%d", &n) != 1)
+ return;
+ if (sscanf(cfdata->locals.action, "%d %d", &g, &a) != 2)
+ return;
+
+ bi = eina_list_nth(cfdata->binding.swipe, n);
+ if (!bi) return;
+
+ actg = eina_list_nth(e_action_groups_get(), g);
+ if (!actg) return;
+ actd = eina_list_nth(actg->acts, a);
+ if (!actd) return;
+
+ eina_stringshare_del(bi->action);
+ bi->action = NULL;
+
+ if (actd->act_cmd) bi->action = eina_stringshare_add(actd->act_cmd);
+
+ eina_stringshare_del(bi->params);
+ bi->params = NULL;
+
+ if (actd->act_params)
+ bi->params = eina_stringshare_add(actd->act_params);
+ else
+ {
+ ok = 1;
+ if (cfdata->locals.params)
+ {
+ if (!strcmp(cfdata->locals.params, TEXT_NO_PARAMS))
+ ok = 0;
+
+ if ((actd->param_example) && (!strcmp(cfdata->locals.params, actd->param_example)))
+ ok = 0;
+ }
+ else
+ ok = 0;
+
+ if (ok)
+ bi->params = eina_stringshare_add(cfdata->locals.params);
+ }
+}
+
+static void
+_fill_data(E_Config_Dialog_Data *cfdata)
+{
+ E_Config_Binding_Swipe *bi, *bi2;
+ Eina_List *l;
+
+ cfdata->locals.params = strdup("");
+ cfdata->locals.action = eina_stringshare_add("");
+ cfdata->locals.binding = eina_stringshare_add("");
+ cfdata->locals.swipe = eina_stringshare_add("");
+ cfdata->locals.source = eina_stringshare_add("");
+ cfdata->locals.cur = NULL;
+ cfdata->locals.dia = NULL;
+ cfdata->binding.swipe = NULL;
+
+ EINA_LIST_FOREACH(e_bindings->swipe_bindings, l, bi)
+ {
+ if (!bi) continue;
+ bi2 = _swipe_binding_copy(bi);
+ cfdata->binding.swipe = eina_list_append(cfdata->binding.swipe, bi2);
+ }
+}
+
+static void *
+_create_data(E_Config_Dialog *cfd)
+{
+ E_Config_Dialog_Data *cfdata;
+
+ cfdata = E_NEW(E_Config_Dialog_Data, 1);
+ cfdata->cfd = cfd;
+ _fill_data(cfdata);
+
+ return cfdata;
+}
+
+static void
+_free_data(E_Config_Dialog *cfd EINA_UNUSED, E_Config_Dialog_Data *cfdata)
+{
+ E_FREE_LIST(cfdata->binding.swipe, _swipe_binding_free);
+
+ eina_stringshare_del(cfdata->locals.cur);
+ eina_stringshare_del(cfdata->params);
+ eina_stringshare_del(cfdata->locals.binding);
+ eina_stringshare_del(cfdata->locals.action);
+ eina_stringshare_del(cfdata->locals.swipe);
+ eina_stringshare_del(cfdata->locals.source);
+
+ if (cfdata->locals.dia) e_object_del(E_OBJECT(cfdata->locals.dia));
+
+ free(cfdata->locals.params);
+ E_FREE(cfdata);
+}
+
+
+static void
+_update_action_params(E_Config_Dialog_Data *cfdata)
+{
+ int g, a, b;
+ E_Action_Group *actg;
+ E_Action_Description *actd;
+ E_Config_Binding_Swipe *bi;
+ const char *action, *params;
+
+#define SIGNAL_EXAMPLE_PARAMS \
+ if ((!actd->param_example) || (!actd->param_example[0])) \
+ e_widget_entry_text_set(cfdata->gui.o_params, TEXT_NO_PARAMS); \
+ else \
+ e_widget_entry_text_set(cfdata->gui.o_params, actd->param_example)
+
+ if ((!cfdata->locals.action) || (!cfdata->locals.action[0]))
+ {
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ e_widget_entry_clear(cfdata->gui.o_params);
+ return;
+ }
+ if (sscanf(cfdata->locals.action, "%d %d", &g, &a) != 2)
+ return;
+
+ actg = eina_list_nth(e_action_groups_get(), g);
+ if (!actg) return;
+ actd = eina_list_nth(actg->acts, a);
+ if (!actd) return;
+
+ if (actd->act_params)
+ {
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ e_widget_entry_text_set(cfdata->gui.o_params, actd->act_params);
+ return;
+ }
+
+ if ((!cfdata->locals.cur) || (!cfdata->locals.cur[0]))
+ {
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ SIGNAL_EXAMPLE_PARAMS;
+ return;
+ }
+
+ if (!actd->editable)
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ else
+ e_widget_disabled_set(cfdata->gui.o_params, 0);
+
+ if (cfdata->locals.cur[0] == 's')
+ {
+ if (sscanf(cfdata->locals.cur, "s%d", &b) != 1)
+ {
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ SIGNAL_EXAMPLE_PARAMS;
+ return;
+ }
+
+ bi = eina_list_nth(cfdata->binding.swipe, b);
+ if (!bi)
+ {
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ SIGNAL_EXAMPLE_PARAMS;
+ return;
+ }
+ action = bi->action;
+ params = bi->params;
+ }
+ else
+ {
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ SIGNAL_EXAMPLE_PARAMS;
+ return;
+ }
+
+ if (action)
+ {
+ if (!strcmp(action, actd->act_cmd))
+ {
+ if ((!params) || (!params[0]))
+ SIGNAL_EXAMPLE_PARAMS;
+ else
+ e_widget_entry_text_set(cfdata->gui.o_params, params);
+ }
+ else
+ SIGNAL_EXAMPLE_PARAMS;
+ }
+ else
+ SIGNAL_EXAMPLE_PARAMS;
+}
+
+static void
+_action_change_cb(void *data)
+{
+ E_Config_Dialog_Data *cfdata;
+
+ cfdata = data;
+ _update_action_params(cfdata);
+}
+
+static int
+_swipe_binding_sort_cb(E_Config_Binding_Swipe *a, E_Config_Binding_Swipe *b)
+{
+ int finger_diff = (a->fingers == b->fingers)*-1;
+ if (!finger_diff)
+ {
+ return a->direction - b->direction;
+ }
+ return finger_diff;
+}
+
+static void
+_update_buttons(E_Config_Dialog_Data *cfdata)
+{
+ if (e_widget_ilist_count(cfdata->gui.o_binding_list))
+ e_widget_disabled_set(cfdata->gui.o_del_all, 0);
+ else
+ e_widget_disabled_set(cfdata->gui.o_del_all, 1);
+
+ if (!cfdata->locals.cur)
+ {
+ e_widget_disabled_set(cfdata->gui.o_del, 1);
+ return;
+ }
+ e_widget_disabled_set(cfdata->gui.o_del, 0);
+}
+
+
+static void
+_find_swipe_binding_action(const char *action, const char *params, int *g, int *a, int *n)
+{
+ Eina_List *l, *l2;
+ int gg = -1, aa = -1, nn = -1, found;
+ E_Action_Group *actg;
+ E_Action_Description *actd;
+
+ if (g) *g = -1;
+ if (a) *a = -1;
+ if (n) *n = -1;
+
+ found = 0;
+ for (l = e_action_groups_get(), gg = 0, nn = 0; l; l = l->next, gg++)
+ {
+ actg = l->data;
+
+ for (l2 = actg->acts, aa = 0; l2; l2 = l2->next, aa++)
+ {
+ actd = l2->data;
+ if (!strcmp((!action ? "" : action), (!actd->act_cmd ? "" : actd->act_cmd)))
+ {
+ if (!params || !params[0])
+ {
+ if ((!actd->act_params) || (!actd->act_params[0]))
+ {
+ if (g) *g = gg;
+ if (a) *a = aa;
+ if (n) *n = nn;
+ return;
+ }
+ else
+ continue;
+ }
+ else
+ {
+ if ((!actd->act_params) || (!actd->act_params[0]))
+ {
+ if (g) *g = gg;
+ if (a) *a = aa;
+ if (n) *n = nn;
+ found = 1;
+ }
+ else
+ {
+ if (!strcmp(params, actd->act_params))
+ {
+ if (g) *g = gg;
+ if (a) *a = aa;
+ if (n) *n = nn;
+ return;
+ }
+ }
+ }
+ }
+ nn++;
+ }
+ if (found) break;
+ }
+
+ if (!found)
+ {
+ if (g) *g = -1;
+ if (a) *a = -1;
+ if (n) *n = -1;
+ }
+}
+
+static void
+_update_action_list(E_Config_Dialog_Data *cfdata)
+{
+ E_Config_Binding_Swipe *bi;
+ int j = -1, i, n;
+ const char *action, *params;
+
+ if (!cfdata->locals.cur) return;
+
+ if (cfdata->locals.cur[0] == 's')
+ {
+ if (sscanf(cfdata->locals.cur, "s%d", &n) != 1)
+ return;
+
+ bi = eina_list_nth(cfdata->binding.swipe, n);
+ if (!bi)
+ {
+ e_widget_ilist_unselect(cfdata->gui.o_action_list);
+ e_widget_entry_clear(cfdata->gui.o_params);
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ return;
+ }
+ action = bi->action;
+ params = bi->params;
+ }
+ else
+ return;
+
+ _find_swipe_binding_action(action, params, NULL, NULL, &j);
+
+ if (j >= 0)
+ {
+ for (i = 0; i < e_widget_ilist_count(cfdata->gui.o_action_list); i++)
+ {
+ if (i > j) break;
+ if (e_widget_ilist_nth_is_header(cfdata->gui.o_action_list, i)) j++;
+ }
+ }
+
+ if (j >= 0)
+ {
+ if (j == e_widget_ilist_selected_get(cfdata->gui.o_action_list))
+ _update_action_params(cfdata);
+ else
+ e_widget_ilist_selected_set(cfdata->gui.o_action_list, j);
+ }
+ else
+ {
+ e_widget_ilist_unselect(cfdata->gui.o_action_list);
+ eina_stringshare_del(cfdata->locals.action);
+ cfdata->locals.action = eina_stringshare_add("");
+ e_widget_entry_clear(cfdata->gui.o_params);
+ }
+}
+
+static void
+_binding_change_cb(void *data)
+{
+ E_Config_Dialog_Data *cfdata;
+
+ cfdata = data;
+
+ _auto_apply_changes(cfdata);
+ if (cfdata->locals.cur) eina_stringshare_del(cfdata->locals.cur);
+ cfdata->locals.cur = NULL;
+
+ if ((!cfdata->locals.binding) || (!cfdata->locals.binding[0])) return;
+
+ cfdata->locals.cur = eina_stringshare_ref(cfdata->locals.binding);
+
+ _update_buttons(cfdata);
+ _update_action_list(cfdata);
+}
+
+
+static int
+_basic_apply_data(E_Config_Dialog *cfd EINA_UNUSED, E_Config_Dialog_Data *cfdata)
+{
+ Eina_List *l;
+ E_Config_Binding_Swipe *bi, *bi2;
+
+ _auto_apply_changes(cfdata);
+ E_FREE_LIST(e_bindings->swipe_bindings, _swipe_binding_free);
+ EINA_LIST_FOREACH(cfdata->binding.swipe, l, bi2)
+ {
+ bi = _swipe_binding_copy(bi2);
+ e_bindings->swipe_bindings = eina_list_append(e_bindings->swipe_bindings, bi);
+ }
+ e_bindings_swipe_reset();
+
+ e_config_save_queue();
+
+ return 1;
+}
+
+static Evas_Object*
+create_visualisation(Evas *e, double direction, double error)
+{
+ Evas_Vg_Container *vg = NULL;
+ Evas_Vg_Shape *container, *shape, *viewport = NULL;
+
+ double center_x = 15, center_y = 15;
+
+ vg = evas_object_vg_add(e);
+ evas_object_vg_viewbox_set(vg, EINA_RECT(0, 0, 50, 50));
+
+ container = evas_vg_container_add(vg);
+
+ viewport = evas_vg_shape_add(container);
+ evas_vg_shape_append_rect(viewport, 0, 0, 51, 51, 0, 0);
+ evas_vg_shape_stroke_cap_set(viewport, EVAS_VG_CAP_SQUARE);
+ evas_vg_shape_stroke_color_set(viewport, 0, 0, 0, 0);
+ evas_vg_shape_stroke_width_set(viewport, 1);
+
+ shape = evas_vg_shape_add(container);
+ evas_vg_shape_append_rect(shape, 1, 1, 29, 29, 0, 0);
+ evas_vg_shape_stroke_cap_set(shape, EVAS_VG_CAP_SQUARE);
+ evas_vg_shape_stroke_color_set(shape, 100, 100, 100, 255);
+ evas_vg_shape_stroke_width_set(shape, 1);
+
+ shape = evas_vg_shape_add(container);
+ evas_vg_shape_append_line_to(shape, center_x, center_y);
+ evas_vg_shape_append_line_to(shape, center_x + cos(direction - error)*11, center_y + sin(direction - error)*11);
+ evas_vg_shape_append_arc_to(shape, center_x + cos(direction + error)*11, center_y + sin(direction + error)*11, 11, 11, 0, EINA_FALSE, EINA_TRUE);
+ evas_vg_shape_append_line_to(shape, center_x, center_y);
+
+ evas_vg_shape_stroke_cap_set(shape, EVAS_VG_CAP_ROUND);
+ evas_vg_shape_stroke_color_set(shape, 255, 0, 0, 255);
+ evas_vg_shape_stroke_width_set(shape, 2);
+
+ evas_object_vg_root_node_set(vg, container);
+
+ evas_object_show(vg);
+
+ return vg;
+}
+
+static void
+_update_swipe_binding_list(E_Config_Dialog_Data *cfdata)
+{
+ int i = 0;
+ char b2[64], b3[64];
+ Eina_List *l;
+ E_Config_Binding_Swipe *bi;
+ unsigned int previous_fingers = 0;
+
+ evas_event_freeze(evas_object_evas_get(cfdata->gui.o_binding_list));
+ edje_freeze();
+ e_widget_ilist_freeze(cfdata->gui.o_binding_list);
+
+ e_widget_ilist_clear(cfdata->gui.o_binding_list);
+ e_widget_ilist_go(cfdata->gui.o_binding_list);
+
+ if (cfdata->binding.swipe)
+ cfdata->binding.swipe = eina_list_sort(cfdata->binding.swipe, 0, (Eina_Compare_Cb)_swipe_binding_sort_cb);
+
+ EINA_LIST_FOREACH(cfdata->binding.swipe, l, bi)
+ {
+ Evas_Object *vis;
+
+ vis = create_visualisation(evas_object_evas_get(cfdata->gui.o_binding_list), bi->direction, bi->error);
+ if (bi->fingers != previous_fingers)
+ {
+ snprintf(b3, sizeof(b3), "%d Fingers", bi->fingers);
+ previous_fingers = bi->fingers;
+ e_widget_ilist_header_append(cfdata->gui.o_binding_list, NULL, b3);
+ }
+ snprintf(b2, sizeof(b2), "s%d", i);
+ snprintf(b3, sizeof(b3), "Length: %.2f Error: %.2f", bi->length, bi->error);
+
+ e_widget_ilist_append(cfdata->gui.o_binding_list, vis, b3, _binding_change_cb, cfdata, b2);
+ i++;
+ }
+ e_widget_ilist_go(cfdata->gui.o_binding_list);
+
+ e_widget_ilist_thaw(cfdata->gui.o_binding_list);
+ edje_thaw();
+ evas_event_thaw(evas_object_evas_get(cfdata->gui.o_binding_list));
+
+ if (eina_list_count(cfdata->binding.swipe))
+ e_widget_disabled_set(cfdata->gui.o_del_all, 0);
+ else
+ e_widget_disabled_set(cfdata->gui.o_del_all, 1);
+}
+
+static void
+_fill_actions_list(E_Config_Dialog_Data *cfdata)
+{
+ char buf[1024];
+ Eina_List *l, *l2;
+ E_Action_Group *actg;
+ E_Action_Description *actd;
+ int g, a;
+
+ evas_event_freeze(evas_object_evas_get(cfdata->gui.o_action_list));
+ edje_freeze();
+ e_widget_ilist_freeze(cfdata->gui.o_action_list);
+
+ e_widget_ilist_clear(cfdata->gui.o_action_list);
+ for (l = e_action_groups_get(), g = 0; l; l = l->next, g++)
+ {
+ actg = l->data;
+
+ if (!actg->acts) continue;
+
+ e_widget_ilist_header_append(cfdata->gui.o_action_list, NULL, _(actg->act_grp));
+
+ for (l2 = actg->acts, a = 0; l2; l2 = l2->next, a++)
+ {
+ actd = l2->data;
+
+ snprintf(buf, sizeof(buf), "%d %d", g, a);
+ e_widget_ilist_append(cfdata->gui.o_action_list, NULL, _(actd->act_name),
+ _action_change_cb, cfdata, buf);
+ }
+ }
+ e_widget_ilist_go(cfdata->gui.o_action_list);
+ e_widget_ilist_thaw(cfdata->gui.o_action_list);
+ edje_thaw();
+ evas_event_thaw(evas_object_evas_get(cfdata->gui.o_action_list));
+}
+
+static void
+_flush_binding_swipe(E_Config_Dialog_Data *cfdata)
+{
+ E_Config_Binding_Swipe *bi;
+
+ bi = E_NEW(E_Config_Binding_Swipe, 1);
+ bi->context = E_BINDING_CONTEXT_NONE;
+ bi->length = cfdata->locals.length;
+ bi->direction = cfdata->locals.degree;
+ bi->error = cfdata->locals.error;
+ bi->fingers = cfdata->locals.fingers;
+
+ cfdata->binding.swipe = eina_list_append(cfdata->binding.swipe, bi);
+ _update_swipe_binding_list(cfdata);
+}
+
+static void
+_swipe_add_cb_ok(void *data, E_Dialog *dia)
+{
+ E_Config_Dialog_Data *cfdata = data;
+
+ _flush_binding_swipe(cfdata);
+
+ e_object_del(E_OBJECT(dia));
+}
+
+static void
+_swipe_add_cb_cancel(void *data EINA_UNUSED, E_Dialog *dia)
+{
+ e_object_del(E_OBJECT(dia));
+}
+
+static void
+_swipe_add_del(void *data)
+{
+ E_Dialog *dia = data;
+ E_Config_Dialog_Data *cfdata;
+
+ if (!dia->data) return;
+ cfdata = dia->data;
+ cfdata->locals.dia = NULL;
+}
+
+static void
+_double_getter(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ double *container = data;
+ double tmp = elm_spinner_value_get(obj);
+ *container = tmp;
+}
+
+static void
+_int_getter(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ unsigned int *container = data;
+ double tmp = elm_spinner_value_get(obj);
+ *container = tmp;
+}
+
+static void
+_swipe_add_show(E_Config_Dialog_Data *cfdata)
+{
+ Evas_Object *o, *obg, *ol, *entry;
+ Evas *evas;
+ int w, h;
+
+ if (cfdata->locals.dia) return;
+
+ cfdata->locals.dia = e_dialog_new(cfdata->cfd->dia->win, "E", "_swipebind_new_dialog");
+ e_dialog_resizable_set(cfdata->locals.dia, 1);
+ e_dialog_title_set(cfdata->locals.dia, _("Add Swipe Binding"));
+ e_dialog_icon_set(cfdata->locals.dia, "enlightenment/swipes", 48);
+ e_dialog_button_add(cfdata->locals.dia, _("OK"), NULL, _swipe_add_cb_ok, cfdata);
+ e_dialog_button_add(cfdata->locals.dia, _("Cancel"), NULL, _swipe_add_cb_cancel, cfdata);
+ e_object_del_attach_func_set(E_OBJECT(cfdata->locals.dia), _swipe_add_del);
+ cfdata->locals.dia->data = cfdata;
+ elm_win_center(cfdata->locals.dia->win, 1, 1);
+
+ evas = evas_object_evas_get(cfdata->locals.dia->win);
+ obg = e_widget_list_add(evas, 1, 0);
+
+ ol = e_widget_framelist_add(evas, _("Direction:"), 0);
+ entry = o = elm_spinner_add(cfdata->locals.dia->win);
+ evas_object_smart_callback_add(o, "changed", _double_getter, &cfdata->locals.degree);
+ elm_spinner_min_max_set(o, 0, 2*M_PI);
+ elm_spinner_label_format_set(o, "%f");
+ elm_spinner_step_set(o, 0.1);
+ elm_spinner_editable_set(o, EINA_TRUE);
+ e_widget_framelist_object_append(ol, o);
+ e_widget_list_object_append(obg, ol, 1, 0, 0.5);
+
+ ol = e_widget_framelist_add(evas, _("Error:"), 0);
+ entry = o = elm_spinner_add(cfdata->locals.dia->win);
+ evas_object_smart_callback_add(o, "changed", _double_getter, &cfdata->locals.error);
+ elm_spinner_min_max_set(o, 0, 2*M_PI);
+ elm_spinner_label_format_set(o, "%f");
+ elm_spinner_step_set(o, 0.1);
+ elm_spinner_editable_set(o, EINA_TRUE);
+ e_widget_framelist_object_append(ol, o);
+ e_widget_list_object_append(obg, ol, 1, 0, 0.5);
+
+ ol = e_widget_framelist_add(evas, _("Length:"), 0);
+ entry = o = elm_spinner_add(cfdata->locals.dia->win);
+ evas_object_smart_callback_add(o, "changed", _double_getter, &cfdata->locals.length);
+ elm_spinner_min_max_set(o, 0, 200);
+ elm_spinner_step_set(o, 5);
+ elm_spinner_editable_set(o, EINA_TRUE);
+ e_widget_framelist_object_append(ol, o);
+ e_widget_list_object_append(obg, ol, 1, 0, 0.5);
+
+ cfdata->locals.fingers = 3;
+ ol = e_widget_framelist_add(evas, _("Fingers:"), 0);
+ entry = o = elm_spinner_add(cfdata->locals.dia->win);
+ evas_object_smart_callback_add(o, "changed", _int_getter, &cfdata->locals.fingers);
+ elm_spinner_min_max_set(o, 3, 10);
+ elm_spinner_value_set(o, 3.0);
+ elm_spinner_editable_set(o, EINA_TRUE);
+ e_widget_framelist_object_append(ol, o);
+ e_widget_list_object_append(obg, ol, 1, 0, 0.5);
+
+ e_widget_size_min_get(obg, &w, &h);
+ e_dialog_content_set(cfdata->locals.dia, obg, MAX(w, 200), MAX(h, 100));
+
+ e_dialog_show(cfdata->locals.dia);
+ e_widget_focus_set(entry, 1);
+}
+
+static void
+_add_swipe_binding_cb(void *data, void *data2 EINA_UNUSED)
+{
+ E_Config_Dialog_Data *cfdata = data;
+
+ _auto_apply_changes(cfdata);
+ _swipe_add_show(cfdata);
+}
+
+static void
+_update_swipe_cb(void *data, Eina_Bool end, double direction, double length, double error, unsigned int fingers)
+{
+ E_Config_Dialog_Data *cfdata = data;
+
+ if (end)
+ {
+ e_object_del(E_OBJECT(cfdata->locals.dia));
+ cfdata->locals.dia = NULL;
+ cfdata->locals.degree = direction;
+ cfdata->locals.length = length;
+ cfdata->locals.error = error;
+ cfdata->locals.fingers = fingers;
+ _flush_binding_swipe(cfdata);
+ e_bindings_swipe_live_update_hook_set(NULL, NULL);
+ }
+ else
+ {
+ char text_buf[1000];
+ Evas_Object *vis;
+
+ vis = create_visualisation(evas_object_evas_get(cfdata->locals.dia->win), direction, error);
+ evas_object_size_hint_align_set(vis, 0.0, 0.5);
+ snprintf(text_buf, sizeof(text_buf), "Using %d Fingers<br> <b>Direction</b> %f <b>Length</b> %f <b>Error</b> %f", fingers, direction, length, error);
+ e_dialog_text_set(cfdata->locals.dia, text_buf);
+ elm_object_part_content_set(cfdata->locals.dia->bg_object, "e.swallow.icon", vis);
+ evas_object_size_hint_min_set(vis, 30 * e_scale, 30 * e_scale);
+ elm_layout_signal_emit(cfdata->locals.dia->bg_object, "e,state,icon", "e");
+ elm_layout_signal_emit(cfdata->locals.dia->bg_object, "e,icon,enabled", "e");
+ }
+}
+
+static void
+_add_swipe_binding_by_sample_cb(void *data, void *data2 EINA_UNUSED)
+{
+ E_Config_Dialog_Data *cfdata = data;
+
+ cfdata->locals.dia = e_dialog_new(cfdata->cfd->dia->win, "E", "_swipe_recognition");
+ e_dialog_title_set(cfdata->locals.dia, "Swipe recognition");
+ e_dialog_text_set(cfdata->locals.dia, "Do your swipe gesture.<br>");
+ elm_win_center(cfdata->locals.dia->win, 1, 1);
+ elm_win_borderless_set(cfdata->locals.dia->win, 1);
+ e_dialog_resizable_set(cfdata->locals.dia, 0);
+ e_dialog_show(cfdata->locals.dia);
+ evas_object_layer_set(e_win_client_get(cfdata->locals.dia->win)->frame, E_LAYER_CLIENT_PRIO);
+
+ e_bindings_swipe_live_update_hook_set(_update_swipe_cb, data);
+}
+
+static void
+_delete_swipe_binding_cb(void *data, void *data2 EINA_UNUSED)
+{
+ Eina_List *l = NULL;
+ int sel, n;
+ E_Config_Dialog_Data *cfdata;
+
+ cfdata = data;
+
+ sel = e_widget_ilist_selected_get(cfdata->gui.o_binding_list);
+ if (cfdata->locals.binding[0] == 's')
+ {
+ if (sscanf(cfdata->locals.binding, "s%d", &n) != 1)
+ return;
+
+ l = eina_list_nth_list(cfdata->binding.swipe, n);
+
+ if (l)
+ {
+ _swipe_binding_free(eina_list_data_get(l));
+ cfdata->binding.swipe = eina_list_remove_list(cfdata->binding.swipe, l);
+ }
+ }
+
+ _update_swipe_binding_list(cfdata);
+
+ if (sel >= e_widget_ilist_count(cfdata->gui.o_binding_list))
+ sel = e_widget_ilist_count(cfdata->gui.o_binding_list) - 1;
+
+ eina_stringshare_del(cfdata->locals.cur);
+ cfdata->locals.cur = NULL;
+
+ e_widget_ilist_selected_set(cfdata->gui.o_binding_list, sel);
+ if (sel < 0)
+ {
+ e_widget_ilist_unselect(cfdata->gui.o_action_list);
+ e_widget_entry_clear(cfdata->gui.o_params);
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+ _update_buttons(cfdata);
+ }
+}
+
+
+static void
+_delete_all_swipe_binding_cb(void *data, void *data2 EINA_UNUSED)
+{
+ E_Config_Binding_Swipe *bi;
+ E_Config_Dialog_Data *cfdata;
+
+ cfdata = data;
+
+ EINA_LIST_FREE(cfdata->binding.swipe, bi)
+ _swipe_binding_free(bi);
+
+ eina_stringshare_del(cfdata->locals.cur);
+ cfdata->locals.cur = NULL;
+
+ e_widget_ilist_clear(cfdata->gui.o_binding_list);
+ e_widget_ilist_go(cfdata->gui.o_binding_list);
+ e_widget_ilist_unselect(cfdata->gui.o_action_list);
+ e_widget_entry_clear(cfdata->gui.o_params);
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+
+ _update_buttons(cfdata);
+}
+
+
+static void
+_restore_swipe_binding_defaults_cb(void *data, void *data2 EINA_UNUSED)
+{
+ E_Config_Bindings *ecb;
+ Eina_Stringshare *prof;
+ E_Config_Dialog_Data *cfdata = data;
+
+ ecb = e_config_domain_system_load("e_bindings", e_config_descriptor_find("E_Config_Bindings"));
+ if (!ecb)
+ {
+ const char *type;
+
+ prof = eina_stringshare_ref(e_config_profile_get());
+ switch (e_config->config_type)
+ {
+ case E_CONFIG_PROFILE_TYPE_DESKTOP:
+ type = "standard";
+ break;
+ case E_CONFIG_PROFILE_TYPE_MOBILE:
+ type = "mobile";
+ break;
+ //case E_CONFIG_PROFILE_TYPE_TABLET: FIXME - not used
+ default:
+ type = "default";
+ break;
+ }
+ e_config_profile_set(type);
+ ecb = e_config_domain_system_load("e_bindings", e_config_descriptor_find("E_Config_Bindings"));
+ e_config_profile_set(prof);
+ eina_stringshare_del(prof);
+ }
+ if (!ecb) return;
+ E_FREE_LIST(cfdata->binding.swipe, e_config_binding_swipe_free);
+ cfdata->binding.swipe = ecb->swipe_bindings, ecb->swipe_bindings = NULL;
+ e_config_bindings_free(ecb);
+
+ eina_stringshare_del(cfdata->locals.cur);
+ cfdata->locals.cur = NULL;
+
+ _update_swipe_binding_list(cfdata);
+ _update_buttons(cfdata);
+
+ e_widget_ilist_unselect(cfdata->gui.o_action_list);
+ e_widget_entry_clear(cfdata->gui.o_params);
+ e_widget_disabled_set(cfdata->gui.o_params, 1);
+}
+
+static Evas_Object *
+_basic_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata)
+{
+ Evas_Object *o, *ol, *ot, *of, *ob;
+
+ cfdata->evas = evas;
+ o = e_widget_list_add(evas, 0, 0);
+ ol = e_widget_list_add(evas, 0, 1);
+
+ of = e_widget_frametable_add(evas, _("Swipe Bindings"), 0);
+ ob = e_widget_ilist_add(evas, 32, 32, &(cfdata->locals.binding));
+ cfdata->gui.o_binding_list = ob;
+ e_widget_size_min_set(ob, 200, 160);
+ e_widget_frametable_object_append(of, ob, 0, 0, 2, 1, 1, 1, 1, 1);
+
+ ob = e_widget_button_add(evas, _("Add"), "list-add", _add_swipe_binding_by_sample_cb, cfdata, NULL);
+ e_widget_frametable_object_append(of, ob, 0, 1, 1, 1, 1, 0, 1, 0);
+
+ ob = e_widget_button_add(evas, _("Add by props"), "list-add", _add_swipe_binding_cb, cfdata, NULL);
+ cfdata->gui.o_add = ob;
+ e_widget_frametable_object_append(of, ob, 0, 2, 1, 1, 1, 0, 1, 0);
+
+ ob = e_widget_button_add(evas, _("Delete"), "list-remove", _delete_swipe_binding_cb, cfdata, NULL);
+ cfdata->gui.o_del = ob;
+ e_widget_disabled_set(ob, 1);
+ e_widget_frametable_object_append(of, ob, 1, 1, 1, 1, 1, 0, 1, 0);
+ ob = e_widget_button_add(evas, _("Delete All"), "edit-clear", _delete_all_swipe_binding_cb, cfdata, NULL);
+ cfdata->gui.o_del_all = ob;
+ e_widget_disabled_set(ob, 1);
+ e_widget_frametable_object_append(of, ob, 1, 2, 1, 1, 1, 0, 1, 0);
+ ob = e_widget_button_add(evas, _("Restore Default Bindings"), "enlightenment", _restore_swipe_binding_defaults_cb, cfdata, NULL);
+ e_widget_frametable_object_append(of, ob, 0, 3, 2, 1, 1, 0, 1, 0);
+ e_widget_list_object_append(ol, of, 1, 1, 0.5);
+
+ ot = e_widget_table_add(e_win_evas_win_get(evas), 0);
+ of = e_widget_framelist_add(evas, _("Action"), 0);
+ ob = e_widget_ilist_add(evas, 24, 24, &(cfdata->locals.action));
+ cfdata->gui.o_action_list = ob;
+ e_widget_size_min_set(ob, 200, 240);
+ e_widget_framelist_object_append(of, ob);
+ e_widget_table_object_append(ot, of, 0, 0, 1, 1, 1, 1, 1, 1);
+
+ of = e_widget_framelist_add(evas, _("Action Params"), 0);
+ ob = e_widget_entry_add(cfd->dia->win, &(cfdata->locals.params), NULL, NULL, NULL);
+ cfdata->gui.o_params = ob;
+ e_widget_disabled_set(ob, 1);
+ e_widget_framelist_object_append(of, ob);
+ e_widget_table_object_append(ot, of, 0, 3, 1, 1, 1, 1, 1, 0);
+ e_widget_list_object_append(ol, ot, 1, 1, 0.5);
+
+ e_widget_list_object_append(o, ol, 1, 1, 0.5);
+
+ _update_swipe_binding_list(cfdata);
+ _fill_actions_list(cfdata);
+
+ e_dialog_resizable_set(cfd->dia, 1);
+ return o;
+}
+
+
+E_Config_Dialog *
+e_int_config_swipebindings(Evas_Object *parent EINA_UNUSED, const char *params)
+{
+ E_Config_Dialog *cfd;
+ E_Config_Dialog_View *v;
+
+ if (e_config_dialog_find("E", "keyboard_and_mouse/swipe_bindings")) return NULL;
+ v = E_NEW(E_Config_Dialog_View, 1);
+
+ v->create_cfdata = _create_data;
+ v->free_cfdata = _free_data;
+ v->basic.apply_cfdata = _basic_apply_data;
+ v->basic.create_widgets = _basic_create_widgets;
+ v->override_auto_apply = 1;
+
+ cfd = e_config_dialog_new(NULL, _("Swipe Bindings Settings"), "E",
+ "keyboard_and_mouse/swipe_bindings",
+ "enlightenment/swipes", 0, v, NULL);
+ if ((params) && (params[0]))
+ {
+ cfd->cfdata->params = eina_stringshare_add(params);
+// _add_swipe_binding_cb(cfd->cfdata, NULL);
+ }
+
+ return cfd;
+}
diff --git a/src/modules/conf_bindings/e_mod_main.c b/src/modules/conf_bindings/e_mod_main.c
index 97f0a46b77..8800faed8a 100644
--- a/src/modules/conf_bindings/e_mod_main.c
+++ b/src/modules/conf_bindings/e_mod_main.c
@@ -33,6 +33,10 @@ e_modapi_init(E_Module *m)
_("Edge Bindings"), NULL,
"preferences-desktop-edge-bindings",
e_int_config_edgebindings);
+ e_configure_registry_item_add("keyboard_and_mouse/swipe_bindings", 10,
+ _("Swipe Bindings"), NULL,
+ "preferences-desktop-swipe-bindings",
+ e_int_config_swipebindings);
e_configure_registry_category_add("advanced", 80, _("Advanced"), NULL, "preferences-advanced");
e_configure_registry_item_add("advanced/signal_bindings", 10,
diff --git a/src/modules/conf_bindings/e_mod_main.h b/src/modules/conf_bindings/e_mod_main.h
index d33fab98de..cc99913a3a 100644
--- a/src/modules/conf_bindings/e_mod_main.h
+++ b/src/modules/conf_bindings/e_mod_main.h
@@ -6,7 +6,7 @@ E_Config_Dialog *e_int_config_keybindings(Evas_Object *parent, const char *param
E_Config_Dialog *e_int_config_mousebindings(Evas_Object *parent, const char *params EINA_UNUSED);
E_Config_Dialog *e_int_config_edgebindings(Evas_Object *parent, const char *params EINA_UNUSED);
E_Config_Dialog *e_int_config_signalbindings(Evas_Object *parent, const char *params);
-
+E_Config_Dialog *e_int_config_swipebindings(Evas_Object *parent EINA_UNUSED, const char *params);
/**
* @addtogroup Optional_Conf
* @{
diff --git a/src/modules/conf_bindings/meson.build b/src/modules/conf_bindings/meson.build
index aec7492fda..ee1adad559 100644
--- a/src/modules/conf_bindings/meson.build
+++ b/src/modules/conf_bindings/meson.build
@@ -5,6 +5,7 @@ src = files(
'e_int_config_keybindings.c',
'e_int_config_mousebindings.c',
'e_int_config_signalbindings.c',
+ 'e_int_config_swipebindings.c',
'e_mod_main.h'
)
diff --git a/src/modules/gesture_recognition/e_mod_main.c b/src/modules/gesture_recognition/e_mod_main.c
new file mode 100644
index 0000000000..2ebbebecc1
--- /dev/null
+++ b/src/modules/gesture_recognition/e_mod_main.c
@@ -0,0 +1,287 @@
+#include <e.h>
+#include <Eina.h>
+#include <libinput.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+E_API E_Module_Api e_modapi =
+ {
+ E_MODULE_API_VERSION,
+ "Gesture Recognition"
+ };
+
+static struct libinput *gesture_recognition_ctx;
+static Ecore_Fd_Handler *fd_listener;
+
+static int open_restricted(const char *path, int flags, void *user_data EINA_UNUSED)
+{
+ int fd = open(path, flags);
+ return fd < 0 ? -errno : fd;
+}
+
+static void close_restricted(int fd, void *user_data EINA_UNUSED)
+{
+ close(fd);
+}
+
+static const struct libinput_interface interface = {
+ .open_restricted = open_restricted,
+ .close_restricted = close_restricted,
+};
+
+static void
+_find_all_touch_input_devices(const char *path, struct libinput *li)
+{
+ Eina_File_Direct_Info *info;
+ Eina_Iterator *input_devies = eina_file_direct_ls(path);
+
+ EINA_ITERATOR_FOREACH(input_devies, info)
+ {
+ struct libinput_device *dev = libinput_path_add_device(li, info->path);
+
+ if (!dev) continue;
+
+ if (!libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_GESTURE))
+ {
+ libinput_path_remove_device(dev);
+ }
+ }
+}
+
+static Eina_Hash *active_gestures;
+
+typedef struct {
+ Eina_Vector2 pos;
+ unsigned int fingers;
+ struct {
+ Evas_Object *visuals, *win;
+ } visuals;
+} Swipe_Stats;
+
+
+static Swipe_Stats*
+_find_swipe_gesture_recognizition(struct libinput_device *dev)
+{
+ Swipe_Stats *stats = eina_hash_find(active_gestures, dev);
+
+ return stats;
+}
+
+static Swipe_Stats*
+_start_swipe_gesture_recognizition(struct libinput_device *dev)
+{
+ Swipe_Stats *stats = _find_swipe_gesture_recognizition(dev);
+
+ if (stats)
+ eina_hash_del_by_key(active_gestures, dev);
+
+ stats = calloc(1, sizeof(Swipe_Stats));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(stats, NULL);
+
+ if (e_bindings_swipe_available())
+ {
+ stats->visuals.win = elm_notify_add(e_comp->elm);
+ elm_notify_align_set(stats->visuals.win, 0.5, 0.5);
+ elm_object_tree_focus_allow_set(stats->visuals.win, EINA_FALSE);
+ evas_object_layer_set(stats->visuals.win, E_LAYER_CLIENT_PRIO);
+ evas_object_show(stats->visuals.win);
+
+ stats->visuals.visuals = elm_progressbar_add(stats->visuals.win);
+ elm_object_text_set(stats->visuals.visuals, "Progress of visuals");
+ evas_object_size_hint_min_set(stats->visuals.visuals, 300, 50);
+ evas_object_show(stats->visuals.visuals);
+ elm_object_content_set(stats->visuals.win, stats->visuals.visuals);
+ }
+
+
+ eina_hash_add(active_gestures, dev, stats);
+
+ return stats;
+}
+
+static void
+_end_swipe_gesture_recognizition(struct libinput_device *dev)
+{
+ eina_hash_del_by_key(active_gestures, dev);
+}
+
+static double
+_config_angle(Eina_Vector2 pos)
+{
+ double res = atan(pos.y/pos.x);
+
+ if (res < 0) res += M_PI;
+ if (pos.y < 0) res += M_PI;
+ return res;
+}
+
+static Eina_Bool
+_cb_input_dispatch(void *data, Ecore_Fd_Handler *hdlr EINA_UNUSED)
+{
+ struct libinput *li = data;
+ struct libinput_event *event;
+
+ if (libinput_dispatch(li) != 0)
+ printf("Failed to dispatch libinput events");
+
+ while((event = libinput_get_event(li)))
+ {
+ E_Bindings_Swipe_Live_Update live_update = e_bindings_swipe_live_update_hook_get();
+
+ enum libinput_event_type type = libinput_event_get_type(event);
+ struct libinput_device *dev = libinput_event_get_device(event);
+ if (type == LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN)
+ {
+ struct libinput_event_gesture *gesture = libinput_event_get_gesture_event(event);
+
+ Swipe_Stats *stats = _start_swipe_gesture_recognizition(dev);
+ stats->fingers = libinput_event_gesture_get_finger_count(gesture);
+ stats->pos.x = stats->pos.y = 0;
+ }
+ else if (type == LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE)
+ {
+ struct libinput_event_gesture *gesture = libinput_event_get_gesture_event(event);
+ Swipe_Stats *stats = _find_swipe_gesture_recognizition(dev);
+
+ stats->pos.x += libinput_event_gesture_get_dx(gesture);
+ stats->pos.y += libinput_event_gesture_get_dy(gesture);
+ if (live_update)
+ {
+ live_update(e_bindings_swipe_live_update_hook_data_get(), EINA_FALSE, _config_angle(stats->pos), eina_vector2_length_get(&stats->pos), 0.8, stats->fingers);
+ }
+ else if (stats->visuals.win)
+ {
+ Eina_Inarray *res = e_bindings_swipe_find_candidates(E_BINDING_CONTEXT_NONE, _config_angle (stats->pos), eina_vector2_length_get(&stats->pos), stats->fingers);
+ E_Binding_Swipe_Candidate *itr;
+ double total = 0.0f;
+ unsigned int len = 0;
+
+ EINA_INARRAY_FOREACH(res, itr)
+ {
+ total += itr->acceptance;
+ len ++;
+ }
+
+ if (len > 0)
+ {
+ char text_buffer[1000];
+
+ snprintf(text_buffer, sizeof(text_buffer), "%d gestures possible", len);
+ elm_progressbar_value_set(stats->visuals.visuals, total/len);
+ elm_object_text_set(stats->visuals.visuals, text_buffer);
+ }
+ else
+ {
+ elm_progressbar_value_set(stats->visuals.visuals, 0.0f);
+ elm_object_text_set(stats->visuals.visuals, "No gesture found");
+ }
+
+ eina_inarray_free(res);
+ }
+ }
+ else if (type == LIBINPUT_EVENT_GESTURE_SWIPE_END)
+ {
+ Swipe_Stats *stats = _find_swipe_gesture_recognizition(dev);
+
+ if (live_update)
+ live_update(e_bindings_swipe_live_update_hook_data_get(), EINA_TRUE, _config_angle(stats->pos), eina_vector2_length_get(&stats->pos), 0.8, stats->fingers);
+ else
+ e_bindings_swipe_handle(E_BINDING_CONTEXT_NONE, NULL, _config_angle(stats->pos), eina_vector2_length_get(&stats->pos), stats->fingers);
+
+ _end_swipe_gesture_recognizition(dev);
+ }
+ libinput_event_destroy(event);
+ }
+ return EINA_TRUE;
+}
+
+static void
+_setup_libinput(void)
+{
+ gesture_recognition_ctx = libinput_path_create_context(&interface, NULL);
+
+ _find_all_touch_input_devices("/dev/input/", gesture_recognition_ctx);
+
+ fd_listener = ecore_main_fd_handler_add(libinput_get_fd(gesture_recognition_ctx), ECORE_FD_READ, _cb_input_dispatch, gesture_recognition_ctx, NULL, NULL);
+}
+
+
+static void
+_tear_down_libinput(void)
+{
+ ecore_main_fd_handler_del(fd_listener);
+ libinput_unref(gesture_recognition_ctx);
+}
+
+static Eina_Bool
+_user_part_of_input(void)
+{
+ uid_t user = getuid();
+ struct passwd *user_pw = getpwuid(user);
+ gid_t *gids = NULL;
+ int number_of_groups = 0;
+ struct group *input_group = getgrnam("input");
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(user_pw, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(input_group, EINA_FALSE);
+
+ if (getgrouplist(user_pw->pw_name, getgid(), NULL, &number_of_groups) != -1)
+ {
+ ERR("Failed to enumerate groups of user");
+ return EINA_FALSE;
+ }
+ number_of_groups ++;
+ gids = alloca((number_of_groups) * sizeof(gid_t));
+ if (getgrouplist(user_pw->pw_name, getgid(), gids, &number_of_groups) == -1)
+ {
+ ERR("Failed to get groups of user");
+ return EINA_FALSE;
+ }
+
+ for (int i = 0; i < number_of_groups; ++i)
+ {
+ if (gids[i] == input_group->gr_gid)
+ return EINA_TRUE;
+ }
+ return EINA_FALSE;
+}
+
+static void
+_stats_free(void *ptr)
+{
+ Swipe_Stats *stats = ptr;
+
+ evas_object_del(stats->visuals.win);
+ free(stats);
+}
+
+E_API int
+e_modapi_init(E_Module *m EINA_UNUSED)
+{
+ if (!_user_part_of_input())
+ {
+ e_module_dialog_show(m, "Gesture Recognition", "Your user is not part of the input group, libinput cannot be used.");
+
+ return 0;
+ }
+ active_gestures = eina_hash_pointer_new(_stats_free);
+ _setup_libinput();
+
+ return 1;
+}
+
+E_API int
+e_modapi_shutdown(E_Module *m EINA_UNUSED)
+{
+ _tear_down_libinput();
+ return 1;
+}
+
+E_API int
+e_modapi_save(E_Module *m EINA_UNUSED)
+{
+
+ return 1;
+}
+
diff --git a/src/modules/gesture_recognition/meson.build b/src/modules/gesture_recognition/meson.build
new file mode 100644
index 0000000000..5a007ee8ed
--- /dev/null
+++ b/src/modules/gesture_recognition/meson.build
@@ -0,0 +1,5 @@
+src = files(
+ 'e_mod_main.c',
+)
+deps += [dependency('libinput')]
+desktop_only = true
diff --git a/src/modules/gesture_recognition/module.desktop b/src/modules/gesture_recognition/module.desktop
new file mode 100644
index 0000000000..8136450cb0
--- /dev/null
+++ b/src/modules/gesture_recognition/module.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Link
+Name=Gesture Recognition
+Comment=Gesture recognition using libinput
+Icon=e-module-start
+X-Enlightenment-ModuleType=utils
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 21a8f1a235..735cd0e77c 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -45,6 +45,7 @@ mods = [
'tiling',
'packagekit',
'vkbd',
+ 'gesture_recognition',
# modules have a custom binary as well
'battery',
'cpufreq',