/**
* @file doubletap.c
* Doubletap control module -- this handles gesture enabling/disabling
*
* Copyright (C) 2013-2019 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 "doubletap.h"
#include "../mce.h"
#include "../mce-log.h"
#include "../mce-io.h"
#include "../mce-conf.h"
#include "../mce-setting.h"
#include
#include
/** Config value for doubletap enable mode */
static dbltap_mode_t dbltap_mode = MCE_DEFAULT_DOUBLETAP_MODE;
static guint dbltap_mode_setting_id = 0;
/** Latest reported proximity sensor state */
static cover_state_t proximity_sensor_actual = COVER_UNDEF;
/** Latest reported proximity blanking */
static bool proximity_blanked = false;
/** Latest reported lid sensor policy decision */
static cover_state_t lid_sensor_filtered = COVER_UNDEF;
/** Path to doubletap wakeup control file */
static char *dbltap_ctrl_path = 0;
/** String to write when enabling double tap wakeups */
static char *dbltap_enable_val = 0;
/** String to write when disabling double tap wakeups */
static char *dbltap_disable_val = 0;
typedef enum {
DT_UNDEF = -1,
DT_DISABLED,
DT_ENABLED,
DT_DISABLED_NOSLEEP,
} dt_state_t;
static const char * const dt_state_name[] =
{
"disabled",
"enabled",
"disabled-no-sleep",
};
/** Path to touchpanel sleep blocking control file */
static char *sleep_mode_ctrl_path = 0;
/** Value to write when touch panel is allowed to enter sleep mode */
static char *sleep_mode_allow_val = 0;
/** Value to write when touch panel is not allowed to enter sleep mode */
static char *sleep_mode_deny_val = 0;
/** Allow/deny touch panel to enter sleep mode
*
* @param allow true to allow touch to sleep, false to deny
*/
static void dbltap_allow_sleep_mode(bool allow)
{
/* Initialized not to match any bool value */
static int allowed = -1;
/* Whether or not this function gets called depends on the
* availability of dbltap_ctrl_path, so we need to check that
* sleep_mode_ctrl_path and related values are also configured
* and available */
if( !sleep_mode_ctrl_path )
goto EXIT;
if( !sleep_mode_allow_val || !sleep_mode_deny_val )
goto EXIT;
if( allowed == allow )
goto EXIT;
allowed = allow;
mce_log(LL_DEBUG, "touch panel sleep mode %s",
allowed ? "allowed" : "denied");
mce_write_string_to_file(sleep_mode_ctrl_path,
allowed ?
sleep_mode_allow_val :
sleep_mode_deny_val);
EXIT:
return;
}
/** Enable/disable doubletap wakeups
*
* @param state disable/enable/disable-without-powering-off
*/
static void dbltap_set_state(dt_state_t state)
{
static dt_state_t prev_state = DT_UNDEF;
bool allow_sleep_mode = true;
if( prev_state == state )
goto EXIT;
prev_state = state;
mce_log(LL_DEBUG, "double tap wakeups: %s", dt_state_name[state]);
const char *val = 0;
switch( state ) {
case DT_DISABLED:
val = dbltap_disable_val;
break;
case DT_ENABLED:
val = dbltap_enable_val;
break;
case DT_DISABLED_NOSLEEP:
val = dbltap_disable_val;
allow_sleep_mode = false;
break;
default:
break;
}
if( val ) {
dbltap_allow_sleep_mode(allow_sleep_mode);
mce_write_string_to_file(dbltap_ctrl_path, val);
}
EXIT:
return;
}
/** Re-evaluate whether doubletap wakeups should be enabled or not
*/
static void dbltap_rethink(void)
{
dt_state_t state = DT_DISABLED;
switch( dbltap_mode ) {
default:
case DBLTAP_ENABLE_ALWAYS:
state = DT_ENABLED;
break;
case DBLTAP_ENABLE_NEVER:
break;
case DBLTAP_ENABLE_NO_PROXIMITY:
/* Default to enabling */
state = DT_ENABLED;
/* Disable due to proximity sensor. */
if( proximity_sensor_actual != COVER_OPEN ) {
/* Note that during in-call proximity blanking we
* want to keep the touch detection powered up but
* not reporting double taps to allow faster touch
* event reporting when unblanking again. */
if( proximity_blanked )
state = DT_DISABLED_NOSLEEP;
else
state = DT_DISABLED;
}
/* Disable due to lid sensor */
if( lid_sensor_filtered == COVER_CLOSED )
state = DT_DISABLED;
break;
}
dbltap_set_state(state);
}
/** Set doubletap wakeup policy
*
* @param mode DBLTAP_ENABLE_ALWAYS|NEVER|NO_PROXIMITY
*/
static void dbltap_mode_set(dbltap_mode_t mode)
{
if( dbltap_mode != mode ) {
dbltap_mode = mode;
dbltap_rethink();
}
}
/** Proximity state changed callback
*
* @param data proximity state as void pointer
*/
static void dbltap_proximity_sensor_actual_trigger(gconstpointer data)
{
cover_state_t state = GPOINTER_TO_INT(data);
if( proximity_sensor_actual != state ) {
proximity_sensor_actual = state;
dbltap_rethink();
}
}
/** Proximity blank changed callback
*
* @param data proximity blank as void pointer
*/
static void dbltap_proximity_blanked_trigger(gconstpointer data)
{
bool blanked = GPOINTER_TO_INT(data);
if( proximity_blanked != blanked ) {
proximity_blanked = blanked;
dbltap_rethink();
}
}
/** Lid sensor policy changed callback
*
* @param data lid policy decision as void pointer
*/
static void dbltap_lid_sensor_filtered_trigger(gconstpointer data)
{
cover_state_t state = GPOINTER_TO_INT(data);
if( lid_sensor_filtered != state ) {
lid_sensor_filtered = state;
dbltap_rethink();
}
}
/** GConf callback for doubletap mode setting
*
* @param gcc (not used)
* @param id Connection ID from gconf_client_notify_add()
* @param entry The modified GConf entry
* @param data (not used)
*/
static void dbltap_mode_setting_cb(GConfClient *const gcc, const guint id,
GConfEntry *const entry, gpointer const data)
{
(void)gcc; (void)data;
const GConfValue *gcv;
if( id != dbltap_mode_setting_id )
goto EXIT;
gint mode = MCE_DEFAULT_DOUBLETAP_MODE;
if( (gcv = gconf_entry_get_value(entry)) )
mode = gconf_value_get_int(gcv);
dbltap_mode_set(mode);
EXIT:
return;
}
/** Check if touch panel sleep mode controls are available
*/
static void dbltap_probe_sleep_mode_controls(void)
{
static const char def_ctrl[] =
"/sys/class/i2c-adapter/i2c-3/3-0020/block_sleep_mode";
static const char def_allow[] = "0";
static const char def_deny[] = "1";
bool success = false;
sleep_mode_ctrl_path =
mce_conf_get_string(MCE_CONF_TPSLEEP_GROUP,
MCE_CONF_TPSLEEP_CONTROL_PATH,
def_ctrl);
if( !sleep_mode_ctrl_path || access(sleep_mode_ctrl_path, F_OK) == -1 )
goto EXIT;
sleep_mode_allow_val =
mce_conf_get_string(MCE_CONF_TPSLEEP_GROUP,
MCE_CONF_TPSLEEP_ALLOW_VALUE,
def_allow);
sleep_mode_deny_val =
mce_conf_get_string(MCE_CONF_TPSLEEP_GROUP,
MCE_CONF_TPSLEEP_DENY_VALUE,
def_deny);
if( !sleep_mode_allow_val || !sleep_mode_deny_val )
goto EXIT;
/* Start from kernel boot time default */
dbltap_allow_sleep_mode(true);
success = true;
EXIT:
/* All or nothing */
if( !success ) {
g_free(sleep_mode_ctrl_path), sleep_mode_ctrl_path = 0;
g_free(sleep_mode_allow_val), sleep_mode_allow_val = 0;
g_free(sleep_mode_deny_val), sleep_mode_deny_val = 0;;
}
return;
}
/** Array of datapipe handlers */
static datapipe_handler_t dbltap_datapipe_handlers[] =
{
// output triggers
{
.datapipe = &proximity_sensor_actual_pipe,
.output_cb = dbltap_proximity_sensor_actual_trigger,
},
{
.datapipe = &proximity_blanked_pipe,
.output_cb = dbltap_proximity_blanked_trigger,
},
{
.datapipe = &lid_sensor_filtered_pipe,
.output_cb = dbltap_lid_sensor_filtered_trigger,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t dbltap_datapipe_bindings =
{
.module = "dbltap",
.handlers = dbltap_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void dbltap_datapipe_init(void)
{
mce_datapipe_init_bindings(&dbltap_datapipe_bindings);
}
/** Remove triggers/filters from datapipes
*/
static void dbltap_datapipe_quit(void)
{
mce_datapipe_quit_bindings(&dbltap_datapipe_bindings);
}
/** Init function for the doubletap module
*
* @param module (not used)
*
* @return NULL on success, a string with an error message on failure
*/
G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module);
const gchar *g_module_check_init(GModule *module)
{
(void)module;
/* Config from ini-files */
dbltap_ctrl_path = mce_conf_get_string(MCE_CONF_DOUBLETAP_GROUP,
MCE_CONF_DOUBLETAP_CONTROL_PATH,
NULL);
dbltap_enable_val = mce_conf_get_string(MCE_CONF_DOUBLETAP_GROUP,
MCE_CONF_DOUBLETAP_ENABLE_VALUE,
"1");
dbltap_disable_val = mce_conf_get_string(MCE_CONF_DOUBLETAP_GROUP,
MCE_CONF_DOUBLETAP_DISABLE_VALUE,
"0");
if( !dbltap_ctrl_path || !dbltap_enable_val || !dbltap_disable_val ) {
mce_log(LL_NOTICE, "no double tap wakeup controls defined");
goto EXIT;
}
dbltap_probe_sleep_mode_controls();
/* Start tracking setting changes */
mce_setting_notifier_add(MCE_SETTING_DOUBLETAP_PATH,
MCE_SETTING_DOUBLETAP_MODE,
dbltap_mode_setting_cb,
&dbltap_mode_setting_id);
gint mode = MCE_DEFAULT_DOUBLETAP_MODE;
mce_setting_get_int(MCE_SETTING_DOUBLETAP_MODE, &mode);
dbltap_mode = mode;
/* Append triggers/filters to datapipes */
dbltap_datapipe_init();
/* Get initial state of datapipes */
proximity_sensor_actual = datapipe_get_gint(proximity_sensor_actual_pipe);
proximity_blanked = datapipe_get_gint(proximity_blanked_pipe);
lid_sensor_filtered = datapipe_get_gint(lid_sensor_filtered_pipe);
/* enable/disable double tap wakeups based on initial conditions */
dbltap_rethink();
EXIT:
return NULL;
}
/** Exit function for the doubletap module
*
* @param module (not used)
*/
G_MODULE_EXPORT void g_module_unload(GModule *module);
void g_module_unload(GModule *module)
{
(void)module;
/* Stop tracking setting changes */
mce_setting_notifier_remove(dbltap_mode_setting_id),
dbltap_mode_setting_id = 0;
/* Remove triggers/filters from datapipes */
dbltap_datapipe_quit();
/* Free config strings */
g_free(dbltap_ctrl_path);
g_free(dbltap_enable_val);
g_free(dbltap_disable_val);
g_free(sleep_mode_ctrl_path);
g_free(sleep_mode_allow_val);
g_free(sleep_mode_deny_val);
return;
}