diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 78306e0eebd0..2aa8e920e418 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -130,6 +130,8 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
private GridLayout _buttonLayout = null;
private ImageView _toggleTouchModeKeyboardBtnIcon = null;
private ImageView _openMenuBtnIcon = null;
+ private ImageView _toggleSecondScreenBtnIcon = null;
+ private SecondScreenManager _secondScreenManager = null;
private LedView _ioLed = null;
private int _layoutOrientation;
@@ -697,6 +699,22 @@ public void run() {
}
};
+ public final View.OnClickListener secondScreenBtnOnClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ if (_secondScreenManager != null) {
+ boolean enabled = _secondScreenManager.toggle();
+ if (_toggleSecondScreenBtnIcon != null) {
+ _toggleSecondScreenBtnIcon.setAlpha(enabled ? 1.0f : 0.4f);
+ }
+ }
+ }
+ });
+ }
+ };
+
private class MyScummVM extends ScummVM {
public MyScummVM(SurfaceHolder holder, final MyScummVMDestroyedCallback destroyedCallback) {
@@ -1113,6 +1131,21 @@ public void handle(int exitResult) {
_toggleTouchModeKeyboardBtnIcon.setOnLongClickListener(touchModeKeyboardBtnOnLongClickListener);
_openMenuBtnIcon.setOnClickListener(menuBtnOnClickListener);
+ // Second screen touchpad support (API 17+)
+ _toggleSecondScreenBtnIcon = findViewById(R.id.toggle_second_screen_button);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ _secondScreenManager = new SecondScreenManager(this, _scummvm);
+ _secondScreenManager.start();
+ if (_secondScreenManager.hasSecondaryDisplay()) {
+ _toggleSecondScreenBtnIcon.setVisibility(View.VISIBLE);
+ _toggleSecondScreenBtnIcon.setOnClickListener(secondScreenBtnOnClickListener);
+ } else {
+ _toggleSecondScreenBtnIcon.setVisibility(View.GONE);
+ }
+ } else {
+ _toggleSecondScreenBtnIcon.setVisibility(View.GONE);
+ }
+
// Keyboard visibility listener - mainly to hide system UI if keyboard is shown and we return from Suspend to the Activity
setKeyboardVisibilityListener(this);
@@ -1260,6 +1293,11 @@ public void onDestroy() {
hideScreenKeyboard();
}
+ if (_secondScreenManager != null) {
+ _secondScreenManager.stop();
+ _secondScreenManager = null;
+ }
+
if (_events != null) {
_finishing = true;
diff --git a/backends/platform/android/org/scummvm/scummvm/SecondScreenHelper.java b/backends/platform/android/org/scummvm/scummvm/SecondScreenHelper.java
new file mode 100644
index 000000000000..76317abc5cfd
--- /dev/null
+++ b/backends/platform/android/org/scummvm/scummvm/SecondScreenHelper.java
@@ -0,0 +1,86 @@
+package org.scummvm.scummvm;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+public class SecondScreenHelper {
+
+ private static SecondScreenManager sManager;
+ private static ImageView sToggleBtn;
+ private static Application.ActivityLifecycleCallbacks sLifecycleCallbacks;
+
+ public static void init(Activity activity, ScummVM scummvm) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return;
+
+ DisplayMetrics dm = new DisplayMetrics();
+ WindowManager wm = (WindowManager) activity.getSystemService(Activity.WINDOW_SERVICE);
+ if (wm != null) {
+ wm.getDefaultDisplay().getRealMetrics(dm);
+ }
+ int screenW = Math.max(dm.widthPixels, dm.heightPixels);
+ int screenH = Math.min(dm.widthPixels, dm.heightPixels);
+
+ sManager = new SecondScreenManager(activity, scummvm, screenW, screenH);
+ sManager.start();
+
+ int btnId = activity.getResources().getIdentifier(
+ "toggle_second_screen_button", "id", activity.getPackageName());
+ if (btnId != 0) {
+ sToggleBtn = activity.findViewById(btnId);
+ }
+
+ if (sToggleBtn != null && sManager.hasSecondaryDisplay()) {
+ sToggleBtn.setVisibility(View.VISIBLE);
+ sToggleBtn.setAlpha(1.0f);
+ sToggleBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (sManager != null) {
+ boolean enabled = sManager.toggle();
+ if (sToggleBtn != null) {
+ sToggleBtn.setAlpha(enabled ? 1.0f : 0.4f);
+ }
+ }
+ }
+ });
+
+ sManager.toggle();
+
+ sLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
+ @Override public void onActivityCreated(Activity a, Bundle s) {}
+ @Override public void onActivityStarted(Activity a) {}
+ @Override
+ public void onActivityResumed(Activity a) {
+ if (a instanceof ScummVMActivity && sManager != null) {
+ sManager.onResume();
+ }
+ }
+ @Override
+ public void onActivityPaused(Activity a) {
+ if (a instanceof ScummVMActivity && sManager != null) {
+ sManager.onPause();
+ }
+ }
+ @Override public void onActivityStopped(Activity a) {}
+ @Override public void onActivitySaveInstanceState(Activity a, Bundle o) {}
+ @Override public void onActivityDestroyed(Activity a) {}
+ };
+ activity.getApplication().registerActivityLifecycleCallbacks(sLifecycleCallbacks);
+ }
+ }
+
+ public static void destroy() {
+ if (sManager != null) {
+ sManager.stop();
+ sManager = null;
+ }
+ sToggleBtn = null;
+ sLifecycleCallbacks = null;
+ }
+}
diff --git a/backends/platform/android/org/scummvm/scummvm/SecondScreenInitializer.java b/backends/platform/android/org/scummvm/scummvm/SecondScreenInitializer.java
new file mode 100644
index 000000000000..fe3f6011a574
--- /dev/null
+++ b/backends/platform/android/org/scummvm/scummvm/SecondScreenInitializer.java
@@ -0,0 +1,78 @@
+package org.scummvm.scummvm;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class SecondScreenInitializer implements Application.ActivityLifecycleCallbacks {
+
+ private SecondScreenManager _manager;
+ private ImageView _toggleBtn;
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ if (!(activity instanceof ScummVMActivity)) return;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return;
+
+ ScummVMActivity svm = (ScummVMActivity) activity;
+
+ try {
+ java.lang.reflect.Field field = ScummVMActivity.class.getDeclaredField("_scummvm");
+ field.setAccessible(true);
+ ScummVM scummvm = (ScummVM) field.get(svm);
+ if (scummvm == null) return;
+
+ _manager = new SecondScreenManager(activity, scummvm);
+ _manager.start();
+
+ _toggleBtn = activity.findViewById(
+ activity.getResources().getIdentifier(
+ "toggle_second_screen_button", "id", activity.getPackageName()));
+
+ if (_toggleBtn != null && _manager.hasSecondaryDisplay()) {
+ _toggleBtn.setVisibility(View.VISIBLE);
+ _toggleBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (_manager != null) {
+ boolean enabled = _manager.toggle();
+ _toggleBtn.setAlpha(enabled ? 1.0f : 0.4f);
+ }
+ }
+ });
+ }
+ } catch (Exception e) {
+ // _scummvm field not accessible or not yet initialized
+ }
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {}
+
+ @Override
+ public void onActivityResumed(Activity activity) {}
+
+ @Override
+ public void onActivityPaused(Activity activity) {}
+
+ @Override
+ public void onActivityStopped(Activity activity) {}
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (!(activity instanceof ScummVMActivity)) return;
+ if (_manager != null) {
+ _manager.stop();
+ _manager = null;
+ }
+ }
+}
diff --git a/backends/platform/android/org/scummvm/scummvm/SecondScreenManager.java b/backends/platform/android/org/scummvm/scummvm/SecondScreenManager.java
new file mode 100644
index 000000000000..e12b676bfc2f
--- /dev/null
+++ b/backends/platform/android/org/scummvm/scummvm/SecondScreenManager.java
@@ -0,0 +1,134 @@
+package org.scummvm.scummvm;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Display;
+
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
+public class SecondScreenManager {
+
+ private final Context _context;
+ private final ScummVM _scummvm;
+ private final DisplayManager _displayManager;
+ private final int _mainScreenWidth;
+ private final int _mainScreenHeight;
+ private SecondScreenPresentation _presentation;
+ private DisplayManager.DisplayListener _displayListener;
+ private boolean _enabled = false;
+
+ public SecondScreenManager(Context context, ScummVM scummvm, int mainScreenWidth, int mainScreenHeight) {
+ _context = context;
+ _scummvm = scummvm;
+ _displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ _mainScreenWidth = mainScreenWidth;
+ _mainScreenHeight = mainScreenHeight;
+ }
+
+ public void start() {
+ if (_displayManager == null) return;
+
+ _displayListener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (_enabled) {
+ tryShowPresentation();
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ dismissPresentation();
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ }
+ };
+
+ _displayManager.registerDisplayListener(_displayListener,
+ new Handler(Looper.getMainLooper()));
+ }
+
+ public void stop() {
+ dismissPresentation();
+ if (_displayManager != null && _displayListener != null) {
+ _displayManager.unregisterDisplayListener(_displayListener);
+ _displayListener = null;
+ }
+ }
+
+ public boolean toggle() {
+ _enabled = !_enabled;
+ if (_enabled) {
+ tryShowPresentation();
+ } else {
+ dismissPresentation();
+ }
+ return _enabled;
+ }
+
+ public boolean isEnabled() {
+ return _enabled;
+ }
+
+ public boolean hasSecondaryDisplay() {
+ return findPresentationDisplay() != null;
+ }
+
+ public void onPause() {
+ dismissPresentation();
+ }
+
+ public void onResume() {
+ if (_enabled) {
+ tryShowPresentation();
+ }
+ }
+
+ private void tryShowPresentation() {
+ Display display = findPresentationDisplay();
+ if (display != null) {
+ showPresentation(display);
+ }
+ }
+
+ private void showPresentation(Display display) {
+ dismissPresentation();
+ try {
+ _presentation = new SecondScreenPresentation(
+ _context, display, _scummvm, _mainScreenWidth, _mainScreenHeight);
+ _presentation.show();
+ } catch (Exception e) {
+ _presentation = null;
+ }
+ }
+
+ private void dismissPresentation() {
+ if (_presentation != null) {
+ try {
+ _presentation.dismiss();
+ } catch (Exception ignored) {
+ }
+ _presentation = null;
+ }
+ }
+
+ private Display findPresentationDisplay() {
+ if (_displayManager == null) return null;
+
+ Display[] displays = _displayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+
+ for (Display d : displays) {
+ if (d.getDisplayId() != 0 && !"HiddenDisplay".equals(d.getName())) {
+ return d;
+ }
+ }
+ return null;
+ }
+}
diff --git a/backends/platform/android/org/scummvm/scummvm/SecondScreenPresentation.java b/backends/platform/android/org/scummvm/scummvm/SecondScreenPresentation.java
new file mode 100644
index 000000000000..ff0add22724f
--- /dev/null
+++ b/backends/platform/android/org/scummvm/scummvm/SecondScreenPresentation.java
@@ -0,0 +1,51 @@
+package org.scummvm.scummvm;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
+public class SecondScreenPresentation extends Presentation {
+
+ private final ScummVM _scummvm;
+ private final int _mainScreenWidth;
+ private final int _mainScreenHeight;
+ private SecondScreenTouchpadView _touchpadView;
+
+ public SecondScreenPresentation(Context context, Display display, ScummVM scummvm,
+ int mainScreenWidth, int mainScreenHeight) {
+ super(context, display);
+ _scummvm = scummvm;
+ _mainScreenWidth = mainScreenWidth;
+ _mainScreenHeight = mainScreenHeight;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Window window = getWindow();
+ if (window != null) {
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ );
+ }
+
+ _touchpadView = new SecondScreenTouchpadView(getContext(), _scummvm,
+ _mainScreenWidth, _mainScreenHeight);
+ setContentView(_touchpadView);
+ }
+
+ public SecondScreenTouchpadView getTouchpadView() {
+ return _touchpadView;
+ }
+}
diff --git a/backends/platform/android/org/scummvm/scummvm/SecondScreenTouchpadView.java b/backends/platform/android/org/scummvm/scummvm/SecondScreenTouchpadView.java
new file mode 100644
index 000000000000..4d45a772c6e9
--- /dev/null
+++ b/backends/platform/android/org/scummvm/scummvm/SecondScreenTouchpadView.java
@@ -0,0 +1,288 @@
+package org.scummvm.scummvm;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Build;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
+public class SecondScreenTouchpadView extends View {
+
+ private static final int JE_LMB_DOWN = 9;
+ private static final int JE_LMB_UP = 10;
+ private static final int JE_RMB_DOWN = 11;
+ private static final int JE_RMB_UP = 12;
+ private static final int JE_MOUSE_MOVE = 13;
+ private static final int JE_MOUSE_WHEEL_UP = 22;
+ private static final int JE_MOUSE_WHEEL_DOWN = 23;
+
+ private static final int TAP_TIMEOUT_MS = 300;
+ private static final float TAP_SLOP_PX = 20f;
+ private static final float ACCELERATION_THRESHOLD = 6f;
+ private static final float ACCELERATION_MULTIPLIER = 1.5f;
+ private static final float SCROLL_THRESHOLD = 30f;
+ private static final float DEFAULT_SENSITIVITY = 1.5f;
+
+ private final ScummVM _scummvm;
+ private final Paint _bgPaint;
+ private final Paint _borderPaint;
+ private final Paint _textPaint;
+ private final Paint _touchPaint;
+ private final Paint _touchPaintActive;
+
+ private int _cursorX;
+ private int _cursorY;
+ private int _screenWidth;
+ private int _screenHeight;
+
+ private float _lastTouchX;
+ private float _lastTouchY;
+ private boolean _isDragging;
+ private int _activePointers;
+
+ private long _touchDownTime;
+ private float _touchDownX;
+ private float _touchDownY;
+
+ private float _scrollBaseY;
+ private boolean _isScrolling;
+ private float _scrollAccum;
+
+ private float _sensitivity = DEFAULT_SENSITIVITY;
+ private boolean _enabled = true;
+
+ private float _currentTouchX = -1;
+ private float _currentTouchY = -1;
+
+ public SecondScreenTouchpadView(Context context, ScummVM scummvm,
+ int mainScreenWidth, int mainScreenHeight) {
+ super(context);
+ _scummvm = scummvm;
+ _screenWidth = mainScreenWidth;
+ _screenHeight = mainScreenHeight;
+ _cursorX = _screenWidth / 2;
+ _cursorY = _screenHeight / 2;
+
+ _bgPaint = new Paint();
+ _bgPaint.setColor(0xFF000000);
+ _bgPaint.setStyle(Paint.Style.FILL);
+
+ _borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ _borderPaint.setColor(0x20FFFFFF);
+ _borderPaint.setStyle(Paint.Style.STROKE);
+ _borderPaint.setStrokeWidth(1f);
+
+ _textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ _textPaint.setColor(0x30FFFFFF);
+ _textPaint.setTextSize(28f);
+ _textPaint.setTextAlign(Paint.Align.CENTER);
+
+ _touchPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ _touchPaint.setColor(0x18FFFFFF);
+ _touchPaint.setStyle(Paint.Style.FILL);
+
+ _touchPaintActive = new Paint(Paint.ANTI_ALIAS_FLAG);
+ _touchPaintActive.setColor(0x30FFFFFF);
+ _touchPaintActive.setStyle(Paint.Style.FILL);
+ }
+
+ public void setScreenResolution(int w, int h) {
+ _screenWidth = Math.max(w, 1);
+ _screenHeight = Math.max(h, 1);
+ _cursorX = clamp(_cursorX, 0, _screenWidth - 1);
+ _cursorY = clamp(_cursorY, 0, _screenHeight - 1);
+ }
+
+ public void setSensitivity(float sensitivity) {
+ _sensitivity = sensitivity;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ int w = getWidth();
+ int h = getHeight();
+
+ canvas.drawRect(0, 0, w, h, _bgPaint);
+
+ float pad = 16f;
+ canvas.drawRoundRect(pad, pad, w - pad, h - pad, 16, 16, _borderPaint);
+
+ if (_currentTouchX >= 0 && _currentTouchY >= 0) {
+ canvas.drawCircle(_currentTouchX, _currentTouchY, 40f,
+ _isDragging ? _touchPaintActive : _touchPaint);
+ }
+
+ canvas.drawText("ScummVM Touchpad", w / 2f, h / 2f - 20, _textPaint);
+
+ Paint hintPaint = new Paint(_textPaint);
+ hintPaint.setTextSize(20f);
+ hintPaint.setColor(0x40FFFFFF);
+ canvas.drawText("tap = click • 2-finger tap = right click • 2-finger drag = scroll",
+ w / 2f, h / 2f + 20, hintPaint);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!_enabled) return false;
+
+ int action = event.getActionMasked();
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ _activePointers = 1;
+ _touchDownX = event.getX();
+ _touchDownY = event.getY();
+ _touchDownTime = System.currentTimeMillis();
+ _lastTouchX = event.getX();
+ _lastTouchY = event.getY();
+ _isDragging = false;
+ _isScrolling = false;
+ _scrollAccum = 0;
+ _currentTouchX = event.getX();
+ _currentTouchY = event.getY();
+ invalidate();
+ return true;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ _activePointers = event.getPointerCount();
+ if (_activePointers == 2) {
+ _scrollBaseY = averageY(event);
+ _isScrolling = false;
+ _scrollAccum = 0;
+ }
+ return true;
+
+ case MotionEvent.ACTION_MOVE:
+ if (_activePointers == 1) {
+ float dx = event.getX() - _lastTouchX;
+ float dy = event.getY() - _lastTouchY;
+ _lastTouchX = event.getX();
+ _lastTouchY = event.getY();
+ _currentTouchX = event.getX();
+ _currentTouchY = event.getY();
+
+ float dist = distance(_touchDownX, _touchDownY, event.getX(), event.getY());
+ if (dist > TAP_SLOP_PX) {
+ _isDragging = true;
+ }
+ if (_isDragging) {
+ moveCursor(dx, dy);
+ }
+ invalidate();
+ } else if (_activePointers == 2) {
+ float currentAvgY = averageY(event);
+ float scrollDelta = currentAvgY - _scrollBaseY;
+ _scrollAccum += scrollDelta;
+ _scrollBaseY = currentAvgY;
+
+ if (Math.abs(_scrollAccum) > SCROLL_THRESHOLD) {
+ _isScrolling = true;
+ if (_scrollAccum < 0) {
+ _scummvm.pushEvent(JE_MOUSE_WHEEL_UP, _cursorX, _cursorY, 0, 0, 0, 0);
+ } else {
+ _scummvm.pushEvent(JE_MOUSE_WHEEL_DOWN, _cursorX, _cursorY, 0, 0, 0, 0);
+ }
+ _scrollAccum = 0;
+ }
+ }
+ return true;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ int newCount = event.getPointerCount() - 1;
+ if (_activePointers == 2 && newCount == 1 && !_isScrolling) {
+ long elapsed = System.currentTimeMillis() - _touchDownTime;
+ if (elapsed < TAP_TIMEOUT_MS) {
+ rightClick();
+ }
+ }
+ _activePointers = newCount;
+ if (_activePointers == 1) {
+ int remaining = findRemainingPointer(event);
+ if (remaining >= 0) {
+ _lastTouchX = event.getX(remaining);
+ _lastTouchY = event.getY(remaining);
+ }
+ }
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ if (_activePointers == 1 && !_isDragging) {
+ long elapsed = System.currentTimeMillis() - _touchDownTime;
+ if (elapsed < TAP_TIMEOUT_MS) {
+ leftClick();
+ }
+ }
+ _activePointers = 0;
+ _currentTouchX = -1;
+ _currentTouchY = -1;
+ invalidate();
+ return true;
+
+ case MotionEvent.ACTION_CANCEL:
+ _activePointers = 0;
+ _currentTouchX = -1;
+ _currentTouchY = -1;
+ invalidate();
+ return true;
+ }
+ return false;
+ }
+
+ private void moveCursor(float rawDx, float rawDy) {
+ float dx = rawDx * _sensitivity;
+ float dy = rawDy * _sensitivity;
+
+ float mag = (float) Math.sqrt(dx * dx + dy * dy);
+ if (mag > ACCELERATION_THRESHOLD) {
+ dx *= ACCELERATION_MULTIPLIER;
+ dy *= ACCELERATION_MULTIPLIER;
+ }
+
+ _cursorX = clamp(_cursorX + Math.round(dx), 0, _screenWidth - 1);
+ _cursorY = clamp(_cursorY + Math.round(dy), 0, _screenHeight - 1);
+
+ _scummvm.pushEvent(JE_MOUSE_MOVE, _cursorX, _cursorY, 0, 0, 0, 0);
+ }
+
+ private void leftClick() {
+ _scummvm.pushEvent(JE_LMB_DOWN, _cursorX, _cursorY, 0, 0, 0, 0);
+ _scummvm.pushEvent(JE_LMB_UP, _cursorX, _cursorY, 0, 0, 0, 0);
+ }
+
+ private void rightClick() {
+ _scummvm.pushEvent(JE_RMB_DOWN, _cursorX, _cursorY, 0, 0, 0, 0);
+ _scummvm.pushEvent(JE_RMB_UP, _cursorX, _cursorY, 0, 0, 0, 0);
+ }
+
+ private float averageY(MotionEvent event) {
+ float sum = 0;
+ int count = Math.min(event.getPointerCount(), 2);
+ for (int i = 0; i < count; i++) {
+ sum += event.getY(i);
+ }
+ return sum / count;
+ }
+
+ private int findRemainingPointer(MotionEvent event) {
+ int actionIndex = event.getActionIndex();
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ if (i != actionIndex) return i;
+ }
+ return -1;
+ }
+
+ private static float distance(float x1, float y1, float x2, float y2) {
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+ return (float) Math.sqrt(dx * dx + dy * dy);
+ }
+
+ private static int clamp(int val, int min, int max) {
+ return Math.max(min, Math.min(max, val));
+ }
+}
diff --git a/dists/android/res/layout/scummvm_activity.xml b/dists/android/res/layout/scummvm_activity.xml
index 7f1ed9050531..5395548655cb 100644
--- a/dists/android/res/layout/scummvm_activity.xml
+++ b/dists/android/res/layout/scummvm_activity.xml
@@ -44,13 +44,24 @@
android:visibility="gone"
tools:visibility="visible" />
+
+