diff --git a/config.def.h b/config.def.h index d7e9c3b6c8..d7cd3611bf 100644 --- a/config.def.h +++ b/config.def.h @@ -1151,6 +1151,16 @@ static const unsigned turbo_default_btn = RETRO_DEVICE_ID_JOYPAD_B; * gamepads, plug-and-play style. */ static const bool input_autodetect_enable = true; +/* Enables accelerometer/gyroscope/illuminance + * sensor input, if supported */ +#if defined(ANDROID) +/* Hardware sensors cause substantial battery + * drain on Android... */ +#define DEFAULT_INPUT_SENSORS_ENABLE false +#else +#define DEFAULT_INPUT_SENSORS_ENABLE true +#endif + /* Show the input descriptors set by the core instead * of the default ones. */ static const bool input_descriptor_label_show = true; diff --git a/configuration.c b/configuration.c index cd40a50a5c..c0ae79290f 100644 --- a/configuration.c +++ b/configuration.c @@ -1707,6 +1707,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("config_save_on_exit", &settings->bools.config_save_on_exit, true, DEFAULT_CONFIG_SAVE_ON_EXIT, false); SETTING_BOOL("show_hidden_files", &settings->bools.show_hidden_files, true, DEFAULT_SHOW_HIDDEN_FILES, false); SETTING_BOOL("input_autodetect_enable", &settings->bools.input_autodetect_enable, true, input_autodetect_enable, false); + SETTING_BOOL("input_sensors_enable", &settings->bools.input_sensors_enable, true, DEFAULT_INPUT_SENSORS_ENABLE, false); SETTING_BOOL("audio_rate_control", &settings->bools.audio_rate_control, true, DEFAULT_RATE_CONTROL, false); #ifdef HAVE_WASAPI SETTING_BOOL("audio_wasapi_exclusive_mode", &settings->bools.audio_wasapi_exclusive_mode, true, DEFAULT_WASAPI_EXCLUSIVE_MODE, false); diff --git a/configuration.h b/configuration.h index 544ce23cf1..b6f1daa8c2 100644 --- a/configuration.h +++ b/configuration.h @@ -496,6 +496,7 @@ typedef struct settings /* Input */ bool input_remap_binds_enable; bool input_autodetect_enable; + bool input_sensors_enable; bool input_overlay_enable; bool input_overlay_enable_autopreferred; bool input_overlay_hide_in_menu; diff --git a/frontend/drivers/platform_unix.h b/frontend/drivers/platform_unix.h index bbaa45c428..b818191b94 100644 --- a/frontend/drivers/platform_unix.h +++ b/frontend/drivers/platform_unix.h @@ -138,9 +138,11 @@ struct android_app /* Below are "private" implementation of RA code. */ bool unfocused; unsigned accelerometer_event_rate; + unsigned gyroscope_event_rate; ASensorManager *sensorManager; ASensorEventQueue *sensorEventQueue; const ASensor* accelerometerSensor; + const ASensor* gyroscopeSensor; uint64_t sensor_state_mask; char current_ime[PATH_MAX_LENGTH]; bool input_alive; diff --git a/input/drivers/android_input.c b/input/drivers/android_input.c index 22c8e68dd0..868d038894 100644 --- a/input/drivers/android_input.c +++ b/input/drivers/android_input.c @@ -47,6 +47,7 @@ #define MAX_TOUCH 16 #define MAX_NUM_KEYBOARDS 3 +#define DEFAULT_ASENSOR_EVENT_RATE 60 /* If using an SDK lower than 14 then add missing mouse button codes */ #if __ANDROID_API__ < 14 @@ -153,6 +154,7 @@ typedef struct android_input unsigned pads_connected; unsigned pointer_count; sensor_t accelerometer_state; /* float alignment */ + sensor_t gyroscope_state; /* float alignment */ float mouse_x_prev, mouse_y_prev; struct input_pointer pointer[MAX_TOUCH]; /* int16_t alignment */ char device_model[256]; @@ -370,18 +372,27 @@ static void android_input_poll_main_cmd(void) case APP_CMD_GAINED_FOCUS: { - bool boolean = false; + bool boolean = false; + bool enable_accelerometer = (android_app->sensor_state_mask & + (UINT64_C(1) << RETRO_SENSOR_ACCELEROMETER_ENABLE)) && + !android_app->accelerometerSensor; + bool enable_gyroscope = (android_app->sensor_state_mask & + (UINT64_C(1) << RETRO_SENSOR_GYROSCOPE_ENABLE)) && + !android_app->gyroscopeSensor; rarch_ctl(RARCH_CTL_SET_PAUSED, &boolean); rarch_ctl(RARCH_CTL_SET_IDLE, &boolean); video_driver_unset_stub_frame(); - if ((android_app->sensor_state_mask - & (UINT64_C(1) << RETRO_SENSOR_ACCELEROMETER_ENABLE)) - && !android_app->accelerometerSensor) + if (enable_accelerometer) input_sensor_set_state(0, RETRO_SENSOR_ACCELEROMETER_ENABLE, android_app->accelerometer_event_rate); + + if (enable_gyroscope) + input_sensor_set_state(0, + RETRO_SENSOR_GYROSCOPE_ENABLE, + android_app->gyroscope_event_rate); } slock_lock(android_app->mutex); android_app->unfocused = false; @@ -390,20 +401,28 @@ static void android_input_poll_main_cmd(void) break; case APP_CMD_LOST_FOCUS: { - bool boolean = true; + bool boolean = true; + bool disable_accelerometer = (android_app->sensor_state_mask & + (UINT64_C(1) << RETRO_SENSOR_ACCELEROMETER_ENABLE)) && + android_app->accelerometerSensor; + bool disable_gyroscope = (android_app->sensor_state_mask & + (UINT64_C(1) << RETRO_SENSOR_GYROSCOPE_ENABLE)) && + android_app->gyroscopeSensor; rarch_ctl(RARCH_CTL_SET_PAUSED, &boolean); rarch_ctl(RARCH_CTL_SET_IDLE, &boolean); video_driver_set_stub_frame(); /* Avoid draining battery while app is not being used. */ - if ((android_app->sensor_state_mask - & (UINT64_C(1) << RETRO_SENSOR_ACCELEROMETER_ENABLE)) - && android_app->accelerometerSensor != NULL - ) + if (disable_accelerometer) input_sensor_set_state(0, RETRO_SENSOR_ACCELEROMETER_DISABLE, android_app->accelerometer_event_rate); + + if (disable_gyroscope) + input_sensor_set_state(0, + RETRO_SENSOR_GYROSCOPE_DISABLE, + android_app->gyroscope_event_rate); } slock_lock(android_app->mutex); android_app->unfocused = true; @@ -1225,18 +1244,44 @@ static void android_input_poll_input(android_input_t *android, static void android_input_poll_user(android_input_t *android) { struct android_app *android_app = (struct android_app*)g_android; + bool poll_accelerometer = false; + bool poll_gyroscope = false; - if ((android_app->sensor_state_mask & (UINT64_C(1) << - RETRO_SENSOR_ACCELEROMETER_ENABLE)) - && android_app->accelerometerSensor) + if (!android_app->sensorEventQueue) + return; + + poll_accelerometer = (android_app->sensor_state_mask & + (UINT64_C(1) << RETRO_SENSOR_ACCELEROMETER_ENABLE)) && + android_app->accelerometerSensor; + + poll_gyroscope = (android_app->sensor_state_mask & + (UINT64_C(1) << RETRO_SENSOR_GYROSCOPE_ENABLE)) && + android_app->gyroscopeSensor; + + if (poll_accelerometer || poll_gyroscope) { ASensorEvent event; while (ASensorEventQueue_getEvents( - android_app->sensorEventQueue, &event, 1) > 0) + android_app->sensorEventQueue, &event, 1) > 0) { - android->accelerometer_state.x = event.acceleration.x; - android->accelerometer_state.y = event.acceleration.y; - android->accelerometer_state.z = event.acceleration.z; + switch (event.type) + { + case ASENSOR_TYPE_ACCELEROMETER: + android->accelerometer_state.x = event.acceleration.x; + android->accelerometer_state.y = event.acceleration.y; + android->accelerometer_state.z = event.acceleration.z; + break; + case ASENSOR_TYPE_GYROSCOPE: + /* ASensorEvent struct is mysterious - have to + * read the raw 'data' field to get rate of + * rotation... */ + android->gyroscope_state.x = event.data[0]; + android->gyroscope_state.y = event.data[1]; + android->gyroscope_state.z = event.data[2]; + break; + default: + break; + } } } } @@ -1462,10 +1507,16 @@ static void android_input_free_input(void *data) if (!android) return; - if (android_app->sensorManager) + if (android_app->sensorManager && + android_app->sensorEventQueue) ASensorManager_destroyEventQueue(android_app->sensorManager, android_app->sensorEventQueue); + android_app->sensorEventQueue = NULL; + android_app->accelerometerSensor = NULL; + android_app->gyroscopeSensor = NULL; + android_app->sensorManager = NULL; + android_app->input_alive = false; #ifdef HAVE_DYNAMIC @@ -1489,22 +1540,39 @@ static uint64_t android_input_get_capabilities(void *data) static void android_input_enable_sensor_manager(struct android_app *android_app) { - android_app->sensorManager = ASensorManager_getInstance(); - android_app->accelerometerSensor = - ASensorManager_getDefaultSensor(android_app->sensorManager, - ASENSOR_TYPE_ACCELEROMETER); - android_app->sensorEventQueue = - ASensorManager_createEventQueue(android_app->sensorManager, - android_app->looper, LOOPER_ID_USER, NULL, NULL); + if (!android_app->sensorManager) + android_app->sensorManager = ASensorManager_getInstance(); + + if (android_app->sensorManager) + { + if (!android_app->accelerometerSensor) + android_app->accelerometerSensor = + ASensorManager_getDefaultSensor(android_app->sensorManager, + ASENSOR_TYPE_ACCELEROMETER); + + if (!android_app->gyroscopeSensor) + android_app->gyroscopeSensor = + ASensorManager_getDefaultSensor(android_app->sensorManager, + ASENSOR_TYPE_GYROSCOPE); + + if (!android_app->sensorEventQueue) + android_app->sensorEventQueue = + ASensorManager_createEventQueue(android_app->sensorManager, + android_app->looper, LOOPER_ID_USER, NULL, NULL); + } } static bool android_input_set_sensor_state(void *data, unsigned port, enum retro_sensor_action action, unsigned event_rate) { struct android_app *android_app = (struct android_app*)g_android; + android_input_t *android = (android_input_t*)data; + + if (port > 0) + return false; if (event_rate == 0) - event_rate = 60; + event_rate = DEFAULT_ASENSOR_EVENT_RATE; switch (action) { @@ -1512,28 +1580,74 @@ static bool android_input_set_sensor_state(void *data, unsigned port, if (!android_app->accelerometerSensor) android_input_enable_sensor_manager(android_app); - if (android_app->accelerometerSensor) + if (android_app->sensorEventQueue && + android_app->accelerometerSensor) + { ASensorEventQueue_enableSensor(android_app->sensorEventQueue, android_app->accelerometerSensor); - /* Events per second (in microseconds). */ - if (android_app->accelerometerSensor) + /* Events per second (in microseconds). */ ASensorEventQueue_setEventRate(android_app->sensorEventQueue, android_app->accelerometerSensor, (1000L / event_rate) * 1000); + } + + android_app->accelerometer_event_rate = event_rate; BIT64_CLEAR(android_app->sensor_state_mask, RETRO_SENSOR_ACCELEROMETER_DISABLE); BIT64_SET(android_app->sensor_state_mask, RETRO_SENSOR_ACCELEROMETER_ENABLE); return true; case RETRO_SENSOR_ACCELEROMETER_DISABLE: - if (android_app->accelerometerSensor) + if (android_app->sensorEventQueue && + android_app->accelerometerSensor) ASensorEventQueue_disableSensor(android_app->sensorEventQueue, android_app->accelerometerSensor); + android->accelerometer_state.x = 0.0f; + android->accelerometer_state.y = 0.0f; + android->accelerometer_state.z = 0.0f; + BIT64_CLEAR(android_app->sensor_state_mask, RETRO_SENSOR_ACCELEROMETER_ENABLE); BIT64_SET(android_app->sensor_state_mask, RETRO_SENSOR_ACCELEROMETER_DISABLE); return true; + + case RETRO_SENSOR_GYROSCOPE_ENABLE: + if (!android_app->gyroscopeSensor) + android_input_enable_sensor_manager(android_app); + + if (android_app->sensorEventQueue && + android_app->gyroscopeSensor) + { + ASensorEventQueue_enableSensor(android_app->sensorEventQueue, + android_app->gyroscopeSensor); + + /* Events per second (in microseconds). */ + ASensorEventQueue_setEventRate(android_app->sensorEventQueue, + android_app->gyroscopeSensor, (1000L / event_rate) + * 1000); + } + + android_app->gyroscope_event_rate = event_rate; + + BIT64_CLEAR(android_app->sensor_state_mask, RETRO_SENSOR_GYROSCOPE_DISABLE); + BIT64_SET(android_app->sensor_state_mask, RETRO_SENSOR_GYROSCOPE_ENABLE); + return true; + + case RETRO_SENSOR_GYROSCOPE_DISABLE: + if (android_app->sensorEventQueue && + android_app->gyroscopeSensor) + ASensorEventQueue_disableSensor(android_app->sensorEventQueue, + android_app->gyroscopeSensor); + + android->gyroscope_state.x = 0.0f; + android->gyroscope_state.y = 0.0f; + android->gyroscope_state.z = 0.0f; + + BIT64_CLEAR(android_app->sensor_state_mask, RETRO_SENSOR_GYROSCOPE_ENABLE); + BIT64_SET(android_app->sensor_state_mask, RETRO_SENSOR_GYROSCOPE_DISABLE); + return true; + default: break; } @@ -1542,10 +1656,13 @@ static bool android_input_set_sensor_state(void *data, unsigned port, } static float android_input_get_sensor_input(void *data, - unsigned port,unsigned id) + unsigned port, unsigned id) { android_input_t *android = (android_input_t*)data; + if (port > 0) + return 0.0f; + switch (id) { case RETRO_SENSOR_ACCELEROMETER_X: @@ -1554,9 +1671,15 @@ static float android_input_get_sensor_input(void *data, return android->accelerometer_state.y; case RETRO_SENSOR_ACCELEROMETER_Z: return android->accelerometer_state.z; + case RETRO_SENSOR_GYROSCOPE_X: + return android->gyroscope_state.x; + case RETRO_SENSOR_GYROSCOPE_Y: + return android->gyroscope_state.y; + case RETRO_SENSOR_GYROSCOPE_Z: + return android->gyroscope_state.z; } - return 0; + return 0.0f; } input_driver_t input_android = { diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 9c5551eb9c..59edb8d1e7 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1302,6 +1302,10 @@ MSG_HASH( MENU_ENUM_LABEL_INPUT_AUTODETECT_ENABLE, "input_autodetect_enable" ) +MSG_HASH( + MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE, + "input_sensors_enable" + ) MSG_HASH( MENU_ENUM_LABEL_INPUT_BUTTON_AXIS_THRESHOLD, "input_axis_threshold" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index c539a48c75..95f431f317 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2010,6 +2010,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE, "Automatically configures controllers that have a profile, Plug-and-Play style." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_SENSORS_ENABLE, + "Auxiliary Sensor Input" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_SENSORS_ENABLE, + "Enables input from accelerometer, gyroscope and illuminance sensors, if supported by the current hardware. May have a performance impact and/or increase power drain on some platforms." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_BUTTON_AXIS_THRESHOLD, "Input Button Axis Threshold" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index dbef79a63b..00e35bd859 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -487,6 +487,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_savestate_file_compression, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_autosave_interval, MENU_ENUM_SUBLABEL_AUTOSAVE_INTERVAL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_remap_binds_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_autodetect_enable, MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_sensors_enable, MENU_ENUM_SUBLABEL_INPUT_SENSORS_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_swap_ok_cancel, MENU_ENUM_SUBLABEL_MENU_INPUT_SWAP_OK_CANCEL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_pause_libretro, MENU_ENUM_SUBLABEL_PAUSE_LIBRETRO) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_savestate_resume, MENU_ENUM_SUBLABEL_MENU_SAVESTATE_RESUME) @@ -2818,6 +2819,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_INPUT_AUTODETECT_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_autodetect_enable); break; + case MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_sensors_enable); + break; case MENU_ENUM_LABEL_INPUT_REMAP_BINDS_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_remap_binds_enable); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 75c5367f11..e8a9176bc9 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5986,6 +5986,10 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_INPUT_BIND_MODE, PARSE_ONLY_UINT, false) == 0) count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_INPUT_HAPTIC_FEEDBACK_SETTINGS, PARSE_ACTION, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index a0dad1a7ef..0467994a88 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -7296,6 +7296,26 @@ static void general_write_handler(rarch_setting_t *setting) audio_driver_load_system_sounds(); #endif break; + case MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE: + /* When toggling sensor input off, ensure + * that all sensors are actually disabled */ + if (!*setting->value.target.boolean) + { + size_t i; + + for (i = 0; i < DEFAULT_MAX_PADS; i++) + { + /* Event rate does not matter when disabling + * sensors - set to zero */ + input_sensor_set_state(i, + RETRO_SENSOR_ACCELEROMETER_DISABLE, 0); + input_sensor_set_state(i, + RETRO_SENSOR_GYROSCOPE_DISABLE, 0); + input_sensor_set_state(i, + RETRO_SENSOR_ILLUMINANCE_DISABLE, 0); + } + } + break; default: break; } @@ -11714,6 +11734,22 @@ static bool setting_append_list( SD_FLAG_ADVANCED ); + CONFIG_BOOL( + list, list_info, + &settings->bools.input_sensors_enable, + MENU_ENUM_LABEL_INPUT_SENSORS_ENABLE, + MENU_ENUM_LABEL_VALUE_INPUT_SENSORS_ENABLE, + DEFAULT_INPUT_SENSORS_ENABLE, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + #if 0 CONFIG_BOOL( list, list_info, diff --git a/msg_hash.h b/msg_hash.h index 0761556028..be6653f518 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -909,6 +909,7 @@ enum msg_hash_enums MENU_LABEL(INPUT_USER_BINDS), MENU_LABEL(INPUT_DUTY_CYCLE), MENU_LABEL(INPUT_AUTODETECT_ENABLE), + MENU_LABEL(INPUT_SENSORS_ENABLE), MENU_LABEL(INPUT_DESCRIPTOR_LABEL_SHOW), MENU_LABEL(INPUT_DESCRIPTOR_HIDE_UNBOUND), MENU_LABEL(INPUT_BUTTON_AXIS_THRESHOLD), diff --git a/retroarch.c b/retroarch.c index c1b7f9ac65..c440108940 100644 --- a/retroarch.c +++ b/retroarch.c @@ -19587,10 +19587,15 @@ static bool rarch_environment_cb(unsigned cmd, void *data) case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE: { - struct retro_sensor_interface *iface = - (struct retro_sensor_interface*)data; + settings_t *settings = p_rarch->configuration_settings; + bool input_sensors_enable = settings->bools.input_sensors_enable; + struct retro_sensor_interface *iface = (struct retro_sensor_interface*)data; RARCH_LOG("[Environ]: GET_SENSOR_INTERFACE.\n"); + + if (!input_sensors_enable) + return false; + iface->set_sensor_state = input_sensor_set_state; iface->get_sensor_input = input_sensor_get_input; break; @@ -23840,6 +23845,17 @@ bool input_sensor_set_state(unsigned port, enum retro_sensor_action action, unsigned rate) { struct rarch_state *p_rarch = &rarch_st; + settings_t *settings = p_rarch->configuration_settings; + bool input_sensors_enable = settings->bools.input_sensors_enable; + + /* If sensors are disabled, inhibit any enable + * actions (but always allow disable actions) */ + if (!input_sensors_enable && + ((action == RETRO_SENSOR_ACCELEROMETER_ENABLE) || + (action == RETRO_SENSOR_GYROSCOPE_ENABLE) || + (action == RETRO_SENSOR_ILLUMINANCE_ENABLE))) + return false; + if (p_rarch->current_input_data && p_rarch->current_input->set_sensor_state) return p_rarch->current_input->set_sensor_state(p_rarch->current_input_data, @@ -23850,8 +23866,12 @@ bool input_sensor_set_state(unsigned port, float input_sensor_get_input(unsigned port, unsigned id) { struct rarch_state *p_rarch = &rarch_st; - if (p_rarch->current_input_data && - p_rarch->current_input->get_sensor_input) + settings_t *settings = p_rarch->configuration_settings; + bool input_sensors_enable = settings->bools.input_sensors_enable; + + if (input_sensors_enable && + p_rarch->current_input_data && + p_rarch->current_input->get_sensor_input) return p_rarch->current_input->get_sensor_input(p_rarch->current_input_data, port, id); return 0.0f;