mirror of
https://github.com/libretro/RetroArch
synced 2025-02-18 18:40:06 +00:00
Fix mouse grab behavior on Android (#16203)
* Add grab_mouse interface for Android Makes mouse grabbing and 'Game Focus' work on Android with a real mouse Properly handle relative mouse motion events on Android (SDK 28 and newer) * Enable workflow_dispatch on CI Android * Update android_mouse_calculate_deltas callsites * Add RETRO_DEVICE_MOUSE to android_input_get_capabilities * Use Handler to trigger UI events (toggle mouse, immersive mode) with 300ms delay * Enable input_auto_mouse_grab by default for Android * Handle RARCH_DEVICE_MOUSE_SCREEN in Android input driver * Add android.hardware.type.pc to manifest * Don't attempt to set pointer speed via scaling in android_mouse_calculate_deltas * Keep x/y values within viewport resolution for screen mouse * Use video_driver_get_size to get width/height --------- Co-authored-by: Bernhard Schelling <14200249+schellingb@users.noreply.github.com>
This commit is contained in:
parent
338c9a4fe4
commit
5452999b2a
2
.github/workflows/Android.yml
vendored
2
.github/workflows/Android.yml
vendored
@ -3,8 +3,10 @@ name: CI Android
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
types: [run_build]
|
||||
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -1552,6 +1552,14 @@
|
||||
#define DEFAULT_TURBO_DEFAULT_BTN RETRO_DEVICE_ID_JOYPAD_B
|
||||
#define DEFAULT_ALLOW_TURBO_DPAD false
|
||||
|
||||
/* Enable automatic mouse grab by default
|
||||
* only on Android */
|
||||
#if defined(ANDROID)
|
||||
#define DEFAULT_INPUT_AUTO_MOUSE_GRAB true
|
||||
#else
|
||||
#define DEFAULT_INPUT_AUTO_MOUSE_GRAB false
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define DEFAULT_INPUT_KEYBOARD_GAMEPAD_ENABLE false
|
||||
#else
|
||||
|
@ -2086,7 +2086,7 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("keyboard_gamepad_enable", &settings->bools.input_keyboard_gamepad_enable, true, DEFAULT_INPUT_KEYBOARD_GAMEPAD_ENABLE, false);
|
||||
SETTING_BOOL("input_autodetect_enable", &settings->bools.input_autodetect_enable, true, DEFAULT_INPUT_AUTODETECT_ENABLE, false);
|
||||
SETTING_BOOL("input_allow_turbo_dpad", &settings->bools.input_allow_turbo_dpad, true, DEFAULT_ALLOW_TURBO_DPAD, false);
|
||||
SETTING_BOOL("input_auto_mouse_grab", &settings->bools.input_auto_mouse_grab, true, false, false);
|
||||
SETTING_BOOL("input_auto_mouse_grab", &settings->bools.input_auto_mouse_grab, true, DEFAULT_INPUT_AUTO_MOUSE_GRAB, false);
|
||||
SETTING_BOOL("input_remap_binds_enable", &settings->bools.input_remap_binds_enable, true, true, false);
|
||||
SETTING_BOOL("input_hotkey_device_merge", &settings->bools.input_hotkey_device_merge, true, DEFAULT_INPUT_HOTKEY_DEVICE_MERGE, false);
|
||||
SETTING_BOOL("all_users_control_menu", &settings->bools.input_all_users_control_menu, true, DEFAULT_ALL_USERS_CONTROL_MENU, false);
|
||||
|
@ -2090,6 +2090,8 @@ static void frontend_unix_init(void *data)
|
||||
"getVolumeCount", "()I");
|
||||
GET_METHOD_ID(env, android_app->getVolumePath, class,
|
||||
"getVolumePath", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
GET_METHOD_ID(env, android_app->inputGrabMouse, class,
|
||||
"inputGrabMouse", "(Z)V");
|
||||
|
||||
GET_OBJECT_CLASS(env, class, obj);
|
||||
GET_METHOD_ID(env, android_app->getStringExtra, class,
|
||||
|
@ -178,6 +178,7 @@ struct android_app
|
||||
|
||||
jmethodID getVolumeCount;
|
||||
jmethodID getVolumePath;
|
||||
jmethodID inputGrabMouse;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -61,11 +61,17 @@ enum {
|
||||
AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
|
||||
AMOTION_EVENT_AXIS_VSCROLL = 9,
|
||||
AMOTION_EVENT_ACTION_HOVER_MOVE = 7,
|
||||
AINPUT_SOURCE_STYLUS = 0x00004002,
|
||||
AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER,
|
||||
AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5,
|
||||
AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6
|
||||
};
|
||||
#endif
|
||||
/* If using an NDK lower than 16b then add missing definition */
|
||||
#ifndef __ANDROID_API_O_MR1__
|
||||
enum {
|
||||
AINPUT_SOURCE_MOUSE_RELATIVE = 0x00020000 | AINPUT_SOURCE_CLASS_NAVIGATION
|
||||
};
|
||||
#endif
|
||||
|
||||
/* If using an SDK lower than 24 then add missing relative axis codes */
|
||||
#ifndef AMOTION_EVENT_AXIS_RELATIVE_X
|
||||
@ -144,6 +150,7 @@ typedef struct android_input
|
||||
{
|
||||
int64_t quick_tap_time;
|
||||
state_device_t pad_states[MAX_USERS]; /* int alignment */
|
||||
int mouse_x, mouse_y;
|
||||
int mouse_x_delta, mouse_y_delta;
|
||||
int mouse_l, mouse_r, mouse_m, mouse_wu, mouse_wd;
|
||||
unsigned pads_connected;
|
||||
@ -638,53 +645,77 @@ static int android_check_quick_tap(android_input_t *android)
|
||||
}
|
||||
|
||||
static INLINE void android_mouse_calculate_deltas(android_input_t *android,
|
||||
AInputEvent *event,size_t motion_ptr)
|
||||
AInputEvent *event,size_t motion_ptr,int source)
|
||||
{
|
||||
/* Adjust mouse speed based on ratio
|
||||
* between core resolution and system resolution */
|
||||
float x = 0, y = 0;
|
||||
float x_scale = 1;
|
||||
float y_scale = 1;
|
||||
settings_t *settings = config_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
struct retro_system_av_info *av_info = &video_st->av_info;
|
||||
unsigned video_width, video_height;
|
||||
video_driver_get_size(&video_width, &video_height);
|
||||
|
||||
if (av_info)
|
||||
float x = 0;
|
||||
float x_delta = 0;
|
||||
float x_min = 0;
|
||||
float x_max = (float)video_width;
|
||||
|
||||
float y = 0;
|
||||
float y_delta = 0;
|
||||
float y_min = 0;
|
||||
float y_max = (float)video_height;
|
||||
|
||||
/* AINPUT_SOURCE_MOUSE_RELATIVE is available on Oreo (SDK 26) and newer,
|
||||
* it passes the relative coordinates in the regular X and Y parts.
|
||||
* NOTE: AINPUT_SOURCE_* defines have multiple bits set so do full check */
|
||||
if ((source & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE)
|
||||
{
|
||||
video_viewport_t *custom_vp = &settings->video_viewport_custom;
|
||||
const struct retro_game_geometry *geom = (const struct retro_game_geometry*)&av_info->geometry;
|
||||
x_scale = 2 * (float)geom->base_width / (float)custom_vp->width;
|
||||
y_scale = 2 * (float)geom->base_height / (float)custom_vp->height;
|
||||
x_delta = AMotionEvent_getX(event, motion_ptr);
|
||||
y_delta = AMotionEvent_getY(event, motion_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This axis is only available on Android Nougat or on
|
||||
* Android devices with NVIDIA extensions */
|
||||
if (p_AMotionEvent_getAxisValue)
|
||||
{
|
||||
x_delta = AMotionEvent_getAxisValue(event,AMOTION_EVENT_AXIS_RELATIVE_X,
|
||||
motion_ptr);
|
||||
y_delta = AMotionEvent_getAxisValue(event,AMOTION_EVENT_AXIS_RELATIVE_Y,
|
||||
motion_ptr);
|
||||
}
|
||||
|
||||
/* If AXIS_RELATIVE had 0 values it might be because we're not
|
||||
* running Android Nougat or on a device
|
||||
* with NVIDIA extension, so re-calculate deltas based on
|
||||
* AXIS_X and AXIS_Y. This has limitations
|
||||
* compared to AXIS_RELATIVE because once the Android mouse cursor
|
||||
* hits the edge of the screen it is
|
||||
* not possible to move the in-game mouse any further in that direction.
|
||||
*/
|
||||
if (!x_delta && !y_delta)
|
||||
{
|
||||
x = AMotionEvent_getX(event, motion_ptr);
|
||||
y = AMotionEvent_getY(event, motion_ptr);
|
||||
|
||||
x_delta = (x_delta - android->mouse_x_prev);
|
||||
y_delta = (y_delta - android->mouse_y_prev);
|
||||
|
||||
android->mouse_x_prev = x;
|
||||
android->mouse_y_prev = y;
|
||||
}
|
||||
}
|
||||
|
||||
/* This axis is only available on Android Nougat and on
|
||||
* Android devices with NVIDIA extensions */
|
||||
if (p_AMotionEvent_getAxisValue)
|
||||
{
|
||||
x = AMotionEvent_getAxisValue(event,AMOTION_EVENT_AXIS_RELATIVE_X,
|
||||
motion_ptr);
|
||||
y = AMotionEvent_getAxisValue(event,AMOTION_EVENT_AXIS_RELATIVE_Y,
|
||||
motion_ptr);
|
||||
}
|
||||
android->mouse_x_delta = x_delta;
|
||||
android->mouse_y_delta = y_delta;
|
||||
|
||||
/* If AXIS_RELATIVE had 0 values it might be because we're not
|
||||
* running Android Nougat or on a device
|
||||
* with NVIDIA extension, so re-calculate deltas based on
|
||||
* AXIS_X and AXIS_Y. This has limitations
|
||||
* compared to AXIS_RELATIVE because once the Android mouse cursor
|
||||
* hits the edge of the screen it is
|
||||
* not possible to move the in-game mouse any further in that direction.
|
||||
*/
|
||||
if (!x && !y)
|
||||
{
|
||||
x = (AMotionEvent_getX(event, motion_ptr) - android->mouse_x_prev);
|
||||
y = (AMotionEvent_getY(event, motion_ptr) - android->mouse_y_prev);
|
||||
android->mouse_x_prev = AMotionEvent_getX(event, motion_ptr);
|
||||
android->mouse_y_prev = AMotionEvent_getY(event, motion_ptr);
|
||||
}
|
||||
if (!x) x = android->mouse_x + android->mouse_x_delta;
|
||||
if (!y) y = android->mouse_y + android->mouse_y_delta;
|
||||
|
||||
android->mouse_x_delta = ceil(x) * x_scale;
|
||||
android->mouse_y_delta = ceil(y) * y_scale;
|
||||
/* x and y are used for the screen mouse, so we want
|
||||
* to avoid values outside of the viewport resolution */
|
||||
if (x < x_min) x = x_min;
|
||||
else if (x > x_max) x = x_max;
|
||||
if (y < y_min) y = y_min;
|
||||
else if (y > y_max) y = y_max;
|
||||
|
||||
android->mouse_x = x;
|
||||
android->mouse_y = y;
|
||||
}
|
||||
|
||||
static INLINE void android_input_poll_event_type_motion(
|
||||
@ -697,13 +728,13 @@ static INLINE void android_input_poll_event_type_motion(
|
||||
bool keyup = (
|
||||
action == AMOTION_EVENT_ACTION_UP
|
||||
|| action == AMOTION_EVENT_ACTION_CANCEL
|
||||
|| action == AMOTION_EVENT_ACTION_POINTER_UP)
|
||||
|| (source == AINPUT_SOURCE_MOUSE &&
|
||||
action != AMOTION_EVENT_ACTION_DOWN);
|
||||
|| action == AMOTION_EVENT_ACTION_POINTER_UP);
|
||||
|
||||
/* If source is mouse then calculate button state
|
||||
* and mouse deltas and don't process as touchscreen event */
|
||||
if (source == AINPUT_SOURCE_MOUSE)
|
||||
* and mouse deltas and don't process as touchscreen event.
|
||||
* NOTE: AINPUT_SOURCE_* defines have multiple bits set so do full check */
|
||||
if ( (source & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE
|
||||
|| (source & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE)
|
||||
{
|
||||
/* getButtonState requires API level 14 */
|
||||
if (p_AMotionEvent_getButtonState)
|
||||
@ -732,7 +763,7 @@ static INLINE void android_input_poll_event_type_motion(
|
||||
android->mouse_l = 0;
|
||||
}
|
||||
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr);
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr,source);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -785,7 +816,7 @@ static INLINE void android_input_poll_event_type_motion(
|
||||
if (( action == AMOTION_EVENT_ACTION_MOVE
|
||||
|| action == AMOTION_EVENT_ACTION_HOVER_MOVE)
|
||||
&& ENABLE_TOUCH_SCREEN_MOUSE)
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr);
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr,source);
|
||||
|
||||
for (motion_ptr = 0; motion_ptr < pointer_max; motion_ptr++)
|
||||
{
|
||||
@ -850,7 +881,7 @@ static INLINE void android_input_poll_event_type_motion_stylus(
|
||||
android->mouse_l = 0;
|
||||
}
|
||||
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr);
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr,source);
|
||||
}
|
||||
|
||||
if (action == AMOTION_EVENT_ACTION_MOVE) {
|
||||
@ -893,7 +924,7 @@ static INLINE void android_input_poll_event_type_motion_stylus(
|
||||
{
|
||||
android->mouse_l = 0;
|
||||
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr);
|
||||
android_mouse_calculate_deltas(android,event,motion_ptr,source);
|
||||
}
|
||||
|
||||
// pointer was already released during AMOTION_EVENT_ACTION_HOVER_MOVE
|
||||
@ -967,7 +998,7 @@ static int android_input_get_id_port(android_input_t *android, int id,
|
||||
unsigned i;
|
||||
int ret = -1;
|
||||
if (source & (AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_MOUSE |
|
||||
AINPUT_SOURCE_TOUCHPAD))
|
||||
AINPUT_SOURCE_MOUSE_RELATIVE | AINPUT_SOURCE_TOUCHPAD))
|
||||
ret = 0; /* touch overlay is always user 1 */
|
||||
|
||||
for (i = 0; i < android->pads_connected; i++)
|
||||
@ -1565,7 +1596,10 @@ static void android_input_poll_input_default(android_input_t *android)
|
||||
else if ((source & AINPUT_SOURCE_STYLUS) == AINPUT_SOURCE_STYLUS)
|
||||
android_input_poll_event_type_motion_stylus(android, event,
|
||||
port, source);
|
||||
else if ((source & (AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_MOUSE)))
|
||||
/* Only handle events from a touchscreen or mouse */
|
||||
else if ((source & (AINPUT_SOURCE_TOUCHSCREEN
|
||||
| AINPUT_SOURCE_MOUSE
|
||||
| AINPUT_SOURCE_MOUSE_RELATIVE)))
|
||||
android_input_poll_event_type_motion(android, event,
|
||||
port, source);
|
||||
else
|
||||
@ -1774,6 +1808,7 @@ static int16_t android_input_state(
|
||||
case RETRO_DEVICE_KEYBOARD:
|
||||
return (id && id < RETROK_LAST) && BIT_GET(android_key_state[ANDROID_KEYBOARD_PORT], rarch_keysym_lut[id]);
|
||||
case RETRO_DEVICE_MOUSE:
|
||||
case RARCH_DEVICE_MOUSE_SCREEN:
|
||||
{
|
||||
int val = 0;
|
||||
if (port > 0)
|
||||
@ -1788,11 +1823,17 @@ static int16_t android_input_state(
|
||||
case RETRO_DEVICE_ID_MOUSE_MIDDLE:
|
||||
return android->mouse_m;
|
||||
case RETRO_DEVICE_ID_MOUSE_X:
|
||||
if (device == RARCH_DEVICE_MOUSE_SCREEN)
|
||||
return android->mouse_x;
|
||||
|
||||
val = android->mouse_x_delta;
|
||||
android->mouse_x_delta = 0;
|
||||
/* flush delta after it has been read */
|
||||
return val;
|
||||
case RETRO_DEVICE_ID_MOUSE_Y:
|
||||
if (device == RARCH_DEVICE_MOUSE_SCREEN)
|
||||
return android->mouse_y;
|
||||
|
||||
val = android->mouse_y_delta;
|
||||
android->mouse_y_delta = 0;
|
||||
/* flush delta after it has been read */
|
||||
@ -1907,6 +1948,7 @@ static uint64_t android_input_get_capabilities(void *data)
|
||||
return
|
||||
(1 << RETRO_DEVICE_JOYPAD)
|
||||
| (1 << RETRO_DEVICE_POINTER)
|
||||
| (1 << RETRO_DEVICE_MOUSE)
|
||||
| (1 << RETRO_DEVICE_KEYBOARD)
|
||||
| (1 << RETRO_DEVICE_LIGHTGUN)
|
||||
| (1 << RETRO_DEVICE_ANALOG);
|
||||
@ -2056,6 +2098,18 @@ static float android_input_get_sensor_input(void *data,
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static void android_input_grab_mouse(void *data, bool state)
|
||||
{
|
||||
JNIEnv *env = jni_thread_getenv();
|
||||
|
||||
if (!env || !g_android)
|
||||
return;
|
||||
|
||||
if (g_android->inputGrabMouse)
|
||||
CALL_VOID_METHOD_PARAM(env, g_android->activity->clazz,
|
||||
g_android->inputGrabMouse, state);
|
||||
}
|
||||
|
||||
static void android_input_keypress_vibrate()
|
||||
{
|
||||
static const int keyboard_press = 3;
|
||||
@ -2077,8 +2131,7 @@ input_driver_t input_android = {
|
||||
android_input_get_sensor_input,
|
||||
android_input_get_capabilities,
|
||||
"android",
|
||||
|
||||
NULL, /* grab_mouse */
|
||||
android_input_grab_mouse,
|
||||
NULL,
|
||||
android_input_keypress_vibrate
|
||||
};
|
||||
|
@ -15272,7 +15272,7 @@ static bool setting_append_list(
|
||||
&settings->bools.input_auto_mouse_grab,
|
||||
MENU_ENUM_LABEL_INPUT_AUTO_MOUSE_GRAB,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_AUTO_MOUSE_GRAB,
|
||||
false,
|
||||
DEFAULT_INPUT_AUTO_MOUSE_GRAB,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
|
@ -6,6 +6,7 @@
|
||||
android:versionName="1.17.0"
|
||||
android:installLocation="internalOnly">
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
<uses-feature android:name="android.hardware.type.pc" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.gamepad" android:required="false"/>
|
||||
|
@ -1,12 +1,17 @@
|
||||
package com.retroarch.browser.retroactivity;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import com.retroarch.browser.preferences.util.ConfigFile;
|
||||
import com.retroarch.browser.preferences.util.UserPreferences;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -14,58 +19,69 @@ import java.lang.reflect.Method;
|
||||
|
||||
public final class RetroActivityFuture extends RetroActivityCamera {
|
||||
|
||||
// If set to true then Retroarch will completely exit when it loses focus
|
||||
// If set to true then RetroArch will completely exit when it loses focus
|
||||
private boolean quitfocus = false;
|
||||
|
||||
// Top-level window decor view
|
||||
private View mDecorView;
|
||||
|
||||
// Constants used for Handler messages
|
||||
private static final int HANDLER_WHAT_TOGGLE_IMMERSIVE = 1;
|
||||
private static final int HANDLER_WHAT_TOGGLE_POINTER_CAPTURE = 2;
|
||||
private static final int HANDLER_WHAT_TOGGLE_POINTER_NVIDIA = 3;
|
||||
private static final int HANDLER_WHAT_TOGGLE_POINTER_ICON = 4;
|
||||
private static final int HANDLER_ARG_TRUE = 1;
|
||||
private static final int HANDLER_ARG_FALSE = 0;
|
||||
private static final int HANDLER_MESSAGE_DELAY_DEFAULT_MS = 300;
|
||||
|
||||
// Handler used for UI events
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
boolean state = (msg.arg1 == HANDLER_ARG_TRUE) ? true : false;
|
||||
|
||||
if (msg.what == HANDLER_WHAT_TOGGLE_IMMERSIVE) {
|
||||
attemptToggleImmersiveMode(state);
|
||||
} else if (msg.what == HANDLER_WHAT_TOGGLE_POINTER_CAPTURE) {
|
||||
attemptTogglePointerCapture(state);
|
||||
} else if (msg.what == HANDLER_WHAT_TOGGLE_POINTER_NVIDIA) {
|
||||
attemptToggleNvidiaCursorVisibility(state);
|
||||
} else if (msg.what == HANDLER_WHAT_TOGGLE_POINTER_ICON) {
|
||||
attemptTogglePointerIcon(state);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mDecorView = getWindow().getDecorView();
|
||||
|
||||
// If QUITFOCUS parameter is provided then enable that Retroarch quits when focus is lost
|
||||
quitfocus = getIntent().hasExtra("QUITFOCUS");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
setSustainedPerformanceMode(sustainedPerformanceMode);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
// Immersive mode
|
||||
// Check for Android UI specific parameters
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
String refresh = getIntent().getStringExtra("REFRESH");
|
||||
|
||||
// Constants from API > 14
|
||||
final int API_SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
|
||||
final int API_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
|
||||
final int API_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
|
||||
final int API_SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
|
||||
final int API_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
|
||||
|
||||
View thisView = getWindow().getDecorView();
|
||||
thisView.setSystemUiVisibility(API_SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| API_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| API_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| API_SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| API_SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
|
||||
// Check for Android UI specific parameters
|
||||
Intent retro = getIntent();
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||
String refresh = retro.getStringExtra("REFRESH");
|
||||
|
||||
// If REFRESH parameter is provided then try to set refreshrate accordingly
|
||||
if(refresh != null) {
|
||||
WindowManager.LayoutParams params = getWindow().getAttributes();
|
||||
params.preferredRefreshRate = Integer.parseInt(refresh);
|
||||
getWindow().setAttributes(params);
|
||||
}
|
||||
// If REFRESH parameter is provided then try to set refreshrate accordingly
|
||||
if (refresh != null) {
|
||||
WindowManager.LayoutParams params = getWindow().getAttributes();
|
||||
params.preferredRefreshRate = Integer.parseInt(refresh);
|
||||
getWindow().setAttributes(params);
|
||||
}
|
||||
|
||||
// If QUITFOCUS parameter is provided then enable that Retroarch quits when focus is lost
|
||||
quitfocus = retro.hasExtra("QUITFOCUS");
|
||||
|
||||
// If HIDEMOUSE parameters is provided then hide the mourse cursor
|
||||
// This requires NVIDIA Android extensions (available on NVIDIA Shield), if they are not
|
||||
// available then nothing will be done
|
||||
if (retro.hasExtra("HIDEMOUSE")) hideMouseCursor();
|
||||
}
|
||||
|
||||
//Checks if Android versions is above 9.0 (28) and enable the screen to write over notch if the user desires
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
// Checks if Android versions is above 9.0 (28) and enable the screen to write over notch if the user desires
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
ConfigFile configFile = new ConfigFile(UserPreferences.getDefaultConfigPath(this));
|
||||
try {
|
||||
if (configFile.getBoolean("video_notch_write_over_enable")) {
|
||||
@ -77,31 +93,114 @@ public final class RetroActivityFuture extends RetroActivityCamera {
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMouseCursor() {
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
// Check for NVIDIA extensions and minimum SDK version
|
||||
Method mInputManager_setCursorVisibility;
|
||||
try { mInputManager_setCursorVisibility =
|
||||
InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return; // Extensions were not available so do nothing
|
||||
}
|
||||
|
||||
// Hide the mouse cursor
|
||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||
try { mInputManager_setCursorVisibility.invoke(inputManager, false); }
|
||||
catch (InvocationTargetException ite) { }
|
||||
catch (IllegalAccessException iae) { }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
// If QUITFOCUS parameter was set then completely exit Retroarch when focus is lost
|
||||
// If QUITFOCUS parameter was set then completely exit RetroArch when focus is lost
|
||||
if (quitfocus) System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
||||
mHandlerSendUiMessage(HANDLER_WHAT_TOGGLE_IMMERSIVE, hasFocus);
|
||||
|
||||
try {
|
||||
ConfigFile configFile = new ConfigFile(UserPreferences.getDefaultConfigPath(this));
|
||||
if (configFile.getBoolean("input_auto_mouse_grab")) {
|
||||
inputGrabMouse(hasFocus);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w("[onWindowFocusChanged] exception thrown:", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void mHandlerSendUiMessage(int what, boolean state) {
|
||||
int arg1 = (state ? HANDLER_ARG_TRUE : HANDLER_ARG_FALSE);
|
||||
int arg2 = -1;
|
||||
|
||||
Message message = mHandler.obtainMessage(what, arg1, arg2);
|
||||
mHandler.sendMessageDelayed(message, HANDLER_MESSAGE_DELAY_DEFAULT_MS);
|
||||
}
|
||||
|
||||
public void inputGrabMouse(boolean state) {
|
||||
mHandlerSendUiMessage(HANDLER_WHAT_TOGGLE_POINTER_CAPTURE, state);
|
||||
mHandlerSendUiMessage(HANDLER_WHAT_TOGGLE_POINTER_NVIDIA, state);
|
||||
mHandlerSendUiMessage(HANDLER_WHAT_TOGGLE_POINTER_ICON, state);
|
||||
}
|
||||
|
||||
private void attemptToggleImmersiveMode(boolean state) {
|
||||
// Attempt to toggle "Immersive Mode" for Android 4.4 (19) and up
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
if (state) {
|
||||
mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LOW_PROFILE
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
} else {
|
||||
mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w("[attemptToggleImmersiveMode] exception thrown:", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptTogglePointerCapture(boolean state) {
|
||||
// Attempt requestPointerCapture for Android 8.0 (26) and up
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
try {
|
||||
if (state) {
|
||||
mDecorView.requestPointerCapture();
|
||||
} else {
|
||||
mDecorView.releasePointerCapture();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w("[attemptTogglePointerCapture] exception thrown:", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptToggleNvidiaCursorVisibility(boolean state) {
|
||||
// Attempt setCursorVisibility for Android 4.1 (16) and up
|
||||
// only works if NVIDIA Android extensions for NVIDIA Shield are available
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
try {
|
||||
boolean cursorVisibility = !state;
|
||||
Method mInputManager_setCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||
mInputManager_setCursorVisibility.invoke(inputManager, cursorVisibility);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Extensions were not available so do nothing
|
||||
} catch (Exception e) {
|
||||
Log.w("[attemptToggleNvidiaCursorVisibility] exception thrown:", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptTogglePointerIcon(boolean state) {
|
||||
// Attempt setPointerIcon for Android 7.x (24, 25) only
|
||||
// For Android 8.0+, requestPointerCapture is used
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
try {
|
||||
if (state) {
|
||||
PointerIcon nullPointerIcon = PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL);
|
||||
mDecorView.setPointerIcon(nullPointerIcon);
|
||||
} else {
|
||||
// Restore the pointer icon to it's default value
|
||||
mDecorView.setPointerIcon(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w("[attemptTogglePointerIcon] exception thrown:", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user