diff --git a/meson_options.txt b/meson_options.txt index 016dd8124..faedfeada 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 084e6ef51..e2d231761 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; } @@ -324,6 +333,18 @@ e_bindings_key_reset(void) e_comp_canvas_keys_grab(); } +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) { @@ -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,123 @@ _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; +} + + +static Eina_Bool +angle_accepted(double min, double max, double direction) +{ + if (min < direction && max > direction) return EINA_TRUE; + if (max > 2.0*M_PI && (max - 2.0*M_PI) > direction) return EINA_TRUE; + + return EINA_FALSE; +} + + +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) && + angle_accepted(binding->direction - binding->error, binding->direction + binding->error, 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) && + angle_accepted(binding->direction - binding->error, binding->direction + binding->error, 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 043118cbf..b65fabe38 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_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 6c792d4f2..3e5d68f76 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(); @@ -2257,6 +2275,16 @@ e_config_mode_changed(void) ecore_event_add(E_EVENT_CONFIG_MODE_CHANGED, NULL, NULL, NULL); } +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) { diff --git a/src/bin/e_config.h b/src/bin/e_config.h index deae01d31..3b1c304ca 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 000000000..7c47aaa92 --- /dev/null +++ b/src/modules/conf_bindings/e_int_config_swipebindings.c @@ -0,0 +1,978 @@ +#include "e.h" + +#define TEXT_NO_PARAMS _("") + +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
Direction %f Length %f Error %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.
"); + 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 97f0a46b7..8800faed8 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 d33fab98d..cc99913a3 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 aec7492fd..ee1adad55 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 000000000..521063e12 --- /dev/null +++ b/src/modules/gesture_recognition/e_mod_main.c @@ -0,0 +1,287 @@ +#include +#include +#include +#include +#include +#include + +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 Eina_Hash *active_gestures; + +typedef struct { + Eina_Vector2 pos; + unsigned int fingers; + struct { + Evas_Object *visuals, *win; + } visuals; +} Swipe_Stats; + +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 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 000000000..5a007ee8e --- /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 000000000..8136450cb --- /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 21a8f1a23..735cd0e77 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',