Android/InputOverlayPointer: Don't assume surface covers whole screen

This assumption is false both in portrait mode (where it only
covers the top half of the screen) and when using two apps at once.

Fixes https://bugs.dolphin-emu.org/issues/12307.
This commit is contained in:
JosJuice 2020-11-02 00:17:56 +01:00
parent 35a113f6a2
commit a66afc864f
3 changed files with 60 additions and 69 deletions

View File

@ -1,6 +1,7 @@
package org.dolphinemu.dolphinemu.fragments; package org.dolphinemu.dolphinemu.fragments;
import android.content.Context; import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Surface; import android.view.Surface;
@ -93,6 +94,15 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
doneButton.setOnClickListener(v -> stopConfiguringControls()); doneButton.setOnClickListener(v -> stopConfiguringControls());
} }
contents.post(() ->
{
int overlayX = mInputOverlay.getLeft();
int overlayY = mInputOverlay.getTop();
mInputOverlay.setSurfacePosition(new Rect(
surfaceView.getLeft() - overlayX, surfaceView.getTop() - overlayY,
surfaceView.getRight() - overlayX, surfaceView.getBottom() - overlayY));
});
// The new Surface created here will get passed to the native code via onSurfaceChanged. // The new Surface created here will get passed to the native code via onSurfaceChanged.
return contents; return contents;

View File

@ -60,7 +60,9 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>(); private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>();
private final Set<InputOverlayDrawableDpad> overlayDpads = new HashSet<>(); private final Set<InputOverlayDrawableDpad> overlayDpads = new HashSet<>();
private final Set<InputOverlayDrawableJoystick> overlayJoysticks = new HashSet<>(); private final Set<InputOverlayDrawableJoystick> overlayJoysticks = new HashSet<>();
private InputOverlayPointer overlayPointer; private InputOverlayPointer overlayPointer = null;
private Rect mSurfacePosition = null;
private boolean mIsFirstRun = true; private boolean mIsFirstRun = true;
private boolean mIsInEditMode = false; private boolean mIsInEditMode = false;
@ -125,19 +127,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Load the controls if we can. If not, EmulationActivity has to do it later. // Load the controls if we can. If not, EmulationActivity has to do it later.
if (NativeLibrary.IsGameMetadataValid()) if (NativeLibrary.IsGameMetadataValid())
{ refreshControls();
if (NativeLibrary.IsRunning())
{
// We would've needed a refreshControls call here in addition to the initTouchPointer call
// if it wasn't for initTouchPointer calling refreshControls.
initTouchPointer();
}
else
{
// We can't call initTouchPointer yet because it needs the aspect ratio of the running game.
refreshControls();
}
}
// Set the on touch listener. // Set the on touch listener.
setOnTouchListener(this); setOnTouchListener(this);
@ -149,24 +139,33 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
requestFocus(); requestFocus();
} }
public void setSurfacePosition(Rect rect)
{
mSurfacePosition = rect;
initTouchPointer();
}
public void initTouchPointer() public void initTouchPointer()
{ {
// Refresh before starting the pointer // Check if we have all the data we need yet
refreshControls(); boolean aspectRatioAvailable = NativeLibrary.IsRunning() && !NativeLibrary.IsBooting();
if (!aspectRatioAvailable || mSurfacePosition == null)
return;
if (NativeLibrary.IsEmulatingWii()) // Check if there's any point in running the pointer code
if (!NativeLibrary.IsEmulatingWii())
return;
int doubleTapButton = IntSetting.MAIN_DOUBLE_TAP_BUTTON.getIntGlobal();
if (mPreferences.getInt("wiiController", OVERLAY_WIIMOTE_NUNCHUK) !=
InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
doubleTapButton == InputOverlayPointer.DOUBLE_TAP_CLASSIC_A)
{ {
int doubleTapButton = IntSetting.MAIN_DOUBLE_TAP_BUTTON.getIntGlobal(); doubleTapButton = InputOverlayPointer.DOUBLE_TAP_A;
if (mPreferences.getInt("wiiController", OVERLAY_WIIMOTE_NUNCHUK) !=
InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
doubleTapButton == InputOverlayPointer.DOUBLE_TAP_CLASSIC_A)
{
doubleTapButton = InputOverlayPointer.DOUBLE_TAP_A;
}
overlayPointer = new InputOverlayPointer(this.getContext(), doubleTapButton);
} }
overlayPointer = new InputOverlayPointer(mSurfacePosition, doubleTapButton);
} }
@Override @Override

View File

@ -1,10 +1,7 @@
package org.dolphinemu.dolphinemu.overlay; package org.dolphinemu.dolphinemu.overlay;
import android.app.Activity; import android.graphics.Rect;
import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.MotionEvent; import android.view.MotionEvent;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
@ -20,10 +17,11 @@ public class InputOverlayPointer
private final float[] axes = {0f, 0f}; private final float[] axes = {0f, 0f};
private float maxHeight; private float mGameCenterX;
private float maxWidth; private float mGameCenterY;
private float aspectAdjusted; private float mGameWidthHalfInv;
private boolean xAdjusted; private float mGameHeightHalfInv;
private boolean doubleTap = false; private boolean doubleTap = false;
private int doubleTapButton; private int doubleTapButton;
private int trackId = -1; private int trackId = -1;
@ -38,39 +36,33 @@ public class InputOverlayPointer
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A); DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A);
} }
public InputOverlayPointer(Context context, int button) public InputOverlayPointer(Rect surfacePosition, int button)
{ {
Display display = ((Activity) context).getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics();
display.getMetrics(outMetrics);
doubleTapButton = button; doubleTapButton = button;
Integer y = outMetrics.heightPixels; mGameCenterX = (surfacePosition.left + surfacePosition.right) / 2.0f;
Integer x = outMetrics.widthPixels; mGameCenterY = (surfacePosition.top + surfacePosition.bottom) / 2.0f;
float gameWidth = surfacePosition.right - surfacePosition.left;
float gameHeight = surfacePosition.bottom - surfacePosition.top;
// Adjusting for device's black bars. // Adjusting for device's black bars.
float deviceAR = (float) x / y; float surfaceAR = gameWidth / gameHeight;
float gameAR = NativeLibrary.GetGameAspectRatio(); float gameAR = NativeLibrary.GetGameAspectRatio();
aspectAdjusted = gameAR / deviceAR;
if (gameAR <= deviceAR) // Black bars on left/right if (gameAR <= surfaceAR)
{ {
xAdjusted = true; // Black bars on left/right
Integer gameX = Math.round((float) y * gameAR); gameWidth = gameHeight * gameAR;
Integer buffer = (x - gameX);
maxWidth = (float) (x - buffer) / 2;
maxHeight = (float) y / 2;
} }
else // Bars on top/bottom else
{ {
xAdjusted = false; // Black bars on top/bottom
Integer gameY = Math.round((float) x / gameAR); gameHeight = gameWidth / gameAR;
Integer buffer = (y - gameY);
maxWidth = (float) x / 2;
maxHeight = (float) (y - buffer) / 2;
} }
mGameWidthHalfInv = 1.0f / (gameWidth * 0.5f);
mGameHeightHalfInv = 1.0f / (gameHeight * 0.5f);
} }
public void onTouch(MotionEvent event) public void onTouch(MotionEvent event)
@ -94,18 +86,8 @@ public class InputOverlayPointer
if (trackId == -1) if (trackId == -1)
return; return;
int x = (int) event.getX(event.findPointerIndex(trackId)); axes[0] = (event.getY(event.findPointerIndex(trackId)) - mGameCenterY) * mGameHeightHalfInv;
int y = (int) event.getY(event.findPointerIndex(trackId)); axes[1] = (event.getX(event.findPointerIndex(trackId)) - mGameCenterX) * mGameWidthHalfInv;
if (xAdjusted)
{
axes[0] = (y - maxHeight) / maxHeight;
axes[1] = ((x * aspectAdjusted) - maxWidth) / maxWidth;
}
else
{
axes[0] = ((y * aspectAdjusted) - maxHeight) / maxHeight;
axes[1] = (x - maxWidth) / maxWidth;
}
} }
private void touchPress() private void touchPress()