/**
* @file sensor-gestures.c
*
* Sensor gesture module for the Mode Control Entity
*
* Copyright © 2014 Jolla Ltd.
*
* @author Simo Piiroinen
*
* mce is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* mce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mce. If not, see .
*/
#include "../mce.h"
#include "../mce-log.h"
#include "../mce-setting.h"
#include "../mce-dbus.h"
#include "display.h"
#include
#include
#include
/* ========================================================================= *
* STATE_DATA
* ========================================================================= */
/** Cached display state */
static display_state_t display_state_curr = MCE_DISPLAY_UNDEF;
/** Cached alarm ui state */
static alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_INVALID_INT32;
/** Cached call state */
static call_state_t call_state = CALL_STATE_INVALID;
/** Cached raw orientation sensor value */
static orientation_state_t orientation_sensor_actual = MCE_ORIENTATION_UNDEFINED;
/** Cached delayed orientation sensor value */
static orientation_state_t orientation_sensor_effective = MCE_ORIENTATION_UNDEFINED;
/** Timer id for delayed orientation_sensor_effective updating */
static gint orientation_sensor_effective_id = 0;
/** Use of flipover gesture enabled */
static gboolean sg_flipover_gesture_enabled = MCE_DEFAULT_FLIPOVER_GESTURE_ENABLED;
static guint sg_flipover_gesture_enabled_setting_id = 0;
/* ========================================================================= *
* FUNCTIONS
* ========================================================================= */
/* ------------------------------------------------------------------------- *
* FLIPOVER_GESTURE
* ------------------------------------------------------------------------- */
static void sg_send_flipover_signal (const char *sig);
static void sg_detect_flipover_gesture (void);
/* ------------------------------------------------------------------------- *
* DATAPIPE_TRACKING
* ------------------------------------------------------------------------- */
static bool sg_have_alarm_dialog (void);
static bool sg_have_incoming_call (void);
static void sg_call_state_cb (gconstpointer const data);
static void sg_alarm_ui_state_cb (gconstpointer data);
static void sg_display_state_curr_cb (gconstpointer data);
static void sg_orientation_sensor_update(void);
static gboolean sg_orientation_sensor_effective_cb (gpointer data);
static void sg_orientation_sensor_actual_cb (gconstpointer data);
static void sg_datapipe_init (void);
static void sg_datapipe_quit (void);
/* ------------------------------------------------------------------------- *
* DYNAMIC_SETTINGS
* ------------------------------------------------------------------------- */
static void sg_setting_cb (GConfClient *const gcc, const guint id, GConfEntry *const entry, gpointer const data);
static void sg_setting_init (void);
static void sg_setting_quit (void);
/* ------------------------------------------------------------------------- *
* PLUGIN_LOAD_UNLOAD
* ------------------------------------------------------------------------- */
G_MODULE_EXPORT const gchar *g_module_check_init (GModule *module);
G_MODULE_EXPORT void g_module_unload (GModule *module);
/* ========================================================================= *
* FLIPOVER_GESTURE
* ========================================================================= */
/** Helper for sending flipover dbus signal
*
* @param signal name
*/
static void sg_send_flipover_signal(const char *sig)
{
/* Do not send the signals if orientation sensor happens to be
* powered on for some other reasons than flipover detection */
if( !sg_flipover_gesture_enabled )
goto EXIT;
// NOTE: introspection data shared with powerkey.c
const char *arg = MCE_FEEDBACK_EVENT_FLIPOVER;
mce_log(LL_DEVEL, "sending dbus signal: %s %s", sig, arg);
dbus_send(0, MCE_SIGNAL_PATH, MCE_SIGNAL_IF, sig, 0,
DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
EXIT:
return;
}
/** Detect and broadcast device flipover during alarm
*
* While display is on and alarm active,
* send "flipover" signal over dbus, if
* we first see orientation = face up,
* followed by orientation = face down.
*/
static void sg_detect_flipover_gesture(void)
{
static bool primed = false;
/* Check display state */
if( display_state_curr != MCE_DISPLAY_ON ) {
primed = false;
goto EXIT;
}
/* Check active alarm / incoming call */
if( !sg_have_alarm_dialog() &&
!sg_have_incoming_call() ) {
primed = false;
goto EXIT;
}
/* Check for undefined orientation state */
if( orientation_sensor_actual == MCE_ORIENTATION_UNDEFINED ||
orientation_sensor_effective == MCE_ORIENTATION_UNDEFINED ) {
primed = false;
goto EXIT;
}
/* Check effective orientation state */
if( orientation_sensor_effective == MCE_ORIENTATION_FACE_UP ) {
primed = true;
}
else if( orientation_sensor_effective != MCE_ORIENTATION_FACE_DOWN ) {
// nop
}
else if( primed ) {
primed = false;
if( sg_have_alarm_dialog() )
sg_send_flipover_signal(MCE_ALARM_UI_FEEDBACK_SIG);
if( sg_have_incoming_call() )
sg_send_flipover_signal(MCE_CALL_UI_FEEDBACK_SIG);
}
EXIT:
return;
}
/* ========================================================================= *
* DATAPIPE_TRACKING
* ========================================================================= */
/** Helper for checking if there is an active alarm dialog
*
* @return true if there is alarm, false otherwise
*/
static bool sg_have_alarm_dialog(void)
{
bool res = false;
switch( alarm_ui_state ) {
case MCE_ALARM_UI_RINGING_INT32:
case MCE_ALARM_UI_VISIBLE_INT32:
res = true;
break;
default:
break;
}
return res;
}
/** Helper for checking if there is an incoming call
*
* @return true if there is incoming call, false otherwise
*/
static bool sg_have_incoming_call(void)
{
bool res = false;
switch( call_state ) {
case CALL_STATE_RINGING:
res = true;
break;
default:
break;
}
return res;
}
/** Handle call_state_pipe notifications
*
* @param data The call state stored in a pointer
*/
static void sg_call_state_cb(gconstpointer const data)
{
call_state_t prev = call_state;
call_state = GPOINTER_TO_INT(data);
if( call_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "call: %s -> %s",
call_state_repr(prev),
call_state_repr(call_state));
sg_detect_flipover_gesture();
EXIT:
return;
}
/** Handle alarm_ui_state_pipe notifications
*
* @param data The alarm state stored in a pointer
*/
static void sg_alarm_ui_state_cb(gconstpointer data)
{
alarm_ui_state_t prev = alarm_ui_state;
alarm_ui_state = GPOINTER_TO_INT(data);
if( alarm_ui_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "alarm: %s -> %s",
alarm_state_repr(prev),
alarm_state_repr(alarm_ui_state));
sg_detect_flipover_gesture();
EXIT:
return;
}
/** Handle display_state_curr_pipe notifications
*
* @param data The display state stored in a pointer
*/
static void sg_display_state_curr_cb(gconstpointer data)
{
display_state_t prev = display_state_curr;
display_state_curr = GPOINTER_TO_INT(data);
if( display_state_curr == prev )
goto EXIT;
mce_log(LL_DEBUG, "display: %s -> %s",
display_state_repr(prev),
display_state_repr(display_state_curr));
sg_detect_flipover_gesture();
EXIT:
return;
}
/** Update effective orientation state from raw sensor state
*/
static void sg_orientation_sensor_update(void)
{
orientation_state_t prev = orientation_sensor_effective;
orientation_sensor_effective = orientation_sensor_actual;
if( orientation_sensor_effective == prev )
goto EXIT;
mce_log(LL_DEBUG, "orient.eff: %s -> %s",
orientation_state_repr(prev),
orientation_state_repr(orientation_sensor_effective));
sg_detect_flipover_gesture();
EXIT:
return;
}
/** Handle delayed orientation_sensor_actual_pipe notifications
*
* @param data (unused)
*
* @return FALSE to stop the timer from repeating
*/
static gboolean sg_orientation_sensor_effective_cb(gpointer data)
{
(void)data;
if( !orientation_sensor_effective_id )
goto EXIT;
mce_log(LL_DEBUG, "orient.eff: timer triggered");
orientation_sensor_effective_id = 0;
sg_orientation_sensor_update();
EXIT:
return FALSE;
}
/** Handle orientation_sensor_actual_pipe notifications
*
* @param data The orientation state stored in a pointer
*/
static void sg_orientation_sensor_actual_cb(gconstpointer data)
{
orientation_state_t prev = orientation_sensor_actual;
orientation_sensor_actual = GPOINTER_TO_INT(data);
if( orientation_sensor_actual == prev )
goto EXIT;
mce_log(LL_DEBUG, "orient.raw: %s -> %s",
orientation_state_repr(prev),
orientation_state_repr(orientation_sensor_actual));
/* Unprime if orientation is unknown */
sg_detect_flipover_gesture();
/* When the orientation sensor is stopped and restarted,
* sensord reports initially the last state that was seen
* before the sensor was stopped.
*
* To avoid false positives, the acceptance of face up
* orientation after sensor startup is delayed by bit
* more than what the sensor ramp up is expected to take.
*/
/* Remove existing delay timer */
if( orientation_sensor_effective_id ) {
g_source_remove(orientation_sensor_effective_id);
orientation_sensor_effective_id = 0;
mce_log(LL_DEBUG, "orient.eff: timer canceled");
}
if( prev == MCE_ORIENTATION_UNDEFINED &&
orientation_sensor_actual == MCE_ORIENTATION_FACE_UP ) {
/* Invalidate effective sensor value */
orientation_sensor_effective = MCE_ORIENTATION_UNDEFINED;
/* Schedule re-validation after 1000 ms */
orientation_sensor_effective_id =
g_timeout_add(1000, sg_orientation_sensor_effective_cb, 0);
mce_log(LL_DEBUG, "orient.eff: timer started");
}
else {
/* Update effective sensor value immediately */
sg_orientation_sensor_update();
}
EXIT:
return;
}
/** Array of datapipe handlers */
static datapipe_handler_t sg_datapipe_handlers[] =
{
// input triggers
{
.datapipe = &call_state_pipe,
.input_cb = sg_call_state_cb,
},
// output triggers
{
.datapipe = &orientation_sensor_actual_pipe,
.output_cb = sg_orientation_sensor_actual_cb,
},
{
.datapipe = &display_state_curr_pipe,
.output_cb = sg_display_state_curr_cb,
},
{
.datapipe = &alarm_ui_state_pipe,
.output_cb = sg_alarm_ui_state_cb,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t sg_datapipe_bindings =
{
.module = "sensor-gestures",
.handlers = sg_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void sg_datapipe_init(void)
{
mce_datapipe_init_bindings(&sg_datapipe_bindings);
}
/** Remove triggers/filters from datapipes */
static void sg_datapipe_quit(void)
{
mce_datapipe_quit_bindings(&sg_datapipe_bindings);
}
/* ========================================================================= *
* DYNAMIC_SETTINGS
* ========================================================================= */
/**
* GConf callback for display related settings
*
* @param gcc Unused
* @param id Connection ID from gconf_client_notify_add()
* @param entry The modified GConf entry
* @param data Unused
*/
static void sg_setting_cb(GConfClient *const gcc, const guint id,
GConfEntry *const entry, gpointer const data)
{
const GConfValue *gcv = gconf_entry_get_value(entry);
(void)gcc;
(void)data;
/* Key is unset */
if (gcv == NULL) {
mce_log(LL_DEBUG, "GConf Key `%s' has been unset",
gconf_entry_get_key(entry));
goto EXIT;
}
if( id == sg_flipover_gesture_enabled_setting_id ) {
sg_flipover_gesture_enabled = gconf_value_get_bool(gcv);
}
else {
mce_log(LL_WARN, "Spurious GConf value received; confused!");
}
EXIT:
return;
}
/** Get initial setting values and start tracking changes
*/
static void sg_setting_init(void)
{
mce_setting_track_bool(MCE_SETTING_FLIPOVER_GESTURE_ENABLED,
&sg_flipover_gesture_enabled,
MCE_DEFAULT_FLIPOVER_GESTURE_ENABLED,
sg_setting_cb,
&sg_flipover_gesture_enabled_setting_id);
}
/** Stop tracking setting changes */
static void sg_setting_quit(void)
{
mce_setting_notifier_remove(sg_flipover_gesture_enabled_setting_id),
sg_flipover_gesture_enabled_setting_id = 0;
}
/* ========================================================================= *
* PLUGIN_LOAD_UNLOAD
* ========================================================================= */
/** Init function for the sensor-gestures module
*
* @param module (not used)
*
* @return NULL on success, a string with an error message on failure
*/
const gchar *g_module_check_init(GModule *module)
{
(void)module;
sg_setting_init();
sg_datapipe_init();
return NULL;
}
/** Exit function for the sensor-gestures module
*
* @param module (not used)
*/
void g_module_unload(GModule *module)
{
(void)module;
sg_datapipe_quit();
sg_setting_quit();
return;
}