From 62880f0fd6f8d7a68b8deb09a63a41927fe0229c Mon Sep 17 00:00:00 2001 From: gabriel Date: Sat, 25 Nov 2023 17:51:17 -0400 Subject: [PATCH] [Android]Add input --- .github/workflows/Android_Build.yml | 89 +++++++++++ patch.patch | 118 ++++++++++++++ src/jni_driver.cpp | 30 +++- src/pandroid/app/src/main/AndroidManifest.xml | 16 +- .../com/panda3ds/pandroid/AlberDriver.java | 6 + .../panda3ds/pandroid/PandaGlSurfaceView.java | 19 --- .../panda3ds/pandroid/app/BaseActivity.java | 6 + .../panda3ds/pandroid/app/GameActivity.java | 56 +++++++ .../pandroid/{ => app}/MainActivity.java | 49 +++--- .../com/panda3ds/pandroid/math/Vector2.java | 23 +++ .../panda3ds/pandroid/utils/Constants.java | 23 +++ .../pandroid/{ => utils}/PathUtils.java | 2 +- .../pandroid/{ => view}/PandaGlRenderer.java | 54 +++++-- .../pandroid/view/PandaGlSurfaceView.java | 20 +++ .../pandroid/view/PandaLayoutController.java | 62 ++++++++ .../view/controller/ControllerLayout.java | 146 ++++++++++++++++++ .../view/controller/ControllerNode.java | 24 +++ .../pandroid/view/controller/TouchEvent.java | 28 ++++ .../listeners/ButtonStateListener.java | 7 + .../listeners/JoystickListener.java | 7 + .../controller/nodes/BasicControllerNode.java | 24 +++ .../view/controller/nodes/Button.java | 73 +++++++++ .../view/controller/nodes/Joystick.java | 132 ++++++++++++++++ .../res/drawable/screen_gamepad_checkbox.xml | 5 + .../main/res/drawable/screen_gamepad_hide.xml | 9 ++ .../main/res/drawable/screen_gamepad_show.xml | 5 + .../res/drawable/simple_analog_background.xml | 19 +++ .../main/res/drawable/simple_card_button.xml | 12 ++ .../res/drawable/simple_card_button_left.xml | 12 ++ .../res/drawable/simple_card_button_right.xml | 12 ++ .../res/drawable/simple_circle_button.xml | 10 ++ .../app/src/main/res/layout/activity_main.xml | 26 ++-- .../src/main/res/layout/controller_dpad.xml | 28 ++++ .../main/res/layout/controller_gamepad.xml | 32 ++++ .../app/src/main/res/layout/game_activity.xml | 127 +++++++++++++++ .../app/src/main/res/values/strings.xml | 1 + .../app/src/main/res/values/styleable.xml | 10 ++ 37 files changed, 1245 insertions(+), 77 deletions(-) create mode 100644 .github/workflows/Android_Build.yml create mode 100644 patch.patch delete mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlSurfaceView.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/BaseActivity.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java rename src/pandroid/app/src/main/java/com/panda3ds/pandroid/{ => app}/MainActivity.java (50%) create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/math/Vector2.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/Constants.java rename src/pandroid/app/src/main/java/com/panda3ds/pandroid/{ => utils}/PathUtils.java (99%) rename src/pandroid/app/src/main/java/com/panda3ds/pandroid/{ => view}/PandaGlRenderer.java (57%) create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlSurfaceView.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaLayoutController.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerLayout.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerNode.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/TouchEvent.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/ButtonStateListener.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/JoystickListener.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/BasicControllerNode.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Button.java create mode 100644 src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Joystick.java create mode 100644 src/pandroid/app/src/main/res/drawable/screen_gamepad_checkbox.xml create mode 100644 src/pandroid/app/src/main/res/drawable/screen_gamepad_hide.xml create mode 100644 src/pandroid/app/src/main/res/drawable/screen_gamepad_show.xml create mode 100644 src/pandroid/app/src/main/res/drawable/simple_analog_background.xml create mode 100644 src/pandroid/app/src/main/res/drawable/simple_card_button.xml create mode 100644 src/pandroid/app/src/main/res/drawable/simple_card_button_left.xml create mode 100644 src/pandroid/app/src/main/res/drawable/simple_card_button_right.xml create mode 100644 src/pandroid/app/src/main/res/drawable/simple_circle_button.xml create mode 100644 src/pandroid/app/src/main/res/layout/controller_dpad.xml create mode 100644 src/pandroid/app/src/main/res/layout/controller_gamepad.xml create mode 100644 src/pandroid/app/src/main/res/layout/game_activity.xml create mode 100644 src/pandroid/app/src/main/res/values/styleable.xml diff --git a/.github/workflows/Android_Build.yml b/.github/workflows/Android_Build.yml new file mode 100644 index 000000000..c94140d70 --- /dev/null +++ b/.github/workflows/Android_Build.yml @@ -0,0 +1,89 @@ +name: Android Build + +on: + push: + branches: + - master + pull_request: + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + x64: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Fetch submodules + run: git submodule update --init --recursive + + - name: Setup Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.0 + with: + vulkan-query-version: latest + vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang + + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '17' + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 + + - name: Build + run: | + cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/x86_64/ + cd src/pandroid + ./gradlew assembleDebug + cd ../.. + + - name: Upload executable + uses: actions/upload-artifact@v2 + with: + name: Android APK (x86-64) + path: './src/pandroid/app/build/outputs/apk/debug/app-debug.apk' + + arm64: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Fetch submodules + run: git submodule update --init --recursive + + - name: Setup Vulkan SDK + uses: humbletim/setup-vulkan-sdk@v1.2.0 + with: + vulkan-query-version: latest + vulkan-use-cache: true + vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang + + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' # See 'Supported distributions' for available options + java-version: '17' + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DCMAKE_CXX_FLAGS="-march=armv8.1-a+crypto" + + - name: Build + run: | + cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/arm64-v8a/ + cd src/pandroid + ./gradlew assembleDebug + cd ../.. + + - name: Upload executable + uses: actions/upload-artifact@v2 + with: + name: Android APK (arm64) + path: './src/pandroid/app/build/outputs/apk/debug/app-debug.apk' + diff --git a/patch.patch b/patch.patch new file mode 100644 index 000000000..98cad32b7 --- /dev/null +++ b/patch.patch @@ -0,0 +1,118 @@ +diff --git a/src/host_shaders/opengl_display.frag b/src/host_shaders/opengl_display.frag +index 612671c8..1937f711 100644 +--- a/src/host_shaders/opengl_display.frag ++++ b/src/host_shaders/opengl_display.frag +@@ -1,4 +1,5 @@ +-#version 410 core ++#version 300 es ++precision mediump float; + in vec2 UV; + out vec4 FragColor; + +diff --git a/src/host_shaders/opengl_display.vert b/src/host_shaders/opengl_display.vert +index 990e2f80..6917c23c 100644 +--- a/src/host_shaders/opengl_display.vert ++++ b/src/host_shaders/opengl_display.vert +@@ -1,4 +1,4 @@ +-#version 410 core ++#version 300 es + out vec2 UV; + + void main() { +diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag +index f6fa6c55..b0850438 100644 +--- a/src/host_shaders/opengl_fragment_shader.frag ++++ b/src/host_shaders/opengl_fragment_shader.frag +@@ -1,4 +1,5 @@ +-#version 410 core ++#version 300 es ++precision mediump float; + + in vec3 v_tangent; + in vec3 v_normal; +@@ -27,7 +28,7 @@ uniform bool u_depthmapEnable; + uniform sampler2D u_tex0; + uniform sampler2D u_tex1; + uniform sampler2D u_tex2; +-uniform sampler1DArray u_tex_lighting_lut; ++// uniform sampler1DArray u_tex_lighting_lut; + + uniform uint u_picaRegs[0x200 - 0x48]; + +@@ -145,9 +146,15 @@ vec4 tevCalculateCombiner(int tev_id) { + #define RR_LUT 6u + + float lutLookup(uint lut, uint light, float value) { +- if (lut >= FR_LUT && lut <= RR_LUT) lut -= 1; +- if (lut == SP_LUT) lut = light + 8; +- return texture(u_tex_lighting_lut, vec2(value, lut)).r; ++ // if (lut >= FR_LUT && lut <= RR_LUT) lut -= 1; ++ // if (lut == SP_LUT) lut = light + 8; ++ // return texture(u_tex_lighting_lut, vec2(value, lut)).r; ++ return 0.0; ++} ++ ++uint bitfieldExtract(uint val, int off, int size) { ++ uint mask = uint((1 << size) - 1); ++ return uint(val >> off) & mask; + } + + vec3 regToColor(uint reg) { +diff --git a/src/host_shaders/opengl_vertex_shader.vert b/src/host_shaders/opengl_vertex_shader.vert +index a25d7a6d..5967ccd6 100644 +--- a/src/host_shaders/opengl_vertex_shader.vert ++++ b/src/host_shaders/opengl_vertex_shader.vert +@@ -1,4 +1,4 @@ +-#version 410 core ++#version 300 es + + layout(location = 0) in vec4 a_coords; + layout(location = 1) in vec4 a_quaternion; +@@ -20,7 +20,7 @@ out vec2 v_texcoord2; + flat out vec4 v_textureEnvColor[6]; + flat out vec4 v_textureEnvBufferColor; + +-out float gl_ClipDistance[2]; ++// out float gl_ClipDistance[2]; + + // TEV uniforms + uniform uint u_textureEnvColor[6]; +@@ -93,6 +93,6 @@ void main() { + ); + + // There's also another, always-on clipping plane based on vertex z +- gl_ClipDistance[0] = -a_coords.z; +- gl_ClipDistance[1] = dot(clipData, a_coords); ++ // gl_ClipDistance[0] = -a_coords.z; ++ // gl_ClipDistance[1] = dot(clipData, a_coords); + } +diff --git a/third_party/opengl/opengl.hpp b/third_party/opengl/opengl.hpp +index f368f573..5ead7f63 100644 +--- a/third_party/opengl/opengl.hpp ++++ b/third_party/opengl/opengl.hpp +@@ -520,21 +520,21 @@ namespace OpenGL { + static void enableBlend() { glEnable(GL_BLEND); } + static void disableBlend() { glDisable(GL_BLEND); } + static void enableLogicOp() { glEnable(GL_COLOR_LOGIC_OP); } +- static void disableLogicOp() { glDisable(GL_COLOR_LOGIC_OP); } ++ static void disableLogicOp() { /* glDisable(GL_COLOR_LOGIC_OP); */ } + static void enableDepth() { glEnable(GL_DEPTH_TEST); } + static void disableDepth() { glDisable(GL_DEPTH_TEST); } + static void enableStencil() { glEnable(GL_STENCIL_TEST); } + static void disableStencil() { glDisable(GL_STENCIL_TEST); } + +- static void enableClipPlane(GLuint index) { glEnable(GL_CLIP_DISTANCE0 + index); } +- static void disableClipPlane(GLuint index) { glDisable(GL_CLIP_DISTANCE0 + index); } ++ static void enableClipPlane(GLuint index) { /* glEnable(GL_CLIP_DISTANCE0 + index); */ } ++ static void disableClipPlane(GLuint index) { /* glDisable(GL_CLIP_DISTANCE0 + index); */ } + + static void setDepthFunc(DepthFunc func) { glDepthFunc(static_cast(func)); } + static void setColourMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { glColorMask(r, g, b, a); } + static void setDepthMask(GLboolean mask) { glDepthMask(mask); } + + // TODO: Add a proper enum for this +- static void setLogicOp(GLenum op) { glLogicOp(op); } ++ static void setLogicOp(GLenum op) { /* glLogicOp(op); */ } + + enum Primitives { + Triangle = GL_TRIANGLES, diff --git a/src/jni_driver.cpp b/src/jni_driver.cpp index 74ac0732e..5cd519e6d 100644 --- a/src/jni_driver.cpp +++ b/src/jni_driver.cpp @@ -27,6 +27,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_RunFram renderer->setFBO(fbo); renderer->resetStateManager(); emulator->runFrame(); + + emulator->getServiceManager().getHID().updateInputs(emulator->getTicks()); } extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_Finalize(JNIEnv* env, jobject obj) { @@ -43,4 +45,30 @@ extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_LoadRom __android_log_print(ANDROID_LOG_INFO, "AlberDriver", "Loading ROM %s", pathStr); romLoaded = emulator->loadROM(pathStr); env->ReleaseStringUTFChars(path, pathStr); -} \ No newline at end of file +} + + + +extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_TouchScreenDown(JNIEnv* env, jobject obj, jint x, jint y) { + emulator->getServiceManager().getHID().setTouchScreenPress((u16)x, (u16)y); +} + +extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_TouchScreenUp(JNIEnv* env, jobject obj) { + emulator->getServiceManager().getHID().releaseTouchScreen(); +} + + +extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_KeyUp(JNIEnv* env, jobject obj, jint keyCode) { + emulator->getServiceManager().getHID().releaseKey((u32)keyCode); +} + +extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_KeyDown(JNIEnv* env, jobject obj, jint keyCode) { + emulator->getServiceManager().getHID().pressKey((u32)keyCode); +} + +extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_SetCirclepadAxis(JNIEnv* env, jobject obj, jint x, jint y) { + emulator->getServiceManager().getHID().setCirclepadX((s16)x); + emulator->getServiceManager().getHID().setCirclepadY((s16)y); +} + + diff --git a/src/pandroid/app/src/main/AndroidManifest.xml b/src/pandroid/app/src/main/AndroidManifest.xml index 0effd35f1..01931edcf 100644 --- a/src/pandroid/app/src/main/AndroidManifest.xml +++ b/src/pandroid/app/src/main/AndroidManifest.xml @@ -2,6 +2,12 @@ + + + + - + + - - \ No newline at end of file + diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java index 1021e1d22..92d276a2d 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java @@ -12,6 +12,12 @@ public class AlberDriver { public static native void LoadRom(String path); public static native void Finalize(); + public static native void KeyDown(int code); + public static native void KeyUp(int code); + public static native void SetCirclepadAxis(int x, int y); + public static native void TouchScreenUp(); + public static native void TouchScreenDown(int x, int y); + static { System.loadLibrary("Alber"); } diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlSurfaceView.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlSurfaceView.java deleted file mode 100644 index 657b327c8..000000000 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlSurfaceView.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.panda3ds.pandroid; - -import android.app.Activity; -import android.content.Context; -import android.opengl.GLSurfaceView; -import android.util.DisplayMetrics; - -import com.panda3ds.pandroid.PandaGlRenderer; - -public class PandaGlSurfaceView extends GLSurfaceView { - final PandaGlRenderer renderer; - - public PandaGlSurfaceView(Context context) { - super(context); - setEGLContextClientVersion(3); - renderer = new PandaGlRenderer(); - setRenderer(renderer); - } -} \ No newline at end of file diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/BaseActivity.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/BaseActivity.java new file mode 100644 index 000000000..597d664bd --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/BaseActivity.java @@ -0,0 +1,6 @@ +package com.panda3ds.pandroid.app; + +import androidx.appcompat.app.AppCompatActivity; + +public class BaseActivity extends AppCompatActivity { +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java new file mode 100644 index 000000000..275797fbb --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java @@ -0,0 +1,56 @@ +package com.panda3ds.pandroid.app; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.FrameLayout; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.panda3ds.pandroid.AlberDriver; +import com.panda3ds.pandroid.R; +import com.panda3ds.pandroid.utils.Constants; +import com.panda3ds.pandroid.view.PandaGlSurfaceView; +import com.panda3ds.pandroid.view.PandaLayoutController; +import com.panda3ds.pandroid.view.controller.ControllerLayout; + +public class GameActivity extends BaseActivity { + private PandaGlSurfaceView pandaSurface; + private PandaLayoutController controllerLayout; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + if(!intent.hasExtra(Constants.EXTRA_PATH)){ + + setContentView(new FrameLayout(this)); + Toast.makeText(this, "INVALID ROM PATH", Toast.LENGTH_LONG).show(); + finish(); + return; + } + + pandaSurface = new PandaGlSurfaceView(this, intent.getStringExtra(Constants.EXTRA_PATH));; + + setContentView(R.layout.game_activity); + + ((FrameLayout)findViewById(R.id.panda_gl_frame)) + .addView(pandaSurface, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + controllerLayout = findViewById(R.id.controller_layout); + controllerLayout.initialize(); + + ((CheckBox)findViewById(R.id.hide_screen_controller)) + .setOnCheckedChangeListener((buttonView, isChecked) -> { + controllerLayout.setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE); + }); + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/MainActivity.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/MainActivity.java similarity index 50% rename from src/pandroid/app/src/main/java/com/panda3ds/pandroid/MainActivity.java rename to src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/MainActivity.java index b7b3726d5..b98bf7236 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/MainActivity.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/MainActivity.java @@ -1,24 +1,20 @@ -package com.panda3ds.pandroid; +package com.panda3ds.pandroid.app; -import androidx.appcompat.app.AppCompatActivity; import static android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION; import android.content.Intent; import android.os.Build; import android.os.Bundle; -import android.view.WindowInsets; -import android.view.View; import android.os.Environment; import android.widget.Toast; -import android.widget.FrameLayout; -import com.panda3ds.pandroid.PathUtils; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; -public class MainActivity extends AppCompatActivity { +import androidx.appcompat.app.AppCompatActivity; - PandaGlSurfaceView glView; +import com.panda3ds.pandroid.utils.Constants; +import com.panda3ds.pandroid.R; +import com.panda3ds.pandroid.utils.PathUtils; +public class MainActivity extends BaseActivity { private static final int PICK_3DS_ROM = 2; private void openFile() { @@ -31,22 +27,18 @@ private void openFile() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (!Environment.isExternalStorageManager()) { - Intent intent = new Intent(ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); - startActivity(intent); - } - - glView = new PandaGlSurfaceView(this); - setContentView(glView); - FloatingActionButton fab = new FloatingActionButton(this); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - openFile(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (!Environment.isExternalStorageManager()) { + Intent intent = new Intent(ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + startActivity(intent); } + } + + setContentView(R.layout.activity_main); + + findViewById(R.id.load_rom).setOnClickListener(v->{ + openFile(); }); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(200, 200); - addContentView(fab, params); } @Override @@ -55,13 +47,10 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { String path = PathUtils.getPath(getApplicationContext(), data.getData()); Toast.makeText(getApplicationContext(), "pandroid opening " + path, Toast.LENGTH_LONG).show(); - glView.queueEvent(new Runnable() { - @Override - public void run() { - AlberDriver.LoadRom(path); - } - }); + startActivity(new Intent(this, GameActivity.class) + .putExtra(Constants.EXTRA_PATH, path)); } + super.onActivityResult(requestCode, resultCode, data); } } } \ No newline at end of file diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/math/Vector2.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/math/Vector2.java new file mode 100644 index 000000000..8d4132032 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/math/Vector2.java @@ -0,0 +1,23 @@ +package com.panda3ds.pandroid.math; + +public class Vector2 { + public float x,y; + public Vector2(){ + this(0.0f); + } + public Vector2(float value){ + this(value,value); + } + + public Vector2(float x, float y){ + this.x = x; + this.y = y; + } + + public float distanceTo(Vector2 vec){ + return distance(x,y,vec.x, vec.y); + } + public static float distance(float x, float y, float x2, float y2){ + return (float) Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2)); + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/Constants.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/Constants.java new file mode 100644 index 000000000..c8ba68cd7 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/Constants.java @@ -0,0 +1,23 @@ +package com.panda3ds.pandroid.utils; + +public class Constants { + + public static final int INPUT_KEY_UP = 1 << 6; + public static final int INPUT_KEY_DOWN = 1 << 7; + public static final int INPUT_KEY_LEFT = 1 << 5; + public static final int INPUT_KEY_RIGHT = 1 << 4; + + public static final int INPUT_KEY_A = 1 << 0; + public static final int INPUT_KEY_B = 1 << 1; + public static final int INPUT_KEY_X = 1 << 10; + public static final int INPUT_KEY_Y = 1 << 11; + + public static final int INPUT_KEY_R = 1 << 8; + public static final int INPUT_KEY_L = 1 << 9; + + public static final int INPUT_KEY_START = 1 << 3; + public static final int INPUT_KEY_SELECT = 1 << 2; + + public static final String EXTRA_PATH = "path"; + public static final String LOG_TAG = "Alber"; +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PathUtils.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PathUtils.java similarity index 99% rename from src/pandroid/app/src/main/java/com/panda3ds/pandroid/PathUtils.java rename to src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PathUtils.java index 740223ebc..0a24603cc 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PathUtils.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PathUtils.java @@ -1,4 +1,4 @@ -package com.panda3ds.pandroid; +package com.panda3ds.pandroid.utils; import android.content.ContentUris; import android.content.Context; diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlRenderer.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java similarity index 57% rename from src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlRenderer.java rename to src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java index cb6f90dad..4adce5758 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/PandaGlRenderer.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java @@ -1,4 +1,4 @@ -package com.panda3ds.pandroid; +package com.panda3ds.pandroid.view; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -7,20 +7,22 @@ import android.content.res.Resources; import android.opengl.GLSurfaceView; -import android.util.DisplayMetrics; import android.util.Log; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; +import com.panda3ds.pandroid.AlberDriver; + +import java.util.ArrayList; public class PandaGlRenderer implements GLSurfaceView.Renderer { - int screenWidth, screenHeight; - int screenTexture; + + private final String romPath; + private int screenWidth, screenHeight; + private int screenTexture; public int screenFbo; - PandaGlRenderer() { + PandaGlRenderer(String romPath) { super(); + this.romPath = romPath; } @Override @@ -33,7 +35,7 @@ protected void finalize() throws Throwable { } super.finalize(); } - + public void onSurfaceCreated(GL10 unused, EGLConfig config) { Log.i("pandroid", glGetString(GL_EXTENSIONS)); Log.w("pandroid", glGetString(GL_VERSION)); @@ -59,6 +61,7 @@ public void onSurfaceCreated(GL10 unused, EGLConfig config) { glBindFramebuffer(GL_FRAMEBUFFER, 0); AlberDriver.Initialize(); + AlberDriver.LoadRom(romPath); } public void onDrawFrame(GL10 unused) { @@ -66,7 +69,36 @@ public void onDrawFrame(GL10 unused) { AlberDriver.RunFrame(screenFbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, screenFbo); - glBlitFramebuffer(0, 0, 400, 480, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); + if (screenWidth > screenHeight) { + int topDisplayWidth = (int) ((screenHeight / 240.0) * 400); + int topDisplayHeight = screenHeight; + + if (topDisplayWidth > (screenWidth*0.7)){ + topDisplayWidth = (int) (screenWidth * 0.7); + topDisplayHeight = (int) ((topDisplayWidth/400.0)*240); + } + + int bottomDisplayHeight = (int) (((screenWidth-topDisplayWidth)/320)*240); + + int topDisplayY = screenHeight-topDisplayHeight; + int bottomDisplayY = screenHeight-bottomDisplayHeight; + + glBlitFramebuffer(0, 240, + 400, 480, + 0, topDisplayY, + topDisplayWidth,topDisplayY+topDisplayHeight, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + + glBlitFramebuffer( + 40, 0, + 360, 240, + topDisplayWidth, bottomDisplayY, + screenWidth,bottomDisplayY+bottomDisplayHeight, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + } else { + int h = (int) ((screenWidth / 400.0) * 480); + glBlitFramebuffer(0, 0, 400, 480, 0, screenHeight - h, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } } } @@ -76,4 +108,4 @@ public void onSurfaceChanged(GL10 unused, int width, int height) { screenHeight = height; glDisable(GL_SCISSOR_TEST); } -} +} \ No newline at end of file diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlSurfaceView.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlSurfaceView.java new file mode 100644 index 000000000..358056575 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlSurfaceView.java @@ -0,0 +1,20 @@ +package com.panda3ds.pandroid.view; + +import android.content.Context; +import android.opengl.GLSurfaceView; + +public class PandaGlSurfaceView extends GLSurfaceView { + final PandaGlRenderer renderer; + + public PandaGlSurfaceView(Context context, String romPath) { + super(context); + setEGLContextClientVersion(3); + setDebugFlags(DEBUG_LOG_GL_CALLS); + renderer = new PandaGlRenderer(romPath); + setRenderer(renderer); + } + + public PandaGlRenderer getRenderer() { + return renderer; + } +} \ No newline at end of file diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaLayoutController.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaLayoutController.java new file mode 100644 index 000000000..63f328da0 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaLayoutController.java @@ -0,0 +1,62 @@ +package com.panda3ds.pandroid.view; + +import android.content.Context; +import android.util.AttributeSet; + +import com.panda3ds.pandroid.AlberDriver; +import com.panda3ds.pandroid.R; +import com.panda3ds.pandroid.utils.Constants; +import com.panda3ds.pandroid.view.controller.ControllerLayout; +import com.panda3ds.pandroid.view.controller.nodes.Button; +import com.panda3ds.pandroid.view.controller.nodes.Joystick; + +public class PandaLayoutController extends ControllerLayout { + public PandaLayoutController(Context context) { + super(context); + } + + public PandaLayoutController(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PandaLayoutController(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public PandaLayoutController(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void initialize(){ + int[] keyButtonList = { + R.id.button_a, Constants.INPUT_KEY_A, + R.id.button_b, Constants.INPUT_KEY_B, + R.id.button_y, Constants.INPUT_KEY_Y, + R.id.button_x, Constants.INPUT_KEY_X, + + R.id.button_left, Constants.INPUT_KEY_LEFT, + R.id.button_right, Constants.INPUT_KEY_RIGHT, + R.id.button_up, Constants.INPUT_KEY_UP, + R.id.button_down, Constants.INPUT_KEY_DOWN, + + R.id.button_start, Constants.INPUT_KEY_START, + R.id.button_select, Constants.INPUT_KEY_SELECT, + + R.id.button_l, Constants.INPUT_KEY_L, + R.id.button_r, Constants.INPUT_KEY_R + }; + + for (int i = 0; i < keyButtonList.length; i+=2){ + final int keyCode = keyButtonList[i+1]; + ((Button)findViewById(keyButtonList[i])).setStateListener((btn, pressed)->{ + if (pressed) AlberDriver.KeyDown(keyCode); + else AlberDriver.KeyUp(keyCode); + }); + } + + ((Joystick)findViewById(R.id.left_analog)) + .setJoystickListener((joystick, axisX, axisY) -> { + AlberDriver.SetCirclepadAxis((int)(axisX*0x9C), (int)(axisY*0x9C)*-1); + }); + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerLayout.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerLayout.java new file mode 100644 index 000000000..97c8eb120 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerLayout.java @@ -0,0 +1,146 @@ +package com.panda3ds.pandroid.view.controller; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RelativeLayout; + +import com.panda3ds.pandroid.math.Vector2; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ControllerLayout extends RelativeLayout { + + private final HashMap activeTouchEvents = new HashMap<>(); + private final ArrayList controllerNodes = new ArrayList<>(); + + public ControllerLayout(Context context) { + this(context,null); + } + + public ControllerLayout(Context context, AttributeSet attrs) { + this(context, attrs,0); + } + + public ControllerLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr,0 ); + } + + public ControllerLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void refreshChildren(){ + controllerNodes.clear(); + populateNodesArray(this, controllerNodes); + } + + private void populateNodesArray(ViewGroup group, ArrayList list){ + for (int i = 0; i < group.getChildCount(); i++){ + View view = group.getChildAt(i); + if(view instanceof ControllerNode){ + list.add((ControllerNode) view); + } else if (view instanceof ViewGroup) { + populateNodesArray((ViewGroup) view, list); + } + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int index = event.getActionIndex(); + + switch (event.getActionMasked()){ + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_POINTER_UP: { + int id = event.getPointerId(index); + processTouch(true, event.getX(index), event.getY(index), id); + }break; + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: { + int id = event.getPointerId(index); + processTouch(false, event.getX(index), event.getY(index), id); + }break; + case MotionEvent.ACTION_MOVE: + for (int id = 0; id < event.getPointerCount(); id++){ + processTouch(false, event.getX(id), event.getY(id), id); + } + break; + } + return true; + } + + private void processTouch(boolean up, float x, float y, int index){ + int[] globalPosition = new int[2]; + getLocationInWindow(globalPosition); + + int action = TouchEvent.ACTION_MOVE; + + if ((!activeTouchEvents.containsKey(index))){ + if (up)return; + ControllerNode node = null; + for (ControllerNode item: controllerNodes){ + Vector2 pos = item.getPosition(); + Vector2 size= item.getSize(); + + float cx = (pos.x - globalPosition[0]); + float cy = (pos.y - globalPosition[1]); + if( x > cx && x < cx+size.x && y > cy && y < cy+size.y){ + node = item; + break; + } + } + if (node != null){ + activeTouchEvents.put(index, node); + action = TouchEvent.ACTION_DOWN; + } else { + return; + } + } + + if (up) action = TouchEvent.ACTION_UP; + + ControllerNode node = activeTouchEvents.get(index); + Vector2 pos = node.getPosition(); + pos.x -= globalPosition[0]; + pos.y -= globalPosition[1]; + + x -= pos.x; + y -= pos.y; + + node.onTouch(new TouchEvent(x,y,action)); + + if(up){ + activeTouchEvents.remove(index); + } + } + + @Override + public void onViewAdded(View child) { + super.onViewAdded(child); + refreshChildren(); + } + + @Override + public void onViewRemoved(View child) { + super.onViewRemoved(child); + refreshChildren(); + } + + /*@TODO: Need replace that methods for prevent Android send events directly to children*/ + + @Override + public ArrayList getTouchables() { + return new ArrayList<>(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return true; + } + +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerNode.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerNode.java new file mode 100644 index 000000000..8b01fa97b --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/ControllerNode.java @@ -0,0 +1,24 @@ +package com.panda3ds.pandroid.view.controller; + + +import android.view.View; + +import androidx.annotation.NonNull; + +import com.panda3ds.pandroid.math.Vector2; + +public interface ControllerNode { + + @NonNull + default Vector2 getPosition(){ + View me = (View) this; + + int[] position = new int[2]; + me.getLocationInWindow(position); + return new Vector2(position[0], position[1]); + } + + @NonNull Vector2 getSize(); + + void onTouch(TouchEvent event); +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/TouchEvent.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/TouchEvent.java new file mode 100644 index 000000000..a804cda1d --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/TouchEvent.java @@ -0,0 +1,28 @@ +package com.panda3ds.pandroid.view.controller; + +public class TouchEvent { + public static final int ACTION_DOWN = 0; + public static final int ACTION_MOVE = 1; + public static final int ACTION_UP = 2; + + private final int action; + private final float x,y; + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public int getAction() { + return action; + } + + public TouchEvent(float x, float y, int action){ + this.x = x; + this.y = y; + this.action = action; + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/ButtonStateListener.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/ButtonStateListener.java new file mode 100644 index 000000000..a658cdc03 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/ButtonStateListener.java @@ -0,0 +1,7 @@ +package com.panda3ds.pandroid.view.controller.listeners; + +import com.panda3ds.pandroid.view.controller.nodes.Button; + +public interface ButtonStateListener { + void onButtonPressedChange(Button button, boolean pressed); +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/JoystickListener.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/JoystickListener.java new file mode 100644 index 000000000..538e2cd82 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/listeners/JoystickListener.java @@ -0,0 +1,7 @@ +package com.panda3ds.pandroid.view.controller.listeners; + +import com.panda3ds.pandroid.view.controller.nodes.Joystick; + +public interface JoystickListener { + void onJoystickAxisChange(Joystick joystick, float axisX, float axisY); +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/BasicControllerNode.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/BasicControllerNode.java new file mode 100644 index 000000000..90d5f5f7c --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/BasicControllerNode.java @@ -0,0 +1,24 @@ +package com.panda3ds.pandroid.view.controller.nodes; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatTextView; + +import com.panda3ds.pandroid.view.controller.ControllerNode; + +public abstract class BasicControllerNode extends AppCompatTextView implements ControllerNode { + public BasicControllerNode(@NonNull Context context) { + super(context); + } + + public BasicControllerNode(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public BasicControllerNode(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Button.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Button.java new file mode 100644 index 000000000..7e8d4e869 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Button.java @@ -0,0 +1,73 @@ +package com.panda3ds.pandroid.view.controller.nodes; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.Gravity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatTextView; + +import com.panda3ds.pandroid.math.Vector2; +import com.panda3ds.pandroid.view.controller.ControllerNode; +import com.panda3ds.pandroid.view.controller.TouchEvent; +import com.panda3ds.pandroid.view.controller.listeners.ButtonStateListener; + +public class Button extends BasicControllerNode { + private boolean pressed = false; + private int width,height; + + private ButtonStateListener stateListener; + + public Button(@NonNull Context context) { + super(context); + init(); + } + + public Button(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public Button(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + width = getWidth(); + height = getHeight(); + } + + private void init(){ + setTextAlignment(TEXT_ALIGNMENT_CENTER); + setGravity(Gravity.CENTER); + } + + public void setStateListener(ButtonStateListener stateListener) { + this.stateListener = stateListener; + } + + public boolean isPressed() { + return pressed; + } + + @NonNull + @Override + public Vector2 getSize() { + return new Vector2(width,height); + } + + @Override + public void onTouch(TouchEvent event) { + pressed = event.getAction() != TouchEvent.ACTION_UP; + setAlpha(pressed ? 0.2F : 1.0F); + if (stateListener != null){ + stateListener.onButtonPressedChange(this, pressed); + } + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Joystick.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Joystick.java new file mode 100644 index 000000000..757ec3cba --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/controller/nodes/Joystick.java @@ -0,0 +1,132 @@ +package com.panda3ds.pandroid.view.controller.nodes; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatTextView; + +import com.panda3ds.pandroid.math.Vector2; +import com.panda3ds.pandroid.view.controller.ControllerNode; +import com.panda3ds.pandroid.view.controller.TouchEvent; +import com.panda3ds.pandroid.view.controller.listeners.JoystickListener; + + +public class Joystick extends BasicControllerNode implements ControllerNode { + private float stick_x = 0; + private float stick_y = 0; + + private int size_width = 0; + private int size_height= 0; + + private JoystickListener joystickListener; + + public Joystick(Context context) { + this(context,null); + } + + public Joystick(Context context, AttributeSet attrs) { + this(context, attrs,0); + } + + public Joystick(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + paint.setColor(Color.RED); + invalidate(); + } + + private final Paint paint = new Paint(); + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + } + + @Override + public void onDrawForeground(Canvas canvas) { + size_width = getWidth(); + size_height = getHeight(); + + int analogIconSize = size_width-getPaddingLeft(); + + float middleIconSize = analogIconSize / 2.0F; + float middle = size_width / 2.0F; + + float maxDistance = (middle - middleIconSize) * 0.9F; + + float tx = maxDistance * stick_x; + float ty = maxDistance * stick_y; + + float radius = Vector2.distance(0.0F, 0.0F, Math.abs(tx), Math.abs(ty)); + radius = Math.min(maxDistance, radius); + + double deg = Math.atan2(ty,tx) * (180.0/Math.PI); + float rx = (float) (radius*Math.cos(Math.PI * 2 * deg/360.0)); + float ry = (float) (radius*Math.sin(Math.PI * 2 * deg/360.0)); + + stick_x = Math.max(-1.0f, Math.min(1.0f, stick_x)); + stick_y = Math.max(-1.0f, Math.min(1.0f, stick_y)); + + + float x = middle-middleIconSize+rx; + float y = middle-middleIconSize+ry; + + + Drawable foreground = getForeground(); + if (foreground != null){ + foreground.setBounds((int) x, (int) y, (int) (x+analogIconSize), (int) (y+analogIconSize)); + foreground.draw(canvas); + } else { + canvas.drawOval(x, y, x+analogIconSize,y+analogIconSize,paint); + } + } + + public Vector2 getAxis() { + return new Vector2( + Math.max(-1.0F, Math.min(1.0F, stick_x)), + Math.max(-1.0F, Math.min(1.0F, stick_y)) + ); + } + + public void setJoystickListener(JoystickListener joystickListener) { + this.joystickListener = joystickListener; + } + + @NonNull + @Override + public Vector2 getSize() { + return new Vector2(size_width,size_height); + } + + @Override + public void onTouch(TouchEvent event) { + + float middle = size_width/2.0F; + + float x = event.getX(); + float y = event.getY(); + + x = Math.max(0, Math.min(middle*2, x)); + y = Math.max(0, Math.min(middle*2, y)); + + stick_x = ((x-middle)/middle); + + stick_y = ((y-middle)/middle); + + if (event.getAction() == TouchEvent.ACTION_UP){ + stick_x = 0; + stick_y = 0; + } + + if (joystickListener != null){ + joystickListener.onJoystickAxisChange(this, stick_x, stick_y); + } + + invalidate(); + } +} diff --git a/src/pandroid/app/src/main/res/drawable/screen_gamepad_checkbox.xml b/src/pandroid/app/src/main/res/drawable/screen_gamepad_checkbox.xml new file mode 100644 index 000000000..8c61f4c88 --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/screen_gamepad_checkbox.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/drawable/screen_gamepad_hide.xml b/src/pandroid/app/src/main/res/drawable/screen_gamepad_hide.xml new file mode 100644 index 000000000..f22e8c9d6 --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/screen_gamepad_hide.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/pandroid/app/src/main/res/drawable/screen_gamepad_show.xml b/src/pandroid/app/src/main/res/drawable/screen_gamepad_show.xml new file mode 100644 index 000000000..133f2e70b --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/screen_gamepad_show.xml @@ -0,0 +1,5 @@ + + + diff --git a/src/pandroid/app/src/main/res/drawable/simple_analog_background.xml b/src/pandroid/app/src/main/res/drawable/simple_analog_background.xml new file mode 100644 index 000000000..81855e147 --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/simple_analog_background.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/drawable/simple_card_button.xml b/src/pandroid/app/src/main/res/drawable/simple_card_button.xml new file mode 100644 index 000000000..d58e9c4fb --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/simple_card_button.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/drawable/simple_card_button_left.xml b/src/pandroid/app/src/main/res/drawable/simple_card_button_left.xml new file mode 100644 index 000000000..baf1f2931 --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/simple_card_button_left.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/drawable/simple_card_button_right.xml b/src/pandroid/app/src/main/res/drawable/simple_card_button_right.xml new file mode 100644 index 000000000..2f69341c2 --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/simple_card_button_right.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/drawable/simple_circle_button.xml b/src/pandroid/app/src/main/res/drawable/simple_circle_button.xml new file mode 100644 index 000000000..15879540b --- /dev/null +++ b/src/pandroid/app/src/main/res/drawable/simple_circle_button.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/layout/activity_main.xml b/src/pandroid/app/src/main/res/layout/activity_main.xml index 17eab17ba..89a17ce98 100644 --- a/src/pandroid/app/src/main/res/layout/activity_main.xml +++ b/src/pandroid/app/src/main/res/layout/activity_main.xml @@ -1,18 +1,22 @@ - + tools:context=".app.MainActivity"> - + +