enlightenment/src/modules/convertible/dbus_acceleration.c

496 lines
15 KiB
C

//
// Created by raffaele on 01/05/19.
//
//#include <Ecore_X.h>
#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);
}
}
}