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..d2bc856c1 --- /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 accelerometer. + +#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_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..d665b7421 --- /dev/null +++ b/src/modules/convertible/dbus_acceleration.c @@ -0,0 +1,495 @@ +// +// 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" + +static DbusAccelerometer* accelerometer_dbus; + + +static int +_convertible_rotation_get(const 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; + } +} + +static 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; +} + + +static int +_is_device_a_touch_pointer(int dev_counter, int num_properties, char **iterator) +{ + // Looking for a device with either a libinput property for calibration or the old evdev Axlis labels property. + int is_correct_device = EINA_FALSE; + for (int i = 0; i < num_properties; i++) + { + if (strstr(*iterator, "libinput Calibration Matrix")) + is_correct_device = EINA_TRUE; + if (strstr(*iterator, "Axis Labels")) + { + int num_ret, unit_size_ret; + Ecore_X_Atom format_ret; + char *result = ecore_x_input_device_property_get(dev_counter, *iterator, &num_ret, &format_ret, &unit_size_ret); + if (result) { + // TODO Shall check for the value "Abs MT Position" + } + DBG("Looks like I found a device with calibration capabilities"); + is_correct_device = EINA_TRUE; + free(result); + } + iterator++; + } + return is_correct_device; +} + + +static int +_fetch_X_device_input_number(void) +{ + // 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; + char **property_name; + int dev_num = ecore_x_input_device_num_get(); + int dev_number = -1; + + for (int dev_counter = 0; dev_counter < dev_num; dev_counter++) + { + dev_name = ecore_x_input_device_name_get(dev_counter); + // Less horrible hack that relies on the presence of a property containing the work Calibration + DBG("Found device with name %s", dev_name); + int num_properties; + property_name = ecore_x_input_device_properties_list(dev_counter, &num_properties); + DBG("Found %d properties", num_properties); + char **iterator = property_name; + int is_correct_device = _is_device_a_touch_pointer(dev_counter, num_properties, iterator); + if (is_correct_device == EINA_FALSE) + continue; + iterator = property_name; + for (int i = 0; i < num_properties; i++) + { + if (!strcmp(*iterator, CTM_name)) + { + dev_number = dev_counter; + DBG("Setting device: %d", dev_number); + break; + } + iterator++; + } + } + + return dev_number; +} + + +/** + * 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 + */ +static void +_fetch_and_rotate_screen(const char* randr_id, screen_rotation orientation) +{ + DBG("Working on screen %s", randr_id); + E_Randr2_Screen *rotatable_screen = e_randr2_screen_id_find(randr_id); + if (rotatable_screen == NULL) + { + DBG("Failed to load screen for id %s", randr_id); + return; + } + + E_Config_Randr2_Screen *screen_randr_cfg = e_randr2_config_screen_find(rotatable_screen, e_randr2_cfg); + if (screen_randr_cfg == NULL) + { + DBG("Failed to load screen configuration for id %s", randr_id); + return; + } + int rotation = _convertible_rotation_get(orientation); + DBG("Screen %s is going to be rotated to %d", randr_id, rotation); + + if (rotation == screen_randr_cfg->rotation) + { + 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)); + if (matrix == NULL) + { + ERR("Unable to allocate memory for the transformation matrix"); + return; + } + + result = ecore_x_input_device_property_get(x_dev_num, CTM_name, &num_ret, &format_ret, &unit_size_ret); + if (result) + { + 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(result); + free(matrix); + } +} + +/** + * Helper to get the interface + * */ +static 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; +} + +/** + * 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 + */ +static Eina_Bool +_access_bool_property(const Eldbus_Message *msg, Eldbus_Message_Iter **variant, Eina_Bool *boolean_property_value) +{ + 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[0] != 'b') + { + WARN("Expected type is int."); + res = EINA_FALSE; + free(type); + return res; + } + if (type[1]) + { + WARN("It is a complex type, not handle yet."); + 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(type); + return res; +} + +/** + * 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 + */ +static 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"); +} + + +/** + * 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 + */ +static 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"); +} + +/** + * 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 + */ +static 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 = data; + accelerometer->has_accelerometer = has_accelerometer; + DBG("Has Accelerometer: %d", accelerometer->has_accelerometer); +} + + +DbusAccelerometer* +sensor_proxy_init(void) +{ + // Initialise DBUS component + if (accelerometer_dbus) + { + INF("We already have a struct filled"); + return accelerometer_dbus; + } + accelerometer_dbus = calloc(1, sizeof(DbusAccelerometer)); + EINA_SAFETY_ON_NULL_RETURN_VAL(accelerometer_dbus, NULL); + + // The next line is probably redundant + accelerometer_dbus->orientation = UNDEFINED; + + 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(void) +{ + 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(); +} + +/** + * Helper function to extract ta string property from the message + * @param msg The message coming from the get property invocation + * @param variant + * @return Enum specifying the orientation. UNDEFINED by default + */ +static screen_rotation +_access_string_property(const Eldbus_Message *msg, Eldbus_Message_Iter **variant) +{ + screen_rotation rotation = UNDEFINED; + char *type = NULL; + + if (!eldbus_message_arguments_get(msg, "v", variant)) + { + WARN("Error getting arguments."); + } + type = eldbus_message_iter_signature_get((*variant)); + if (type == NULL) + { + WARN("Unable to get the type."); + return rotation; + } + if (type[0] != 's') + { + WARN("Expected type is string(s)."); + free(type); + return rotation; + } + if (type[1]) + { + WARN("It is a complex type, not handle yet."); + } + + const char *string_property_value; + if (!eldbus_message_iter_arguments_get(*variant, "s", &string_property_value)) + { + WARN("error in eldbus_message_iter_arguments_get()"); + } + + 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(type); + return rotation; +} + +void +on_accelerometer_orientation(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + INF("New orientation received"); + Instance *inst = data; + + if (inst->locked_position == EINA_TRUE) + { + WARN("Locked position. Ignoring rotation"); + return; + } + + const char *errname, *errmsg; + screen_rotation orientation; + Eldbus_Message_Iter *variant = NULL; + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + ERR("Error: %s %s", errname, errmsg); + return; + } + + orientation = _access_string_property(msg, &variant); + if (orientation == UNDEFINED) + { + INF("Failed to retrieve the orientation from dbus message"); + return; + } + + inst->accelerometer->orientation = orientation; + DBG("Current Orientation: %d", inst->accelerometer->orientation); + + if (inst->randr2_ids == NULL) + ERR("Screen not set."); + else + { + Eina_List *l; + char *randr_id = NULL; + EINA_LIST_FOREACH(inst->randr2_ids, l, randr_id) + { + _fetch_and_rotate_screen(randr_id, orientation); + } + } +} + diff --git a/src/modules/convertible/dbus_acceleration.h b/src/modules/convertible/dbus_acceleration.h new file mode 100644 index 000000000..fcd4d2f81 --- /dev/null +++ b/src/modules/convertible/dbus_acceleration.h @@ -0,0 +1,45 @@ +#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 +typedef enum {UNDEFINED, NORMAL, RIGHT_UP, FLIPPED, LEFT_UP} screen_rotation; + +typedef struct _DbusAccelerometer DbusAccelerometer; + +struct _DbusAccelerometer +{ + Eina_Bool has_accelerometer; + 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); + + +void sensor_proxy_shutdown(void); + +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); +#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..14ba9db80 --- /dev/null +++ b/src/modules/convertible/e-gadget-convertible.c @@ -0,0 +1,56 @@ +// +// Created by raffaele on 04/05/19. +// +#include "convertible_logging.h" +#include "e-gadget-convertible.h" +#include "e_mod_main.h" + + +static void +_update_instances(const Instance *current_instance) +{ + Eina_List *l; + Instance *instance; + 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, "e,lock,rotation,icon", "convertible/tablet"); + else + edje_object_signal_emit(instance->o_button, "e,unlock,rotation,icon", "convertible/tablet"); + + instance->disabled_keyboard = current_instance->disabled_keyboard; + if (instance->disabled_keyboard == EINA_TRUE) + edje_object_signal_emit(instance->o_button, "e,disable,keyboard,icon", "convertible/input"); + else + edje_object_signal_emit(instance->o_button, "e,enable,keyboard,icon", "convertible/input"); + } + } +} + +void +_rotation_signal_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, + const char *src EINA_UNUSED) +{ + Instance *inst = data; + if (eina_str_has_prefix(sig, "e,unlock")) + inst->locked_position = EINA_FALSE; + if (eina_str_has_prefix(sig, "e,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) +{ + Instance *inst = data; + if (eina_str_has_prefix(sig, "e,enable,keyboard")) + inst->disabled_keyboard = EINA_FALSE; + if (eina_str_has_prefix(sig, "e,disable,keyboard")) + inst->disabled_keyboard = EINA_TRUE; + _update_instances(inst); + +} diff --git a/src/modules/convertible/e-gadget-convertible.h b/src/modules/convertible/e-gadget-convertible.h new file mode 100644 index 000000000..fe006a3f2 --- /dev/null +++ b/src/modules/convertible/e-gadget-convertible.h @@ -0,0 +1,24 @@ +// +// 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 + +/* LIST OF INSTANCES */ +extern Eina_List *instances; + +/* 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); + +/* 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 100644 index 000000000..74f653d31 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..4e3b868ef --- /dev/null +++ b/src/modules/convertible/e_mod_config.c @@ -0,0 +1,173 @@ +// +// Created by raffaele on 01/08/19. +// +#include "convertible_logging.h" +#include "e.h" +#include "e_mod_config.h" + +static Convertible_Config *conv_config = NULL; +E_Config_DD *config_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 + config_edd = E_CONFIG_DD_NEW("Convertible_Config", Convertible_Config); + + E_CONFIG_VAL(config_edd, Convertible_Config, version, INT); + E_CONFIG_VAL(config_edd, Convertible_Config, disable_keyboard_on_rotation, INT); +// E_CONFIG_LIST(config_edd, Convertible_Config, rotatable_screen_configuration, c_zone); +} + +/** + * Update the *conv_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); + conv_config->disable_keyboard_on_rotation = config->disable_keyboard_on_rotation; + e_config_domain_save("module.convertible", config_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 = conv_config->disable_keyboard_on_rotation; +// dialog_data->config->rotatable_screen_configuration = conv_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 conv_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(void) +{ + _econvertible_config_dd_new(); + conv_config = e_config_domain_load("module.econvertible", config_edd); + + // Check version + if (conv_config && !e_util_module_config_check(_("Convertible Module"), + conv_config->version, + MOD_CONFIG_FILE_VERSION)) + { + free(conv_config); + return; + } + + + if (!conv_config) + { + conv_config = E_NEW(Convertible_Config, 1); + conv_config->disable_keyboard_on_rotation = 1; +// conv_config->rotatable_screen_configuration = NULL; + } + + conv_config->version = MOD_CONFIG_FILE_VERSION; + DBG("Config loaded"); +} + +void econvertible_config_shutdown(void) +{ + E_CONFIG_DD_FREE(config_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..9f424ab19 --- /dev/null +++ b/src/modules/convertible/e_mod_config.h @@ -0,0 +1,40 @@ +// +// 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 + +/* Increment for Major Changes */ +#define MOD_CONFIG_FILE_EPOCH 1 +/* Increment for Minor Changes (ie: user doesn't need a new config) */ +#define MOD_CONFIG_FILE_GENERATION 0 +#define MOD_CONFIG_FILE_VERSION ((MOD_CONFIG_FILE_EPOCH * 1000000) + MOD_CONFIG_FILE_GENERATION) + +// Definition of the data structure to hold the gadget configuration +typedef struct _Convertible_Config Convertible_Config; +struct _Convertible_Config +{ + int version; + int disable_keyboard_on_rotation; +}; + +// 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); +void +econvertible_config_shutdown(void); + +#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..cedcf400d --- /dev/null +++ b/src/modules/convertible/e_mod_main.c @@ -0,0 +1,373 @@ +// +// 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; +static E_Config_DD *config_edd; + +// Logger +int _convertible_log_dom; + +/* module setup */ +E_API E_Module_Api e_modapi = + { + E_MODULE_API_VERSION, + "convertible" + }; + + +/* LIST OF INSTANCES */ +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); + + 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, "e,lock,rotation", "tablet", _rotation_signal_cb, instance); + edje_object_signal_callback_add(evas_object, "e,unlock,rotation", "tablet", _rotation_signal_cb, instance); + edje_object_signal_callback_add(evas_object, "e,enable,keyboard", "input", _keyboard_signal_cb, instance); + edje_object_signal_callback_add(evas_object, "e,disable,keyboard", "input", _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) +{ + Evas_Coord mw, mh; + char buf[PATH_MAX]; + const char *s = "float"; + + Instance *current_instance = 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(current_instance->o_button, buf, "e"); + edje_object_message_signal_process(current_instance->o_button); + + mw = 0, mh = 0; + edje_object_size_min_get(current_instance->o_button, &mw, &mh); + if ((mw < 1) || (mh < 1)) + edje_object_size_min_calc(current_instance->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, "icon"); + return o; +} + +static const char * +_gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + static char buf[PATH_MAX]; + + 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 *current_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(current_instance->accelerometer->sensor_proxy, + "AccelerometerOrientation", + on_accelerometer_orientation, current_instance); + 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(); + + // 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; + + // 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) + { + char *randr2_id = strdup(zone->randr2_id); + if (randr2_id == NULL) + ERR("Can't copy the screen name"); + else + 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-edge-bindings", 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", config_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..7ecf1e98c --- /dev/null +++ b/src/modules/convertible/meson.build @@ -0,0 +1,12 @@ +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' +) diff --git a/src/modules/convertible/module.desktop b/src/modules/convertible/module.desktop new file mode 100644 index 000000000..a86d3199a --- /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=e-module-convertible +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',