[Scummvm-git-logs] scummvm master -> a98df4e445dc83e500a813491afcccba48b7ce53
lephilousophe
noreply at scummvm.org
Sat Aug 31 14:45:36 UTC 2024
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
678a8fe4cb ANDROID: Add alpha setting to textures in 3D backend
64eff5293a ANDROID: Add compatibility helper to get system gestures insets
5065891f0c ANDROID: Make system gestures insets available to C++
a98df4e445 ANDROID: Rework the virtual gamepad controller
Commit: 678a8fe4cbf9292d659d50484d41c432c3fbbcd8
https://github.com/scummvm/scummvm/commit/678a8fe4cbf9292d659d50484d41c432c3fbbcd8
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-08-31T16:45:32+02:00
Commit Message:
ANDROID: Add alpha setting to textures in 3D backend
This allows to tune the transparency of the texture easily.
Changed paths:
backends/graphics3d/android/texture.cpp
backends/graphics3d/android/texture.h
diff --git a/backends/graphics3d/android/texture.cpp b/backends/graphics3d/android/texture.cpp
index c1243978525..114e0d731fb 100644
--- a/backends/graphics3d/android/texture.cpp
+++ b/backends/graphics3d/android/texture.cpp
@@ -103,9 +103,10 @@ static const char *controlFragment =
"precision mediump float;\n"
"#endif\n"
"varying vec2 Texcoord;\n"
+ "uniform float alpha;\n"
"uniform sampler2D tex;\n"
"void main() {\n"
- "gl_FragColor = texture2D(tex, Texcoord);\n"
+ "gl_FragColor = texture2D(tex, Texcoord) * vec4(1.0, 1.0, 1.0, alpha);\n"
"}\n";
void GLESBaseTexture::initGL() {
@@ -135,6 +136,7 @@ GLESBaseTexture::GLESBaseTexture(GLenum glFormat, GLenum glType,
_surface(),
_texture_width(0),
_texture_height(0),
+ _alpha(1.f),
_draw_rect(),
_all_dirty(false),
_dirty_rect(),
@@ -266,6 +268,7 @@ void GLESBaseTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h,
clipV.w() /= _texture_height;
// LOGD("*** Drawing at (%f,%f) , size %f x %f", float(x) / float(_surface.w), float(y) / float(_surface.h), tex_width, tex_height);
+ _box_shader->setUniform1f("alpha", _alpha);
_box_shader->setUniform("offsetXY", Math::Vector2d(offsetX, offsetY));
_box_shader->setUniform("sizeWH", Math::Vector2d(sizeW, sizeH));
_box_shader->setUniform("clip", clipV);
diff --git a/backends/graphics3d/android/texture.h b/backends/graphics3d/android/texture.h
index 318115028d2..49b9384aefd 100644
--- a/backends/graphics3d/android/texture.h
+++ b/backends/graphics3d/android/texture.h
@@ -147,6 +147,10 @@ public:
_is_game_texture = true;
}
+ void setAlpha(float alpha) {
+ _alpha = alpha;
+ }
+
protected:
void initSize();
@@ -193,6 +197,8 @@ protected:
bool _is_game_texture;
+ GLfloat _alpha;
+
static bool _npot_supported;
static OpenGL::Shader *_box_shader;
static GLuint _verticesVBO;
Commit: 64eff5293a542b4632a3f9872646ecf5efd5db3f
https://github.com/scummvm/scummvm/commit/64eff5293a542b4632a3f9872646ecf5efd5db3f
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-08-31T16:45:32+02:00
Commit Message:
ANDROID: Add compatibility helper to get system gestures insets
Changed paths:
backends/platform/android/org/scummvm/scummvm/CompatHelpers.java
diff --git a/backends/platform/android/org/scummvm/scummvm/CompatHelpers.java b/backends/platform/android/org/scummvm/scummvm/CompatHelpers.java
index a43c3a74ff1..07fbea39686 100644
--- a/backends/platform/android/org/scummvm/scummvm/CompatHelpers.java
+++ b/backends/platform/android/org/scummvm/scummvm/CompatHelpers.java
@@ -8,6 +8,7 @@ import android.content.pm.ShortcutManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -80,6 +81,95 @@ class CompatHelpers {
}
}
+ static class SystemInsets {
+ public interface SystemInsetsListener {
+ void systemInsetsUpdated(int insets[]);
+ }
+
+ public static void registerSystemInsetsListener(View v, SystemInsetsListener l) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
+ v.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListenerR(l));
+ } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
+ v.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListenerQ(l));
+ } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT_WATCH) {
+ v.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListenerKitKatW(l));
+ } else {
+ // Not available
+ int[] insets = new int[] { 0, 0, 0, 0 };
+ l.systemInsetsUpdated(insets);
+ }
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.KITKAT_WATCH)
+ @SuppressWarnings("deprecation")
+ private static class OnApplyWindowInsetsListenerKitKatW implements View.OnApplyWindowInsetsListener {
+ private SystemInsetsListener l;
+
+ public OnApplyWindowInsetsListenerKitKatW(SystemInsetsListener l) {
+ this.l = l;
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ // No system gestures inset before Android Q
+ int[] insetsArray = new int[] {
+ insets.getStableInsetLeft(),
+ insets.getStableInsetTop(),
+ insets.getStableInsetRight(),
+ insets.getStableInsetBottom()
+ };
+ l.systemInsetsUpdated(insetsArray);
+ return v.onApplyWindowInsets(insets);
+ }
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.Q)
+ @SuppressWarnings("deprecation")
+ private static class OnApplyWindowInsetsListenerQ implements View.OnApplyWindowInsetsListener {
+ private SystemInsetsListener l;
+
+ public OnApplyWindowInsetsListenerQ(SystemInsetsListener l) {
+ this.l = l;
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ Insets insetsStruct = insets.getStableInsets();
+ int[] insetsArray = new int[] {
+ insetsStruct.left,
+ insetsStruct.top,
+ insetsStruct.right,
+ insetsStruct.bottom,
+ };
+ l.systemInsetsUpdated(insetsArray);
+ return v.onApplyWindowInsets(insets);
+ }
+ }
+
+ @RequiresApi(android.os.Build.VERSION_CODES.R)
+ @SuppressWarnings("deprecation")
+ private static class OnApplyWindowInsetsListenerR implements View.OnApplyWindowInsetsListener {
+ private SystemInsetsListener l;
+
+ public OnApplyWindowInsetsListenerR(SystemInsetsListener l) {
+ this.l = l;
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ Insets insetsStruct = insets.getInsetsIgnoringVisibility(WindowInsets.Type.systemGestures());
+ int[] insetsArray = new int[] {
+ insetsStruct.left,
+ insetsStruct.top,
+ insetsStruct.right,
+ insetsStruct.bottom,
+ };
+ l.systemInsetsUpdated(insetsArray);
+ return v.onApplyWindowInsets(insets);
+ }
+ }
+ }
+
static class AudioTrackCompat {
public static class AudioTrackCompatReturn {
public AudioTrack audioTrack;
Commit: 5065891f0c06bbc2723c32122c0b2e2fa9d026be
https://github.com/scummvm/scummvm/commit/5065891f0c06bbc2723c32122c0b2e2fa9d026be
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-08-31T16:45:32+02:00
Commit Message:
ANDROID: Make system gestures insets available to C++
This will be used by touch controls
Changed paths:
backends/platform/android/jni-android.cpp
backends/platform/android/jni-android.h
backends/platform/android/org/scummvm/scummvm/ScummVM.java
backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
diff --git a/backends/platform/android/jni-android.cpp b/backends/platform/android/jni-android.cpp
index a8f42e98906..1c0bb45807d 100644
--- a/backends/platform/android/jni-android.cpp
+++ b/backends/platform/android/jni-android.cpp
@@ -80,6 +80,7 @@ int JNI::egl_surface_height = 0;
int JNI::egl_bits_per_pixel = 0;
bool JNI::_ready_for_events = 0;
bool JNI::virt_keyboard_state = false;
+int32 JNI::gestures_insets[4] = { 0, 0, 0, 0 };
jmethodID JNI::_MID_getDPI = 0;
jmethodID JNI::_MID_displayMessageOnOSD = 0;
@@ -140,6 +141,8 @@ const JNINativeMethod JNI::_natives[] = {
(void *)JNI::syncVirtkeyboardState },
{ "setPause", "(Z)V",
(void *)JNI::setPause },
+ { "systemInsetsUpdated", "([I)V",
+ (void *)JNI::systemInsetsUpdated },
{ "getNativeVersionInfo", "()Ljava/lang/String;",
(void *)JNI::getNativeVersionInfo }
};
@@ -1034,6 +1037,12 @@ void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
}
}
+void JNI::systemInsetsUpdated(JNIEnv *env, jobject self, jintArray insets) {
+ assert(env->GetArrayLength(insets) == ARRAYSIZE(gestures_insets));
+
+ env->GetIntArrayRegion(insets, 0, ARRAYSIZE(gestures_insets), gestures_insets);
+}
+
jstring JNI::getNativeVersionInfo(JNIEnv *env, jobject self) {
return convertToJString(env, Common::U32String(gScummVMVersion));
}
diff --git a/backends/platform/android/jni-android.h b/backends/platform/android/jni-android.h
index 29dae5ebf22..6535aa4330e 100644
--- a/backends/platform/android/jni-android.h
+++ b/backends/platform/android/jni-android.h
@@ -60,6 +60,8 @@ public:
static bool virt_keyboard_state;
+ static int32 gestures_insets[4];
+
static jint onLoad(JavaVM *vm);
static inline JNIEnv *getEnv() {
@@ -202,6 +204,8 @@ private:
static void syncVirtkeyboardState(JNIEnv *env, jobject self, jboolean newState);
static void setPause(JNIEnv *env, jobject self, jboolean value);
+ static void systemInsetsUpdated(JNIEnv *env, jobject self, jintArray insets);
+
static jstring getNativeVersionInfo(JNIEnv *env, jobject self);
static jstring convertToJString(JNIEnv *env, const Common::U32String &str);
static Common::U32String convertFromJString(JNIEnv *env, const jstring &jstr);
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index c47db1f380c..69bd2f1efbc 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -22,7 +22,8 @@ import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
-public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
+public abstract class ScummVM implements SurfaceHolder.Callback,
+ CompatHelpers.SystemInsets.SystemInsetsListener, Runnable {
public static final int SHOW_ON_SCREEN_MENU = 1;
public static final int SHOW_ON_SCREEN_INPUT_MODE = 2;
@@ -68,6 +69,10 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
final public native String getNativeVersionInfo();
+ // CompatHelpers.WindowInsets.SystemInsetsListener interface
+ @Override
+ final public native void systemInsetsUpdated(int insets[]);
+
// Callbacks from C++ peer instance
abstract protected void getDPI(float[] values);
abstract protected void displayMessageOnOSD(String msg);
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index d4934385577..141806b06be 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -1003,6 +1003,9 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
}
});
+ // We need to register on root as something is eating the events between the surface and the root
+ CompatHelpers.SystemInsets.registerSystemInsetsListener(_main_surface.getRootView(), _scummvm);
+
float[] dpiValues = new float[] { 0.0f, 0.0f, 0.0f };
_scummvm.getDPI(dpiValues);
Log.d(ScummVM.LOG_TAG, "Current xdpi: " + dpiValues[0] + ", ydpi: " + dpiValues[1] + " and density: " + dpiValues[2]);
Commit: a98df4e445dc83e500a813491afcccba48b7ce53
https://github.com/scummvm/scummvm/commit/a98df4e445dc83e500a813491afcccba48b7ce53
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2024-08-31T16:45:32+02:00
Commit Message:
ANDROID: Rework the virtual gamepad controller
- Use a SVG asset to allow for better scalability
- Make the controller visible when touching the screen
- Allow for oblique moving by placing finger between two directions
- Add more buttons on the center area of the screen (GUIDE, START, LEFT
STICK, RIGHT STICK) and simplify right area (only four buttons)
- Don't track all fingers, only the ones needed
- Cleanup now unused code
Changed paths:
A dists/android/gamepad.svg
R dists/android/res/drawable/touch_arrows.png
backends/graphics/android/android-graphics.cpp
backends/graphics/android/android-graphics.h
backends/graphics3d/android/android-graphics3d.cpp
backends/graphics3d/android/android-graphics3d.h
backends/platform/android/android.cpp
backends/platform/android/android.mk
backends/platform/android/jni-android.cpp
backends/platform/android/jni-android.h
backends/platform/android/org/scummvm/scummvm/ScummVM.java
backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
backends/platform/android/touchcontrols.cpp
backends/platform/android/touchcontrols.h
diff --git a/backends/graphics/android/android-graphics.cpp b/backends/graphics/android/android-graphics.cpp
index f65e963bcd3..e0804d43ae2 100644
--- a/backends/graphics/android/android-graphics.cpp
+++ b/backends/graphics/android/android-graphics.cpp
@@ -42,24 +42,7 @@
#include "backends/graphics/opengl/pipelines/pipeline.h"
#include "graphics/blit.h"
-
-static void loadBuiltinTexture(JNI::BitmapResources resource, OpenGL::Surface *surf) {
- const Graphics::Surface *src = JNI::getBitmapResource(resource);
- if (!src) {
- error("Failed to fetch touch arrows bitmap");
- }
-
- surf->allocate(src->w, src->h);
- Graphics::Surface *dst = surf->getSurface();
-
- Graphics::crossBlit(
- (byte *)dst->getPixels(), (const byte *)src->getPixels(),
- dst->pitch, src->pitch,
- src->w, src->h,
- src->format, dst->format);
-
- delete src;
-}
+#include "graphics/managed_surface.h"
//
// AndroidGraphicsManager
@@ -72,10 +55,6 @@ AndroidGraphicsManager::AndroidGraphicsManager() :
// Initialize our OpenGL ES context.
initSurface();
- _touchcontrols = createSurface(_defaultFormatAlpha);
- loadBuiltinTexture(JNI::BitmapResources::TOUCH_ARROWS_BITMAP, _touchcontrols);
- _touchcontrols->updateGLTexture();
-
// not in 3D, not in GUI
dynamic_cast<OSystem_Android *>(g_system)->applyTouchSettings(false, false);
dynamic_cast<OSystem_Android *>(g_system)->applyOrientationSettings();
@@ -85,6 +64,8 @@ AndroidGraphicsManager::~AndroidGraphicsManager() {
ENTER();
deinitSurface();
+
+ delete _touchcontrols;
}
void AndroidGraphicsManager::initSurface() {
@@ -118,8 +99,10 @@ void AndroidGraphicsManager::initSurface() {
if (_touchcontrols) {
_touchcontrols->recreate();
_touchcontrols->updateGLTexture();
+ } else {
+ _touchcontrols = createSurface(_defaultFormatAlpha);
}
- dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().init(
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
this, JNI::egl_surface_width, JNI::egl_surface_height);
handleResize(JNI::egl_surface_width, JNI::egl_surface_height);
@@ -132,7 +115,7 @@ void AndroidGraphicsManager::deinitSurface() {
LOGD("deinitializing 2D surface");
// Deregister us from touch control
- dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().init(
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
nullptr, 0, 0);
if (_touchcontrols) {
_touchcontrols->destroy();
@@ -157,7 +140,7 @@ void AndroidGraphicsManager::resizeSurface() {
error("JNI::initSurface failed");
}
- dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().init(
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
this, JNI::egl_surface_width, JNI::egl_surface_height);
handleResize(JNI::egl_surface_width, JNI::egl_surface_height);
@@ -170,6 +153,9 @@ void AndroidGraphicsManager::updateScreen() {
if (!JNI::haveSurface())
return;
+ // Sets _forceRedraw if needed
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().beforeDraw();
+
OpenGLGraphicsManager::updateScreen();
}
@@ -246,12 +232,34 @@ void AndroidGraphicsManager::syncVirtkeyboardState(bool virtkeybd_on) {
_forceRedraw = true;
}
-void AndroidGraphicsManager::touchControlDraw(int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) {
+void AndroidGraphicsManager::touchControlInitSurface(const Graphics::ManagedSurface &surf) {
+ if (_touchcontrols->getWidth() == surf.w && _touchcontrols->getHeight() == surf.h) {
+ return;
+ }
+
+ _touchcontrols->allocate(surf.w, surf.h);
+ Graphics::Surface *dst = _touchcontrols->getSurface();
+
+ Graphics::crossBlit(
+ (byte *)dst->getPixels(), (const byte *)surf.getPixels(),
+ dst->pitch, surf.pitch,
+ surf.w, surf.h,
+ surf.format, dst->format);
+ _touchcontrols->updateGLTexture();
+}
+
+void AndroidGraphicsManager::touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) {
_targetBuffer->enableBlend(OpenGL::Framebuffer::kBlendModeTraditionalTransparency);
OpenGL::Pipeline *pipeline = getPipeline();
pipeline->activate();
+ if (alpha != 255) {
+ pipeline->setColor(1.0f, 1.0f, 1.0f, alpha / 255.0f);
+ }
pipeline->drawTexture(_touchcontrols->getGLTexture(),
x, y, w, h, clip);
+ if (alpha != 255) {
+ pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
+ }
}
void AndroidGraphicsManager::touchControlNotifyChanged() {
diff --git a/backends/graphics/android/android-graphics.h b/backends/graphics/android/android-graphics.h
index 4ae9bae0c5e..c865f3cc29b 100644
--- a/backends/graphics/android/android-graphics.h
+++ b/backends/graphics/android/android-graphics.h
@@ -88,8 +88,9 @@ public:
float getHiDPIScreenFactor() const override;
+ void touchControlInitSurface(const Graphics::ManagedSurface &surf) override;
void touchControlNotifyChanged() override;
- void touchControlDraw(int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) override;
+ void touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) override;
protected:
void setSystemMousePosition(const int x, const int y) override {}
diff --git a/backends/graphics3d/android/android-graphics3d.cpp b/backends/graphics3d/android/android-graphics3d.cpp
index bd4a2435255..aba80ddafcf 100644
--- a/backends/graphics3d/android/android-graphics3d.cpp
+++ b/backends/graphics3d/android/android-graphics3d.cpp
@@ -41,6 +41,7 @@
#include "common/tokenizer.h"
#include "graphics/blit.h"
+#include "graphics/managed_surface.h"
#include "graphics/opengl/shader.h"
#include "graphics/opengl/context.h"
@@ -54,26 +55,6 @@
#define CONTEXT_RESET_ENABLE(gl_param) if (!(saved ## gl_param)) { GLCALL(glDisable(gl_param)); }
#define CONTEXT_RESET_DISABLE(gl_param) if (saved ## gl_param) { GLCALL(glEnable(gl_param)); }
-static GLES8888Texture *loadBuiltinTexture(JNI::BitmapResources resource) {
- const Graphics::Surface *src = JNI::getBitmapResource(JNI::BitmapResources::TOUCH_ARROWS_BITMAP);
- if (!src) {
- error("Failed to fetch touch arrows bitmap");
- }
-
- GLES8888Texture *ret = new GLES8888Texture();
- ret->allocBuffer(src->w, src->h);
- Graphics::Surface *dst = ret->surface();
-
- Graphics::crossBlit(
- (byte *)dst->getPixels(), (const byte *)src->getPixels(),
- dst->pitch, src->pitch,
- src->w, src->h,
- src->format, dst->format);
-
- delete src;
- return ret;
-}
-
AndroidGraphics3dManager::AndroidGraphics3dManager() :
_screenChangeID(0),
_graphicsMode(0),
@@ -94,7 +75,7 @@ AndroidGraphics3dManager::AndroidGraphics3dManager() :
_mouse_hotspot(),
_mouse_dont_scale(false),
_show_mouse(false),
- _touchcontrols_texture(nullptr),
+ _touchcontrols_texture(new GLES8888Texture()),
_old_touch_mode(OSystem_Android::TOUCH_MODE_TOUCHPAD) {
if (JNI::egl_bits_per_pixel == 16) {
@@ -112,8 +93,6 @@ AndroidGraphics3dManager::AndroidGraphics3dManager() :
}
_mouse_texture = _mouse_texture_palette;
- _touchcontrols_texture = loadBuiltinTexture(JNI::BitmapResources::TOUCH_ARROWS_BITMAP);
-
initSurface();
// in 3D, not in GUI
@@ -218,7 +197,7 @@ void AndroidGraphics3dManager::initSurface() {
if (_touchcontrols_texture) {
_touchcontrols_texture->reinit();
}
- dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().init(
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
this, JNI::egl_surface_width, JNI::egl_surface_height);
updateScreenRect();
@@ -256,7 +235,7 @@ void AndroidGraphics3dManager::deinitSurface() {
_mouse_texture_palette->release();
}
- dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().init(
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
nullptr, 0, 0);
if (_touchcontrols_texture) {
_touchcontrols_texture->release();
@@ -286,7 +265,7 @@ void AndroidGraphics3dManager::resizeSurface() {
initOverlay();
}
- dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().init(
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().setDrawer(
this, JNI::egl_surface_width, JNI::egl_surface_height);
updateScreenRect();
@@ -303,6 +282,9 @@ void AndroidGraphics3dManager::updateScreen() {
return;
}
+ // Sets _forceRedraw if needed
+ dynamic_cast<OSystem_Android *>(g_system)->getTouchControls().beforeDraw();
+
if (!_force_redraw &&
!_game_texture->dirty() &&
!_overlay_texture->dirty() &&
@@ -1103,12 +1085,28 @@ bool AndroidGraphics3dManager::setState(const AndroidCommonGraphics::State &stat
return true;
}
+void AndroidGraphics3dManager::touchControlInitSurface(const Graphics::ManagedSurface &surf) {
+ if (_touchcontrols_texture->width() == surf.w && _touchcontrols_texture->height() == surf.h) {
+ return;
+ }
+
+ _touchcontrols_texture->allocBuffer(surf.w, surf.h);
+ Graphics::Surface *dst = _touchcontrols_texture->surface();
+
+ Graphics::crossBlit(
+ (byte *)dst->getPixels(), (const byte *)surf.getPixels(),
+ dst->pitch, surf.pitch,
+ surf.w, surf.h,
+ surf.format, dst->format);
+}
+
void AndroidGraphics3dManager::touchControlNotifyChanged() {
// Make sure we redraw the screen
_force_redraw = true;
}
-void AndroidGraphics3dManager::touchControlDraw(int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) {
+void AndroidGraphics3dManager::touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) {
+ _touchcontrols_texture->setAlpha(alpha / 255.f);
_touchcontrols_texture->drawTexture(x, y, w, h, clip);
}
diff --git a/backends/graphics3d/android/android-graphics3d.h b/backends/graphics3d/android/android-graphics3d.h
index 22ebae16386..92f9318abea 100644
--- a/backends/graphics3d/android/android-graphics3d.h
+++ b/backends/graphics3d/android/android-graphics3d.h
@@ -121,8 +121,9 @@ public:
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
#endif
+ void touchControlInitSurface(const Graphics::ManagedSurface &surf) override;
void touchControlNotifyChanged() override;
- void touchControlDraw(int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) override;
+ void touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) override;
void syncVirtkeyboardState(bool virtkeybd_on) override;
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index a1ccd4f49d5..2dd3259e690 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -557,6 +557,10 @@ void OSystem_Android::initBackend() {
_audio_thread_exit = false;
pthread_create(&_audio_thread, 0, audioThreadFunc, this);
+ JNI::DPIValues dpi;
+ JNI::getDPI(dpi);
+ _touchControls.init(dpi[2]);
+
_graphicsManager = new AndroidGraphicsManager();
// renice this thread to boost the audio thread
diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk
index 03d421016b8..835095828a5 100644
--- a/backends/platform/android/android.mk
+++ b/backends/platform/android/android.mk
@@ -13,7 +13,7 @@ APK_MAIN = ScummVM-debug.apk
APK_MAIN_RELEASE = ScummVM-release-unsigned.apk
AAB_MAIN_RELEASE = ScummVM-release.aab
-DIST_FILES_HELP = $(PATH_DIST)/android-help.zip
+DIST_FILES_PLATFORM = $(PATH_DIST)/android-help.zip $(PATH_DIST)/gamepad.svg
$(PATH_BUILD):
$(MKDIR) $(PATH_BUILD)
@@ -38,9 +38,9 @@ $(PATH_BUILD)/local.properties: configure.stamp | $(PATH_BUILD)
$(PATH_BUILD)/src.properties: configure.stamp | $(PATH_BUILD)
$(ECHO) "srcdir=$(realpath $(srcdir))\n" > $(PATH_BUILD)/src.properties
-$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_ENGINEDATA_BIG) $(DIST_FILES_SOUNDFONTS) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_DOCS) $(DIST_FILES_HELP) | $(PATH_BUILD)
+$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_ENGINEDATA_BIG) $(DIST_FILES_SOUNDFONTS) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_DOCS) $(DIST_FILES_PLATFORM) | $(PATH_BUILD)
$(INSTALL) -d $(PATH_BUILD_ASSETS)
- $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_ENGINEDATA_BIG) $(DIST_FILES_SOUNDFONTS) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_DOCS) $(DIST_FILES_HELP) $(PATH_BUILD_ASSETS)/
+ $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_ENGINEDATA_BIG) $(DIST_FILES_SOUNDFONTS) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_DOCS) $(DIST_FILES_PLATFORM) $(PATH_BUILD_ASSETS)/
ifneq ($(DIST_FILES_SHADERS),)
$(INSTALL) -d $(PATH_BUILD_ASSETS)/shaders
$(INSTALL) -c -m 644 $(DIST_FILES_SHADERS) $(PATH_BUILD_ASSETS)/shaders
diff --git a/backends/platform/android/jni-android.cpp b/backends/platform/android/jni-android.cpp
index 1c0bb45807d..7a01ddbcb55 100644
--- a/backends/platform/android/jni-android.cpp
+++ b/backends/platform/android/jni-android.cpp
@@ -92,7 +92,6 @@ jmethodID JNI::_MID_isConnectionLimited = 0;
jmethodID JNI::_MID_setWindowCaption = 0;
jmethodID JNI::_MID_showVirtualKeyboard = 0;
jmethodID JNI::_MID_showOnScreenControls = 0;
-jmethodID JNI::_MID_getBitmapResource = 0;
jmethodID JNI::_MID_setTouchMode = 0;
jmethodID JNI::_MID_getTouchMode = 0;
jmethodID JNI::_MID_setOrientation = 0;
@@ -437,71 +436,6 @@ void JNI::showOnScreenControls(int enableMask) {
}
}
-Graphics::Surface *JNI::getBitmapResource(BitmapResources resource) {
- JNIEnv *env = JNI::getEnv();
-
- jobject bitmap = env->CallObjectMethod(_jobj, _MID_getBitmapResource, (int) resource);
-
- if (env->ExceptionCheck()) {
- LOGE("Can't get bitmap resource");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
-
- return nullptr;
- }
-
- if (bitmap == nullptr) {
- LOGE("Bitmap resource was not found");
- return nullptr;
- }
-
- AndroidBitmapInfo bitmap_info;
- if (AndroidBitmap_getInfo(env, bitmap, &bitmap_info) != ANDROID_BITMAP_RESULT_SUCCESS) {
- LOGE("Error reading bitmap info");
- env->DeleteLocalRef(bitmap);
- return nullptr;
- }
-
- Graphics::PixelFormat fmt;
- switch(bitmap_info.format) {
- case ANDROID_BITMAP_FORMAT_RGBA_8888:
-#ifdef SCUMM_BIG_ENDIAN
- fmt = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
-#else
- fmt = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
-#endif
- break;
- case ANDROID_BITMAP_FORMAT_RGBA_4444:
- fmt = Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0);
- break;
- case ANDROID_BITMAP_FORMAT_RGB_565:
- fmt = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
- break;
- default:
- LOGE("Bitmap has unsupported format");
- env->DeleteLocalRef(bitmap);
- return nullptr;
- }
-
- void *src_pixels = nullptr;
- if (AndroidBitmap_lockPixels(env, bitmap, &src_pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
- LOGE("Error locking bitmap pixels");
- env->DeleteLocalRef(bitmap);
- return nullptr;
- }
-
- Graphics::Surface *ret = new Graphics::Surface();
- ret->create(bitmap_info.width, bitmap_info.height, fmt);
- ret->copyRectToSurface(src_pixels, bitmap_info.stride,
- 0, 0, bitmap_info.width, bitmap_info.height);
-
- AndroidBitmap_unlockPixels(env, bitmap);
- env->DeleteLocalRef(bitmap);
-
- return ret;
-}
-
void JNI::setTouchMode(int touchMode) {
JNIEnv *env = JNI::getEnv();
@@ -822,7 +756,6 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
FIND_METHOD(, isConnectionLimited, "()Z");
FIND_METHOD(, showVirtualKeyboard, "(Z)V");
FIND_METHOD(, showOnScreenControls, "(I)V");
- FIND_METHOD(, getBitmapResource, "(I)Landroid/graphics/Bitmap;");
FIND_METHOD(, setTouchMode, "(I)V");
FIND_METHOD(, getTouchMode, "()I");
FIND_METHOD(, setOrientation, "(I)V");
diff --git a/backends/platform/android/jni-android.h b/backends/platform/android/jni-android.h
index 6535aa4330e..8c6d721bad6 100644
--- a/backends/platform/android/jni-android.h
+++ b/backends/platform/android/jni-android.h
@@ -46,10 +46,6 @@ private:
virtual ~JNI();
public:
- enum struct BitmapResources {
- TOUCH_ARROWS_BITMAP = 0
- };
-
static bool pause;
static sem_t pause_sem;
@@ -94,7 +90,6 @@ public:
static bool isConnectionLimited();
static void showVirtualKeyboard(bool enable);
static void showOnScreenControls(int enableMask);
- static Graphics::Surface *getBitmapResource(BitmapResources resource);
static void setTouchMode(int touchMode);
static int getTouchMode();
static void setOrientation(int touchMode);
@@ -157,7 +152,6 @@ private:
static jmethodID _MID_setWindowCaption;
static jmethodID _MID_showVirtualKeyboard;
static jmethodID _MID_showOnScreenControls;
- static jmethodID _MID_getBitmapResource;
static jmethodID _MID_setTouchMode;
static jmethodID _MID_getTouchMode;
static jmethodID _MID_setOrientation;
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index 69bd2f1efbc..ede71d2863f 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -84,7 +84,6 @@ public abstract class ScummVM implements SurfaceHolder.Callback,
abstract protected void setWindowCaption(String caption);
abstract protected void showVirtualKeyboard(boolean enable);
abstract protected void showOnScreenControls(int enableMask);
- abstract protected Bitmap getBitmapResource(int resource);
abstract protected void setTouchMode(int touchMode);
abstract protected int getTouchMode();
abstract protected void setOrientation(int orientation);
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 141806b06be..c51b2208363 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -777,23 +777,6 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
});
}
- @Override
- protected Bitmap getBitmapResource(int resource) {
- int id;
- switch(resource) {
- case 0: // TOUCH_ARROWS_BITMAP
- id = R.drawable.touch_arrows;
- break;
- default:
- return null;
- }
-
- BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inScaled = false;
-
- return BitmapFactory.decodeResource(getResources(), id, opts);
- }
-
@Override
protected void setTouchMode(final int touchMode) {
if (_events.getTouchMode() == touchMode) {
diff --git a/backends/platform/android/touchcontrols.cpp b/backends/platform/android/touchcontrols.cpp
index 380513daa5b..73aa8965e40 100644
--- a/backends/platform/android/touchcontrols.cpp
+++ b/backends/platform/android/touchcontrols.cpp
@@ -38,150 +38,174 @@
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#include "backends/platform/android/android.h"
+#include "backends/platform/android/jni-android.h"
#include "backends/platform/android/touchcontrols.h"
+#include "common/file.h"
+#include "graphics/svg.h"
+
+#define SVG_WIDTH 384
+#define SVG_HEIGHT 256
+#define BG_WIDTH 128
+#define BG_HEIGHT 128
+#define ARROW_DEAD 21
+#define ARROW_SUPER 42
+#define ARROW_END 64
+#define ARROW_WIDTH 64
+#define ARROW_SEMI_HEIGHT 52
+#define ARROW_HEIGHT 64
+#define ARROW_HL_LEFT 0
+#define ARROW_HL_TOP 128
+#define BUTTON_DEAD 12
+#define BUTTON_END 64
+#define BUTTON_WIDTH 64
+#define BUTTON_HEIGHT 64
+#define BUTTON_HL_LEFT 128
+#define BUTTON_HL_TOP 128
+#define BUTTON2_HL_LEFT 256
+#define BUTTON2_HL_TOP 128
+
+#define ALPHA_OPAQUE 255
+#define ZOMBIE_TIMEOUT 500 // ms
+
+// The scale factor is stored as a fixed point 30.2 bits
+// This avoids floating point operations
+#define SCALE_FACTOR_FXP 4
+
+// gamepad.svg was designed with a basis of 128x128 sized widget
+// As it's too small on screen, we apply a factor of 1.5x
+// It can be tuned here and in SVG viewBox without changing anything else
+#define SVG_UNSCALED(x) ((x) * 3 / 2)
+#define SVG_SQ_UNSCALED(x) ((x * x) * 9 / 4)
+
+#define SVG_SCALED(x) (SVG_UNSCALED(x) * _scale)
+#define SCALED_PIXELS(x) ((x) / SCALE_FACTOR_FXP)
+
+#define SVG_PIXELS(x) SCALED_PIXELS(SVG_SCALED(x))
+
+#define FUNC_SVG_SQ_SCALED(x) (SVG_SQ_UNSCALED(x) * parent->_scale2)
+
TouchControls::TouchControls() :
_drawer(nullptr),
_screen_width(0),
- _screen_height(0) {
+ _screen_height(0),
+ _svg(nullptr),
+ _zombieCount(0),
+ _scale(0),
+ _scale2(0) {
+ _functions[kFunctionLeft] = new FunctionLeft(this);
+ _functions[kFunctionRight] = new FunctionRight(this);
+ _functions[kFunctionCenter] = new FunctionCenter(this);
}
-TouchControls::Function TouchControls::getFunction(int x, int y) {
- if (_screen_width == 0) {
- // Avoid divide by 0 error
- return kFunctionNone;
- }
+void TouchControls::init(float scale) {
+ _scale = scale * SCALE_FACTOR_FXP;
+ // As scale is small, this should fit in int
+ _scale2 = _scale * _scale;
- float xPercent = float(x) / _screen_width;
+ Common::File stream;
- if (xPercent < 0.3) {
- return kFunctionJoystick;
- } else if (xPercent < 0.8) {
- return kFunctionCenter;
- } else {
- return kFunctionRight;
+ if (!stream.open("gamepad.svg")) {
+ error("Failed to fetch gamepad image");
}
+
+ delete _svg;
+ _svg = new Graphics::SVGBitmap(&stream, SVG_PIXELS(SVG_WIDTH), SVG_PIXELS(SVG_HEIGHT));
}
-void TouchControls::touchToJoystickState(int dX, int dY, FunctionState &state) {
- int sqNorm = dX * dX + dY * dY;
- if (sqNorm < 50 * 50) {
- return;
+TouchControls::~TouchControls() {
+ delete _svg;
+ for(unsigned int i = 0; i < kFunctionCount; i++) {
+ delete _functions[i];
}
+}
- if (dY > abs(dX)) {
- state.main = Common::JOYSTICK_BUTTON_DPAD_DOWN;
- state.clip = Common::Rect(256, 0, 384, 128);
- } else if (dX > abs(dY)) {
- state.main = Common::JOYSTICK_BUTTON_DPAD_RIGHT;
- state.clip = Common::Rect(128, 0, 256, 128);
- } else if (-dY > abs(dX)) {
- state.main = Common::JOYSTICK_BUTTON_DPAD_UP;
- state.clip = Common::Rect(0, 0, 128, 128);
- } else if (-dX > abs(dY)) {
- state.main = Common::JOYSTICK_BUTTON_DPAD_LEFT;
- state.clip = Common::Rect(384, 0, 512, 128);
- } else {
+void TouchControls::beforeDraw() {
+ if (!_zombieCount) {
return;
}
-
- if (sqNorm > 20000) {
- state.modifier = Common::JOYSTICK_BUTTON_RIGHT_SHOULDER;
+ // We have zombies, force redraw to render fading out
+ if (_drawer) {
+ _drawer->touchControlNotifyChanged();
}
-}
-
-void TouchControls::touchToCenterState(int dX, int dY, FunctionState &state) {
- int sqNorm = dX * dX + dY * dY;
- if (sqNorm < 50 * 50) {
- state.main = Common::JOYSTICK_BUTTON_GUIDE;
+ // Check for zombie functions, clear them out if expired
+ unsigned int zombieCount = 0;
+ uint32 now = g_system->getMillis(true);
+ for (uint i = 0; i < kFunctionCount; i++) {
+ Function *func = _functions[i];
+ if (func->status != kFunctionZombie) {
+ continue;
+ }
+ if (func->lastActivable < now) {
+ func->status = kFunctionInactive;
+ continue;
+ }
+ zombieCount++;
}
+ _zombieCount = zombieCount;
}
-void TouchControls::touchToRightState(int dX, int dY, FunctionState &state) {
- if (dX * dX + dY * dY < 100 * 100) {
- return;
+TouchControls::FunctionId TouchControls::getFunctionId(int x, int y) {
+ if (_screen_width == 0) {
+ // Avoid divide by 0 error
+ return kFunctionNone;
}
- if (dX > abs(dY)) {
- // right
- state.main = Common::JOYSTICK_BUTTON_RIGHT_STICK;
- state.clip = Common::Rect(512, 128, 640, 256);
- return;
- } else if (-dX > abs(dY)) {
- // left
- state.main = Common::JOYSTICK_BUTTON_LEFT_STICK;
- state.clip = Common::Rect(512, 0, 640, 128);
- return;
+ // Exclude areas reserved for system
+ if ((x < JNI::gestures_insets[0] * SCALE_FACTOR_FXP) ||
+ (y < JNI::gestures_insets[1] * SCALE_FACTOR_FXP) ||
+ (x >= _screen_width - JNI::gestures_insets[2] * SCALE_FACTOR_FXP) ||
+ (y >= _screen_height - JNI::gestures_insets[3] * SCALE_FACTOR_FXP)) {
+ return kFunctionNone;
}
- static Common::JoystickButton buttons[5] = {
- Common::JOYSTICK_BUTTON_Y, Common::JOYSTICK_BUTTON_B, // top zone
- Common::JOYSTICK_BUTTON_INVALID, // center
- Common::JOYSTICK_BUTTON_A, Common::JOYSTICK_BUTTON_X // bottom zone
- };
-
- static int16 clips[5][4] = {
- { 0, 128, 128, 256 }, // y
- { 128, 128, 256, 256 }, // b
- { 0, 0, 0, 0 }, // center
- { 256, 128, 384, 256 }, // a
- { 384, 128, 512, 256 } // x
- };
- static const uint offset = (ARRAYSIZE(buttons) - 1) / 2;
+ float xRatio = float(x) / _screen_width;
- int idx = (dY / 100) + offset;
- if (idx < 0) {
- idx = 0;
- }
- if (idx >= ARRAYSIZE(buttons)) {
- idx = ARRAYSIZE(buttons) - 1;
+ if (xRatio < 0.3) {
+ return kFunctionLeft;
+ } else if (xRatio < 0.7) {
+ return kFunctionCenter;
+ } else {
+ return kFunctionRight;
}
- state.main = buttons[idx];
- state.clip = Common::Rect(clips[idx][0], clips[idx][1], clips[idx][2], clips[idx][3]);
}
-TouchControls::FunctionBehavior TouchControls::functionBehaviors[TouchControls::kFunctionMax + 1] =
-{
- { touchToJoystickState, false, .2f, .5f },
- { touchToCenterState, true, .5f, .5f },
- { touchToRightState, true, .8f, .5f }
-};
-
-void TouchControls::init(TouchControlsDrawer *drawer, int width, int height) {
+void TouchControls::setDrawer(TouchControlsDrawer *drawer, int width, int height) {
_drawer = drawer;
- _screen_width = width;
- _screen_height = height;
+ _screen_width = width * SCALE_FACTOR_FXP;
+ _screen_height = height * SCALE_FACTOR_FXP;
+
+ if (drawer) {
+ drawer->touchControlInitSurface(*_svg);
+ }
}
-TouchControls::Pointer *TouchControls::getPointerFromId(int ptrId, bool createNotFound) {
- uint freeEntry = -1;
- for (uint i = 0; i < kNumPointers; i++) {
- Pointer &ptr = _pointers[i];
- if (ptr.active && (ptr.id == ptrId)) {
- return &ptr;
+TouchControls::Function *TouchControls::getFunctionFromPointerId(int ptrId) {
+ for (uint i = 0; i < kFunctionCount; i++) {
+ Function *func = _functions[i];
+ if (func->status != kFunctionActive) {
+ continue;
}
- if (createNotFound && (freeEntry == -1) && !ptr.active) {
- freeEntry = i;
+ if (func->pointerId == ptrId) {
+ return func;
}
}
- // Too much fingers or not found
- if (freeEntry == -1) {
- return nullptr;
- }
-
- Pointer &ptr = _pointers[freeEntry];
- ptr.reset();
- ptr.id = ptrId;
-
- return &ptr;
+ return nullptr;
}
-TouchControls::Pointer *TouchControls::findPointerFromFunction(Function function) {
- for (uint i = 0; i < kNumPointers; i++) {
- Pointer &ptr = _pointers[i];
- if (ptr.active && (ptr.function == function)) {
- return &ptr;
+TouchControls::Function *TouchControls::getZombieFunctionFromPos(int x, int y) {
+ if (!_zombieCount) {
+ return nullptr;
+ }
+ for (uint i = 0; i < kFunctionCount; i++) {
+ Function *func = _functions[i];
+ if (func->status != kFunctionZombie) {
+ // Already assigned to a finger or dead
+ continue;
+ }
+ if (func->isInside(x, y)) {
+ return func;
}
}
return nullptr;
@@ -190,130 +214,119 @@ TouchControls::Pointer *TouchControls::findPointerFromFunction(Function function
void TouchControls::draw() {
assert(_drawer != nullptr);
- for (uint i = 0; i < kFunctionMax + 1; i++) {
- FunctionState &state = _functionStates[i];
- FunctionBehavior behavior = functionBehaviors[i];
-
- if (state.clip.isEmpty()) {
+ uint32 now = g_system->getMillis(true);
+
+ for (uint i = 0; i < kFunctionCount; i++) {
+ Function *func = _functions[i];
+ uint8 alpha;
+ switch (func->status) {
+ case kFunctionActive:
+ alpha = ALPHA_OPAQUE;
+ break;
+ case kFunctionZombie:
+ if (func->lastActivable < now) {
+ // This function is definitely dead
+ continue;
+ }
+ alpha = (func->lastActivable - now) * ALPHA_OPAQUE / ZOMBIE_TIMEOUT;
+ break;
+ case kFunctionInactive:
+ default:
continue;
}
- _drawer->touchControlDraw(_screen_width * behavior.xRatio, _screen_height * behavior.yRatio,
- 64, 64, state.clip);
-
+ func->draw(alpha);
}
}
void TouchControls::update(Action action, int ptrId, int x, int y) {
+ x *= SCALE_FACTOR_FXP;
+ y *= SCALE_FACTOR_FXP;
if (action == JACTION_DOWN) {
- Pointer *ptr = getPointerFromId(ptrId, true);
- if (!ptr) {
- return;
+ Function *func = getZombieFunctionFromPos(x, y);
+ if (!func) {
+ // Finger was not pressed on a zombie function
+ // Determine which function it could be
+ FunctionId funcId = getFunctionId(x, y);
+ if (funcId != kFunctionNone) {
+ func = _functions[funcId];
+ }
+ if (!func) {
+ // No function for this finger
+ return;
+ }
+ if (func->status == kFunctionActive) {
+ // Another finger is already on this function
+ return;
+ }
+
+ // When zombie, we reuse the old start coordinates
+ // but not when starting over
+ func->reset();
+ func->startX = x;
+ func->startY = y;
}
- TouchControls::Function function = getFunction(x, y);
- // ptrId is active no matter what
- ptr->active = true;
- if (function == kFunctionNone) {
- // No function for this finger
- return;
- }
- if (findPointerFromFunction(function)) {
- // Some finger is already using this function: don't do anything
- return;
- }
+ func->status = kFunctionActive;
+ func->pointerId = ptrId;
+ func->currentX = x;
+ func->currentY = y;
+
+ int dX = x - func->startX;
+ int dY = y - func->startY;
- ptr->startX = ptr->currentX = x;
- ptr->startY = ptr->currentY = y;
- ptr->function = function;
+ func->touch(dX, dY, action);
+ if (_drawer) {
+ _drawer->touchControlNotifyChanged();
+ }
} else if (action == JACTION_MOVE) {
- Pointer *ptr = getPointerFromId(ptrId, false);
- if (!ptr || ptr->function == kFunctionNone) {
+ Function *func = getFunctionFromPointerId(ptrId);
+ if (!func) {
return;
}
- FunctionBehavior &behavior = functionBehaviors[ptr->function];
-
- ptr->currentX = x;
- ptr->currentY = y;
+ func->currentX = x;
+ func->currentY = y;
- int dX = x - ptr->startX;
- int dY = y - ptr->startY;
+ int dX = x - func->startX;
+ int dY = y - func->startY;
- FunctionState newState;
- functionBehaviors[ptr->function].touchToState(dX, dY, newState);
-
- FunctionState &oldState = _functionStates[ptr->function];
-
- if (!behavior.pressOnRelease) {
- // send key presses continuously
- // first old remove main key, then update modifier, then press new main key
- if (oldState.main != newState.main) {
- buttonUp(oldState.main);
- }
- if (oldState.modifier != newState.modifier) {
- buttonUp(oldState.modifier);
- buttonDown(newState.modifier);
- }
- if (oldState.main != newState.main) {
- buttonDown(newState.main);
- }
- }
- oldState = newState;
+ func->touch(dX, dY, action);
if (_drawer) {
_drawer->touchControlNotifyChanged();
}
} else if (action == JACTION_UP) {
- Pointer *ptr = getPointerFromId(ptrId, false);
- if (!ptr || ptr->function == kFunctionNone) {
+ Function *func = getFunctionFromPointerId(ptrId);
+ if (!func) {
return;
}
- FunctionBehavior &behavior = functionBehaviors[ptr->function];
- FunctionState &functionState = _functionStates[ptr->function];
-
- if (!behavior.pressOnRelease) {
- // We sent key down continuously: buttonUp everything
- buttonUp(functionState.main);
- buttonUp(functionState.modifier);
- } else {
- int dX = x - ptr->startX;
- int dY = y - ptr->startY;
-
- FunctionState newState;
- functionBehaviors[ptr->function].touchToState(dX, dY, newState);
+ func->currentX = x;
+ func->currentY = y;
- buttonDown(newState.modifier);
- buttonPress(newState.main);
- buttonUp(newState.modifier);
- }
+ int dX = x - func->startX;
+ int dY = y - func->startY;
- functionState.reset();
- ptr->active = false;
+ func->touch(dX, dY, action);
+ func->status = kFunctionZombie;
+ func->lastActivable = g_system->getMillis(true) + ZOMBIE_TIMEOUT;
if (_drawer) {
_drawer->touchControlNotifyChanged();
}
+ _zombieCount++;
} else if (action == JACTION_CANCEL) {
- for (uint i = 0; i < kNumPointers; i++) {
- Pointer &ptr = _pointers[i];
- ptr.reset();
- }
-
- for (uint i = 0; i < kFunctionMax + 1; i++) {
- FunctionBehavior &behavior = functionBehaviors[i];
- FunctionState &functionState = _functionStates[i];
+ for (uint i = 0; i < kFunctionCount; i++) {
+ Function *func = _functions[i];
- if (!behavior.pressOnRelease) {
- // We sent key down continuously: buttonUp everything
- buttonUp(functionState.main);
- buttonUp(functionState.modifier);
+ if (func->status == kFunctionActive) {
+ func->touch(0, 0, action);
}
-
- functionState.reset();
+ func->reset();
}
if (_drawer) {
_drawer->touchControlNotifyChanged();
}
+ _zombieCount = 0;
}
}
@@ -322,6 +335,7 @@ void TouchControls::buttonDown(Common::JoystickButton jb) {
return;
}
+ //LOGD("TouchControls::buttonDown: %d", jb);
Common::Event ev;
ev.type = Common::EVENT_JOYBUTTON_DOWN;
ev.joystick.button = jb;
@@ -333,6 +347,7 @@ void TouchControls::buttonUp(Common::JoystickButton jb) {
return;
}
+ //LOGD("TouchControls::buttonUp: %d", jb);
Common::Event ev;
ev.type = Common::EVENT_JOYBUTTON_UP;
ev.joystick.button = jb;
@@ -344,6 +359,7 @@ void TouchControls::buttonPress(Common::JoystickButton jb) {
return;
}
+ //LOGD("TouchControls::buttonPress: %d", jb);
Common::Event ev1, ev2;
ev1.type = Common::EVENT_JOYBUTTON_DOWN;
ev1.joystick.button = jb;
@@ -351,3 +367,388 @@ void TouchControls::buttonPress(Common::JoystickButton jb) {
ev2.joystick.button = jb;
dynamic_cast<OSystem_Android *>(g_system)->pushEvent(ev1, ev2);
}
+
+void TouchControls::drawSurface(uint8 alpha, int x, int y, int offX, int offY, const Common::Rect &clip) const {
+ Common::Rect clip_(SVG_PIXELS(clip.left), SVG_PIXELS(clip.top),
+ SVG_PIXELS(clip.right), SVG_PIXELS(clip.bottom));
+ _drawer->touchControlDraw(alpha,
+ SCALED_PIXELS(x + SVG_SCALED(offX)), SCALED_PIXELS(y + SVG_SCALED(offY)),
+ clip_.width(), clip_.height(), clip_);
+}
+
+bool TouchControls::FunctionLeft::isInside(int x, int y) {
+ int dX = x - startX;
+ int dY = y - startY;
+ // norm 2 squared (to avoid square root)
+ unsigned int sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ if (sqNorm <= FUNC_SVG_SQ_SCALED(ARROW_END)) {
+ return true;
+ }
+
+ // Also accept touches near the old last touch
+ dX = x - currentX;
+ dY = y - currentY;
+ sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ if (sqNorm <= FUNC_SVG_SQ_SCALED(ARROW_DEAD)) {
+ return true;
+ }
+
+ return false;
+}
+
+void TouchControls::FunctionLeft::maskToLeftButtons(uint32 oldMask, uint32 newMask) {
+ static const Common::JoystickButton buttons[] = {
+ Common::JOYSTICK_BUTTON_DPAD_UP, Common::JOYSTICK_BUTTON_DPAD_RIGHT,
+ Common::JOYSTICK_BUTTON_DPAD_DOWN, Common::JOYSTICK_BUTTON_DPAD_LEFT
+ };
+
+ uint32 diff = newMask ^ oldMask;
+
+ for(int i = 0, m = 1; i < ARRAYSIZE(buttons); i++, m <<= 1) {
+ if (!(diff & m)) {
+ continue;
+ }
+ if (oldMask & m) {
+ TouchControls::buttonUp(buttons[i]);
+ }
+ }
+ if (diff & 16) {
+ if (oldMask & 16) {
+ TouchControls::buttonUp(Common::JOYSTICK_BUTTON_RIGHT_SHOULDER);
+ } else {
+ TouchControls::buttonDown(Common::JOYSTICK_BUTTON_RIGHT_SHOULDER);
+ }
+ }
+ for(int i = 0, m = 1; i < ARRAYSIZE(buttons); i++, m <<= 1) {
+ if (!(diff & m)) {
+ continue;
+ }
+ if (newMask & m) {
+ TouchControls::buttonDown(buttons[i]);
+ }
+ }
+}
+
+void TouchControls::FunctionLeft::touch(int dX, int dY, Action action) {
+ if (action == JACTION_CANCEL ||
+ action == JACTION_UP) {
+ maskToLeftButtons(mask, 0);
+ resetState();
+ return;
+ }
+
+ uint32 newMask = 0;
+
+ // norm 2 squared (to avoid square root)
+ unsigned int sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ if (sqNorm >= FUNC_SVG_SQ_SCALED(ARROW_DEAD)) {
+ // We are far enough from the center
+ // For right we use angles -60,60 as a sensitive zone
+ // For left it's the same but mirrored in negative
+ // We must be between the two lines which are of tan(60),tan(-60) and this corrsponds to sqrt(3)
+ // For up down we use angles -30,30 as a sensitive zone
+ // We must be outside the two lines which are of tan(30),tan(-30) and this corrsponds to 1/sqrt(3)
+ /*
+ static const double SQRT3 = 1.73205080756887719318;
+ unsigned int sq3 = SQRT3 * abs(dX);
+ */
+ // Optimize by using an approximation of sqrt(3)
+ unsigned int sq3 = abs(dX) * 51409 / 29681;
+ unsigned int isq3 = abs(dX) * 29681 / 51409;
+
+ unsigned int adY = abs(dY);
+
+ if (adY <= sq3) {
+ // Left or right
+ if (dX < 0) {
+ newMask |= 8;
+ } else {
+ newMask |= 2;
+ }
+ }
+ if (adY >= isq3) {
+ // Up or down
+ if (dY < 0) {
+ newMask |= 1;
+ } else {
+ newMask |= 4;
+ }
+
+ }
+ }
+
+ if (sqNorm > FUNC_SVG_SQ_SCALED(ARROW_SUPER)) {
+ newMask |= 16;
+ }
+
+ if (mask != newMask) {
+ maskToLeftButtons(mask, newMask);
+ }
+
+ mask = newMask;
+}
+
+void TouchControls::FunctionLeft::draw(uint8 alpha) {
+ // Draw background
+ {
+ Common::Rect clip(0, 0, BG_WIDTH, BG_HEIGHT);
+ parent->drawSurface(alpha, startX, startY, -clip.width() / 2, -clip.height() / 2, clip);
+ }
+
+ if (mask == 0) {
+ return;
+ }
+
+
+ // Width and height here are rotated for left/right
+ uint16 width = ARROW_WIDTH;
+ uint16 height;
+ if (mask & 16) {
+ height = ARROW_HEIGHT;
+ } else {
+ height = ARROW_SEMI_HEIGHT;
+ }
+
+ // We can draw multiple arrows
+ if (mask & 1) {
+ // Draw UP
+ Common::Rect clip(width, height);
+ clip.translate(0, ARROW_HL_TOP + ARROW_HEIGHT - height);
+ int16 offX = -1, offY = -2;
+ parent->drawSurface(ALPHA_OPAQUE, startX, startY, offX * clip.width() / 2, offY * clip.height() / 2, clip);
+ }
+ if (mask & 2) {
+ // Draw RIGHT
+ Common::Rect clip(height, width);
+ clip.translate(ARROW_WIDTH, ARROW_HL_TOP);
+ int16 offX = 0, offY = -1;
+ parent->drawSurface(ALPHA_OPAQUE, startX, startY, offX * clip.width() / 2, offY * clip.height() / 2, clip);
+ }
+ if (mask & 4) {
+ // Draw DOWN
+ Common::Rect clip(width, height);
+ clip.translate(0, ARROW_HL_TOP + ARROW_HEIGHT);
+ int16 offX = -1, offY = 0;
+ parent->drawSurface(ALPHA_OPAQUE, startX, startY, offX * clip.width() / 2, offY * clip.height() / 2, clip);
+ }
+ if (mask & 8) {
+ // Draw LEFT
+ Common::Rect clip(height, width);
+ clip.translate(ARROW_WIDTH + ARROW_WIDTH - height, ARROW_HL_TOP + ARROW_HEIGHT);
+ int16 offX = -2, offY = -1;
+ parent->drawSurface(ALPHA_OPAQUE, startX, startY, offX * clip.width() / 2, offY * clip.height() / 2, clip);
+ }
+}
+
+bool TouchControls::FunctionRight::isInside(int x, int y) {
+ int dX = x - startX;
+ int dY = y - startY;
+ // norm 2 squared (to avoid square root)
+ unsigned int sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ return sqNorm <= FUNC_SVG_SQ_SCALED(BUTTON_END);
+}
+
+void TouchControls::FunctionRight::touch(int dX, int dY, Action action) {
+ if (action == JACTION_CANCEL) {
+ return;
+ }
+
+ // norm 2 squared (to avoid square root)
+ unsigned int sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ if (sqNorm >= FUNC_SVG_SQ_SCALED(BUTTON_DEAD) && sqNorm <= FUNC_SVG_SQ_SCALED(BUTTON_END)) {
+ // We are far enough from the center
+ // For right we use angles -45,45 as a sensitive zone
+ // For left it's the same but mirrored in negative
+ // We must be between the two lines which are of tan(45),tan(-45) and this corrsponds to 1
+ // For up down we use angles -45,45 as a sensitive zone
+ // We must be outside the two lines which are of tan(45),tan(-45) and this corrsponds to 1
+ unsigned int adX = abs(dX);
+ unsigned int adY = abs(dY);
+
+ if (adY <= adX) {
+ // X or B
+ if (dX < 0) {
+ button = 4;
+ } else {
+ button = 2;
+ }
+ } else {
+ // Y or A
+ if (dY < 0) {
+ button = 1;
+ } else {
+ button = 3;
+ }
+
+ }
+ } else {
+ button = 0;
+ }
+
+ static const Common::JoystickButton buttons[] = {
+ Common::JOYSTICK_BUTTON_Y, Common::JOYSTICK_BUTTON_B,
+ Common::JOYSTICK_BUTTON_A, Common::JOYSTICK_BUTTON_X
+ };
+ static const Common::JoystickButton modifiers[] = {
+ Common::JOYSTICK_BUTTON_INVALID, Common::JOYSTICK_BUTTON_INVALID,
+ Common::JOYSTICK_BUTTON_INVALID, Common::JOYSTICK_BUTTON_INVALID
+ };
+ if (action == JACTION_UP && button) {
+ buttonDown(modifiers[button - 1]);
+ buttonPress(buttons[button - 1]);
+ buttonUp(modifiers[button - 1]);
+ button = 0;
+ }
+}
+
+void TouchControls::FunctionRight::draw(uint8 alpha) {
+ // Draw background
+ {
+ Common::Rect clip(BG_WIDTH, 0, 2 * BG_WIDTH, BG_HEIGHT);
+ parent->drawSurface(alpha, startX, startY, -clip.width() / 2, -clip.height() / 2, clip);
+ }
+
+ if (button == 0) {
+ return;
+ }
+
+
+ Common::Rect clip(BUTTON_WIDTH, BUTTON_HEIGHT);
+
+ int16 offX, offY;
+
+ if (button == 1) {
+ // Draw Y
+ clip.translate(BUTTON_HL_LEFT, BUTTON_HL_TOP);
+ offX = -1;
+ offY = -2;
+ } else if (button == 2) {
+ // Draw B
+ clip.translate(BUTTON_HL_LEFT + BUTTON_WIDTH, BUTTON_HL_TOP);
+ offX = 0;
+ offY = -1;
+ } else if (button == 3) {
+ // Draw A
+ clip.translate(BUTTON_HL_LEFT, BUTTON_HL_TOP + BUTTON_HEIGHT);
+ offX = -1;
+ offY = 0;
+ } else if (button == 4) {
+ // Draw X
+ clip.translate(BUTTON_HL_LEFT + BUTTON_WIDTH, BUTTON_HL_TOP + BUTTON_HEIGHT);
+ offX = -2;
+ offY = -1;
+ } else {
+ return;
+ }
+ parent->drawSurface(ALPHA_OPAQUE, startX, startY, offX * clip.width() / 2, offY * clip.height() / 2, clip);
+}
+
+bool TouchControls::FunctionCenter::isInside(int x, int y) {
+ int dX = x - startX;
+ int dY = y - startY;
+ // norm 2 squared (to avoid square root)
+ unsigned int sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ return sqNorm <= FUNC_SVG_SQ_SCALED(BUTTON_END);
+}
+
+void TouchControls::FunctionCenter::touch(int dX, int dY, Action action) {
+ if (action == JACTION_CANCEL) {
+ return;
+ }
+
+ // norm 2 squared (to avoid square root)
+ unsigned int sqNorm = (unsigned int)(dX * dX) + (unsigned int)(dY * dY);
+
+ if (sqNorm >= FUNC_SVG_SQ_SCALED(BUTTON_DEAD) && sqNorm <= FUNC_SVG_SQ_SCALED(BUTTON_END)) {
+ // We are far enough from the center
+ // For right we use angles -45,45 as a sensitive zone
+ // For left it's the same but mirrored in negative
+ // We must be between the two lines which are of tan(45),tan(-45) and this corrsponds to 1
+ // For up down we use angles -45,45 as a sensitive zone
+ // We must be outside the two lines which are of tan(45),tan(-45) and this corrsponds to 1
+ unsigned int adX = abs(dX);
+ unsigned int adY = abs(dY);
+
+ if (adY <= adX) {
+ // X or B
+ if (dX < 0) {
+ button = 4;
+ } else {
+ button = 2;
+ }
+ } else {
+ // Y or A
+ if (dY < 0) {
+ button = 1;
+ } else {
+ button = 3;
+ }
+
+ }
+ } else {
+ button = 0;
+ }
+
+ static const Common::JoystickButton buttons[] = {
+ Common::JOYSTICK_BUTTON_GUIDE, Common::JOYSTICK_BUTTON_RIGHT_STICK,
+ Common::JOYSTICK_BUTTON_START, Common::JOYSTICK_BUTTON_LEFT_STICK
+ };
+ static const Common::JoystickButton modifiers[] = {
+ Common::JOYSTICK_BUTTON_INVALID, Common::JOYSTICK_BUTTON_INVALID,
+ Common::JOYSTICK_BUTTON_INVALID, Common::JOYSTICK_BUTTON_INVALID
+ };
+ if (action == JACTION_UP && button) {
+ buttonDown(modifiers[button - 1]);
+ buttonPress(buttons[button - 1]);
+ buttonUp(modifiers[button - 1]);
+ button = 0;
+ }
+}
+
+void TouchControls::FunctionCenter::draw(uint8 alpha) {
+ // Draw background
+ {
+ Common::Rect clip(BG_WIDTH * 2, 0, 3 * BG_WIDTH, BG_HEIGHT);
+ parent->drawSurface(alpha, startX, startY, -clip.width() / 2, -clip.height() / 2, clip);
+ }
+
+ if (button == 0) {
+ return;
+ }
+
+ Common::Rect clip(BUTTON_WIDTH, BUTTON_HEIGHT);
+
+ int16 offX, offY;
+
+ if (button == 1) {
+ // Draw Y
+ clip.translate(BUTTON2_HL_LEFT, BUTTON2_HL_TOP);
+ offX = -1;
+ offY = -2;
+ } else if (button == 2) {
+ // Draw B
+ clip.translate(BUTTON2_HL_LEFT + BUTTON_WIDTH, BUTTON2_HL_TOP);
+ offX = 0;
+ offY = -1;
+ } else if (button == 3) {
+ // Draw A
+ clip.translate(BUTTON2_HL_LEFT, BUTTON2_HL_TOP + BUTTON_HEIGHT);
+ offX = -1;
+ offY = 0;
+ } else if (button == 4) {
+ // Draw X
+ clip.translate(BUTTON2_HL_LEFT + BUTTON_WIDTH, BUTTON2_HL_TOP + BUTTON_HEIGHT);
+ offX = -2;
+ offY = -1;
+ } else {
+ return;
+ }
+ // Always draw overlay as opaque
+ parent->drawSurface(ALPHA_OPAQUE, startX, startY, offX * clip.width() / 2, offY * clip.height() / 2, clip);
+}
diff --git a/backends/platform/android/touchcontrols.h b/backends/platform/android/touchcontrols.h
index 09811a7c8af..01d10b8c12f 100644
--- a/backends/platform/android/touchcontrols.h
+++ b/backends/platform/android/touchcontrols.h
@@ -24,10 +24,15 @@
#include "common/events.h"
+namespace Graphics {
+class ManagedSurface;
+}
+
class TouchControlsDrawer {
public:
+ virtual void touchControlInitSurface(const Graphics::ManagedSurface &surf) = 0;
virtual void touchControlNotifyChanged() = 0;
- virtual void touchControlDraw(int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) = 0;
+ virtual void touchControlDraw(uint8 alpha, int16 x, int16 y, int16 w, int16 h, const Common::Rect &clip) = 0;
protected:
~TouchControlsDrawer() {}
@@ -44,81 +49,126 @@ public:
};
TouchControls();
+ ~TouchControls();
- void init(TouchControlsDrawer *drawer, int width, int height);
+ void init(float scale);
+ void setDrawer(TouchControlsDrawer *drawer, int width, int height);
+ void beforeDraw();
void draw();
void update(Action action, int ptr, int x, int y);
private:
TouchControlsDrawer *_drawer;
- int _screen_width, _screen_height;
+ unsigned int _screen_width, _screen_height;
+ unsigned int _scale, _scale2;
- enum Function {
- kFunctionNone = -1,
- kFunctionJoystick = 0,
- kFunctionCenter = 1,
- kFunctionRight = 2,
- kFunctionMax = 2
+ Graphics::ManagedSurface *_svg;
+
+ unsigned int _zombieCount;
+
+ enum State {
+ kFunctionInactive = 0,
+ kFunctionActive = 1,
+ kFunctionZombie = 2
};
- Function getFunction(int x, int y);
- struct Pointer {
- Pointer() : id(-1), startX(-1), startY(-1),
+ struct Function {
+ virtual bool isInside(int, int) = 0;
+ virtual void touch(int, int, Action) = 0;
+ virtual void draw(uint8 alpha) = 0;
+ virtual void resetState() {}
+
+ Function(const TouchControls *parent_) :
+ parent(parent_), pointerId(-1),
+ startX(-1), startY(-1),
currentX(-1), currentY(-1),
- function(kFunctionNone), active(false) {}
+ lastActivable(0), status(kFunctionInactive) {}
+ virtual ~Function() {}
+
void reset() {
- id = -1;
+ pointerId = -1;
startX = startY = currentX = currentY = -1;
- function = kFunctionNone;
- active = false;
+ lastActivable = 0;
+ status = kFunctionInactive;
+ resetState();
}
- int id;
+ const TouchControls *parent;
+
+ int pointerId;
uint16 startX, startY;
uint16 currentX, currentY;
- Function function;
- bool active;
+ uint32 lastActivable;
+ State status;
};
+ Function *getFunctionFromPointerId(int ptr);
+ Function *getZombieFunctionFromPos(int x, int y);
- enum { kNumPointers = 5 };
- Pointer _pointers[kNumPointers];
+ enum FunctionId {
+ kFunctionNone = -1,
+ kFunctionLeft = 0,
+ kFunctionRight = 1,
+ kFunctionCenter = 2,
+ kFunctionCount = 3
+ };
+ FunctionId getFunctionId(int x, int y);
+
+ Function *_functions[kFunctionCount];
+
+ static void buttonDown(Common::JoystickButton jb);
+ static void buttonUp(Common::JoystickButton jb);
+ static void buttonPress(Common::JoystickButton jb);
+
+ /**
+ * Draws a part of the joystick surface on the screen
+ *
+ * @param x The left coordinate in fixed-point screen pixels
+ * @param y The top coordinate in fixed-point screen pixels
+ * @param offX The left offset in SVG pixels
+ * @param offY The top offset in SVG pixels
+ * @param clip The clipping rectangle in source surface in SVG pixels
+ */
+ void drawSurface(uint8 alpha, int x, int y, int offX, int offY, const Common::Rect &clip) const;
+
+
+ // Functions implementations
+ struct FunctionLeft : Function {
+ FunctionLeft(const TouchControls *parent) :
+ Function(parent), mask(0) {}
+ void resetState() override { mask = 0; }
+
+ bool isInside(int, int) override;
+ void touch(int, int, Action) override;
+ void draw(uint8 alpha) override;
+
+ uint32 mask;
+ void maskToLeftButtons(uint32 oldMask, uint32 newMask);
+ };
- Pointer *getPointerFromId(int ptr, bool createNotFound);
- Pointer *findPointerFromFunction(Function function);
+ struct FunctionRight : Function {
+ FunctionRight(const TouchControls *parent) :
+ Function(parent), button(0) {}
+ void resetState() override { button = 0; }
- struct FunctionState {
- FunctionState() : main(Common::JOYSTICK_BUTTON_INVALID),
- modifier(Common::JOYSTICK_BUTTON_INVALID) {}
- void reset() {
- main = Common::JOYSTICK_BUTTON_INVALID;
- modifier = Common::JOYSTICK_BUTTON_INVALID;
- clip = Common::Rect();
- }
+ bool isInside(int, int) override;
+ void touch(int, int, Action) override;
+ void draw(uint8 alpha) override;
- Common::JoystickButton main;
- Common::JoystickButton modifier;
- Common::Rect clip;
+ uint32 button;
};
- FunctionState _functionStates[kFunctionMax + 1];
+ struct FunctionCenter : Function {
+ FunctionCenter(const TouchControls *parent) :
+ Function(parent), button(0) {}
+ void resetState() override { button = 0; }
- void buttonDown(Common::JoystickButton jb);
- void buttonUp(Common::JoystickButton jb);
- void buttonPress(Common::JoystickButton jb);
+ bool isInside(int, int) override;
+ void touch(int, int, Action) override;
+ void draw(uint8 alpha) override;
- /* Functions implementations */
- struct FunctionBehavior {
- void (*touchToState)(int, int, TouchControls::FunctionState &);
- bool pressOnRelease;
- float xRatio;
- float yRatio;
+ uint32 button;
};
- static FunctionBehavior functionBehaviors[TouchControls::kFunctionMax + 1];
-
- static void touchToJoystickState(int dX, int dY, FunctionState &state);
- static void touchToCenterState(int dX, int dY, FunctionState &state);
- static void touchToRightState(int dX, int dY, FunctionState &state);
};
#endif
diff --git a/dists/android/gamepad.svg b/dists/android/gamepad.svg
new file mode 100644
index 00000000000..6595e5526d5
--- /dev/null
+++ b/dists/android/gamepad.svg
@@ -0,0 +1,90 @@
+<svg width="384" height="256" version="1.1" viewBox="0 0 384 256" xmlns="http://www.w3.org/2000/svg">
+ <g transform="translate(64,64)">
+ <path d="m62 0a62 62 0 0 1-62 62 62 62 0 0 1-62-62 62 62 0 0 1 62-62 62 62 0 0 1 62 62z" fill="#808080" fill-opacity=".4"/>
+ <g fill-opacity=".4" fill-rule="evenodd" stroke="#808080" stroke-opacity=".8">
+ <path d="m-18-48h36v27l-18 18-18-18z"/>
+ <path d="m48-18v36h-27l-18-18 18-18z"/>
+ <path d="m18 48h-36v-27l18-18 18 18z"/>
+ <path d="m-48 18v-36h27l18 18-18 18z"/>
+ </g>
+ <g fill="none" fill-rule="evenodd" stroke="#808080" stroke-opacity=".2">
+ <path d="m-16-56 16-4 16 4"/>
+ <path d="m56-16 4 16-4 16"/>
+ <path d="m16 56-16 4-16-4"/>
+ <path d="m-56 16-4-16 4-16"/>
+ </g>
+ </g>
+ <g transform="translate(32,160)" fill="#fff" fill-rule="evenodd" stroke="#808080">
+ <path d="m-18-16h36v27l-18 18-18-18z"/>
+ <path d="m80-18v36h-27l-18-18 18-18z"/>
+ <path d="m18 80h-36v-27l18-18 18 18z"/>
+ <path d="m48 82v-36h27l18 18-18 18z"/>
+ </g>
+ <g transform="translate(32,160)" fill="none" fill-rule="evenodd" stroke="#fff" stroke-width="2">
+ <path d="m-16-24 16-4 16 4"/>
+ <path d="m88-16 4 16-4 16"/>
+ <path d="m16 88-16 4-16-4"/>
+ <path d="m40 80-4-16 4-16"/>
+ </g>
+ <g transform="translate(192,64)">
+ <path d="m62 0a62 62 0 0 1-62 62 62 62 0 0 1-62-62 62 62 0 0 1 62-62 62 62 0 0 1 62 62z" fill="#808080" fill-opacity=".4"/>
+ <g transform="translate(0,-36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m3.3496 3.0547v11.945h-6.3164v-11.945l-9.9688-17.037h6.6387l6.4453 12.16 6.5312-12.16h6.6387z" fill="#ffd733" aria-label="Y"/>
+ </g>
+ <g transform="translate(36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m12.48 6.7285q0 3.8887-2.6641 6.0801-2.6641 2.1914-7.3691 2.1914h-12.504v-28.982h11.279q4.6836 0 7.0898 1.8906 2.4277 1.8691 2.4277 5.3926 0 2.4707-1.418 4.168-1.418 1.6758-4.3613 2.2988 3.6738 0.38672 5.5859 2.1914 1.9336 1.8047 1.9336 4.7695zm-8.1211-12.568q0-3.4375-3.5879-3.4375h-4.4902v6.8535h4.5332q3.5449 0 3.5449-3.416zm1.7402 12.031q0-1.8906-1.1387-2.9004-1.1387-1.0312-3.3086-1.0312h-5.3711v8.0352h5.5215q4.2969 0 4.2969-4.1035z" fill="#f33" aria-label="B"/>
+ </g>
+ <g transform="translate(0,36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m13.404 15h-6.3164l-1.9766-7.0898h-9.7969l-1.9766 7.0898h-6.3379l9.5176-28.982h7.3906zm-13.191-25.072q-0.17188 0.90234-0.64453 2.707-0.45117 1.7832-2.9219 10.506h7.1328q-2.5137-8.8516-2.9648-10.592-0.42969-1.7402-0.60156-2.6211z" fill="#3f3" aria-label="A"/>
+ </g>
+ <g transform="translate(-36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m6.7441 15-6.5098-10.592-6.5312 10.592h-6.7031l9.8613-15.275-8.9805-13.707h6.7031l5.6504 9.2383 5.6289-9.2383h6.6602l-9.1953 13.707 10.076 15.275z" fill="#33f" aria-label="X"/>
+ </g>
+ </g>
+ <g transform="translate(160,160)" stroke="#808080">
+ <path d="m24-4a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill="#ffd733"/>
+ <path d="m92 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill="#f33"/>
+ <path d="m24 68a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill="#3f3"/>
+ <path d="m84 64a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill="#33f"/>
+ </g>
+ <g transform="translate(320,64)">
+ <path d="m62 0a62 62 0 0 1-62 62 62 62 0 0 1-62-62 62 62 0 0 1 62-62 62 62 0 0 1 62 62z" fill="#808080" fill-opacity=".4"/>
+ <g transform="translate(0,-36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <g transform="translate(-16,-20)" stroke-width=".080658">
+ <path d="m29.088 19.268c1.9849 2.3662 2.7235 6.6713 2.4405 8.9046-0.42208 3.3316-1.9359 6.5163-4.4324 8.3948-2.5977 1.9549-5.7752 3.1121-9.741 3.3791-5.9662 0.40054-10.606-1.4999-13.691-4.3769-1.5813-1.4765-4.0645-3.5962-3.0067-6.6043 0.54452-1.5481 2.5441-3.4614 3.8371-4.7854 0.46435-0.47556 1.3787-1.3564 1.279-1.9332-0.22891-0.97692-1.7271-2.4455-2.3604-3.4873-1.8986-2.9765-2.3227-7.4998-0.65171-11.259 1.9924-4.4807 7.0057-7.3285 13.861-7.0827 2.838 0.10179 6.2954 1.0368 8.6715 2.4034 3.3942 1.9507 6.1567 4.2012 5.8079 6.6797-0.21439 1.5229-1.346 3.2141-2.6848 4.8449-0.83037 1.0115-1.216 1.6294-1.1682 2.2229 0.04065 0.5037 1.2677 2.0168 1.8397 2.6986" opacity=".3"/>
+ <path d="m28.685 18.865c1.9849 2.3662 2.7235 6.6713 2.4405 8.9046-0.42208 3.3316-1.9359 6.5163-4.4325 8.3948-2.5977 1.9549-5.7752 3.1121-9.741 3.3791-5.9662 0.40054-10.606-1.4999-13.691-4.3769-1.5813-1.4765-4.0645-3.5962-3.0067-6.6043 0.54452-1.5481 2.5441-3.4614 3.8371-4.7854 0.46434-0.47556 1.3787-1.3564 1.279-1.9332-0.22891-0.97692-1.7271-2.4455-2.3604-3.4873-1.8986-2.9765-2.3227-7.4998-0.65171-11.259 1.9924-4.4807 7.0057-7.3285 13.861-7.0827 2.838 0.10179 6.2954 1.0368 8.6715 2.4034 3.3942 1.9507 6.1567 4.2012 5.8079 6.6797-0.21439 1.5229-1.346 3.2141-2.6848 4.8449-0.83037 1.0115-1.216 1.6294-1.1682 2.2229 0.04065 0.50371 1.2677 2.0168 1.8397 2.6986" fill="#333"/>
+ <g fill="#00721d">
+ <path d="m27.778 8.29-2.7389 4.149v0.0062c-0.04291 0.06107-0.10136 0.12276-0.15207 0.16752-0.14412 0.12258-0.32261 0.1914-0.50195 0.1914-0.11486 0-0.2382-0.03322-0.34813-0.08605v0.0036c-0.06298-0.03098-0.12371-0.07051-0.18103-0.11943-0.03436-0.02928-0.68677-0.58475-1.4468-1.0681-0.71729-0.45475-1.4261-0.86126-2.1151-1.1924l-1.61e-4 0.01155c-0.03923-0.01889-0.07858-0.03746-0.11774-0.056-0.7289-0.34166-1.4096-0.59904-2.0434-0.76987-0.63373-0.17091-1.1877-0.25736-1.6625-0.25736-0.48643 0-0.86336 0.10201-1.1305 0.30576v-0.011706l-0.01598 0.00902c-0.02299 0.020245-0.09185 0.095192-0.11298 0.11721-0.26932 0.2802-0.40351 0.63779-0.40351 1.0739v1.4005c0-0.43603 0.13421-0.79354 0.40351-1.0737 0.02113-0.02202 0.1063-0.09001 0.12929-0.11025v-0.0046c0.26711-0.20359 0.64394-0.30561 1.1302-0.30561 0.47483 0 1.0288 0.08653 1.6625 0.25736 0.63373 0.17083 1.3145 0.42829 2.0434 0.77004 0.03554 0.01668 0.07121 0.03382 0.10682 0.05093v0.0043c0.68898 0.3311 1.4088 0.72664 2.1261 1.1815 0.76004 0.48322 1.4125 1.0389 1.4468 1.0681 0.05712 0.04875 0.11764 0.08817 0.1804 0.11912v0.0016c0.10994 0.05291 0.23391 0.08115 0.34876 0.08115 0.17752 0 0.35166-0.06213 0.49483-0.18191v0.0046c0.05493-0.04847 0.13898-0.13768 0.18246-0.20358l-1.61e-4 -0.01234 2.7157-4.1208z"/>
+ <path d="m17.754 26.593v1.4005c0-0.59122-0.32594-1.1509-0.97418-1.6807-0.6497-0.52903-1.4576-1.0809-2.4238-1.6578-0.9662-0.57549-2.0116-1.1974-3.136-1.868-1.1281-0.67204-2.1799-1.4619-3.1352-2.357-0.96692-0.90215-1.7741-1.938-2.4238-3.1047-0.64897-1.1674-0.97346-2.5135-0.97346-4.0391v-1.4006c0 1.5256 0.32448 2.8716 0.97346 4.0391 0.64978 1.1667 1.4569 2.2026 2.4238 3.1048 0.95531 0.89498 2.0072 1.6849 3.1352 2.3569 1.1244 0.67067 2.1698 1.2926 3.136 1.868 0.9662 0.57694 1.7741 1.1289 2.4238 1.6578 0.64824 0.52968 0.97418 1.0895 0.97418 1.6807"/>
+ <path d="m27.969 25.612c0 1.6807-0.35639 3.1362-1.0693 4.365-0.5254 0.90683-1.1667 1.6945-1.9242 2.3641v1.4004c0.75754-0.66946 1.3988-1.4569 1.9242-2.3637 0.71285-1.2289 1.0693-2.6843 1.0693-4.365zm-24.755 3.3622v1.4005c1.584 2.1476 3.3654 3.7511 5.3449 4.8092 0.11731 0.06258 0.23654 0.12295 0.35715 0.18191l-6.453e-4 0.03069c1.9013 0.91345 4.2758 1.3743 6.9847 1.3743 1.4562 0 2.9058-0.19509 4.3475-0.58338 1.4401-0.38958 2.7323-0.9808 3.8719-1.7743 0.29988-0.20882 0.58502-0.43274 0.85529-0.67164v-1.4005c-0.27028 0.23891-0.55542 0.46274-0.85529 0.67164-1.1396 0.79351-2.4317 1.3846-3.8719 1.7743-1.4417 0.3882-2.8913 0.58338-4.3475 0.58338-2.7089 0-5.057-0.50562-6.9582-1.4191l-4.84e-4 0.02642c-0.12932-0.062824-0.25693-0.12736-0.38247-0.19441-1.9796-1.058-3.761-2.6616-5.3449-4.809z"/>
+ </g>
+ <path d="m15.235 2.827c2.5335 0 4.8063 0.45967 6.8178 1.3776 2.0115 0.91861 3.92 2.2805 5.7253 4.0849l-2.7331 4.1463c-0.15898 0.24133-0.40611 0.36772-0.65994 0.36772-0.18309 0-0.36973-0.06582-0.52911-0.2018-0.03436-0.02928-0.68672-0.58477-1.4468-1.0681-0.76012-0.48185-1.5048-0.8936-2.2329-1.2368-0.72882-0.34167-1.4097-0.59904-2.0435-0.76988-0.63372-0.17091-1.1876-0.25738-1.6624-0.25738-0.57049 0-0.9908 0.1401-1.2594 0.42031-0.26932 0.28028-0.40361 0.63776-0.40361 1.0738 0 0.55976 0.32448 1.0817 0.9741 1.5642 0.64824 0.48257 1.4643 0.98007 2.4463 1.4941 1.0628 0.55831 2.1169 1.1339 3.16 1.7272 1.1237 0.6384 2.1769 1.409 3.1592 2.3112 0.98217 0.90361 1.7981 1.9852 2.4478 3.2456 0.64897 1.2604 0.97418 2.7624 0.97418 4.5052 0 1.6807-0.35643 3.1363-1.0693 4.3651-0.71285 1.2303-1.6391 2.2412-2.7795 3.0355-1.1397 0.79351-2.4318 1.3847-3.872 1.7744-1.4417 0.38812-2.8913 0.58332-4.3475 0.58332-2.9146 0-5.3616-0.52976-7.3412-1.587-1.9796-1.0581-3.761-2.6615-5.3449-4.809l3.3755-4.1964c0.16164-0.2006 0.38127-0.30174 0.60211-0.30174 0.20648 0 0.41401 0.08848 0.57606 0.26673 0.019358 0.02129 0.38764 0.42603 1.0533 0.94006 0.67365 0.51968 1.3981 0.97289 2.1618 1.3533 0.76294 0.38466 1.5571 0.70487 2.3752 0.95724 0.80795 0.2502 1.56 0.37385 2.2569 0.37385 0.69616 0 1.2275-0.12437 1.5919-0.37385 0.36368-0.24883 0.54654-0.71559 0.54654-1.4005 0-0.59122-0.32594-1.151-0.97418-1.6807-0.64962-0.52895-1.4576-1.0809-2.4238-1.6578-0.9662-0.57541-2.0115-1.1974-3.136-1.868-1.1281-0.67196-2.1799-1.4619-3.1352-2.3569-0.96692-0.90223-1.7741-1.9381-2.4238-3.1048-0.64897-1.1674-0.97346-2.5135-0.97346-4.0391 0-1.4942 0.28456-2.801 0.85513-3.9212 0.56985-1.121 1.3379-2.0618 2.3041-2.8253 0.9662-0.76205 2.0827-1.3375 3.3494-1.7271 1.2667-0.38893 2.6133-0.58412 4.039-0.58412" fill="#00c832"/>
+ </g>
+ </g>
+ <g transform="translate(36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m-6.0156 10.5-4.5312-8.0469h-2.7031v8.0469h-4.6094v-21.078h7.7188q4.125 0 6.1719 1.6094t2.0469 4.6406q0 2.1406-1.1562 3.6406t-3.2031 2.1094l5.4531 9.0781zm-0.54688-14.641q0-1.4688-0.96875-2.1562-0.96875-0.70312-3.0938-0.70312h-2.625v5.875h2.75q3.9375 0 3.9375-3.0156zm23.969 8.5312q0 3-2.2969 4.7188-2.2812 1.7031-6.3594 1.7031-3.6719 0-5.9844-1.5469t-2.9688-4.5l4.4531-0.57812q0.35938 1.4688 1.5 2.3281 1.1406 0.84375 3.125 0.84375 2.0625 0 3.0469-0.65625 1-0.65625 1-2.0469 0-1.0312-0.875-1.7344-0.875-0.71875-2.5469-1.125-3.3594-0.8125-4.75-1.4062-1.375-0.60938-2.2031-1.3594t-1.2812-1.75q-0.4375-1.0156-0.4375-2.2656 0-2.7344 2.1875-4.3281 2.2031-1.5938 5.7969-1.5938 3.5 0 5.4688 1.3906 1.9688 1.375 2.5469 4.2812l-4.4688 0.45312q-0.6875-2.8594-3.6406-2.8594-1.6562 0-2.5312 0.625-0.875 0.60938-0.875 1.7656 0 0.76562 0.42188 1.2812 0.42188 0.5 1.1406 0.85938 0.73438 0.34375 2.9688 0.92188 2.9375 0.71875 4.5156 1.6094 1.5781 0.875 2.3125 2.125t0.73438 2.8438z" fill="#c0c0c0" aria-label="RS"/>
+ </g>
+ <g transform="translate(0,36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m-11.812-0.1875c0.32476-20.46 0.32476-20.46 17.881-9.9487 17.556 10.511 17.556 10.511-0.32476 20.46-17.881 9.9487-17.881 9.9487-17.556-10.511z" fill="#c0c0c0" fill-rule="evenodd"/>
+ </g>
+ <g transform="translate(-36)">
+ <path d="m24 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill-opacity=".4" stroke="#808080" stroke-opacity=".8"/>
+ <path d="m-16.703 10.5v-21.078h4.6094v17.516h9.625v3.5625zm34.109-6.1094q0 3-2.2969 4.7188-2.2812 1.7031-6.3594 1.7031-3.6719 0-5.9844-1.5469t-2.9688-4.5l4.4531-0.57812q0.35938 1.4688 1.5 2.3281 1.1406 0.84375 3.125 0.84375 2.0625 0 3.0469-0.65625 1-0.65625 1-2.0469 0-1.0312-0.875-1.7344-0.875-0.71875-2.5469-1.125-3.3594-0.8125-4.75-1.4062-1.375-0.60938-2.2031-1.3594t-1.2812-1.75q-0.4375-1.0156-0.4375-2.2656 0-2.7344 2.1875-4.3281 2.2031-1.5938 5.7969-1.5938 3.5 0 5.4688 1.3906 1.9688 1.375 2.5469 4.2812l-4.4688 0.45312q-0.6875-2.8594-3.6406-2.8594-1.6562 0-2.5312 0.625-0.875 0.60938-0.875 1.7656 0 0.76562 0.42188 1.2812 0.42188 0.5 1.1406 0.85938 0.73438 0.34375 2.9688 0.92188 2.9375 0.71875 4.5156 1.6094 1.5781 0.875 2.3125 2.125t0.73438 2.8438z" fill="#c0c0c0" aria-label="LS"/>
+ </g>
+ </g>
+ <g transform="translate(288,160)" stroke="#808080">
+ <path d="m24-4a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z" fill="#00c832"/>
+ <g fill="#c0c0c0">
+ <path d="m92 0a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z"/>
+ <path d="m24 68a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z"/>
+ <path d="m84 64a24 24 0 0 1-24 24 24 24 0 0 1-24-24 24 24 0 0 1 24-24 24 24 0 0 1 24 24z"/>
+ </g>
+ </g>
+</svg>
diff --git a/dists/android/res/drawable/touch_arrows.png b/dists/android/res/drawable/touch_arrows.png
deleted file mode 100644
index 816dabfd998..00000000000
Binary files a/dists/android/res/drawable/touch_arrows.png and /dev/null differ
More information about the Scummvm-git-logs
mailing list