diff --git a/.gitattributes b/.gitattributes index 60957e6..605224a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,14 +2,23 @@ *.c text eol=lf *.cc text eol=lf *.cmake text eol=lf +*.gradle text eol=lf *.h text eol=lf -*.md text eol=lf +*.java text eol=lf *.json text eol=lf +*.md text eol=lf *.plist text eol=lf +*.pro text eol=lf +*.properties text eol=lf +*.xml text eol=lf *.yml text eol=lf .clang-format text eol=lf .editorconfig text eol=lf .gitattributes text eol=lf .gitignore text eol=lf +gradlew text eol=lf CMakeLists.txt text eol=lf LICENSE text eol=lf + +# Force CRLF +*.bat text eol=crlf diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index f12d1c5..aefe72a 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -38,6 +38,49 @@ jobs: - name: cppcheck run: cppcheck --std=c++17 src/ + android: + name: Android + + runs-on: ubuntu-latest + + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: temurin + java-version: 11 + cache: gradle + + - name: Cache cmake build + uses: actions/cache@v3 + with: + path: os/android/app/.cxx + key: android-cmake-v1 + + - name: Setup signing config + run: | + cd os/android + echo "$KEYSTORE_FILE_BASE64" | base64 --decode > debug.keystore + echo "$KEYSTORE_PROPERTIES_FILE_BASE64" | base64 --decode > debug-keystore.properties + env: + KEYSTORE_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_FILE_BASE64 }} + KEYSTORE_PROPERTIES_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_PROPERTIES_FILE_BASE64 }} + + - name: Build + run: | + cd os/android + ./gradlew assembleDebug + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: fallout2-ce-debug.apk + path: os/android/app/build/outputs/apk/debug/app-debug.apk + retention-days: 7 + linux: name: Linux (${{ matrix.arch }}) diff --git a/os/android/.gitignore b/os/android/.gitignore index cf7a227..f0f2a29 100644 --- a/os/android/.gitignore +++ b/os/android/.gitignore @@ -7,3 +7,7 @@ /.idea/navEditor.xml /.idea/workspace.xml /local.properties +/debug-keystore.properties +/debug.keystore +/release-keystore.properties +/release.keystore diff --git a/os/android/app/.gitignore b/os/android/app/.gitignore index 8268dac..3a72371 100644 --- a/os/android/app/.gitignore +++ b/os/android/app/.gitignore @@ -1,2 +1,5 @@ /.cxx /build + +# TODO: Cleanup root .gitignore +!/src/debug diff --git a/os/android/app/build.gradle b/os/android/app/build.gradle index 633ebdf..e5f4ff0 100644 --- a/os/android/app/build.gradle +++ b/os/android/app/build.gradle @@ -26,10 +26,49 @@ android { } } + signingConfigs { + // Override default debug signing config to make sure every CI runner + // uses the same key for painless updates. + def debugKeystorePropertiesFile = rootProject.file('debug-keystore.properties') + if (debugKeystorePropertiesFile.exists()) { + def debugKeystoreProperties = new Properties() + debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile)) + + debug { + storeFile rootProject.file(debugKeystoreProperties.getProperty('storeFile')) + storePassword debugKeystoreProperties.getProperty('storePassword') + keyAlias debugKeystoreProperties.getProperty('keyAlias') + keyPassword debugKeystoreProperties.getProperty('keyPassword') + } + } + + def releaseKeystoreProperties = new Properties() + def releaseKeystorePropertiesFile = rootProject.file('release-keystore.properties') + if (releaseKeystorePropertiesFile.exists()) { + releaseKeystoreProperties.load(new FileInputStream(releaseKeystorePropertiesFile)) + + release { + storeFile rootProject.file(releaseKeystoreProperties.getProperty('storeFile')) + storePassword releaseKeystoreProperties.getProperty('storePassword') + keyAlias releaseKeystoreProperties.getProperty('keyAlias') + keyPassword releaseKeystoreProperties.getProperty('keyPassword') + } + } + } + buildTypes { + debug { + // Prevents signing keys clashes between debug and release versions + // for painless development. + applicationIdSuffix '.debug' + } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + + // Release signing config is optional and might not be present in CI + // builds, hence `findByName`. + signingConfig signingConfigs.findByName('release') } } diff --git a/os/android/app/src/debug/res/values/strings.xml b/os/android/app/src/debug/res/values/strings.xml new file mode 100644 index 0000000..80ed3dc --- /dev/null +++ b/os/android/app/src/debug/res/values/strings.xml @@ -0,0 +1,3 @@ + + Fallout 2 (Debug) + diff --git a/os/android/app/src/main/res/values/strings.xml b/os/android/app/src/main/res/values/strings.xml index 6ea845c..03e515a 100644 --- a/os/android/app/src/main/res/values/strings.xml +++ b/os/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - Fallout 2 Community Edition + Fallout 2 diff --git a/src/core.cc b/src/core.cc index 274c141..284d34f 100644 --- a/src/core.cc +++ b/src/core.cc @@ -1272,6 +1272,11 @@ void _GNW95_process_message() // The data is accumulated in SDL itself and will be processed // in `_mouse_info`. break; + case SDL_FINGERDOWN: + case SDL_FINGERMOTION: + case SDL_FINGERUP: + handleTouchFingerEvent(&(e.tfinger)); + break; case SDL_KEYDOWN: case SDL_KEYUP: if (!keyboardIsDisabled()) { diff --git a/src/dinput.cc b/src/dinput.cc index b72987b..5272ccc 100644 --- a/src/dinput.cc +++ b/src/dinput.cc @@ -1,6 +1,18 @@ #include "dinput.h" -#include +static int gTouchMouseLastX = 0; +static int gTouchMouseLastY = 0; +static int gTouchMouseDeltaX = 0; +static int gTouchMouseDeltaY = 0; + +static int gTouchFingers = 0; +static unsigned int gTouchGestureLastTouchDownTimestamp = 0; +static unsigned int gTouchGestureLastTouchUpTimestamp = 0; +static int gTouchGestureTaps = 0; +static bool gTouchGestureHandled = false; + +extern int screenGetWidth(); +extern int screenGetHeight(); // 0x4E0400 bool directInputInit() @@ -47,9 +59,42 @@ bool mouseDeviceUnacquire() // 0x4E053C bool mouseDeviceGetData(MouseData* mouseState) { +#if __ANDROID__ + mouseState->x = gTouchMouseDeltaX; + mouseState->y = gTouchMouseDeltaY; + mouseState->buttons[0] = 0; + mouseState->buttons[1] = 0; + gTouchMouseDeltaX = 0; + gTouchMouseDeltaY = 0; + + if (gTouchFingers == 0) { + if (SDL_GetTicks() - gTouchGestureLastTouchUpTimestamp > 150) { + if (!gTouchGestureHandled) { + if (gTouchGestureTaps == 2) { + mouseState->buttons[0] = 1; + gTouchGestureHandled = true; + } else if (gTouchGestureTaps == 3) { + mouseState->buttons[1] = 1; + gTouchGestureHandled = true; + } + } + } + } else if (gTouchFingers == 1) { + if (SDL_GetTicks() - gTouchGestureLastTouchDownTimestamp > 150) { + if (gTouchGestureTaps == 1) { + mouseState->buttons[0] = 1; + gTouchGestureHandled = true; + } else if (gTouchGestureTaps == 2) { + mouseState->buttons[1] = 1; + gTouchGestureHandled = true; + } + } + } +#else Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y)); mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; +#endif return true; } @@ -100,3 +145,44 @@ bool keyboardDeviceInit() void keyboardDeviceFree() { } + +void handleTouchFingerEvent(SDL_TouchFingerEvent* event) +{ + int windowWidth = screenGetWidth(); + int windowHeight = screenGetHeight(); + + if (event->type == SDL_FINGERDOWN) { + gTouchFingers++; + + gTouchMouseLastX = (int)(event->x * windowWidth); + gTouchMouseLastY = (int)(event->y * windowHeight); + gTouchMouseDeltaX = 0; + gTouchMouseDeltaY = 0; + + if (event->timestamp - gTouchGestureLastTouchDownTimestamp > 250) { + gTouchGestureTaps = 0; + gTouchGestureHandled = false; + } + + gTouchGestureLastTouchDownTimestamp = event->timestamp; + } else if (event->type == SDL_FINGERMOTION) { + int prevX = gTouchMouseLastX; + int prevY = gTouchMouseLastY; + gTouchMouseLastX = (int)(event->x * windowWidth); + gTouchMouseLastY = (int)(event->y * windowHeight); + gTouchMouseDeltaX += gTouchMouseLastX - prevX; + gTouchMouseDeltaY += gTouchMouseLastY - prevY; + } else if (event->type == SDL_FINGERUP) { + gTouchFingers--; + + int prevX = gTouchMouseLastX; + int prevY = gTouchMouseLastY; + gTouchMouseLastX = (int)(event->x * windowWidth); + gTouchMouseLastY = (int)(event->y * windowHeight); + gTouchMouseDeltaX += gTouchMouseLastX - prevX; + gTouchMouseDeltaY += gTouchMouseLastY - prevY; + + gTouchGestureTaps++; + gTouchGestureLastTouchUpTimestamp = event->timestamp; + } +} diff --git a/src/dinput.h b/src/dinput.h index 113e998..7cb8144 100644 --- a/src/dinput.h +++ b/src/dinput.h @@ -1,6 +1,8 @@ #ifndef DINPUT_H #define DINPUT_H +#include + typedef struct MouseData { int x; int y; @@ -26,4 +28,6 @@ void mouseDeviceFree(); bool keyboardDeviceInit(); void keyboardDeviceFree(); +void handleTouchFingerEvent(SDL_TouchFingerEvent* event); + #endif /* DINPUT_H */ diff --git a/src/win32.cc b/src/win32.cc index 70c6747..8b4ab30 100644 --- a/src/win32.cc +++ b/src/win32.cc @@ -40,6 +40,8 @@ int main(int argc, char* argv[]) #endif #if __ANDROID__ + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); chdir(SDL_AndroidGetExternalStoragePath()); #endif