diff --git a/meson_options.txt b/meson_options.txt index ce0f88d69..3f751cbfb 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -132,6 +132,10 @@ option('connman', type: 'boolean', value: true, description: 'enable connman module: (default=true)') +option('convertible', + type: 'boolean', + value: true, + description: 'enable convertible module: (default=true)') option('cpufreq', type: 'boolean', value: true, diff --git a/src/modules/convertible/accelerometer-orientation.h b/src/modules/convertible/accelerometer-orientation.h new file mode 100755 index 000000000..f1f92d08e --- /dev/null +++ b/src/modules/convertible/accelerometer-orientation.h @@ -0,0 +1,16 @@ +// +// Created by raffaele on 01/05/19. +// + +#ifndef E_GADGET_CONVERTIBLE_IIO_SENSOR_ACCELEROMETER_H +#define E_GADGET_CONVERTIBLE_IIO_SENSOR_ACCELEROMETER_H + +// Those costants are the possible states for the orientation of the acceleromenter. + +#define ACCELEROMETER_ORIENTATION_UNDEFINED "undefined" +#define ACCELEROMETER_ORIENTATION_NORMAL "normal" +#define ACCELEROMETER_ORIENTATION_LEFT "left-up" +#define ACCELEROMETER_ORIENTATION_RIGHT "right-up" +#define ACCELEROMETER_ORIENTATION_BOTTOM "bottom-up" + +#endif //E_GADGET_CONVERTIBLE_IIO_SENSOR_ACCELEROMETER_H diff --git a/src/modules/convertible/convertible-icon.edj b/src/modules/convertible/convertible-icon.edj new file mode 100755 index 000000000..eb0b05378 Binary files /dev/null and b/src/modules/convertible/convertible-icon.edj differ diff --git a/src/modules/convertible/convertible_logging.h b/src/modules/convertible/convertible_logging.h new file mode 100755 index 000000000..3c9fffb7b --- /dev/null +++ b/src/modules/convertible/convertible_logging.h @@ -0,0 +1,22 @@ +// +// Created by raffaele on 29/05/19. +// + +#ifndef E_GADGET_CONVERTIBLE_CONVERTIBLE_LOGGING_H +#define E_GADGET_CONVERTIBLE_CONVERTIBLE_LOGGING_H + +#undef CRIT +#undef ERR +#undef WARN +#undef INF +#undef DBG + +extern int _convertible_log_dom; +#define CRIT(...) EINA_LOG_DOM_CRIT(_convertible_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_convertible_log_dom, __VA_ARGS__) +#define WARN(...) EINA_LOG_DOM_WARN(_convertible_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_convertible_log_dom, __VA_ARGS__) +#define DBG(...) EINA_LOG_DOM_DBG(_convertible_log_dom, __VA_ARGS__) + + +#endif //E_GADGET_CONVERTIBLE_CONVERTIBLE_LOGGING_H diff --git a/src/modules/convertible/dbus_acceleration.c b/src/modules/convertible/dbus_acceleration.c new file mode 100644 index 000000000..6111c69ed --- /dev/null +++ b/src/modules/convertible/dbus_acceleration.c @@ -0,0 +1,438 @@ +// +// Created by raffaele on 01/05/19. +// +//#include +#include "convertible_logging.h" +#include "accelerometer-orientation.h" +#include "dbus_acceleration.h" +#include "e_mod_main.h" +#include "input_rotation.h" + +DbusAccelerometer* accelerometer_dbus; + +DbusAccelerometer* sensor_proxy_init() { + // Initialise DBUS component + if (accelerometer_dbus != NULL) + { + INF("We already have a struct filled"); + return accelerometer_dbus; + } + accelerometer_dbus = calloc(1, sizeof(DbusAccelerometer)); + + // The next line is probably redundant + accelerometer_dbus->orientation = undefined; + + DBG("Before eldbus initialization"); + int initialization = eldbus_init(); + if (initialization == EXIT_FAILURE) + { + ERR("Unable to initialise ELDBUS"); + } + + INF("Getting dbus interfaces"); + accelerometer_dbus->sensor_proxy = get_dbus_interface(EFL_DBUS_ACC_IFACE); + accelerometer_dbus->sensor_proxy_properties = get_dbus_interface(ELDBUS_FDO_INTERFACE_PROPERTIES); + if (accelerometer_dbus->sensor_proxy == NULL) + { + ERR("Unable to get the proxy for interface %s", EFL_DBUS_ACC_IFACE); + return accelerometer_dbus; + } + + accelerometer_dbus->pending_has_orientation = eldbus_proxy_property_get(accelerometer_dbus->sensor_proxy, + "HasAccelerometer", on_has_accelerometer, + accelerometer_dbus); + if (!accelerometer_dbus->pending_has_orientation) + { + ERR("Error: could not get property HasAccelerometer"); + } + + // Claim the accelerometer_dbus + accelerometer_dbus->pending_acc_claim = eldbus_proxy_call(accelerometer_dbus->sensor_proxy, "ClaimAccelerometer", + on_accelerometer_claimed, accelerometer_dbus, -1, ""); + + if (!accelerometer_dbus->pending_acc_claim) + { + ERR("Error: could not call ClaimAccelerometer"); + } + + return accelerometer_dbus; +} + +void sensor_proxy_shutdown() +{ + INF("Removing signal handler dbus_property_changed_sh"); + eldbus_signal_handler_del(accelerometer_dbus->dbus_property_changed_sh); + + // TODO Should to this and wait for the release before continuing + INF("Freeing convertible resources"); + accelerometer_dbus->pending_acc_crelease = eldbus_proxy_call(accelerometer_dbus->sensor_proxy, "ReleaseAccelerometer", on_accelerometer_released, accelerometer_dbus, -1, ""); + if (accelerometer_dbus) + { + e_object_del(E_OBJECT(accelerometer_dbus)); + free(accelerometer_dbus); + accelerometer_dbus = NULL; + } + + DBG("Shutting down ELDBUS"); + eldbus_shutdown(); +} + +int _convertible_rotation_get(const enum screen_rotation orientation); + +int _is_device_a_touch_pointer(int dev_counter, int num_properties, char **iterator); + +Eldbus_Proxy *get_dbus_interface(const char *IFACE) +{ + DBG("Working on interface: %s", IFACE); + Eldbus_Connection *conn; + Eldbus_Object *obj; + Eldbus_Proxy *sensor_proxy; + + conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + if (!conn) + { + ERR("Error: could not get system bus"); + return NULL; + } + obj = eldbus_object_get(conn, EFL_DBUS_ACC_BUS, EFL_DBUS_ACC_PATH); + if (!obj) + { + ERR("Error: could not get object"); + return NULL; + } + sensor_proxy = eldbus_proxy_get(obj, IFACE); + if (!sensor_proxy) + { + ERR("Error: could not get proxy for interface %s", IFACE); + return NULL; + } + + return sensor_proxy; +} + +enum screen_rotation access_string_property(const Eldbus_Message *msg, Eldbus_Message_Iter **variant, Eina_Bool* result) +{ + const char *type = NULL; + *result = EINA_TRUE; + + if (!eldbus_message_arguments_get(msg, "v", variant)) + { + WARN("Error getting arguments."); + *result = EINA_FALSE; + } + type = eldbus_message_iter_signature_get((*variant)); + if (type == NULL) + { + WARN("Unable to get the type."); + *result = EINA_FALSE; + return undefined; + } + + type = eldbus_message_iter_signature_get((*variant)); + if (type[1]) + { + WARN("It is a complex type, not handle yet."); + *result = EINA_FALSE; + } + if (type[0] != 's') + { + WARN("Expected type is string(s)."); + *result = EINA_FALSE; + } + const char **string_property_value = calloc(PATH_MAX, sizeof(char)); + if (!eldbus_message_iter_arguments_get((*variant), "s", string_property_value)) + { + WARN("error in eldbus_message_iter_arguments_get()"); + *result = EINA_FALSE; + } + + enum screen_rotation rotation = undefined; + if (strcmp(ACCELEROMETER_ORIENTATION_RIGHT, *string_property_value) == 0) + rotation = right_up; + if (strcmp(ACCELEROMETER_ORIENTATION_LEFT, *string_property_value) == 0) + rotation = left_up; + if (strcmp(ACCELEROMETER_ORIENTATION_BOTTOM, *string_property_value) == 0) + rotation = flipped; + if (strcmp(ACCELEROMETER_ORIENTATION_NORMAL, *string_property_value) == 0) + rotation = normal; + + free((void *) type); + free(string_property_value); + return rotation; +} + +Eina_Bool +access_bool_property(const Eldbus_Message *msg, Eldbus_Message_Iter **variant, Eina_Bool *boolean_property_value) +{ + const char *type; + Eina_Bool res = EINA_TRUE; + + if (!eldbus_message_arguments_get(msg, "v", variant)) + { + WARN("Error getting arguments."); + res = EINA_FALSE; + } + type = eldbus_message_iter_signature_get((*variant)); + if (type == NULL) + { + WARN("Unable to get the type."); + res = EINA_FALSE; + return res; + } + + if (type[1]) + { + WARN("It is a complex type, not handle yet."); + res = EINA_FALSE; + } + if (type[0] != 'b') + { + WARN("Expected type is int."); + res = EINA_FALSE; + } + if (!eldbus_message_iter_arguments_get((*variant), "b", boolean_property_value)) + { + WARN("error in eldbus_message_iter_arguments_get()"); + res = EINA_FALSE; + } + free((void *) type); + return res; +} + +void +on_has_accelerometer(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *errname, *errmsg; + Eina_Bool has_accelerometer = EINA_FALSE; + Eldbus_Message_Iter *variant = NULL; + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + ERR("Error: %s %s", errname, errmsg); + } + + access_bool_property(msg, &variant, &has_accelerometer); + DbusAccelerometer *accelerometer = (DbusAccelerometer *) data; + accelerometer->has_accelerometer = has_accelerometer; + DBG("Has Accelerometer: %d", accelerometer->has_accelerometer); +} + +void +on_accelerometer_orientation(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + INF("New orientation received"); + Instance *inst = (Instance *) data; + + if (inst->locked_position == EINA_TRUE) + { + WARN("Locked position. Ignoring rotation"); + return; + } + + + const char *errname, *errmsg; + enum screen_rotation orientation; + Eldbus_Message_Iter *variant = NULL; + Eina_Bool* result = calloc(1, sizeof(Eina_Bool)); + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + ERR("Error: %s %s", errname, errmsg); + return; + } + + orientation = access_string_property(msg, &variant, result); + if (*result == EINA_FALSE) + { + INF("Failed to retrieve the orientation from dbus message"); + free(result); + return; + } + free(result); + + inst->accelerometer->orientation = orientation; + DBG("Current Orientation: %d", inst->accelerometer->orientation); + + if (inst->randr2_ids == NULL) + ERR("Screen not set."); + else + { + Eina_List *l; + const char *randr_id = NULL; + EINA_LIST_FOREACH(inst->randr2_ids, l, randr_id) + { + _fetch_and_rotate_screen(randr_id, orientation); + } + free((void *)randr_id); + } +} + +int _convertible_rotation_get(const enum screen_rotation orientation) +{ + switch (orientation) + { + case normal: return 0; + case left_up: return 90; + case flipped: return 180; + case right_up: return 270; + + default: return 0; + } +} + +const float * _get_matrix_rotation_transformation(int rotation) +{ + const float *transformation; + switch (rotation) { + case 90: + transformation = MATRIX_ROTATION_90; + break; + case 180: + transformation = MATRIX_ROTATION_180; + break; + case 270: + transformation = MATRIX_ROTATION_270; + break; + default: + transformation = MATRIX_ROTATION_IDENTITY; + } + return transformation; +} + +int _fetch_X_device_input_number() +{ + // I should get the touchscreen associated with the screen probably by looking at the classes of the input devices + // I need to submit my patch to add getters for other XIDeviceInfo fields, like raster mentioned in his commit. + const char *dev_name = NULL; + char **property_name = NULL; + int dev_num = ecore_x_input_device_num_get(); + int dev_number = -1; + + for (int dev_counter=0; dev_counterrotation) + { + WARN("Screen %s is already rotated to %d degrees", randr_id, rotation); + } else + { + screen_randr_cfg->rotation = rotation; + e_randr2_config_apply(); + DBG("Screen %s rotated to %d", randr_id, rotation); + + int x_dev_num = _fetch_X_device_input_number(); + if (x_dev_num == -1) + { + ERR("Unable to find a pointer device with coordinate transformation capabilities"); + return; + } + DBG("Rotating input number %d", x_dev_num); + + int num_ret, unit_size_ret; + Ecore_X_Atom format_ret; + char *result = NULL; + TransformationMatrix *matrix = calloc(1, sizeof(TransformationMatrix)); + result = ecore_x_input_device_property_get(x_dev_num, CTM_name, &num_ret, &format_ret, &unit_size_ret); + if (result != NULL) + { + + DBG("Device with coordinates transformation matrix"); + // format_ret of 116 -> ECORE_X_ATOM_FLOAT + // num_ret of 9 -> 9 (float) to read + // unit_size_ret of 32 -> each float is 32 bits + memcpy(matrix->values, result, sizeof(matrix->values)); + const float * rotation_matrix_2d = _get_matrix_rotation_transformation(rotation); + memcpy(matrix->values, rotation_matrix_2d, 6 * sizeof(*rotation_matrix_2d)); + ecore_x_input_device_property_set(x_dev_num, CTM_name, matrix->values, num_ret, format_ret, unit_size_ret); + + DBG("Input device %d rotated to %d", x_dev_num, rotation); + } else { + ERR("Unable to fetch coordinates transformation matrix for device %d", x_dev_num); + } + free(matrix); + } +} + +void +on_accelerometer_claimed(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *errname, *errmsg; + + INF("Going to claim the accelerometer_dbus"); + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + ERR("Error: %s %s", errname, errmsg); + return; + } + INF("Accelerometer claimed"); +} + +void +on_accelerometer_released(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + const char *errname, *errmsg; + + INF("Going to release the accelerometer_dbus"); + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + ERR("Error: %s %s", errname, errmsg); + return; + } + INF("Accelerometer released"); +} diff --git a/src/modules/convertible/dbus_acceleration.h b/src/modules/convertible/dbus_acceleration.h new file mode 100644 index 000000000..b194ff08d --- /dev/null +++ b/src/modules/convertible/dbus_acceleration.h @@ -0,0 +1,104 @@ +#include +#include +#include + +#ifndef EFL_DBUS_ACCELERATION +#define EFL_DBUS_ACCELERATION + +#define EFL_DBUS_ACC_BUS "net.hadess.SensorProxy" +#define EFL_DBUS_ACC_PATH "/net/hadess/SensorProxy" +#define EFL_DBUS_ACC_IFACE "net.hadess.SensorProxy" + +// This enum represents the 4 states of screen rotation plus undefined +enum screen_rotation {undefined, normal, right_up, flipped, left_up}; + +typedef struct _DbusAccelerometer DbusAccelerometer; + +struct _DbusAccelerometer +{ + Eina_Bool has_accelerometer; + enum screen_rotation orientation; + Eldbus_Proxy *sensor_proxy, *sensor_proxy_properties; + Eldbus_Pending *pending_has_orientation, *pending_orientation, *pending_acc_claim, *pending_acc_crelease; + Eldbus_Signal_Handler *dbus_property_changed_sh; +}; + +/** + * Fetch the DBUS interfaces and fill the DbusAccelerometer struct + * */ +DbusAccelerometer* sensor_proxy_init(); + + +void sensor_proxy_shutdown(); + +/** + * Helper to get the interface + * */ +Eldbus_Proxy *get_dbus_interface(const char *IFACE); + +/** + * Helper function to extract ta string property from the message + * @param msg The message coming from the get property invocation + * @param variant + * @param result 1 if result is ok, 0 if it failed + * @return Enum specifying the orientation + */ +enum screen_rotation +access_string_property(const Eldbus_Message *msg, Eldbus_Message_Iter **variant, Eina_Bool* result); + +/** + * Helper function to extract ta boolean property from the message + * @param msg The message coming from the get property invocation + * @param variant + * @param boolean_property_value The boolean property pointer where the value should be stored, if read + * @return + */ +Eina_Bool +access_bool_property(const Eldbus_Message *msg, Eldbus_Message_Iter **variant, Eina_Bool *boolean_property_value); + +/** + * Callback definition to handle the request of the hasAccelerometer property of DBUS interface net.hadess.SensorProxy + * @param data DbusAccelerometer + * @param msg The message + * @param pending + */ +void +on_has_accelerometer(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED); + +/** + * Callback definition to handle the request of the accelerometer property of DBUS interface net.hadess.SensorProxy + * @param data DbusAccelerometer + * @param msg The message + * @param pending + */ +void +on_accelerometer_orientation(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED); + +/** + * Callback definition to handle the execution of the ClaimAccelerometer() method of DBUS + * interface net.hadess.SensorProxy + * @param data not used + * @param msg The message + * @param pending + */ +void +on_accelerometer_claimed(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED); + +/** + * Callback definition to handle the execution of the ReleaseAccelerometer() method of DBUS + * interface net.hadess.SensorProxy + * @param data not used + * @param msg The message + * @param pending + */ +void +on_accelerometer_released(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED); + + +/** + * Fetch a screen from its ID and rotate it according to the rotation parameter + * @param randr_id The randr2 id + * @param rotation The expected rotation + */ +void _fetch_and_rotate_screen(const char* randr_id, enum screen_rotation orientation); +#endif diff --git a/src/modules/convertible/e-gadget-convertible.c b/src/modules/convertible/e-gadget-convertible.c new file mode 100644 index 000000000..8030bc4ca --- /dev/null +++ b/src/modules/convertible/e-gadget-convertible.c @@ -0,0 +1,61 @@ +// +// Created by raffaele on 04/05/19. +// +#include "convertible_logging.h" +#include "e-gadget-convertible.h" +#include "e_mod_main.h" + +/* LIST OF INSTANCES */ +static Eina_List *instances = NULL; + +void _update_instances(const Instance *current_instance) +{ + Eina_List *l; + Instance *instance = NULL; + EINA_LIST_FOREACH(instances, l, instance) + { + if (current_instance != instance) + { + instance->locked_position = current_instance->locked_position; + if (instance->locked_position == EINA_TRUE) + edje_object_signal_emit(instance->o_button, "lock,rotation,icon", "convertible/tablet"); + else + edje_object_signal_emit(instance->o_button, "unlock,rotation,icon", "convertible/tablet"); + } + } +} + +void _rotation_signal_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, + const char *src EINA_UNUSED) +{ + DBG("Rotation: Signal %s received from %s", sig, src); + Instance *inst = data; + if (eina_str_has_prefix(sig, "unlock")) + inst->locked_position = EINA_FALSE; + if (eina_str_has_prefix(sig, "lock")) + inst->locked_position = EINA_TRUE; + _update_instances(inst); +} + +void _keyboard_signal_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, + const char *src EINA_UNUSED) +{ + DBG("Keyboard: Signal %s received from %s", sig, src); +} + + +/** + * Callback for gadget creation + * */ +static void +_gadget_created(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + DBG("Inside gadget created"); + // do_orient(inst, e_gadget_site_orient_get(obj), e_gadget_site_anchor_get(obj)); + evas_object_smart_callback_del_full(obj, "gadget_created", _gadget_created, NULL); +} + + +void update_instances(Eina_List *new_instances) { + instances = new_instances; +} diff --git a/src/modules/convertible/e-gadget-convertible.h b/src/modules/convertible/e-gadget-convertible.h new file mode 100644 index 000000000..e353ccf2f --- /dev/null +++ b/src/modules/convertible/e-gadget-convertible.h @@ -0,0 +1,22 @@ +// +// Created by raffaele on 04/05/19. +// +#include +#include +#include "e_mod_main.h" + +#ifndef E_GADGET_CONVERTIBLE_E_GADGET_CONVERTIBLE_H +#define E_GADGET_CONVERTIBLE_E_GADGET_CONVERTIBLE_H + +/* gadcon callback for actions */ +void +_rotation_signal_cb(void *data EINA_UNUSED, Evas_Object *obj, const char *sig EINA_UNUSED, const char *src EINA_UNUSED); + +void +_keyboard_signal_cb(void *data EINA_UNUSED, Evas_Object *obj, const char *sig EINA_UNUSED, const char *src EINA_UNUSED); + +void update_instances(Eina_List *instances); +/* end gadcon callback for actions */ + + +#endif //E_GADGET_CONVERTIBLE_E_GADGET_CONVERTIBLE_H diff --git a/src/modules/convertible/e-module-convertible.edj b/src/modules/convertible/e-module-convertible.edj new file mode 100755 index 000000000..d3e4d4033 Binary files /dev/null and b/src/modules/convertible/e-module-convertible.edj differ diff --git a/src/modules/convertible/e_mod_config.c b/src/modules/convertible/e_mod_config.c new file mode 100644 index 000000000..6f529cd49 --- /dev/null +++ b/src/modules/convertible/e_mod_config.c @@ -0,0 +1,160 @@ +// +// Created by raffaele on 01/08/19. +// +#include "convertible_logging.h" +#include "e.h" +#include "e_mod_config.h" + +static Convertible_Config *_config = NULL; +E_Config_DD *edd = NULL; +EINTERN Convertible_Config *convertible_config; + +/** + * Create the config structure + * */ +static void +_econvertible_config_dd_new(void) +{ +// c_zone = E_CONFIG_DD_NEW("Econvertible_Zone_Config", Convertible_Zone_Config); +// E_CONFIG_VAL(c_zone, Convertible_Zone_Config, name, STR); +// E_CONFIG_VAL(c_zone, Convertible_Zone_Config, follow_rotation, INT); + + // TODO Not sure what his line does. Apparently, it is needed to specify the type of the configuration data structure + edd = E_CONFIG_DD_NEW("Convertible_Config", Convertible_Config); + + E_CONFIG_VAL(edd, Convertible_Config, disable_keyboard_on_rotation, INT); +// E_CONFIG_LIST(edd, Convertible_Config, rotatable_screen_configuration, c_zone); +} + +/** + * Update the *_config data structure based on the settings coming from the dialog panel + * @param config The config coming from the Dialog Panel (E_Config_Dialog_data) + */ +static void +_config_set(Convertible_Config *config) +{ + DBG("config_set disable_keyboard_on_rotation %d", config->disable_keyboard_on_rotation); + _config->disable_keyboard_on_rotation = config->disable_keyboard_on_rotation; + e_config_domain_save("module.convertible", edd, config); +} + +/** + * Initialise the configuration object + * + * @param cfg + * @return + */ +static void* +_create_data(E_Config_Dialog *cfg EINA_UNUSED) +{ + E_Config_Dialog_Data *dialog_data; + + dialog_data = E_NEW(E_Config_Dialog_Data, 1); + dialog_data->config = malloc(sizeof(Convertible_Config)); + dialog_data->config->disable_keyboard_on_rotation = _config->disable_keyboard_on_rotation; +// dialog_data->config->rotatable_screen_configuration = _config->rotatable_screen_configuration; + + DBG("disable_keyboard_on_rotation %d", dialog_data->config->disable_keyboard_on_rotation); + return dialog_data; +} + +/** + * Release memory for the data structure holding the configuration + * + * @param c + * @param dialog_data + */ +static void +_free_data(E_Config_Dialog *c EINA_UNUSED, E_Config_Dialog_Data *dialog_data) +{ + free(dialog_data->config); + free(dialog_data); +} + +/** + * This function should store the modified settings into the data structure referred by the pointer _config + * @param cfd + * @param cfdata + * @return + */ +static int +_basic_apply_data(E_Config_Dialog *cfd EINA_UNUSED, E_Config_Dialog_Data *cfdata) +{ + DBG("_basic_apply_data"); + _config_set(cfdata->config); + return 1; +} + +/** + * Create the panel by adding all the items like labels, checkbox and lists + * + * @param cfd + * @param evas + * @param cfdata + * @return + */ +static Evas_Object * +_basic_create_widgets(E_Config_Dialog *cfd EINA_UNUSED, Evas *evas, + E_Config_Dialog_Data *cfdata) +{ + Evas_Object *o, *evas_option_input_disable; //, *evas_label_section_screens; // *list_item_screen, +// Evas_Object *screen_list = NULL; + + o = e_widget_list_add(evas, 0, 0); + + evas_option_input_disable = e_widget_check_add(evas, "Disable input when rotated", &(cfdata->config->disable_keyboard_on_rotation)); + e_widget_list_object_append(o, evas_option_input_disable, 0, 0, 0); + + DBG("After basic_create_widget"); + return o; + } + +/** + * This function initialise the config dialog for the module + * @param comp + * @param p + * @return + */ +E_Config_Dialog* +e_int_config_convertible_module(Evas_Object *comp, const char *p EINA_UNUSED) +{ + E_Config_Dialog *cfd; + E_Config_Dialog_View *v; + + if (e_config_dialog_find("E", "windows/convertible")) + 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; + + cfd = e_config_dialog_new(comp, + "Convertible Configuration", + "E", "windows/convertible", + NULL, + 0, v, NULL); + return cfd; +} + +void +econvertible_config_init() +{ + _econvertible_config_dd_new(); + _config = e_config_domain_load("module.econvertible", edd); + if (!_config) + { + _config = E_NEW(Convertible_Config, 1); + _config->disable_keyboard_on_rotation = 1; +// _config->rotatable_screen_configuration = NULL; + } + + DBG("Config loaded"); +} + +void econvertible_config_shutdown() +{ + E_CONFIG_DD_FREE(edd); + E_FREE(convertible_config); +} \ No newline at end of file diff --git a/src/modules/convertible/e_mod_config.h b/src/modules/convertible/e_mod_config.h new file mode 100644 index 000000000..3226aea3f --- /dev/null +++ b/src/modules/convertible/e_mod_config.h @@ -0,0 +1,44 @@ +// +// Created by raffaele on 01/08/19. +// +#include +#include "e_mod_main.h" + +#ifndef E_GADGET_CONVERTIBLE_E_MOD_CONFIG_H +#define E_GADGET_CONVERTIBLE_E_MOD_CONFIG_H + +// Definition for a zone configuration +typedef struct _Convertible_Zone_Config Convertible_Zone_Config; + +struct _Convertible_Zone_Config +{ + char *name; + int follow_rotation; +}; + +// Definition of the data structure to hold the gadget configuration +typedef struct _Convertible_Config Convertible_Config; + +struct _Convertible_Config +{ + int disable_keyboard_on_rotation; +// Eina_List *rotatable_screen_configuration; +}; + +// As far as I understand, this structure should hold data useful for the configuration and a pointer to +// another structure holding the configuration options +struct _E_Config_Dialog_Data +{ + Convertible_Config *config; + Evas_Object *zones; + Evas_Object *inputs; +}; + +E_Config_Dialog* e_int_config_convertible_module(Evas_Object *comp, const char *p); +void econvertible_config_init(); +void econvertible_config_shutdown(); +//E_Config_Dialog* econvertible_config_init(Evas_Object *comp, const char*p); +void _menu_new(Instance *inst, Evas_Event_Mouse_Down *ev); +void _mouse_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event); + +#endif //E_GADGET_CONVERTIBLE_E_MOD_CONFIG_H diff --git a/src/modules/convertible/e_mod_main.c b/src/modules/convertible/e_mod_main.c new file mode 100644 index 000000000..1d080e8d0 --- /dev/null +++ b/src/modules/convertible/e_mod_main.c @@ -0,0 +1,378 @@ +// +// Created by raffaele on 04/05/19. +// +#include +#include +#include +#include "convertible_logging.h" +#include "accelerometer-orientation.h" +#include "e-gadget-convertible.h" +#include "e_mod_main.h" +#include "dbus_acceleration.h" +#include "e_mod_config.h" + + +// The main module reference +E_Module *convertible_module; +Instance *inst; + +// Configuration +extern Convertible_Config *convertible_config; +extern E_Config_DD *edd; + +// Logger +int _convertible_log_dom; + +/* module setup */ +E_API E_Module_Api e_modapi = + { + E_MODULE_API_VERSION, + "convertible" + }; + + +/* LIST OF INSTANCES */ +static Eina_List *instances = NULL; + + +/* gadcon requirements */ +static E_Gadcon_Client *_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style); +static void _gc_shutdown(E_Gadcon_Client *gcc); +static void _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient); +static const char *_gc_label(const E_Gadcon_Client_Class *client_class); +static Evas_Object *_gc_icon(const E_Gadcon_Client_Class *client_class, Evas *evas); +static const char *_gc_id_new(const E_Gadcon_Client_Class *client_class); +/* and actually define the gadcon class that this module provides (just 1) */ +static const E_Gadcon_Client_Class _gadcon_class = +{ + GADCON_CLIENT_CLASS_VERSION, + "convertible", + { + _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL, + e_gadcon_site_is_not_toolbar + }, + E_GADCON_CLIENT_STYLE_PLAIN +}; + + +static E_Gadcon_Client * +_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) +{ + Evas_Object *evas_object; + E_Gadcon_Client *gcc; + Instance *instance; + + evas_object = edje_object_add(gc->evas); + e_theme_edje_object_set(evas_object, "base/theme/modules/convertible", + "e/modules/convertible/main"); + + instance = E_NEW(Instance, 1); + instance->accelerometer = inst->accelerometer; + instance->disabled_keyboard = inst->disabled_keyboard; + instance->locked_position = inst->locked_position; + instance->randr2_ids = inst->randr2_ids; + instance->o_button = evas_object; + + instances = eina_list_append(instances, instance); + update_instances(instances); + + gcc = e_gadcon_client_new(gc, name, id, style, evas_object); + gcc->data = instance; + + evas_object_size_hint_aspect_set(evas_object, EVAS_ASPECT_CONTROL_BOTH, 1, 1); + // evas_object_smart_callback_add(parent, "gadget_site_anchor", _anchor_change, inst); + + // Adding callback for EDJE object + INF("Adding callback for creation and other events from EDJE"); + edje_object_signal_callback_add(evas_object, "lock,rotation", "tablet", _rotation_signal_cb, instance); + edje_object_signal_callback_add(evas_object, "unlock,rotation", "tablet", _rotation_signal_cb, instance); + edje_object_signal_callback_add(evas_object, "enable,keyboard", "keyboard", _keyboard_signal_cb, instance); + edje_object_signal_callback_add(evas_object, "disable,keyboard", "keyboard", _keyboard_signal_cb, instance); + + inst->o_button = evas_object; + + return gcc; +} + +static void +_gc_shutdown(E_Gadcon_Client *gcc) +{ + DBG("CONVERTIBLE gadcon shutdown"); + Instance *instance; + + if (!(instance = gcc->data)) return; + instances = eina_list_remove(instances, instance); + instance->accelerometer = NULL; + + // Remove callbacks + DBG("Removing EDJE callbacks"); + edje_object_signal_callback_del(instance->o_button, "lock,rotation", "tablet", _rotation_signal_cb); + edje_object_signal_callback_del(instance->o_button, "unlock,rotation", "tablet", _rotation_signal_cb); + edje_object_signal_callback_del(instance->o_button, "enable,keyboard", "keyboard", _keyboard_signal_cb); + edje_object_signal_callback_del(instance->o_button, "disable,keyboard", "keyboard", _keyboard_signal_cb); + + evas_object_del(instance->o_button); + + E_FREE(instance); +} + +static void +_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient) +{ + Instance *inst; + Evas_Coord mw, mh; + char buf[4096]; + const char *s = "float"; + + inst = gcc->data; + switch (orient) + { + case E_GADCON_ORIENT_FLOAT: + s = "float"; + break; + + case E_GADCON_ORIENT_HORIZ: + s = "horizontal"; + break; + + case E_GADCON_ORIENT_VERT: + s = "vertical"; + break; + + case E_GADCON_ORIENT_LEFT: + s = "left"; + break; + + case E_GADCON_ORIENT_RIGHT: + s = "right"; + break; + + case E_GADCON_ORIENT_TOP: + s = "top"; + break; + + case E_GADCON_ORIENT_BOTTOM: + s = "bottom"; + break; + + case E_GADCON_ORIENT_CORNER_TL: + s = "top_left"; + break; + + case E_GADCON_ORIENT_CORNER_TR: + s = "top_right"; + break; + + case E_GADCON_ORIENT_CORNER_BL: + s = "bottom_left"; + break; + + case E_GADCON_ORIENT_CORNER_BR: + s = "bottom_right"; + break; + + case E_GADCON_ORIENT_CORNER_LT: + s = "left_top"; + break; + + case E_GADCON_ORIENT_CORNER_RT: + s = "right_top"; + break; + + case E_GADCON_ORIENT_CORNER_LB: + s = "left_bottom"; + break; + + case E_GADCON_ORIENT_CORNER_RB: + s = "right_bottom"; + break; + + default: + break; + } + snprintf(buf, sizeof(buf), "e,state,orientation,%s", s); + edje_object_signal_emit(inst->o_button, buf, "e"); + edje_object_message_signal_process(inst->o_button); + + mw = 0, mh = 0; + edje_object_size_min_get(inst->o_button, &mw, &mh); + if ((mw < 1) || (mh < 1)) + edje_object_size_min_calc(inst->o_button, &mw, &mh); + if (mw < 4) mw = 4; + if (mh < 4) mh = 4; + e_gadcon_client_aspect_set(gcc, mw, mh); + e_gadcon_client_min_size_set(gcc, mw, mh); +} + +static const char * +_gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + return "Convertible"; +} + +static Evas_Object * +_gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas) +{ + Evas_Object *o; + char buf[PATH_MAX]; + + o = edje_object_add(evas); + snprintf(buf, sizeof(buf), "%s/e-module-convertible.edj", convertible_module->dir); + edje_object_file_set(o, buf, "main"); + return o; +} + +static const char * +_gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + static char buf[4096]; + + snprintf(buf, sizeof(buf), "%s.%d", client_class->name, + eina_list_count(instances) + 1); + return buf; +} + + +/** + * Prepare to fetch the new value for the DBUS property that has changed + * */ +static void +_cb_properties_changed(void *data, const Eldbus_Message *msg) +{ + Instance *inst = (Instance *) data; + Eldbus_Message_Iter *array, *invalidate; + char *iface; + + if (!eldbus_message_arguments_get(msg, "sa{sv}as", &iface, &array, &invalidate)) + ERR("Error getting data from properties changed signal."); + // Given that the property changed, let's get the new value + Eldbus_Pending *pending_operation = eldbus_proxy_property_get(inst->accelerometer->sensor_proxy, + "AccelerometerOrientation", + on_accelerometer_orientation, inst); + if (!pending_operation) + ERR("Error: could not get property AccelerometerOrientation"); +} + +E_API void * +e_modapi_init(E_Module *m) +{ + // Initialise the logger + _convertible_log_dom = eina_log_domain_register("convertible", EINA_COLOR_LIGHTBLUE); + + convertible_module = m; + char theme_overlay_path[PATH_MAX]; + snprintf(theme_overlay_path, sizeof(theme_overlay_path), "%s/e-module-convertible.edj", convertible_module->dir); + elm_theme_extension_add(NULL, theme_overlay_path); + + econvertible_config_init(NULL); + + // Config DBus + DbusAccelerometer *accelerometer = sensor_proxy_init(); + inst = E_NEW(Instance, 1); + inst->accelerometer = accelerometer; + inst->locked_position = EINA_FALSE; + inst->disabled_keyboard = EINA_FALSE; + + // Making sure we rotate the screen to the current orientation coming from the sensor + inst->accelerometer->pending_orientation = eldbus_proxy_property_get(inst->accelerometer->sensor_proxy, + "AccelerometerOrientation", + on_accelerometer_orientation, inst); + if (!inst->accelerometer->pending_orientation) + { + ERR("Error: could not get property AccelerometerOrientation"); + } + + // Set the callback for property changed event + accelerometer->dbus_property_changed_sh = eldbus_proxy_signal_handler_add(accelerometer->sensor_proxy_properties, + "PropertiesChanged", + _cb_properties_changed, inst); + if (!accelerometer->dbus_property_changed_sh) + ERR("Error: could not add the signal handler for PropertiesChanged"); + + // Screen related part + E_Zone *zone = NULL; + + // Initialise screen part + DBG("Looking for the main screen"); + Eina_List *l; + inst->randr2_ids = NULL; + EINA_LIST_FOREACH(e_comp->zones, l, zone) + { + // Get the screen for the zone + E_Randr2_Screen *screen = e_randr2_screen_id_find(zone->randr2_id); + DBG("name randr2 id %s", zone->randr2_id); + DBG("rot_90 %i", screen->info.can_rot_90); + // Arbitrarily chosen a condition to check that rotation is enabled + if (screen->info.can_rot_90 == EINA_TRUE) + { + int max_screen_length = 300; + char *randr2_id = malloc(sizeof(char) * max_screen_length); + int copied_chars = eina_strlcpy(randr2_id, zone->randr2_id, max_screen_length); + if (copied_chars > max_screen_length) + ERR("Screen name %s has been truncated. Cannot handle screens.", randr2_id); + if (copied_chars < 0) + ERR("Can't copy the screen name"); + + inst->randr2_ids = eina_list_append(inst->randr2_ids, randr2_id); + if (eina_error_get()) + ERR("Memory is low. List allocation failed."); + } + } + + if (inst->randr2_ids == NULL) + ERR("Unable to find rotatable screens"); + + DBG("%d screen(s) has been found", eina_list_count(inst->randr2_ids)); + + e_gadcon_provider_register(&_gadcon_class); + + INF("Creating menu entries for settings"); + e_configure_registry_category_add("extensions", 90, "Extensions", NULL, + "preferences-extensions"); + e_configure_registry_item_add("extensions/convertible", 30, "convertible", NULL, + "preferences-desktop-convertible", e_int_config_convertible_module); + + instances = eina_list_append(instances, inst); + + return m; +} + +E_API int +e_modapi_shutdown(E_Module *m EINA_UNUSED) +{ + INF("Freeing configuration"); + econvertible_config_shutdown(); + + e_configure_registry_item_del("extensions/convertible"); + + // Shutdown Dbus + sensor_proxy_shutdown(); + + // Remove screen info + char *element; + EINA_LIST_FREE(inst->randr2_ids, element) + free(element); + + free(inst); + + INF("Shutting down the module"); + convertible_module = NULL; + e_gadcon_provider_unregister(&_gadcon_class); + + // Removing logger + DBG("Removing the logger"); + eina_log_domain_unregister(_convertible_log_dom); + _convertible_log_dom = -1; + + return 1; +} + +E_API int +e_modapi_save(E_Module *m EINA_UNUSED) +{ + if (convertible_config) + { + e_config_domain_save("module.convertible", edd, convertible_config); + } + return 1; +} diff --git a/src/modules/convertible/e_mod_main.h b/src/modules/convertible/e_mod_main.h new file mode 100644 index 000000000..f1a8e1e6d --- /dev/null +++ b/src/modules/convertible/e_mod_main.h @@ -0,0 +1,25 @@ +// +// Created by raffaele on 04/05/19. +// +#include +#include "dbus_acceleration.h" + + +#ifndef E_GADGET_CONVERTIBLE_CONVERTIBLE_H +#define E_GADGET_CONVERTIBLE_CONVERTIBLE_H + +typedef struct _Instance Instance; + +struct _Instance +{ + Evas_Object *o_button; + DbusAccelerometer *accelerometer; + // I hate to put DBUS related stuff in this struct. Unfortunately, I did not (quickly) find a better way of + // handling signals across multiple instances sharing one DbusAccelerometer struct. + Eina_List *randr2_ids; + + Eina_Bool locked_position; + Eina_Bool disabled_keyboard; +}; + +#endif //E_GADGET_CONVERTIBLE_CONVERTIBLE_H diff --git a/src/modules/convertible/input_rotation.h b/src/modules/convertible/input_rotation.h new file mode 100755 index 000000000..90a1d3cf6 --- /dev/null +++ b/src/modules/convertible/input_rotation.h @@ -0,0 +1,20 @@ +// +// Created by raffaele on 18/12/19. +// + +#ifndef E_GADGET_CONVERTIBLE_INPUT_ROTATION_H +#define E_GADGET_CONVERTIBLE_INPUT_ROTATION_H + +typedef struct _TransformationMatrix { + float values[9]; +} TransformationMatrix; + + +static const float MATRIX_ROTATION_IDENTITY[6] = {1, 0, 0, 0, 1, 0 }; +static const float MATRIX_ROTATION_270[6] = {0, 1, 0, -1, 0, 1 }; +static const float MATRIX_ROTATION_180[6] = {-1, 0, 1, 0, -1, 1 }; +static const float MATRIX_ROTATION_90[6] = {0, -1, 1, 1, 0, 0 }; + +// "Coordinate Transformation Matrix" +static const char *CTM_name = "Coordinate Transformation Matrix"; +#endif //E_GADGET_CONVERTIBLE_INPUT_ROTATION_H diff --git a/src/modules/convertible/meson.build b/src/modules/convertible/meson.build new file mode 100644 index 000000000..5960ef2c3 --- /dev/null +++ b/src/modules/convertible/meson.build @@ -0,0 +1,23 @@ +src = files( + 'e_mod_main.c', + 'dbus_acceleration.h', + 'dbus_acceleration.c', + 'e-gadget-convertible.h', + 'e-gadget-convertible.c', + 'accelerometer-orientation.h', + 'convertible_logging.h', + 'e_mod_main.h', + 'e_mod_config.h', + 'e_mod_config.c' +) + +#shared_module('module', build_files, +# include_directories: inc, + # name_prefix : '', + # dependencies : [ dep_e, deps ], + # install_dir : join_paths([dir_gadgets, module_arch]), + # install : true, + # link_args : '-Wl,--unresolved-symbols=ignore-all' + # ) + + diff --git a/src/modules/convertible/module.desktop b/src/modules/convertible/module.desktop new file mode 100644 index 000000000..3f3992b15 --- /dev/null +++ b/src/modules/convertible/module.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Link +Name=E-convertible +GenericName=E-convertible +Comment=Helps to manage screen rotation based on accelerometer +Comment[it]=Aiuta a gestire la rotazione dello schermo basandosi sui dati dell'accelerometro +Icon=convertible-icon +X-Enlightenment-ModuleType=system diff --git a/src/modules/meson.build b/src/modules/meson.build index 52e543dab..82bd4c8c3 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -5,7 +5,7 @@ module_includes2 = [ '../..' , '../bin' , '../bin/efx' ] module_deps = [ deps_e, dep_dl ] mods = [ -# standard run of the mill modules with cion and desktop +# standard run of the mill modules with icon and desktop 'ibar', 'pager', 'temperature', @@ -29,6 +29,7 @@ mods = [ 'conf_performance', 'conf_paths', 'conf_interaction', + 'convertible', 'gadman', 'geolocation', 'connman',